summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/area_2d.cpp123
-rw-r--r--scene/2d/area_2d.h28
-rw-r--r--scene/2d/audio_stream_player_2d.cpp8
-rw-r--r--scene/2d/camera_2d.cpp2
-rw-r--r--scene/2d/cpu_particles_2d.cpp2
-rw-r--r--scene/2d/gpu_particles_2d.cpp2
-rw-r--r--scene/2d/light_2d.cpp2
-rw-r--r--scene/2d/navigation_obstacle_2d.cpp65
-rw-r--r--scene/2d/navigation_obstacle_2d.h17
-rw-r--r--scene/2d/navigation_region_2d.cpp6
-rw-r--r--scene/2d/physics_body_2d.cpp55
-rw-r--r--scene/2d/physics_body_2d.h20
-rw-r--r--scene/2d/polygon_2d.cpp11
-rw-r--r--scene/2d/ray_cast_2d.cpp32
-rw-r--r--scene/2d/ray_cast_2d.h5
-rw-r--r--scene/2d/shape_cast_2d.cpp459
-rw-r--r--scene/2d/shape_cast_2d.h120
-rw-r--r--scene/2d/sprite_2d.cpp2
-rw-r--r--scene/2d/tile_map.cpp33
-rw-r--r--scene/3d/area_3d.cpp124
-rw-r--r--scene/3d/area_3d.h29
-rw-r--r--scene/3d/audio_stream_player_3d.cpp8
-rw-r--r--scene/3d/camera_3d.cpp6
-rw-r--r--scene/3d/cpu_particles_3d.cpp2
-rw-r--r--scene/3d/decal.cpp2
-rw-r--r--scene/3d/fog_volume.cpp1
-rw-r--r--scene/3d/gpu_particles_3d.cpp2
-rw-r--r--scene/3d/light_3d.cpp6
-rw-r--r--scene/3d/lightmap_gi.cpp6
-rw-r--r--scene/3d/mesh_instance_3d.cpp14
-rw-r--r--scene/3d/navigation_obstacle_3d.cpp65
-rw-r--r--scene/3d/navigation_obstacle_3d.h17
-rw-r--r--scene/3d/node_3d.cpp2
-rw-r--r--scene/3d/occluder_instance_3d.cpp4
-rw-r--r--scene/3d/physics_body_3d.cpp100
-rw-r--r--scene/3d/physics_body_3d.h40
-rw-r--r--scene/3d/ray_cast_3d.cpp32
-rw-r--r--scene/3d/ray_cast_3d.h9
-rw-r--r--scene/3d/reflection_probe.cpp2
-rw-r--r--scene/3d/skeleton_3d.cpp14
-rw-r--r--scene/3d/spring_arm_3d.cpp27
-rw-r--r--scene/3d/vehicle_body_3d.cpp8
-rw-r--r--scene/3d/voxel_gi.cpp6
-rw-r--r--scene/animation/animation_blend_space_1d.cpp12
-rw-r--r--scene/animation/animation_blend_space_2d.cpp27
-rw-r--r--scene/animation/animation_blend_tree.cpp87
-rw-r--r--scene/animation/animation_blend_tree.h26
-rw-r--r--scene/animation/animation_node_state_machine.cpp12
-rw-r--r--scene/animation/animation_player.cpp143
-rw-r--r--scene/animation/animation_player.h2
-rw-r--r--scene/animation/animation_tree.cpp279
-rw-r--r--scene/animation/animation_tree.h4
-rw-r--r--scene/animation/tween.cpp2
-rw-r--r--scene/gui/base_button.cpp6
-rw-r--r--scene/gui/base_button.h6
-rw-r--r--scene/gui/box_container.cpp2
-rw-r--r--scene/gui/code_edit.cpp18
-rw-r--r--scene/gui/color_picker.cpp16
-rw-r--r--scene/gui/dialogs.cpp2
-rw-r--r--scene/gui/file_dialog.cpp6
-rw-r--r--scene/gui/gradient_edit.cpp112
-rw-r--r--scene/gui/gradient_edit.h7
-rw-r--r--scene/gui/graph_edit.cpp73
-rw-r--r--scene/gui/graph_edit.h5
-rw-r--r--scene/gui/graph_node.cpp4
-rw-r--r--scene/gui/item_list.cpp21
-rw-r--r--scene/gui/label.cpp10
-rw-r--r--scene/gui/line_edit.cpp61
-rw-r--r--scene/gui/line_edit.h6
-rw-r--r--scene/gui/menu_button.cpp74
-rw-r--r--scene/gui/menu_button.h9
-rw-r--r--scene/gui/option_button.cpp2
-rw-r--r--scene/gui/popup.cpp4
-rw-r--r--scene/gui/popup_menu.cpp263
-rw-r--r--scene/gui/popup_menu.h30
-rw-r--r--scene/gui/rich_text_label.cpp94
-rw-r--r--scene/gui/rich_text_label.h2
-rw-r--r--scene/gui/scroll_bar.cpp8
-rw-r--r--scene/gui/scroll_container.cpp10
-rw-r--r--scene/gui/slider.cpp6
-rw-r--r--scene/gui/spin_box.cpp14
-rw-r--r--scene/gui/split_container.cpp2
-rw-r--r--scene/gui/tab_bar.cpp10
-rw-r--r--scene/gui/tab_container.cpp2
-rw-r--r--scene/gui/text_edit.cpp50
-rw-r--r--scene/gui/text_edit.h2
-rw-r--r--scene/gui/tree.cpp62
-rw-r--r--scene/gui/tree.h2
-rw-r--r--scene/main/canvas_layer.cpp2
-rw-r--r--scene/main/node.cpp66
-rw-r--r--scene/main/node.h8
-rw-r--r--scene/main/resource_preloader.cpp2
-rw-r--r--scene/main/viewport.cpp87
-rw-r--r--scene/main/viewport.h4
-rw-r--r--scene/main/window.cpp2
-rw-r--r--scene/property_utils.cpp188
-rw-r--r--scene/property_utils.h51
-rw-r--r--scene/register_scene_types.cpp6
-rw-r--r--scene/resources/animation.cpp697
-rw-r--r--scene/resources/animation.h24
-rw-r--r--scene/resources/audio_stream_sample.cpp8
-rw-r--r--scene/resources/audio_stream_sample.h2
-rw-r--r--scene/resources/bit_map.cpp2
-rw-r--r--scene/resources/camera_effects.cpp2
-rw-r--r--scene/resources/concave_polygon_shape_3d.cpp2
-rw-r--r--scene/resources/curve.cpp6
-rw-r--r--scene/resources/default_theme/default_theme.cpp2
-rw-r--r--scene/resources/environment.cpp20
-rw-r--r--scene/resources/font.cpp60
-rw-r--r--scene/resources/font.h9
-rw-r--r--scene/resources/gradient.cpp30
-rw-r--r--scene/resources/gradient.h57
-rw-r--r--scene/resources/importer_mesh.cpp2
-rw-r--r--scene/resources/material.cpp12
-rw-r--r--scene/resources/mesh.cpp4
-rw-r--r--scene/resources/mesh_data_tool.cpp2
-rw-r--r--scene/resources/navigation_mesh.cpp4
-rw-r--r--scene/resources/packed_scene.cpp218
-rw-r--r--scene/resources/packed_scene.h16
-rw-r--r--scene/resources/particles_material.cpp2
-rw-r--r--scene/resources/polygon_path_finder.cpp2
-rw-r--r--scene/resources/resource_format_text.cpp6
-rw-r--r--scene/resources/shader.cpp36
-rw-r--r--scene/resources/shader.h6
-rw-r--r--scene/resources/skeleton_modification_2d_jiggle.cpp8
-rw-r--r--scene/resources/skeleton_modification_3d_jiggle.cpp8
-rw-r--r--scene/resources/skin.cpp2
-rw-r--r--scene/resources/sprite_frames.cpp2
-rw-r--r--scene/resources/style_box.cpp2
-rw-r--r--scene/resources/text_paragraph.cpp14
-rw-r--r--scene/resources/text_paragraph.h3
-rw-r--r--scene/resources/texture.cpp50
-rw-r--r--scene/resources/texture.h11
-rw-r--r--scene/resources/tile_set.cpp298
-rw-r--r--scene/resources/tile_set.h44
-rw-r--r--scene/resources/visual_shader.cpp66
-rw-r--r--scene/resources/visual_shader.h9
-rw-r--r--scene/resources/visual_shader_nodes.cpp17
-rw-r--r--scene/resources/visual_shader_particle_nodes.cpp361
-rw-r--r--scene/resources/visual_shader_particle_nodes.h56
-rw-r--r--scene/resources/world_3d.cpp4
141 files changed, 4294 insertions, 1421 deletions
diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp
index fff9c47d4d..8db1491953 100644
--- a/scene/2d/area_2d.cpp
+++ b/scene/2d/area_2d.cpp
@@ -33,13 +33,13 @@
#include "scene/scene_string_names.h"
#include "servers/audio_server.h"
-void Area2D::set_space_override_mode(SpaceOverride p_mode) {
- space_override = p_mode;
- PhysicsServer2D::get_singleton()->area_set_space_override_mode(get_rid(), PhysicsServer2D::AreaSpaceOverrideMode(p_mode));
+void Area2D::set_gravity_space_override_mode(SpaceOverride p_mode) {
+ gravity_space_override = p_mode;
+ PhysicsServer2D::get_singleton()->area_set_param(get_rid(), PhysicsServer2D::AREA_PARAM_GRAVITY_OVERRIDE_MODE, p_mode);
}
-Area2D::SpaceOverride Area2D::get_space_override_mode() const {
- return space_override;
+Area2D::SpaceOverride Area2D::get_gravity_space_override_mode() const {
+ return gravity_space_override;
}
void Area2D::set_gravity_is_point(bool p_enabled) {
@@ -51,21 +51,30 @@ bool Area2D::is_gravity_a_point() const {
return gravity_is_point;
}
-void Area2D::set_gravity_distance_scale(real_t p_scale) {
+void Area2D::set_gravity_point_distance_scale(real_t p_scale) {
gravity_distance_scale = p_scale;
PhysicsServer2D::get_singleton()->area_set_param(get_rid(), PhysicsServer2D::AREA_PARAM_GRAVITY_DISTANCE_SCALE, p_scale);
}
-real_t Area2D::get_gravity_distance_scale() const {
+real_t Area2D::get_gravity_point_distance_scale() const {
return gravity_distance_scale;
}
-void Area2D::set_gravity_vector(const Vector2 &p_vec) {
- gravity_vec = p_vec;
- PhysicsServer2D::get_singleton()->area_set_param(get_rid(), PhysicsServer2D::AREA_PARAM_GRAVITY_VECTOR, p_vec);
+void Area2D::set_gravity_point_center(const Vector2 &p_center) {
+ gravity_vec = p_center;
+ PhysicsServer2D::get_singleton()->area_set_param(get_rid(), PhysicsServer2D::AREA_PARAM_GRAVITY_VECTOR, p_center);
}
-Vector2 Area2D::get_gravity_vector() const {
+const Vector2 &Area2D::get_gravity_point_center() const {
+ return gravity_vec;
+}
+
+void Area2D::set_gravity_direction(const Vector2 &p_direction) {
+ gravity_vec = p_direction;
+ PhysicsServer2D::get_singleton()->area_set_param(get_rid(), PhysicsServer2D::AREA_PARAM_GRAVITY_VECTOR, p_direction);
+}
+
+const Vector2 &Area2D::get_gravity_direction() const {
return gravity_vec;
}
@@ -78,6 +87,24 @@ real_t Area2D::get_gravity() const {
return gravity;
}
+void Area2D::set_linear_damp_space_override_mode(SpaceOverride p_mode) {
+ linear_damp_space_override = p_mode;
+ PhysicsServer2D::get_singleton()->area_set_param(get_rid(), PhysicsServer2D::AREA_PARAM_LINEAR_DAMP_OVERRIDE_MODE, p_mode);
+}
+
+Area2D::SpaceOverride Area2D::get_linear_damp_space_override_mode() const {
+ return linear_damp_space_override;
+}
+
+void Area2D::set_angular_damp_space_override_mode(SpaceOverride p_mode) {
+ angular_damp_space_override = p_mode;
+ PhysicsServer2D::get_singleton()->area_set_param(get_rid(), PhysicsServer2D::AREA_PARAM_ANGULAR_DAMP_OVERRIDE_MODE, p_mode);
+}
+
+Area2D::SpaceOverride Area2D::get_angular_damp_space_override_mode() const {
+ return angular_damp_space_override;
+}
+
void Area2D::set_linear_damp(real_t p_linear_damp) {
linear_damp = p_linear_damp;
PhysicsServer2D::get_singleton()->area_set_param(get_rid(), PhysicsServer2D::AREA_PARAM_LINEAR_DAMP, p_linear_damp);
@@ -369,12 +396,11 @@ void Area2D::set_monitoring(bool p_enable) {
monitoring = p_enable;
if (monitoring) {
- PhysicsServer2D::get_singleton()->area_set_monitor_callback(get_rid(), this, SceneStringNames::get_singleton()->_body_inout);
- PhysicsServer2D::get_singleton()->area_set_area_monitor_callback(get_rid(), this, SceneStringNames::get_singleton()->_area_inout);
-
+ PhysicsServer2D::get_singleton()->area_set_monitor_callback(get_rid(), callable_mp(this, &Area2D::_body_inout));
+ PhysicsServer2D::get_singleton()->area_set_area_monitor_callback(get_rid(), callable_mp(this, &Area2D::_area_inout));
} else {
- PhysicsServer2D::get_singleton()->area_set_monitor_callback(get_rid(), nullptr, StringName());
- PhysicsServer2D::get_singleton()->area_set_area_monitor_callback(get_rid(), nullptr, StringName());
+ PhysicsServer2D::get_singleton()->area_set_monitor_callback(get_rid(), Callable());
+ PhysicsServer2D::get_singleton()->area_set_area_monitor_callback(get_rid(), Callable());
_clear_monitoring();
}
}
@@ -484,25 +510,56 @@ void Area2D::_validate_property(PropertyInfo &property) const {
}
property.hint_string = options;
+ } else if (property.name.begins_with("gravity") && property.name != "gravity_space_override") {
+ if (gravity_space_override == SPACE_OVERRIDE_DISABLED) {
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
+ } else {
+ if (gravity_is_point) {
+ if (property.name == "gravity_direction") {
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
+ }
+ } else {
+ if (property.name.begins_with("gravity_point_")) {
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
+ }
+ }
+ }
+ } else if (property.name.begins_with("linear_damp") && property.name != "linear_damp_space_override") {
+ if (linear_damp_space_override == SPACE_OVERRIDE_DISABLED) {
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
+ }
+ } else if (property.name.begins_with("angular_damp") && property.name != "angular_damp_space_override") {
+ if (angular_damp_space_override == SPACE_OVERRIDE_DISABLED) {
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
+ }
}
}
void Area2D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_space_override_mode", "space_override_mode"), &Area2D::set_space_override_mode);
- ClassDB::bind_method(D_METHOD("get_space_override_mode"), &Area2D::get_space_override_mode);
+ ClassDB::bind_method(D_METHOD("set_gravity_space_override_mode", "space_override_mode"), &Area2D::set_gravity_space_override_mode);
+ ClassDB::bind_method(D_METHOD("get_gravity_space_override_mode"), &Area2D::get_gravity_space_override_mode);
ClassDB::bind_method(D_METHOD("set_gravity_is_point", "enable"), &Area2D::set_gravity_is_point);
ClassDB::bind_method(D_METHOD("is_gravity_a_point"), &Area2D::is_gravity_a_point);
- ClassDB::bind_method(D_METHOD("set_gravity_distance_scale", "distance_scale"), &Area2D::set_gravity_distance_scale);
- ClassDB::bind_method(D_METHOD("get_gravity_distance_scale"), &Area2D::get_gravity_distance_scale);
+ ClassDB::bind_method(D_METHOD("set_gravity_point_distance_scale", "distance_scale"), &Area2D::set_gravity_point_distance_scale);
+ ClassDB::bind_method(D_METHOD("get_gravity_point_distance_scale"), &Area2D::get_gravity_point_distance_scale);
- ClassDB::bind_method(D_METHOD("set_gravity_vector", "vector"), &Area2D::set_gravity_vector);
- ClassDB::bind_method(D_METHOD("get_gravity_vector"), &Area2D::get_gravity_vector);
+ ClassDB::bind_method(D_METHOD("set_gravity_point_center", "center"), &Area2D::set_gravity_point_center);
+ ClassDB::bind_method(D_METHOD("get_gravity_point_center"), &Area2D::get_gravity_point_center);
+
+ ClassDB::bind_method(D_METHOD("set_gravity_direction", "direction"), &Area2D::set_gravity_direction);
+ ClassDB::bind_method(D_METHOD("get_gravity_direction"), &Area2D::get_gravity_direction);
ClassDB::bind_method(D_METHOD("set_gravity", "gravity"), &Area2D::set_gravity);
ClassDB::bind_method(D_METHOD("get_gravity"), &Area2D::get_gravity);
+ ClassDB::bind_method(D_METHOD("set_linear_damp_space_override_mode", "space_override_mode"), &Area2D::set_linear_damp_space_override_mode);
+ ClassDB::bind_method(D_METHOD("get_linear_damp_space_override_mode"), &Area2D::get_linear_damp_space_override_mode);
+
+ ClassDB::bind_method(D_METHOD("set_angular_damp_space_override_mode", "space_override_mode"), &Area2D::set_angular_damp_space_override_mode);
+ ClassDB::bind_method(D_METHOD("get_angular_damp_space_override_mode"), &Area2D::get_angular_damp_space_override_mode);
+
ClassDB::bind_method(D_METHOD("set_linear_damp", "linear_damp"), &Area2D::set_linear_damp);
ClassDB::bind_method(D_METHOD("get_linear_damp"), &Area2D::get_linear_damp);
@@ -530,9 +587,6 @@ void Area2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_audio_bus_override", "enable"), &Area2D::set_audio_bus_override);
ClassDB::bind_method(D_METHOD("is_overriding_audio_bus"), &Area2D::is_overriding_audio_bus);
- ClassDB::bind_method(D_METHOD("_body_inout"), &Area2D::_body_inout);
- ClassDB::bind_method(D_METHOD("_area_inout"), &Area2D::_area_inout);
-
ADD_SIGNAL(MethodInfo("body_shape_entered", PropertyInfo(Variant::RID, "body_rid"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node2D"), PropertyInfo(Variant::INT, "body_shape_index"), PropertyInfo(Variant::INT, "local_shape_index")));
ADD_SIGNAL(MethodInfo("body_shape_exited", PropertyInfo(Variant::RID, "body_rid"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node2D"), PropertyInfo(Variant::INT, "body_shape_index"), PropertyInfo(Variant::INT, "local_shape_index")));
ADD_SIGNAL(MethodInfo("body_entered", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node2D")));
@@ -547,13 +601,20 @@ void Area2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "monitorable"), "set_monitorable", "is_monitorable");
ADD_PROPERTY(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,128,1"), "set_priority", "get_priority");
- ADD_GROUP("Physics Overrides", "");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "space_override", PROPERTY_HINT_ENUM, "Disabled,Combine,Combine-Replace,Replace,Replace-Combine"), "set_space_override_mode", "get_space_override_mode");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gravity_point"), "set_gravity_is_point", "is_gravity_a_point");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_distance_scale", PROPERTY_HINT_RANGE, "0,1024,0.001,or_greater,exp"), "set_gravity_distance_scale", "get_gravity_distance_scale");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "gravity_vec"), "set_gravity_vector", "get_gravity_vector");
+ ADD_GROUP("Gravity", "gravity_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "gravity_space_override", PROPERTY_HINT_ENUM, "Disabled,Combine,Combine-Replace,Replace,Replace-Combine", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_gravity_space_override_mode", "get_gravity_space_override_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gravity_point", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_gravity_is_point", "is_gravity_a_point");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_point_distance_scale", PROPERTY_HINT_RANGE, "0,1024,0.001,or_greater,exp"), "set_gravity_point_distance_scale", "get_gravity_point_distance_scale");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "gravity_point_center"), "set_gravity_point_center", "get_gravity_point_center");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "gravity_direction"), "set_gravity_direction", "get_gravity_direction");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity", PROPERTY_HINT_RANGE, "-4096,4096,0.001,or_lesser,or_greater"), "set_gravity", "get_gravity");
+
+ ADD_GROUP("Linear Damp", "linear_damp_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "linear_damp_space_override", PROPERTY_HINT_ENUM, "Disabled,Combine,Combine-Replace,Replace,Replace-Combine", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_linear_damp_space_override_mode", "get_linear_damp_space_override_mode");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "linear_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_linear_damp", "get_linear_damp");
+
+ ADD_GROUP("Angular Damp", "angular_damp_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "angular_damp_space_override", PROPERTY_HINT_ENUM, "Disabled,Combine,Combine-Replace,Replace,Replace-Combine", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_angular_damp_space_override_mode", "get_angular_damp_space_override_mode");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_angular_damp", "get_angular_damp");
ADD_GROUP("Audio Bus", "audio_bus_");
@@ -570,7 +631,7 @@ void Area2D::_bind_methods() {
Area2D::Area2D() :
CollisionObject2D(PhysicsServer2D::get_singleton()->area_create(), true) {
set_gravity(980);
- set_gravity_vector(Vector2(0, 1));
+ set_gravity_direction(Vector2(0, 1));
set_monitoring(true);
set_monitorable(true);
}
diff --git a/scene/2d/area_2d.h b/scene/2d/area_2d.h
index 2c29e4660d..98ba270a61 100644
--- a/scene/2d/area_2d.h
+++ b/scene/2d/area_2d.h
@@ -47,14 +47,19 @@ public:
};
private:
- SpaceOverride space_override = SPACE_OVERRIDE_DISABLED;
+ SpaceOverride gravity_space_override = SPACE_OVERRIDE_DISABLED;
Vector2 gravity_vec;
real_t gravity;
bool gravity_is_point = false;
real_t gravity_distance_scale = 0.0;
+
+ SpaceOverride linear_damp_space_override = SPACE_OVERRIDE_DISABLED;
+ SpaceOverride angular_damp_space_override = SPACE_OVERRIDE_DISABLED;
real_t linear_damp = 0.1;
real_t angular_damp = 1.0;
+
int priority = 0;
+
bool monitoring = false;
bool monitorable = false;
bool locked = false;
@@ -133,21 +138,30 @@ protected:
void _validate_property(PropertyInfo &property) const override;
public:
- void set_space_override_mode(SpaceOverride p_mode);
- SpaceOverride get_space_override_mode() const;
+ void set_gravity_space_override_mode(SpaceOverride p_mode);
+ SpaceOverride get_gravity_space_override_mode() const;
void set_gravity_is_point(bool p_enabled);
bool is_gravity_a_point() const;
- void set_gravity_distance_scale(real_t p_scale);
- real_t get_gravity_distance_scale() const;
+ void set_gravity_point_distance_scale(real_t p_scale);
+ real_t get_gravity_point_distance_scale() const;
- void set_gravity_vector(const Vector2 &p_vec);
- Vector2 get_gravity_vector() const;
+ void set_gravity_point_center(const Vector2 &p_center);
+ const Vector2 &get_gravity_point_center() const;
+
+ void set_gravity_direction(const Vector2 &p_direction);
+ const Vector2 &get_gravity_direction() const;
void set_gravity(real_t p_gravity);
real_t get_gravity() const;
+ void set_linear_damp_space_override_mode(SpaceOverride p_mode);
+ SpaceOverride get_linear_damp_space_override_mode() const;
+
+ void set_angular_damp_space_override_mode(SpaceOverride p_mode);
+ SpaceOverride get_angular_damp_space_override_mode() const;
+
void set_linear_damp(real_t p_linear_damp);
real_t get_linear_damp() const;
diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp
index f73d52152e..24da2ce9ce 100644
--- a/scene/2d/audio_stream_player_2d.cpp
+++ b/scene/2d/audio_stream_player_2d.cpp
@@ -112,7 +112,13 @@ StringName AudioStreamPlayer2D::_get_actual_bus() {
PhysicsDirectSpaceState2D *space_state = PhysicsServer2D::get_singleton()->space_get_direct_state(world_2d->get_space());
PhysicsDirectSpaceState2D::ShapeResult sr[MAX_INTERSECT_AREAS];
- int areas = space_state->intersect_point(global_pos, sr, MAX_INTERSECT_AREAS, Set<RID>(), area_mask, false, true);
+ PhysicsDirectSpaceState2D::PointParameters point_params;
+ point_params.position = global_pos;
+ point_params.collision_mask = area_mask;
+ point_params.collide_with_bodies = false;
+ point_params.collide_with_areas = true;
+
+ int areas = space_state->intersect_point(point_params, sr, MAX_INTERSECT_AREAS);
for (int i = 0; i < areas; i++) {
Area2D *area2d = Object::cast_to<Area2D>(sr[i].collider);
diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp
index 8195d98f55..b4ee85120e 100644
--- a/scene/2d/camera_2d.cpp
+++ b/scene/2d/camera_2d.cpp
@@ -660,7 +660,7 @@ bool Camera2D::is_margin_drawing_enabled() const {
void Camera2D::_validate_property(PropertyInfo &property) const {
if (!smoothing_enabled && property.name == "smoothing_speed") {
- property.usage = PROPERTY_USAGE_NOEDITOR;
+ property.usage = PROPERTY_USAGE_NO_EDITOR;
}
}
diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp
index bf26ec1f20..80c17b6e88 100644
--- a/scene/2d/cpu_particles_2d.cpp
+++ b/scene/2d/cpu_particles_2d.cpp
@@ -1168,7 +1168,7 @@ void CPUParticles2D::convert_from_particles(Node *p_particles) {
set_color(material->get_color());
- Ref<GradientTexture> gt = material->get_color_ramp();
+ Ref<GradientTexture1D> gt = material->get_color_ramp();
if (gt.is_valid()) {
set_color_ramp(gt->get_gradient());
}
diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp
index 6950fefdbe..f1f4d1b769 100644
--- a/scene/2d/gpu_particles_2d.cpp
+++ b/scene/2d/gpu_particles_2d.cpp
@@ -284,7 +284,7 @@ TypedArray<String> GPUParticles2D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (RenderingServer::get_singleton()->is_low_end()) {
- warnings.push_back(TTR("GPU-based particles are not supported by the GLES2 video driver.\nUse the CPUParticles2D node instead. You can use the \"Convert to CPUParticles2D\" option for this purpose."));
+ warnings.push_back(TTR("GPU-based particles are not supported by the OpenGL video driver.\nUse the CPUParticles2D node instead. You can use the \"Convert to CPUParticles2D\" option for this purpose."));
}
if (process_material.is_null()) {
diff --git a/scene/2d/light_2d.cpp b/scene/2d/light_2d.cpp
index 1853b3428c..66c0f979ae 100644
--- a/scene/2d/light_2d.cpp
+++ b/scene/2d/light_2d.cpp
@@ -221,7 +221,7 @@ real_t Light2D::get_shadow_smooth() const {
void Light2D::_validate_property(PropertyInfo &property) const {
if (!shadow && (property.name == "shadow_color" || property.name == "shadow_filter" || property.name == "shadow_filter_smooth" || property.name == "shadow_item_cull_mask")) {
- property.usage = PROPERTY_USAGE_NOEDITOR;
+ property.usage = PROPERTY_USAGE_NO_EDITOR;
}
}
diff --git a/scene/2d/navigation_obstacle_2d.cpp b/scene/2d/navigation_obstacle_2d.cpp
index 0a105826c0..8802a1098a 100644
--- a/scene/2d/navigation_obstacle_2d.cpp
+++ b/scene/2d/navigation_obstacle_2d.cpp
@@ -34,19 +34,41 @@
#include "servers/navigation_server_2d.h"
void NavigationObstacle2D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_estimate_radius", "estimate_radius"), &NavigationObstacle2D::set_estimate_radius);
+ ClassDB::bind_method(D_METHOD("is_radius_estimated"), &NavigationObstacle2D::is_radius_estimated);
+ ClassDB::bind_method(D_METHOD("set_radius", "radius"), &NavigationObstacle2D::set_radius);
+ ClassDB::bind_method(D_METHOD("get_radius"), &NavigationObstacle2D::get_radius);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "estimate_radius"), "set_estimate_radius", "is_radius_estimated");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.01,500,0.01"), "set_radius", "get_radius");
+}
+
+void NavigationObstacle2D::_validate_property(PropertyInfo &p_property) const {
+ if (p_property.name == "radius") {
+ if (estimate_radius) {
+ p_property.usage = PROPERTY_USAGE_NO_EDITOR;
+ }
+ }
}
void NavigationObstacle2D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_READY: {
+ initialize_agent();
+ parent_node2d = Object::cast_to<Node2D>(get_parent());
+ if (parent_node2d != nullptr) {
+ // place agent on navigation map first or else the RVO agent callback creation fails silently later
+ NavigationServer2D::get_singleton()->agent_set_map(get_rid(), parent_node2d->get_world_2d()->get_navigation_map());
+ }
set_physics_process_internal(true);
} break;
case NOTIFICATION_EXIT_TREE: {
+ parent_node2d = nullptr;
set_physics_process_internal(false);
} break;
case NOTIFICATION_PARENTED: {
parent_node2d = Object::cast_to<Node2D>(get_parent());
- update_agent_shape();
+ reevaluate_agent_radius();
} break;
case NOTIFICATION_UNPARENTED: {
parent_node2d = nullptr;
@@ -78,7 +100,22 @@ TypedArray<String> NavigationObstacle2D::get_configuration_warnings() const {
return warnings;
}
-void NavigationObstacle2D::update_agent_shape() {
+void NavigationObstacle2D::initialize_agent() {
+ NavigationServer2D::get_singleton()->agent_set_neighbor_dist(agent, 0.0);
+ NavigationServer2D::get_singleton()->agent_set_max_neighbors(agent, 0);
+ NavigationServer2D::get_singleton()->agent_set_time_horizon(agent, 0.0);
+ NavigationServer2D::get_singleton()->agent_set_max_speed(agent, 0.0);
+}
+
+void NavigationObstacle2D::reevaluate_agent_radius() {
+ if (!estimate_radius) {
+ NavigationServer2D::get_singleton()->agent_set_radius(agent, radius);
+ } else if (parent_node2d) {
+ NavigationServer2D::get_singleton()->agent_set_radius(agent, estimate_agent_radius());
+ }
+}
+
+real_t NavigationObstacle2D::estimate_agent_radius() const {
if (parent_node2d) {
// Estimate the radius of this physics body
real_t radius = 0.0;
@@ -101,15 +138,21 @@ void NavigationObstacle2D::update_agent_shape() {
Vector2 s = parent_node2d->get_global_scale();
radius *= MAX(s.x, s.y);
- if (radius == 0.0) {
- radius = 1.0; // Never a 0 radius
+ if (radius > 0.0) {
+ return radius;
}
-
- // Initialize the Agent as an object
- NavigationServer2D::get_singleton()->agent_set_neighbor_dist(agent, 0.0);
- NavigationServer2D::get_singleton()->agent_set_max_neighbors(agent, 0);
- NavigationServer2D::get_singleton()->agent_set_time_horizon(agent, 0.0);
- NavigationServer2D::get_singleton()->agent_set_radius(agent, radius);
- NavigationServer2D::get_singleton()->agent_set_max_speed(agent, 0.0);
}
+ return 1.0; // Never a 0 radius
+}
+
+void NavigationObstacle2D::set_estimate_radius(bool p_estimate_radius) {
+ estimate_radius = p_estimate_radius;
+ notify_property_list_changed();
+ reevaluate_agent_radius();
+}
+
+void NavigationObstacle2D::set_radius(real_t p_radius) {
+ ERR_FAIL_COND_MSG(p_radius <= 0.0, "Radius must be greater than 0.");
+ radius = p_radius;
+ reevaluate_agent_radius();
}
diff --git a/scene/2d/navigation_obstacle_2d.h b/scene/2d/navigation_obstacle_2d.h
index 9cffc2c0c3..a5603f059f 100644
--- a/scene/2d/navigation_obstacle_2d.h
+++ b/scene/2d/navigation_obstacle_2d.h
@@ -40,8 +40,12 @@ class NavigationObstacle2D : public Node {
Node2D *parent_node2d = nullptr;
RID agent;
+ bool estimate_radius = true;
+ real_t radius = 1.0;
+
protected:
static void _bind_methods();
+ void _validate_property(PropertyInfo &p_property) const override;
void _notification(int p_what);
public:
@@ -52,10 +56,21 @@ public:
return agent;
}
+ void set_estimate_radius(bool p_estimate_radius);
+ bool is_radius_estimated() const {
+ return estimate_radius;
+ }
+ void set_radius(real_t p_radius);
+ real_t get_radius() const {
+ return radius;
+ }
+
TypedArray<String> get_configuration_warnings() const override;
private:
- void update_agent_shape();
+ void initialize_agent();
+ void reevaluate_agent_radius();
+ real_t estimate_agent_radius() const;
};
#endif
diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp
index cbf0d50c4e..bdb6cda5af 100644
--- a/scene/2d/navigation_region_2d.cpp
+++ b/scene/2d/navigation_region_2d.cpp
@@ -346,9 +346,9 @@ void NavigationPolygon::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_outlines", "outlines"), &NavigationPolygon::_set_outlines);
ClassDB::bind_method(D_METHOD("_get_outlines"), &NavigationPolygon::_get_outlines);
- ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_vertices", "get_vertices");
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_polygons", "_get_polygons");
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "outlines", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_outlines", "_get_outlines");
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_vertices", "get_vertices");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_polygons", "_get_polygons");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "outlines", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_outlines", "_get_outlines");
}
void NavigationRegion2D::set_enabled(bool p_enabled) {
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index 4d4d21bad7..5ec2a81108 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -133,9 +133,12 @@ bool PhysicsBody2D::test_move(const Transform2D &p_from, const Vector2 &p_linear
ERR_FAIL_COND_V(!is_inside_tree(), false);
PhysicsServer2D::MotionResult *r = nullptr;
+ PhysicsServer2D::MotionResult temp_result;
if (r_collision.is_valid()) {
// Needs const_cast because method bindings don't support non-const Ref.
r = const_cast<PhysicsServer2D::MotionResult *>(&r_collision->result);
+ } else {
+ r = &temp_result;
}
// Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky.
@@ -143,7 +146,14 @@ bool PhysicsBody2D::test_move(const Transform2D &p_from, const Vector2 &p_linear
PhysicsServer2D::MotionParameters parameters(p_from, p_linear_velocity * delta, p_margin);
- return PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), parameters, r);
+ bool colliding = PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), parameters, r);
+
+ if (colliding) {
+ // Don't report collision when the whole motion is done.
+ return (r->collision_safe_fraction < 1.0);
+ } else {
+ return false;
+ }
}
TypedArray<PhysicsBody2D> PhysicsBody2D::get_collision_exceptions() {
@@ -684,6 +694,24 @@ real_t RigidDynamicBody2D::get_gravity_scale() const {
return gravity_scale;
}
+void RigidDynamicBody2D::set_linear_damp_mode(DampMode p_mode) {
+ linear_damp_mode = p_mode;
+ PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_LINEAR_DAMP_MODE, linear_damp_mode);
+}
+
+RigidDynamicBody2D::DampMode RigidDynamicBody2D::get_linear_damp_mode() const {
+ return linear_damp_mode;
+}
+
+void RigidDynamicBody2D::set_angular_damp_mode(DampMode p_mode) {
+ angular_damp_mode = p_mode;
+ PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_ANGULAR_DAMP_MODE, angular_damp_mode);
+}
+
+RigidDynamicBody2D::DampMode RigidDynamicBody2D::get_angular_damp_mode() const {
+ return angular_damp_mode;
+}
+
void RigidDynamicBody2D::set_linear_damp(real_t p_linear_damp) {
ERR_FAIL_COND(p_linear_damp < -1);
linear_damp = p_linear_damp;
@@ -916,6 +944,12 @@ void RigidDynamicBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_gravity_scale", "gravity_scale"), &RigidDynamicBody2D::set_gravity_scale);
ClassDB::bind_method(D_METHOD("get_gravity_scale"), &RigidDynamicBody2D::get_gravity_scale);
+ ClassDB::bind_method(D_METHOD("set_linear_damp_mode", "linear_damp_mode"), &RigidDynamicBody2D::set_linear_damp_mode);
+ ClassDB::bind_method(D_METHOD("get_linear_damp_mode"), &RigidDynamicBody2D::get_linear_damp_mode);
+
+ ClassDB::bind_method(D_METHOD("set_angular_damp_mode", "angular_damp_mode"), &RigidDynamicBody2D::set_angular_damp_mode);
+ ClassDB::bind_method(D_METHOD("get_angular_damp_mode"), &RigidDynamicBody2D::get_angular_damp_mode);
+
ClassDB::bind_method(D_METHOD("set_linear_damp", "linear_damp"), &RigidDynamicBody2D::set_linear_damp);
ClassDB::bind_method(D_METHOD("get_linear_damp"), &RigidDynamicBody2D::get_linear_damp);
@@ -992,9 +1026,11 @@ void RigidDynamicBody2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "freeze_mode", PROPERTY_HINT_ENUM, "Static,Kinematic"), "set_freeze_mode", "get_freeze_mode");
ADD_GROUP("Linear", "linear_");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "linear_velocity"), "set_linear_velocity", "get_linear_velocity");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "linear_damp_mode", PROPERTY_HINT_ENUM, "Combine,Replace"), "set_linear_damp_mode", "get_linear_damp_mode");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "linear_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), "set_linear_damp", "get_linear_damp");
ADD_GROUP("Angular", "angular_");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_velocity"), "set_angular_velocity", "get_angular_velocity");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "angular_damp_mode", PROPERTY_HINT_ENUM, "Combine,Replace"), "set_angular_damp_mode", "get_angular_damp_mode");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), "set_angular_damp", "get_angular_damp");
ADD_GROUP("Applied Forces", "applied_");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "applied_force"), "set_applied_force", "get_applied_force");
@@ -1012,6 +1048,9 @@ void RigidDynamicBody2D::_bind_methods() {
BIND_ENUM_CONSTANT(CENTER_OF_MASS_MODE_AUTO);
BIND_ENUM_CONSTANT(CENTER_OF_MASS_MODE_CUSTOM);
+ BIND_ENUM_CONSTANT(DAMP_MODE_COMBINE);
+ BIND_ENUM_CONSTANT(DAMP_MODE_REPLACE);
+
BIND_ENUM_CONSTANT(CCD_MODE_DISABLED);
BIND_ENUM_CONSTANT(CCD_MODE_CAST_RAY);
BIND_ENUM_CONSTANT(CCD_MODE_CAST_SHAPE);
@@ -1020,7 +1059,7 @@ void RigidDynamicBody2D::_bind_methods() {
void RigidDynamicBody2D::_validate_property(PropertyInfo &property) const {
if (center_of_mass_mode != CENTER_OF_MASS_MODE_CUSTOM) {
if (property.name == "center_of_mass") {
- property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
}
}
}
@@ -1072,6 +1111,10 @@ bool CharacterBody2D::move_and_slide() {
if (bs) {
Vector2 local_position = gt.elements[2] - bs->get_transform().elements[2];
current_platform_velocity = bs->get_velocity_at_local_position(local_position);
+ } else {
+ // Body is removed or destroyed, invalidate floor.
+ current_platform_velocity = Vector2();
+ platform_rid = RID();
}
} else {
current_platform_velocity = Vector2();
@@ -1686,9 +1729,9 @@ void CharacterBody2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "motion_mode", PROPERTY_HINT_ENUM, "Grounded,Free", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_motion_mode", "get_motion_mode");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "up_direction"), "set_up_direction", "get_up_direction");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "motion_velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_motion_velocity", "get_motion_velocity");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "motion_velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_motion_velocity", "get_motion_velocity");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slide_on_ceiling"), "set_slide_on_ceiling_enabled", "is_slide_on_ceiling_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_max_slides", "get_max_slides");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_max_slides", "get_max_slides");
ADD_GROUP("Free Mode", "free_mode_");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "free_mode_min_slide_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians", PROPERTY_USAGE_DEFAULT), "set_free_mode_min_slide_angle", "get_free_mode_min_slide_angle");
@@ -1715,11 +1758,11 @@ void CharacterBody2D::_bind_methods() {
void CharacterBody2D::_validate_property(PropertyInfo &property) const {
if (motion_mode == MOTION_MODE_FREE) {
if (property.name.begins_with("floor_") || property.name == "up_direction" || property.name == "slide_on_ceiling") {
- property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
}
} else {
if (property.name == "free_mode_min_slide_angle") {
- property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
}
}
}
diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h
index 15e8469bb4..2abce4b0a5 100644
--- a/scene/2d/physics_body_2d.h
+++ b/scene/2d/physics_body_2d.h
@@ -127,6 +127,11 @@ public:
CENTER_OF_MASS_MODE_CUSTOM,
};
+ enum DampMode {
+ DAMP_MODE_COMBINE,
+ DAMP_MODE_REPLACE,
+ };
+
enum CCDMode {
CCD_MODE_DISABLED,
CCD_MODE_CAST_RAY,
@@ -146,8 +151,12 @@ private:
Ref<PhysicsMaterial> physics_material_override;
real_t gravity_scale = 1.0;
- real_t linear_damp = -1.0;
- real_t angular_damp = -1.0;
+
+ DampMode linear_damp_mode = DAMP_MODE_COMBINE;
+ DampMode angular_damp_mode = DAMP_MODE_COMBINE;
+
+ real_t linear_damp = 0.0;
+ real_t angular_damp = 0.0;
Vector2 linear_velocity;
real_t angular_velocity = 0.0;
@@ -241,6 +250,12 @@ public:
void set_gravity_scale(real_t p_gravity_scale);
real_t get_gravity_scale() const;
+ void set_linear_damp_mode(DampMode p_mode);
+ DampMode get_linear_damp_mode() const;
+
+ void set_angular_damp_mode(DampMode p_mode);
+ DampMode get_angular_damp_mode() const;
+
void set_linear_damp(real_t p_linear_damp);
real_t get_linear_damp() const;
@@ -300,6 +315,7 @@ private:
VARIANT_ENUM_CAST(RigidDynamicBody2D::FreezeMode);
VARIANT_ENUM_CAST(RigidDynamicBody2D::CenterOfMassMode);
+VARIANT_ENUM_CAST(RigidDynamicBody2D::DampMode);
VARIANT_ENUM_CAST(RigidDynamicBody2D::CCDMode);
class CharacterBody2D : public PhysicsBody2D {
diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp
index 7366be5a7d..f00959bbb6 100644
--- a/scene/2d/polygon_2d.cpp
+++ b/scene/2d/polygon_2d.cpp
@@ -92,7 +92,7 @@ bool Polygon2D::_edit_is_selected_on_click(const Point2 &p_point, double p_toler
void Polygon2D::_validate_property(PropertyInfo &property) const {
if (!invert && property.name == "invert_border") {
- property.usage = PROPERTY_USAGE_NOEDITOR;
+ property.usage = PROPERTY_USAGE_NO_EDITOR;
}
}
@@ -554,7 +554,9 @@ void Polygon2D::set_bone_path(int p_index, const NodePath &p_path) {
Array Polygon2D::_get_bones() const {
Array bones;
for (int i = 0; i < get_bone_count(); i++) {
- bones.push_back(get_bone_path(i));
+ // Convert path property to String to avoid errors due to invalid node path in editor,
+ // because it's relative to the Skeleton2D node and not Polygon2D.
+ bones.push_back(String(get_bone_path(i)));
bones.push_back(get_bone_weights(i));
}
return bones;
@@ -564,7 +566,8 @@ void Polygon2D::_set_bones(const Array &p_bones) {
ERR_FAIL_COND(p_bones.size() & 1);
clear_bones();
for (int i = 0; i < p_bones.size(); i += 2) {
- add_bone(p_bones[i], p_bones[i + 1]);
+ // Convert back from String to NodePath.
+ add_bone(NodePath(p_bones[i]), p_bones[i + 1]);
}
}
@@ -659,7 +662,7 @@ void Polygon2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "uv"), "set_uv", "get_uv");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "vertex_colors"), "set_vertex_colors", "get_vertex_colors");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons"), "set_polygons", "get_polygons");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bones", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_bones", "_get_bones");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bones", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "_set_bones", "_get_bones");
ADD_PROPERTY(PropertyInfo(Variant::INT, "internal_vertex_count", PROPERTY_HINT_RANGE, "0,1000"), "set_internal_vertex_count", "get_internal_vertex_count");
}
diff --git a/scene/2d/ray_cast_2d.cpp b/scene/2d/ray_cast_2d.cpp
index 0a8e9e2a58..f9830a8743 100644
--- a/scene/2d/ray_cast_2d.cpp
+++ b/scene/2d/ray_cast_2d.cpp
@@ -192,7 +192,17 @@ void RayCast2D::_update_raycast_state() {
PhysicsDirectSpaceState2D::RayResult rr;
bool prev_collision_state = collided;
- if (dss->intersect_ray(gt.get_origin(), gt.xform(to), rr, exclude, collision_mask, collide_with_bodies, collide_with_areas)) {
+
+ PhysicsDirectSpaceState2D::RayParameters ray_params;
+ ray_params.from = gt.get_origin();
+ ray_params.to = gt.xform(to);
+ ray_params.exclude = exclude;
+ ray_params.collision_mask = collision_mask;
+ ray_params.collide_with_bodies = collide_with_bodies;
+ ray_params.collide_with_areas = collide_with_areas;
+ ray_params.hit_from_inside = hit_from_inside;
+
+ if (dss->intersect_ray(ray_params, rr)) {
collided = true;
against = rr.collider_id;
collision_point = rr.position;
@@ -281,22 +291,30 @@ void RayCast2D::clear_exceptions() {
exclude.clear();
}
-void RayCast2D::set_collide_with_areas(bool p_clip) {
- collide_with_areas = p_clip;
+void RayCast2D::set_collide_with_areas(bool p_enabled) {
+ collide_with_areas = p_enabled;
}
bool RayCast2D::is_collide_with_areas_enabled() const {
return collide_with_areas;
}
-void RayCast2D::set_collide_with_bodies(bool p_clip) {
- collide_with_bodies = p_clip;
+void RayCast2D::set_collide_with_bodies(bool p_enabled) {
+ collide_with_bodies = p_enabled;
}
bool RayCast2D::is_collide_with_bodies_enabled() const {
return collide_with_bodies;
}
+void RayCast2D::set_hit_from_inside(bool p_enabled) {
+ hit_from_inside = p_enabled;
+}
+
+bool RayCast2D::is_hit_from_inside_enabled() const {
+ return hit_from_inside;
+}
+
void RayCast2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &RayCast2D::set_enabled);
ClassDB::bind_method(D_METHOD("is_enabled"), &RayCast2D::is_enabled);
@@ -335,10 +353,14 @@ void RayCast2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_collide_with_bodies", "enable"), &RayCast2D::set_collide_with_bodies);
ClassDB::bind_method(D_METHOD("is_collide_with_bodies_enabled"), &RayCast2D::is_collide_with_bodies_enabled);
+ ClassDB::bind_method(D_METHOD("set_hit_from_inside", "enable"), &RayCast2D::set_hit_from_inside);
+ ClassDB::bind_method(D_METHOD("is_hit_from_inside_enabled"), &RayCast2D::is_hit_from_inside_enabled);
+
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "exclude_parent"), "set_exclude_parent_body", "get_exclude_parent_body");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "target_position"), "set_target_position", "get_target_position");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_mask", "get_collision_mask");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hit_from_inside"), "set_hit_from_inside", "is_hit_from_inside_enabled");
ADD_GROUP("Collide With", "collide_with");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_areas", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collide_with_areas", "is_collide_with_areas_enabled");
diff --git a/scene/2d/ray_cast_2d.h b/scene/2d/ray_cast_2d.h
index 65b6e7899b..3ee09fad32 100644
--- a/scene/2d/ray_cast_2d.h
+++ b/scene/2d/ray_cast_2d.h
@@ -51,6 +51,8 @@ class RayCast2D : public Node2D {
bool collide_with_areas = false;
bool collide_with_bodies = true;
+ bool hit_from_inside = false;
+
void _draw_debug_shape();
protected:
@@ -65,6 +67,9 @@ public:
void set_collide_with_bodies(bool p_clip);
bool is_collide_with_bodies_enabled() const;
+ void set_hit_from_inside(bool p_enable);
+ bool is_hit_from_inside_enabled() const;
+
void set_enabled(bool p_enabled);
bool is_enabled() const;
diff --git a/scene/2d/shape_cast_2d.cpp b/scene/2d/shape_cast_2d.cpp
new file mode 100644
index 0000000000..50b44eb4ef
--- /dev/null
+++ b/scene/2d/shape_cast_2d.cpp
@@ -0,0 +1,459 @@
+/*************************************************************************/
+/* shape_cast_2d.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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 "shape_cast_2d.h"
+
+#include "core/config/engine.h"
+#include "core/core_string_names.h"
+#include "scene/2d/collision_object_2d.h"
+#include "scene/2d/physics_body_2d.h"
+#include "scene/resources/circle_shape_2d.h"
+#include "servers/physics_2d/godot_physics_server_2d.h"
+
+void ShapeCast2D::set_target_position(const Vector2 &p_point) {
+ target_position = p_point;
+ if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_collisions_hint())) {
+ update();
+ }
+}
+
+Vector2 ShapeCast2D::get_target_position() const {
+ return target_position;
+}
+
+void ShapeCast2D::set_margin(real_t p_margin) {
+ margin = p_margin;
+}
+
+real_t ShapeCast2D::get_margin() const {
+ return margin;
+}
+
+void ShapeCast2D::set_max_results(int p_max_results) {
+ max_results = p_max_results;
+}
+
+int ShapeCast2D::get_max_results() const {
+ return max_results;
+}
+
+void ShapeCast2D::set_collision_mask(uint32_t p_mask) {
+ collision_mask = p_mask;
+}
+
+uint32_t ShapeCast2D::get_collision_mask() const {
+ return collision_mask;
+}
+
+void ShapeCast2D::set_collision_mask_value(int p_layer_number, bool p_value) {
+ ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive.");
+ uint32_t mask = get_collision_mask();
+ if (p_value) {
+ mask |= 1 << (p_layer_number - 1);
+ } else {
+ mask &= ~(1 << (p_layer_number - 1));
+ }
+ set_collision_mask(mask);
+}
+
+bool ShapeCast2D::get_collision_mask_value(int p_layer_number) const {
+ ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive.");
+ return get_collision_mask() & (1 << (p_layer_number - 1));
+}
+
+int ShapeCast2D::get_collision_count() const {
+ return result.size();
+}
+
+bool ShapeCast2D::is_colliding() const {
+ return collided;
+}
+
+Object *ShapeCast2D::get_collider(int p_idx) const {
+ ERR_FAIL_INDEX_V_MSG(p_idx, result.size(), nullptr, "No collider found.");
+
+ if (result[p_idx].collider_id.is_null()) {
+ return nullptr;
+ }
+ return ObjectDB::get_instance(result[p_idx].collider_id);
+}
+
+int ShapeCast2D::get_collider_shape(int p_idx) const {
+ ERR_FAIL_INDEX_V_MSG(p_idx, result.size(), -1, "No collider shape found.");
+ return result[p_idx].shape;
+}
+
+Vector2 ShapeCast2D::get_collision_point(int p_idx) const {
+ ERR_FAIL_INDEX_V_MSG(p_idx, result.size(), Vector2(), "No collision point found.");
+ return result[p_idx].point;
+}
+
+Vector2 ShapeCast2D::get_collision_normal(int p_idx) const {
+ ERR_FAIL_INDEX_V_MSG(p_idx, result.size(), Vector2(), "No collision normal found.");
+ return result[p_idx].normal;
+}
+
+real_t ShapeCast2D::get_closest_collision_safe_fraction() const {
+ return collision_safe_fraction;
+}
+
+real_t ShapeCast2D::get_closest_collision_unsafe_fraction() const {
+ return collision_unsafe_fraction;
+}
+
+void ShapeCast2D::set_enabled(bool p_enabled) {
+ enabled = p_enabled;
+ update();
+ if (is_inside_tree() && !Engine::get_singleton()->is_editor_hint()) {
+ set_physics_process_internal(p_enabled);
+ }
+ if (!p_enabled) {
+ collided = false;
+ }
+}
+
+bool ShapeCast2D::is_enabled() const {
+ return enabled;
+}
+
+void ShapeCast2D::set_shape(const Ref<Shape2D> &p_shape) {
+ shape = p_shape;
+ if (p_shape.is_valid()) {
+ shape->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &ShapeCast2D::_redraw_shape));
+ shape_rid = shape->get_rid();
+ }
+ update_configuration_warnings();
+ update();
+}
+
+Ref<Shape2D> ShapeCast2D::get_shape() const {
+ return shape;
+}
+
+void ShapeCast2D::set_exclude_parent_body(bool p_exclude_parent_body) {
+ if (exclude_parent_body == p_exclude_parent_body) {
+ return;
+ }
+ exclude_parent_body = p_exclude_parent_body;
+
+ if (!is_inside_tree()) {
+ return;
+ }
+ if (Object::cast_to<CollisionObject2D>(get_parent())) {
+ if (exclude_parent_body) {
+ exclude.insert(Object::cast_to<CollisionObject2D>(get_parent())->get_rid());
+ } else {
+ exclude.erase(Object::cast_to<CollisionObject2D>(get_parent())->get_rid());
+ }
+ }
+}
+
+bool ShapeCast2D::get_exclude_parent_body() const {
+ return exclude_parent_body;
+}
+
+void ShapeCast2D::_redraw_shape() {
+ update();
+}
+
+void ShapeCast2D::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ if (enabled && !Engine::get_singleton()->is_editor_hint()) {
+ set_physics_process_internal(true);
+ } else {
+ set_physics_process_internal(false);
+ }
+ if (Object::cast_to<CollisionObject2D>(get_parent())) {
+ if (exclude_parent_body) {
+ exclude.insert(Object::cast_to<CollisionObject2D>(get_parent())->get_rid());
+ } else {
+ exclude.erase(Object::cast_to<CollisionObject2D>(get_parent())->get_rid());
+ }
+ }
+ } break;
+ case NOTIFICATION_EXIT_TREE: {
+ if (enabled) {
+ set_physics_process_internal(false);
+ }
+ } break;
+
+ case NOTIFICATION_DRAW: {
+#ifdef TOOLS_ENABLED
+ ERR_FAIL_COND(!is_inside_tree());
+ if (!Engine::get_singleton()->is_editor_hint() && !get_tree()->is_debugging_collisions_hint()) {
+ break;
+ }
+ if (shape.is_null()) {
+ break;
+ }
+ Color draw_col = get_tree()->get_debug_collisions_color();
+ if (!enabled) {
+ float g = draw_col.get_v();
+ draw_col.r = g;
+ draw_col.g = g;
+ draw_col.b = g;
+ }
+ // Draw continuos chain of shapes along the cast.
+ const int steps = MAX(2, target_position.length() / shape->get_rect().get_size().length() * 4);
+ for (int i = 0; i <= steps; ++i) {
+ Vector2 t = (real_t(i) / steps) * target_position;
+ draw_set_transform(t, 0.0, Size2(1, 1));
+ shape->draw(get_canvas_item(), draw_col);
+ }
+ draw_set_transform(Vector2(), 0.0, Size2(1, 1));
+
+ // Draw an arrow indicating where the ShapeCast is pointing to.
+ if (target_position != Vector2()) {
+ Transform2D xf;
+ xf.rotate(target_position.angle());
+ xf.translate(Vector2(target_position.length(), 0));
+
+ draw_line(Vector2(), target_position, draw_col, 2);
+ Vector<Vector2> pts;
+ float tsize = 8;
+ pts.push_back(xf.xform(Vector2(tsize, 0)));
+ pts.push_back(xf.xform(Vector2(0, Math_SQRT12 * tsize)));
+ pts.push_back(xf.xform(Vector2(0, -Math_SQRT12 * tsize)));
+ Vector<Color> cols;
+ for (int i = 0; i < 3; i++)
+ cols.push_back(draw_col);
+
+ draw_primitive(pts, cols, Vector<Vector2>());
+ }
+#endif
+ } break;
+ case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
+ if (!enabled) {
+ break;
+ }
+ _update_shapecast_state();
+ } break;
+ }
+}
+
+void ShapeCast2D::_update_shapecast_state() {
+ result.clear();
+
+ ERR_FAIL_COND_MSG(shape.is_null(), "Invalid shape.");
+
+ Ref<World2D> w2d = get_world_2d();
+ ERR_FAIL_COND(w2d.is_null());
+
+ PhysicsDirectSpaceState2D *dss = PhysicsServer2D::get_singleton()->space_get_direct_state(w2d->get_space());
+ ERR_FAIL_COND(!dss);
+
+ Transform2D gt = get_global_transform();
+
+ PhysicsDirectSpaceState2D::ShapeParameters params;
+ params.shape_rid = shape_rid;
+ params.transform = gt;
+ params.motion = gt.basis_xform(target_position);
+ params.margin = margin;
+ params.exclude = exclude;
+ params.collision_mask = collision_mask;
+ params.collide_with_bodies = collide_with_bodies;
+ params.collide_with_areas = collide_with_areas;
+
+ collision_safe_fraction = 0.0;
+ collision_unsafe_fraction = 0.0;
+
+ if (target_position != Vector2()) {
+ dss->cast_motion(params, collision_safe_fraction, collision_unsafe_fraction);
+ if (collision_unsafe_fraction < 1.0) {
+ // Move shape transform to the point of impact,
+ // so we can collect contact info at that point.
+ gt.set_origin(gt.get_origin() + params.motion * (collision_unsafe_fraction + CMP_EPSILON));
+ params.transform = gt;
+ }
+ }
+ // Regardless of whether the shape is stuck or it's moved along
+ // the motion vector, we'll only consider static collisions from now on.
+ params.motion = Vector2();
+
+ bool intersected = true;
+ while (intersected && result.size() < max_results) {
+ PhysicsDirectSpaceState2D::ShapeRestInfo info;
+ intersected = dss->rest_info(params, &info);
+ if (intersected) {
+ result.push_back(info);
+ params.exclude.insert(info.rid);
+ }
+ }
+ collided = !result.is_empty();
+}
+
+void ShapeCast2D::force_shapecast_update() {
+ _update_shapecast_state();
+}
+
+void ShapeCast2D::add_exception_rid(const RID &p_rid) {
+ exclude.insert(p_rid);
+}
+
+void ShapeCast2D::add_exception(const Object *p_object) {
+ ERR_FAIL_NULL(p_object);
+ const CollisionObject2D *co = Object::cast_to<CollisionObject2D>(p_object);
+ if (!co) {
+ return;
+ }
+ add_exception_rid(co->get_rid());
+}
+
+void ShapeCast2D::remove_exception_rid(const RID &p_rid) {
+ exclude.erase(p_rid);
+}
+
+void ShapeCast2D::remove_exception(const Object *p_object) {
+ ERR_FAIL_NULL(p_object);
+ const CollisionObject2D *co = Object::cast_to<CollisionObject2D>(p_object);
+ if (!co) {
+ return;
+ }
+ remove_exception_rid(co->get_rid());
+}
+
+void ShapeCast2D::clear_exceptions() {
+ exclude.clear();
+}
+
+void ShapeCast2D::set_collide_with_areas(bool p_clip) {
+ collide_with_areas = p_clip;
+}
+
+bool ShapeCast2D::is_collide_with_areas_enabled() const {
+ return collide_with_areas;
+}
+
+void ShapeCast2D::set_collide_with_bodies(bool p_clip) {
+ collide_with_bodies = p_clip;
+}
+
+bool ShapeCast2D::is_collide_with_bodies_enabled() const {
+ return collide_with_bodies;
+}
+
+Array ShapeCast2D::_get_collision_result() const {
+ Array ret;
+
+ for (int i = 0; i < result.size(); ++i) {
+ const PhysicsDirectSpaceState2D::ShapeRestInfo &sri = result[i];
+
+ Dictionary col;
+ col["point"] = sri.point;
+ col["normal"] = sri.normal;
+ col["rid"] = sri.rid;
+ col["collider"] = ObjectDB::get_instance(sri.collider_id);
+ col["collider_id"] = sri.collider_id;
+ col["shape"] = sri.shape;
+ col["linear_velocity"] = sri.linear_velocity;
+
+ ret.push_back(col);
+ }
+ return ret;
+}
+
+TypedArray<String> ShapeCast2D::get_configuration_warnings() const {
+ TypedArray<String> warnings = Node2D::get_configuration_warnings();
+
+ if (shape.is_null()) {
+ warnings.push_back(TTR("This node cannot interact with other objects unless a Shape2D is assigned."));
+ }
+ return warnings;
+}
+
+void ShapeCast2D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &ShapeCast2D::set_enabled);
+ ClassDB::bind_method(D_METHOD("is_enabled"), &ShapeCast2D::is_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_shape", "shape"), &ShapeCast2D::set_shape);
+ ClassDB::bind_method(D_METHOD("get_shape"), &ShapeCast2D::get_shape);
+
+ ClassDB::bind_method(D_METHOD("set_target_position", "local_point"), &ShapeCast2D::set_target_position);
+ ClassDB::bind_method(D_METHOD("get_target_position"), &ShapeCast2D::get_target_position);
+
+ ClassDB::bind_method(D_METHOD("set_margin", "margin"), &ShapeCast2D::set_margin);
+ ClassDB::bind_method(D_METHOD("get_margin"), &ShapeCast2D::get_margin);
+
+ ClassDB::bind_method(D_METHOD("set_max_results", "max_results"), &ShapeCast2D::set_max_results);
+ ClassDB::bind_method(D_METHOD("get_max_results"), &ShapeCast2D::get_max_results);
+
+ ClassDB::bind_method(D_METHOD("is_colliding"), &ShapeCast2D::is_colliding);
+ ClassDB::bind_method(D_METHOD("get_collision_count"), &ShapeCast2D::get_collision_count);
+
+ ClassDB::bind_method(D_METHOD("force_shapecast_update"), &ShapeCast2D::force_shapecast_update);
+
+ ClassDB::bind_method(D_METHOD("get_collider", "index"), &ShapeCast2D::get_collider);
+ ClassDB::bind_method(D_METHOD("get_collider_shape", "index"), &ShapeCast2D::get_collider_shape);
+ ClassDB::bind_method(D_METHOD("get_collision_point", "index"), &ShapeCast2D::get_collision_point);
+ ClassDB::bind_method(D_METHOD("get_collision_normal", "index"), &ShapeCast2D::get_collision_normal);
+
+ ClassDB::bind_method(D_METHOD("get_closest_collision_safe_fraction"), &ShapeCast2D::get_closest_collision_safe_fraction);
+ ClassDB::bind_method(D_METHOD("get_closest_collision_unsafe_fraction"), &ShapeCast2D::get_closest_collision_unsafe_fraction);
+
+ ClassDB::bind_method(D_METHOD("add_exception_rid", "rid"), &ShapeCast2D::add_exception_rid);
+ ClassDB::bind_method(D_METHOD("add_exception", "node"), &ShapeCast2D::add_exception);
+
+ ClassDB::bind_method(D_METHOD("remove_exception_rid", "rid"), &ShapeCast2D::remove_exception_rid);
+ ClassDB::bind_method(D_METHOD("remove_exception", "node"), &ShapeCast2D::remove_exception);
+
+ ClassDB::bind_method(D_METHOD("clear_exceptions"), &ShapeCast2D::clear_exceptions);
+
+ ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &ShapeCast2D::set_collision_mask);
+ ClassDB::bind_method(D_METHOD("get_collision_mask"), &ShapeCast2D::get_collision_mask);
+
+ ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &ShapeCast2D::set_collision_mask_value);
+ ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &ShapeCast2D::get_collision_mask_value);
+
+ ClassDB::bind_method(D_METHOD("set_exclude_parent_body", "mask"), &ShapeCast2D::set_exclude_parent_body);
+ ClassDB::bind_method(D_METHOD("get_exclude_parent_body"), &ShapeCast2D::get_exclude_parent_body);
+
+ ClassDB::bind_method(D_METHOD("set_collide_with_areas", "enable"), &ShapeCast2D::set_collide_with_areas);
+ ClassDB::bind_method(D_METHOD("is_collide_with_areas_enabled"), &ShapeCast2D::is_collide_with_areas_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_collide_with_bodies", "enable"), &ShapeCast2D::set_collide_with_bodies);
+ ClassDB::bind_method(D_METHOD("is_collide_with_bodies_enabled"), &ShapeCast2D::is_collide_with_bodies_enabled);
+
+ ClassDB::bind_method(D_METHOD("_get_collision_result"), &ShapeCast2D::_get_collision_result);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape2D"), "set_shape", "get_shape");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "exclude_parent"), "set_exclude_parent_body", "get_exclude_parent_body");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "target_position"), "set_target_position", "get_target_position");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin", PROPERTY_HINT_RANGE, "0,100,0.01"), "set_margin", "get_margin");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "max_results"), "set_max_results", "get_max_results");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_mask", "get_collision_mask");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "collision_result", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "", "_get_collision_result");
+ ADD_GROUP("Collide With", "collide_with");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_areas", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collide_with_areas", "is_collide_with_areas_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_bodies", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collide_with_bodies", "is_collide_with_bodies_enabled");
+}
diff --git a/scene/2d/shape_cast_2d.h b/scene/2d/shape_cast_2d.h
new file mode 100644
index 0000000000..fca6b46155
--- /dev/null
+++ b/scene/2d/shape_cast_2d.h
@@ -0,0 +1,120 @@
+/*************************************************************************/
+/* shape_cast_2d.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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 SHAPE_CAST_2D
+#define SHAPE_CAST_2D
+
+#include "scene/2d/node_2d.h"
+#include "scene/resources/shape_2d.h"
+
+class ShapeCast2D : public Node2D {
+ GDCLASS(ShapeCast2D, Node2D);
+
+ bool enabled = true;
+
+ Ref<Shape2D> shape;
+ RID shape_rid;
+ Vector2 target_position = Vector2(0, 50);
+
+ Set<RID> exclude;
+ real_t margin = 0.0;
+ uint32_t collision_mask = 1;
+ bool exclude_parent_body = true;
+ bool collide_with_areas = false;
+ bool collide_with_bodies = true;
+
+ // Result
+ int max_results = 32;
+ Vector<PhysicsDirectSpaceState2D::ShapeRestInfo> result;
+ bool collided = false;
+ real_t collision_safe_fraction = 1.0;
+ real_t collision_unsafe_fraction = 1.0;
+
+ Array _get_collision_result() const;
+ void _redraw_shape();
+
+protected:
+ void _notification(int p_what);
+ void _update_shapecast_state();
+ static void _bind_methods();
+
+public:
+ void set_collide_with_areas(bool p_clip);
+ bool is_collide_with_areas_enabled() const;
+
+ void set_collide_with_bodies(bool p_clip);
+ bool is_collide_with_bodies_enabled() const;
+
+ void set_enabled(bool p_enabled);
+ bool is_enabled() const;
+
+ void set_shape(const Ref<Shape2D> &p_shape);
+ Ref<Shape2D> get_shape() const;
+
+ void set_target_position(const Vector2 &p_point);
+ Vector2 get_target_position() const;
+
+ void set_margin(real_t p_margin);
+ real_t get_margin() const;
+
+ void set_max_results(int p_max_results);
+ int get_max_results() const;
+
+ void set_collision_mask(uint32_t p_mask);
+ uint32_t get_collision_mask() const;
+
+ void set_collision_mask_value(int p_layer_number, bool p_value);
+ bool get_collision_mask_value(int p_layer_number) const;
+
+ void set_exclude_parent_body(bool p_exclude_parent_body);
+ bool get_exclude_parent_body() const;
+
+ void force_shapecast_update();
+ bool is_colliding() const;
+
+ int get_collision_count() const;
+ Object *get_collider(int p_idx) const;
+ int get_collider_shape(int p_idx) const;
+ Vector2 get_collision_point(int p_idx) const;
+ Vector2 get_collision_normal(int p_idx) const;
+
+ real_t get_closest_collision_safe_fraction() const;
+ real_t get_closest_collision_unsafe_fraction() const;
+
+ void add_exception_rid(const RID &p_rid);
+ void add_exception(const Object *p_object);
+ void remove_exception_rid(const RID &p_rid);
+ void remove_exception(const Object *p_object);
+ void clear_exceptions();
+
+ TypedArray<String> get_configuration_warnings() const override;
+};
+
+#endif
diff --git a/scene/2d/sprite_2d.cpp b/scene/2d/sprite_2d.cpp
index 5761f19a53..b2302d09db 100644
--- a/scene/2d/sprite_2d.cpp
+++ b/scene/2d/sprite_2d.cpp
@@ -385,7 +385,7 @@ void Sprite2D::_validate_property(PropertyInfo &property) const {
}
if (!region_enabled && (property.name == "region_rect" || property.name == "region_filter_clip")) {
- property.usage = PROPERTY_USAGE_NOEDITOR;
+ property.usage = PROPERTY_USAGE_NO_EDITOR;
}
}
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index c11262e0c9..96c4164721 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -1259,7 +1259,7 @@ void TileMap::_rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
if (atlas_source) {
Vector2i grid_size = atlas_source->get_atlas_grid_size();
- if (!atlas_source->get_texture().is_valid() || c.get_atlas_coords().x >= grid_size.x || c.get_atlas_coords().y >= grid_size.y) {
+ if (!atlas_source->get_runtime_texture().is_valid() || c.get_atlas_coords().x >= grid_size.x || c.get_atlas_coords().y >= grid_size.y) {
// Generate a random color from the hashed values of the tiles.
Array to_hash;
to_hash.push_back(c.source_id);
@@ -1299,7 +1299,7 @@ void TileMap::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSe
}
// Get the texture.
- Ref<Texture2D> tex = atlas_source->get_texture();
+ Ref<Texture2D> tex = atlas_source->get_runtime_texture();
if (!tex.is_valid()) {
return;
}
@@ -1321,7 +1321,7 @@ void TileMap::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSe
// Get destination rect.
Rect2 dest_rect;
- dest_rect.size = atlas_source->get_tile_texture_region(p_atlas_coords).size;
+ dest_rect.size = atlas_source->get_runtime_tile_texture_region(p_atlas_coords).size;
dest_rect.size.x += FP_ADJUST;
dest_rect.size.y += FP_ADJUST;
@@ -1342,10 +1342,10 @@ void TileMap::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSe
// Draw the tile.
if (p_frame >= 0) {
- Rect2i source_rect = atlas_source->get_tile_texture_region(p_atlas_coords, p_frame);
+ Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, p_frame);
tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping());
} else if (atlas_source->get_tile_animation_frames_count(p_atlas_coords) == 1) {
- Rect2i source_rect = atlas_source->get_tile_texture_region(p_atlas_coords, 0);
+ Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, 0);
tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping());
} else {
real_t speed = atlas_source->get_tile_animation_speed(p_atlas_coords);
@@ -1355,7 +1355,7 @@ void TileMap::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSe
real_t frame_duration = atlas_source->get_tile_animation_frame_duration(p_atlas_coords, frame) / speed;
RenderingServer::get_singleton()->canvas_item_add_animation_slice(p_canvas_item, animation_duration, time, time + frame_duration, 0.0);
- Rect2i source_rect = atlas_source->get_tile_texture_region(p_atlas_coords, frame);
+ Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, frame);
tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping());
time += frame_duration;
@@ -2146,18 +2146,16 @@ Set<TileSet::TerrainsPattern> TileMap::_get_valid_terrains_patterns_for_constrai
Set<TileSet::TerrainsPattern> compatible_terrain_tile_patterns;
for (TileSet::TerrainsPattern &terrain_pattern : tile_set->get_terrains_pattern_set(p_terrain_set)) {
int valid = true;
- int in_pattern_count = 0;
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
if (tile_set->is_valid_peering_bit_terrain(p_terrain_set, bit)) {
// Check if the bit is compatible with the constraints.
- TerrainConstraint terrain_bit_constraint = TerrainConstraint(this, p_position, bit, terrain_pattern[in_pattern_count]);
+ TerrainConstraint terrain_bit_constraint = TerrainConstraint(this, p_position, bit, terrain_pattern.get_terrain(bit));
Set<TerrainConstraint>::Element *in_set_constraint_element = p_constraints.find(terrain_bit_constraint);
if (in_set_constraint_element && in_set_constraint_element->get().get_terrain() != terrain_bit_constraint.get_terrain()) {
valid = false;
break;
}
- in_pattern_count++;
}
}
@@ -2249,13 +2247,11 @@ Set<TileMap::TerrainConstraint> TileMap::get_terrain_constraints_from_added_tile
// Compute the constraints needed from the surrounding tiles.
Set<TerrainConstraint> output;
- int in_pattern_count = 0;
for (uint32_t i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor side = TileSet::CellNeighbor(i);
if (tile_set->is_valid_peering_bit_terrain(p_terrain_set, side)) {
- TerrainConstraint c = TerrainConstraint(this, p_position, side, p_terrains_pattern[in_pattern_count]);
+ TerrainConstraint c = TerrainConstraint(this, p_position, side, p_terrains_pattern.get_terrain(side));
output.insert(c);
- in_pattern_count++;
}
}
@@ -2312,8 +2308,11 @@ Map<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_wave_function_collapse(
int pattern_index = 0;
for (const TileSet::TerrainsPattern &pattern : valid_tiles) {
Set<int> terrains;
- for (int i = 0; i < pattern.size(); i++) {
- terrains.insert(pattern[i]);
+ for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ TileSet::CellNeighbor side = TileSet::CellNeighbor(i);
+ if (tile_set->is_valid_peering_bit_terrain(p_terrain_set, side)) {
+ terrains.insert(pattern.get_terrain(side));
+ }
}
min_terrain_count = MIN(min_terrain_count, terrains.size());
terrains_counts.push_back(terrains.size());
@@ -2369,7 +2368,7 @@ void TileMap::set_cells_from_surrounding_terrains(int p_layer, TypedArray<Vector
Map<Vector2i, TileSet::TerrainsPattern> wfc_output = terrain_wave_function_collapse(coords_set, p_terrain_set, constraints);
for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &kv : wfc_output) {
- TileMapCell cell = tile_set->get_random_tile_from_pattern(p_terrain_set, kv.value);
+ TileMapCell cell = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, kv.value);
set_cell(p_layer, kv.key, cell.source_id, cell.get_atlas_coords(), cell.alternative_tile);
}
}
@@ -2711,7 +2710,7 @@ bool TileMap::_get(const StringName &p_name, Variant &r_ret) const {
}
void TileMap::_get_property_list(List<PropertyInfo> *p_list) const {
- p_list->push_back(PropertyInfo(Variant::INT, "format", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
+ p_list->push_back(PropertyInfo(Variant::INT, "format", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
p_list->push_back(PropertyInfo(Variant::NIL, "Layers", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
for (unsigned int i = 0; i < layers.size(); i++) {
p_list->push_back(PropertyInfo(Variant::STRING, vformat("layer_%d/name", i), PROPERTY_HINT_NONE));
@@ -2720,7 +2719,7 @@ void TileMap::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::BOOL, vformat("layer_%d/y_sort_enabled", i), PROPERTY_HINT_NONE));
p_list->push_back(PropertyInfo(Variant::INT, vformat("layer_%d/y_sort_origin", i), PROPERTY_HINT_NONE));
p_list->push_back(PropertyInfo(Variant::INT, vformat("layer_%d/z_index", i), PROPERTY_HINT_NONE));
- p_list->push_back(PropertyInfo(Variant::OBJECT, vformat("layer_%d/tile_data", i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ p_list->push_back(PropertyInfo(Variant::OBJECT, vformat("layer_%d/tile_data", i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
}
}
diff --git a/scene/3d/area_3d.cpp b/scene/3d/area_3d.cpp
index 9179983220..073543638f 100644
--- a/scene/3d/area_3d.cpp
+++ b/scene/3d/area_3d.cpp
@@ -33,13 +33,13 @@
#include "scene/scene_string_names.h"
#include "servers/audio_server.h"
-void Area3D::set_space_override_mode(SpaceOverride p_mode) {
- space_override = p_mode;
- PhysicsServer3D::get_singleton()->area_set_space_override_mode(get_rid(), PhysicsServer3D::AreaSpaceOverrideMode(p_mode));
+void Area3D::set_gravity_space_override_mode(SpaceOverride p_mode) {
+ gravity_space_override = p_mode;
+ PhysicsServer3D::get_singleton()->area_set_param(get_rid(), PhysicsServer3D::AREA_PARAM_GRAVITY_OVERRIDE_MODE, p_mode);
}
-Area3D::SpaceOverride Area3D::get_space_override_mode() const {
- return space_override;
+Area3D::SpaceOverride Area3D::get_gravity_space_override_mode() const {
+ return gravity_space_override;
}
void Area3D::set_gravity_is_point(bool p_enabled) {
@@ -51,21 +51,30 @@ bool Area3D::is_gravity_a_point() const {
return gravity_is_point;
}
-void Area3D::set_gravity_distance_scale(real_t p_scale) {
+void Area3D::set_gravity_point_distance_scale(real_t p_scale) {
gravity_distance_scale = p_scale;
PhysicsServer3D::get_singleton()->area_set_param(get_rid(), PhysicsServer3D::AREA_PARAM_GRAVITY_DISTANCE_SCALE, p_scale);
}
-real_t Area3D::get_gravity_distance_scale() const {
+real_t Area3D::get_gravity_point_distance_scale() const {
return gravity_distance_scale;
}
-void Area3D::set_gravity_vector(const Vector3 &p_vec) {
- gravity_vec = p_vec;
- PhysicsServer3D::get_singleton()->area_set_param(get_rid(), PhysicsServer3D::AREA_PARAM_GRAVITY_VECTOR, p_vec);
+void Area3D::set_gravity_point_center(const Vector3 &p_center) {
+ gravity_vec = p_center;
+ PhysicsServer3D::get_singleton()->area_set_param(get_rid(), PhysicsServer3D::AREA_PARAM_GRAVITY_VECTOR, p_center);
}
-Vector3 Area3D::get_gravity_vector() const {
+const Vector3 &Area3D::get_gravity_point_center() const {
+ return gravity_vec;
+}
+
+void Area3D::set_gravity_direction(const Vector3 &p_direction) {
+ gravity_vec = p_direction;
+ PhysicsServer3D::get_singleton()->area_set_param(get_rid(), PhysicsServer3D::AREA_PARAM_GRAVITY_VECTOR, p_direction);
+}
+
+const Vector3 &Area3D::get_gravity_direction() const {
return gravity_vec;
}
@@ -78,6 +87,24 @@ real_t Area3D::get_gravity() const {
return gravity;
}
+void Area3D::set_linear_damp_space_override_mode(SpaceOverride p_mode) {
+ linear_damp_space_override = p_mode;
+ PhysicsServer3D::get_singleton()->area_set_param(get_rid(), PhysicsServer3D::AREA_PARAM_LINEAR_DAMP_OVERRIDE_MODE, p_mode);
+}
+
+Area3D::SpaceOverride Area3D::get_linear_damp_space_override_mode() const {
+ return linear_damp_space_override;
+}
+
+void Area3D::set_angular_damp_space_override_mode(SpaceOverride p_mode) {
+ angular_damp_space_override = p_mode;
+ PhysicsServer3D::get_singleton()->area_set_param(get_rid(), PhysicsServer3D::AREA_PARAM_ANGULAR_DAMP_OVERRIDE_MODE, p_mode);
+}
+
+Area3D::SpaceOverride Area3D::get_angular_damp_space_override_mode() const {
+ return angular_damp_space_override;
+}
+
void Area3D::set_linear_damp(real_t p_linear_damp) {
linear_damp = p_linear_damp;
PhysicsServer3D::get_singleton()->area_set_param(get_rid(), PhysicsServer3D::AREA_PARAM_LINEAR_DAMP, p_linear_damp);
@@ -334,11 +361,11 @@ void Area3D::set_monitoring(bool p_enable) {
monitoring = p_enable;
if (monitoring) {
- PhysicsServer3D::get_singleton()->area_set_monitor_callback(get_rid(), this, SceneStringNames::get_singleton()->_body_inout);
- PhysicsServer3D::get_singleton()->area_set_area_monitor_callback(get_rid(), this, SceneStringNames::get_singleton()->_area_inout);
+ PhysicsServer3D::get_singleton()->area_set_monitor_callback(get_rid(), callable_mp(this, &Area3D::_body_inout));
+ PhysicsServer3D::get_singleton()->area_set_area_monitor_callback(get_rid(), callable_mp(this, &Area3D::_area_inout));
} else {
- PhysicsServer3D::get_singleton()->area_set_monitor_callback(get_rid(), nullptr, StringName());
- PhysicsServer3D::get_singleton()->area_set_area_monitor_callback(get_rid(), nullptr, StringName());
+ PhysicsServer3D::get_singleton()->area_set_monitor_callback(get_rid(), Callable());
+ PhysicsServer3D::get_singleton()->area_set_area_monitor_callback(get_rid(), Callable());
_clear_monitoring();
}
}
@@ -579,27 +606,58 @@ void Area3D::_validate_property(PropertyInfo &property) const {
}
property.hint_string = options;
+ } else if (property.name.begins_with("gravity") && property.name != "gravity_space_override") {
+ if (gravity_space_override == SPACE_OVERRIDE_DISABLED) {
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
+ } else {
+ if (gravity_is_point) {
+ if (property.name == "gravity_direction") {
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
+ }
+ } else {
+ if (property.name.begins_with("gravity_point_")) {
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
+ }
+ }
+ }
+ } else if (property.name.begins_with("linear_damp") && property.name != "linear_damp_space_override") {
+ if (linear_damp_space_override == SPACE_OVERRIDE_DISABLED) {
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
+ }
+ } else if (property.name.begins_with("angular_damp") && property.name != "angular_damp_space_override") {
+ if (angular_damp_space_override == SPACE_OVERRIDE_DISABLED) {
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
+ }
}
CollisionObject3D::_validate_property(property);
}
void Area3D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_space_override_mode", "enable"), &Area3D::set_space_override_mode);
- ClassDB::bind_method(D_METHOD("get_space_override_mode"), &Area3D::get_space_override_mode);
+ ClassDB::bind_method(D_METHOD("set_gravity_space_override_mode", "space_override_mode"), &Area3D::set_gravity_space_override_mode);
+ ClassDB::bind_method(D_METHOD("get_gravity_space_override_mode"), &Area3D::get_gravity_space_override_mode);
ClassDB::bind_method(D_METHOD("set_gravity_is_point", "enable"), &Area3D::set_gravity_is_point);
ClassDB::bind_method(D_METHOD("is_gravity_a_point"), &Area3D::is_gravity_a_point);
- ClassDB::bind_method(D_METHOD("set_gravity_distance_scale", "distance_scale"), &Area3D::set_gravity_distance_scale);
- ClassDB::bind_method(D_METHOD("get_gravity_distance_scale"), &Area3D::get_gravity_distance_scale);
+ ClassDB::bind_method(D_METHOD("set_gravity_point_distance_scale", "distance_scale"), &Area3D::set_gravity_point_distance_scale);
+ ClassDB::bind_method(D_METHOD("get_gravity_point_distance_scale"), &Area3D::get_gravity_point_distance_scale);
+
+ ClassDB::bind_method(D_METHOD("set_gravity_point_center", "center"), &Area3D::set_gravity_point_center);
+ ClassDB::bind_method(D_METHOD("get_gravity_point_center"), &Area3D::get_gravity_point_center);
- ClassDB::bind_method(D_METHOD("set_gravity_vector", "vector"), &Area3D::set_gravity_vector);
- ClassDB::bind_method(D_METHOD("get_gravity_vector"), &Area3D::get_gravity_vector);
+ ClassDB::bind_method(D_METHOD("set_gravity_direction", "direction"), &Area3D::set_gravity_direction);
+ ClassDB::bind_method(D_METHOD("get_gravity_direction"), &Area3D::get_gravity_direction);
ClassDB::bind_method(D_METHOD("set_gravity", "gravity"), &Area3D::set_gravity);
ClassDB::bind_method(D_METHOD("get_gravity"), &Area3D::get_gravity);
+ ClassDB::bind_method(D_METHOD("set_linear_damp_space_override_mode", "space_override_mode"), &Area3D::set_linear_damp_space_override_mode);
+ ClassDB::bind_method(D_METHOD("get_linear_damp_space_override_mode"), &Area3D::get_linear_damp_space_override_mode);
+
+ ClassDB::bind_method(D_METHOD("set_angular_damp_space_override_mode", "space_override_mode"), &Area3D::set_angular_damp_space_override_mode);
+ ClassDB::bind_method(D_METHOD("get_angular_damp_space_override_mode"), &Area3D::get_angular_damp_space_override_mode);
+
ClassDB::bind_method(D_METHOD("set_angular_damp", "angular_damp"), &Area3D::set_angular_damp);
ClassDB::bind_method(D_METHOD("get_angular_damp"), &Area3D::get_angular_damp);
@@ -630,9 +688,6 @@ void Area3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("overlaps_body", "body"), &Area3D::overlaps_body);
ClassDB::bind_method(D_METHOD("overlaps_area", "area"), &Area3D::overlaps_area);
- ClassDB::bind_method(D_METHOD("_body_inout"), &Area3D::_body_inout);
- ClassDB::bind_method(D_METHOD("_area_inout"), &Area3D::_area_inout);
-
ClassDB::bind_method(D_METHOD("set_audio_bus_override", "enable"), &Area3D::set_audio_bus_override);
ClassDB::bind_method(D_METHOD("is_overriding_audio_bus"), &Area3D::is_overriding_audio_bus);
@@ -665,14 +720,23 @@ void Area3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "monitorable"), "set_monitorable", "is_monitorable");
ADD_PROPERTY(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,128,1"), "set_priority", "get_priority");
- ADD_GROUP("Physics Overrides", "");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "space_override", PROPERTY_HINT_ENUM, "Disabled,Combine,Combine-Replace,Replace,Replace-Combine"), "set_space_override_mode", "get_space_override_mode");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gravity_point"), "set_gravity_is_point", "is_gravity_a_point");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_distance_scale", PROPERTY_HINT_RANGE, "0,1024,0.001,or_greater,exp"), "set_gravity_distance_scale", "get_gravity_distance_scale");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity_vec"), "set_gravity_vector", "get_gravity_vector");
+ ADD_GROUP("Gravity", "gravity_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "gravity_space_override", PROPERTY_HINT_ENUM, "Disabled,Combine,Combine-Replace,Replace,Replace-Combine", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_gravity_space_override_mode", "get_gravity_space_override_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gravity_point", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_gravity_is_point", "is_gravity_a_point");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_point_distance_scale", PROPERTY_HINT_RANGE, "0,1024,0.001,or_greater,exp"), "set_gravity_point_distance_scale", "get_gravity_point_distance_scale");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity_point_center"), "set_gravity_point_center", "get_gravity_point_center");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity_direction"), "set_gravity_direction", "get_gravity_direction");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity", PROPERTY_HINT_RANGE, "-32,32,0.001,or_lesser,or_greater"), "set_gravity", "get_gravity");
+
+ ADD_GROUP("Linear Damp", "linear_damp_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "linear_damp_space_override", PROPERTY_HINT_ENUM, "Disabled,Combine,Combine-Replace,Replace,Replace-Combine", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_linear_damp_space_override_mode", "get_linear_damp_space_override_mode");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "linear_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_linear_damp", "get_linear_damp");
+
+ ADD_GROUP("Angular Damp", "angular_damp_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "angular_damp_space_override", PROPERTY_HINT_ENUM, "Disabled,Combine,Combine-Replace,Replace,Replace-Combine", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_angular_damp_space_override_mode", "get_angular_damp_space_override_mode");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_angular_damp", "get_angular_damp");
+
+ ADD_GROUP("Wind", "wind_");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wind_force_magnitude", PROPERTY_HINT_RANGE, "0,10,0.001,or_greater"), "set_wind_force_magnitude", "get_wind_force_magnitude");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wind_attenuation_factor", PROPERTY_HINT_RANGE, "0.0,3.0,0.001,or_greater"), "set_wind_attenuation_factor", "get_wind_attenuation_factor");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "wind_source_path", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D"), "set_wind_source_path", "get_wind_source_path");
@@ -697,7 +761,7 @@ void Area3D::_bind_methods() {
Area3D::Area3D() :
CollisionObject3D(PhysicsServer3D::get_singleton()->area_create(), true) {
set_gravity(9.8);
- set_gravity_vector(Vector3(0, -1, 0));
+ set_gravity_direction(Vector3(0, -1, 0));
set_monitoring(true);
set_monitorable(true);
}
diff --git a/scene/3d/area_3d.h b/scene/3d/area_3d.h
index 847d1c5966..7f31be2e17 100644
--- a/scene/3d/area_3d.h
+++ b/scene/3d/area_3d.h
@@ -47,17 +47,23 @@ public:
};
private:
- SpaceOverride space_override = SPACE_OVERRIDE_DISABLED;
+ SpaceOverride gravity_space_override = SPACE_OVERRIDE_DISABLED;
Vector3 gravity_vec;
real_t gravity;
bool gravity_is_point = false;
real_t gravity_distance_scale = 0.0;
+
+ SpaceOverride linear_damp_space_override = SPACE_OVERRIDE_DISABLED;
+ SpaceOverride angular_damp_space_override = SPACE_OVERRIDE_DISABLED;
real_t angular_damp = 0.1;
real_t linear_damp = 0.1;
+
int priority = 0;
+
real_t wind_force_magnitude = 0.0;
real_t wind_attenuation_factor = 0.0;
NodePath wind_source_path;
+
bool monitoring = false;
bool monitorable = false;
bool locked = false;
@@ -144,21 +150,30 @@ protected:
static void _bind_methods();
public:
- void set_space_override_mode(SpaceOverride p_mode);
- SpaceOverride get_space_override_mode() const;
+ void set_gravity_space_override_mode(SpaceOverride p_mode);
+ SpaceOverride get_gravity_space_override_mode() const;
void set_gravity_is_point(bool p_enabled);
bool is_gravity_a_point() const;
- void set_gravity_distance_scale(real_t p_scale);
- real_t get_gravity_distance_scale() const;
+ void set_gravity_point_distance_scale(real_t p_scale);
+ real_t get_gravity_point_distance_scale() const;
- void set_gravity_vector(const Vector3 &p_vec);
- Vector3 get_gravity_vector() const;
+ void set_gravity_point_center(const Vector3 &p_center);
+ const Vector3 &get_gravity_point_center() const;
+
+ void set_gravity_direction(const Vector3 &p_direction);
+ const Vector3 &get_gravity_direction() const;
void set_gravity(real_t p_gravity);
real_t get_gravity() const;
+ void set_linear_damp_space_override_mode(SpaceOverride p_mode);
+ SpaceOverride get_linear_damp_space_override_mode() const;
+
+ void set_angular_damp_space_override_mode(SpaceOverride p_mode);
+ SpaceOverride get_angular_damp_space_override_mode() const;
+
void set_angular_damp(real_t p_angular_damp);
real_t get_angular_damp() const;
diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp
index b5e4eac5d5..34f748b197 100644
--- a/scene/3d/audio_stream_player_3d.cpp
+++ b/scene/3d/audio_stream_player_3d.cpp
@@ -327,7 +327,13 @@ Area3D *AudioStreamPlayer3D::_get_overriding_area() {
PhysicsDirectSpaceState3D::ShapeResult sr[MAX_INTERSECT_AREAS];
- int areas = space_state->intersect_point(global_pos, sr, MAX_INTERSECT_AREAS, Set<RID>(), area_mask, false, true);
+ PhysicsDirectSpaceState3D::PointParameters point_params;
+ point_params.position = global_pos;
+ point_params.collision_mask = area_mask;
+ point_params.collide_with_bodies = false;
+ point_params.collide_with_areas = true;
+
+ int areas = space_state->intersect_point(point_params, sr, MAX_INTERSECT_AREAS);
for (int i = 0; i < areas; i++) {
if (!sr[i].collider) {
diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp
index af3b3ae5bc..cc5b7078e3 100644
--- a/scene/3d/camera_3d.cpp
+++ b/scene/3d/camera_3d.cpp
@@ -60,15 +60,15 @@ void Camera3D::_update_camera_mode() {
void Camera3D::_validate_property(PropertyInfo &p_property) const {
if (p_property.name == "fov") {
if (mode != PROJECTION_PERSPECTIVE) {
- p_property.usage = PROPERTY_USAGE_NOEDITOR;
+ p_property.usage = PROPERTY_USAGE_NO_EDITOR;
}
} else if (p_property.name == "size") {
if (mode != PROJECTION_ORTHOGONAL && mode != PROJECTION_FRUSTUM) {
- p_property.usage = PROPERTY_USAGE_NOEDITOR;
+ p_property.usage = PROPERTY_USAGE_NO_EDITOR;
}
} else if (p_property.name == "frustum_offset") {
if (mode != PROJECTION_FRUSTUM) {
- p_property.usage = PROPERTY_USAGE_NOEDITOR;
+ p_property.usage = PROPERTY_USAGE_NO_EDITOR;
}
}
diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp
index 5f13ed3c66..d347d24c2c 100644
--- a/scene/3d/cpu_particles_3d.cpp
+++ b/scene/3d/cpu_particles_3d.cpp
@@ -1328,7 +1328,7 @@ void CPUParticles3D::convert_from_particles(Node *p_particles) {
set_color(material->get_color());
- Ref<GradientTexture> gt = material->get_color_ramp();
+ Ref<GradientTexture1D> gt = material->get_color_ramp();
if (gt.is_valid()) {
set_color_ramp(gt->get_gradient());
}
diff --git a/scene/3d/decal.cpp b/scene/3d/decal.cpp
index e3c63d62f9..c9f634a5e5 100644
--- a/scene/3d/decal.cpp
+++ b/scene/3d/decal.cpp
@@ -158,7 +158,7 @@ Vector<Face3> Decal::get_faces(uint32_t p_usage_flags) const {
void Decal::_validate_property(PropertyInfo &property) const {
if (!distance_fade_enabled && (property.name == "distance_fade_begin" || property.name == "distance_fade_length")) {
- property.usage = PROPERTY_USAGE_NOEDITOR;
+ property.usage = PROPERTY_USAGE_NO_EDITOR;
}
VisualInstance3D::_validate_property(property);
}
diff --git a/scene/3d/fog_volume.cpp b/scene/3d/fog_volume.cpp
index cc4fbbb41b..694defd7dc 100644
--- a/scene/3d/fog_volume.cpp
+++ b/scene/3d/fog_volume.cpp
@@ -50,6 +50,7 @@ void FogVolume::_validate_property(PropertyInfo &property) const {
property.usage = PROPERTY_USAGE_NONE;
return;
}
+ VisualInstance3D::_validate_property(property);
}
void FogVolume::set_extents(const Vector3 &p_extents) {
diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp
index 13cb8b7dfb..b35a45576f 100644
--- a/scene/3d/gpu_particles_3d.cpp
+++ b/scene/3d/gpu_particles_3d.cpp
@@ -277,7 +277,7 @@ TypedArray<String> GPUParticles3D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (RenderingServer::get_singleton()->is_low_end()) {
- warnings.push_back(TTR("GPU-based particles are not supported by the GLES2 video driver.\nUse the CPUParticles3D node instead. You can use the \"Convert to CPUParticles3D\" option for this purpose."));
+ warnings.push_back(TTR("GPU-based particles are not supported by the OpenGL video driver.\nUse the CPUParticles3D node instead. You can use the \"Convert to CPUParticles3D\" option for this purpose."));
}
bool meshes_found = false;
diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp
index fbdbd79c0c..5d9ae019c2 100644
--- a/scene/3d/light_3d.cpp
+++ b/scene/3d/light_3d.cpp
@@ -197,7 +197,7 @@ bool Light3D::is_editor_only() const {
void Light3D::_validate_property(PropertyInfo &property) const {
if (!shadow && (property.name == "shadow_color" || property.name == "shadow_bias" || property.name == "shadow_normal_bias" || property.name == "shadow_reverse_cull_face" || property.name == "shadow_transmittance_bias" || property.name == "shadow_fog_fade" || property.name == "shadow_blur")) {
- property.usage = PROPERTY_USAGE_NOEDITOR;
+ property.usage = PROPERTY_USAGE_NO_EDITOR;
}
if (get_light_type() != RS::LIGHT_DIRECTIONAL && property.name == "light_angular_distance") {
@@ -379,12 +379,12 @@ bool DirectionalLight3D::is_sky_only() const {
void DirectionalLight3D::_validate_property(PropertyInfo &property) const {
if (shadow_mode == SHADOW_ORTHOGONAL && (property.name == "directional_shadow_split_1" || property.name == "directional_shadow_blend_splits")) {
// Split 2 and split blending are only used with the PSSM 2 Splits and PSSM 4 Splits shadow modes.
- property.usage = PROPERTY_USAGE_NOEDITOR;
+ property.usage = PROPERTY_USAGE_NO_EDITOR;
}
if ((shadow_mode == SHADOW_ORTHOGONAL || shadow_mode == SHADOW_PARALLEL_2_SPLITS) && (property.name == "directional_shadow_split_2" || property.name == "directional_shadow_split_3")) {
// Splits 3 and 4 are only used with the PSSM 4 Splits shadow mode.
- property.usage = PROPERTY_USAGE_NOEDITOR;
+ property.usage = PROPERTY_USAGE_NO_EDITOR;
}
if (property.name == "light_size" || property.name == "light_projector" || property.name == "light_specular") {
diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp
index 3bcb6add76..1b5d4ad243 100644
--- a/scene/3d/lightmap_gi.cpp
+++ b/scene/3d/lightmap_gi.cpp
@@ -200,9 +200,9 @@ void LightmapGIData::_bind_methods() {
ClassDB::bind_method(D_METHOD("_get_probe_data"), &LightmapGIData::_get_probe_data);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "light_texture", PROPERTY_HINT_RESOURCE_TYPE, "TextureLayered"), "set_light_texture", "get_light_texture");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uses_spherical_harmonics", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_uses_spherical_harmonics", "is_using_spherical_harmonics");
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "user_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_user_data", "_get_user_data");
- ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "probe_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_probe_data", "_get_probe_data");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uses_spherical_harmonics", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_uses_spherical_harmonics", "is_using_spherical_harmonics");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "user_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_user_data", "_get_user_data");
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "probe_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_probe_data", "_get_probe_data");
}
LightmapGIData::LightmapGIData() {
diff --git a/scene/3d/mesh_instance_3d.cpp b/scene/3d/mesh_instance_3d.cpp
index c148f95461..ae686143e4 100644
--- a/scene/3d/mesh_instance_3d.cpp
+++ b/scene/3d/mesh_instance_3d.cpp
@@ -244,7 +244,7 @@ Node *MeshInstance3D::create_trimesh_collision_node() {
StaticBody3D *static_body = memnew(StaticBody3D);
CollisionShape3D *cshape = memnew(CollisionShape3D);
cshape->set_shape(shape);
- static_body->add_child(cshape);
+ static_body->add_child(cshape, true);
return static_body;
}
@@ -253,7 +253,7 @@ void MeshInstance3D::create_trimesh_collision() {
ERR_FAIL_COND(!static_body);
static_body->set_name(String(get_name()) + "_col");
- add_child(static_body);
+ add_child(static_body, true);
if (get_owner()) {
CollisionShape3D *cshape = Object::cast_to<CollisionShape3D>(static_body->get_child(0));
static_body->set_owner(get_owner());
@@ -274,7 +274,7 @@ Node *MeshInstance3D::create_convex_collision_node(bool p_clean, bool p_simplify
StaticBody3D *static_body = memnew(StaticBody3D);
CollisionShape3D *cshape = memnew(CollisionShape3D);
cshape->set_shape(shape);
- static_body->add_child(cshape);
+ static_body->add_child(cshape, true);
return static_body;
}
@@ -283,7 +283,7 @@ void MeshInstance3D::create_convex_collision(bool p_clean, bool p_simplify) {
ERR_FAIL_COND(!static_body);
static_body->set_name(String(get_name()) + "_col");
- add_child(static_body);
+ add_child(static_body, true);
if (get_owner()) {
CollisionShape3D *cshape = Object::cast_to<CollisionShape3D>(static_body->get_child(0));
static_body->set_owner(get_owner());
@@ -306,7 +306,7 @@ Node *MeshInstance3D::create_multiple_convex_collisions_node() {
for (int i = 0; i < shapes.size(); i++) {
CollisionShape3D *cshape = memnew(CollisionShape3D);
cshape->set_shape(shapes[i]);
- static_body->add_child(cshape);
+ static_body->add_child(cshape, true);
}
return static_body;
}
@@ -316,7 +316,7 @@ void MeshInstance3D::create_multiple_convex_collisions() {
ERR_FAIL_COND(!static_body);
static_body->set_name(String(get_name()) + "_col");
- add_child(static_body);
+ add_child(static_body, true);
if (get_owner()) {
static_body->set_owner(get_owner());
int count = static_body->get_child_count();
@@ -460,7 +460,7 @@ void MeshInstance3D::create_debug_tangents() {
MeshInstance3D *mi = memnew(MeshInstance3D);
mi->set_mesh(am);
mi->set_name("DebugTangents");
- add_child(mi);
+ add_child(mi, true);
#ifdef TOOLS_ENABLED
if (is_inside_tree() && this == get_tree()->get_edited_scene_root()) {
diff --git a/scene/3d/navigation_obstacle_3d.cpp b/scene/3d/navigation_obstacle_3d.cpp
index 20ffc3b00e..f9fff802e0 100644
--- a/scene/3d/navigation_obstacle_3d.cpp
+++ b/scene/3d/navigation_obstacle_3d.cpp
@@ -35,19 +35,41 @@
#include "servers/navigation_server_3d.h"
void NavigationObstacle3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_estimate_radius", "estimate_radius"), &NavigationObstacle3D::set_estimate_radius);
+ ClassDB::bind_method(D_METHOD("is_radius_estimated"), &NavigationObstacle3D::is_radius_estimated);
+ ClassDB::bind_method(D_METHOD("set_radius", "radius"), &NavigationObstacle3D::set_radius);
+ ClassDB::bind_method(D_METHOD("get_radius"), &NavigationObstacle3D::get_radius);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "estimate_radius"), "set_estimate_radius", "is_radius_estimated");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.01,100,0.01"), "set_radius", "get_radius");
+}
+
+void NavigationObstacle3D::_validate_property(PropertyInfo &p_property) const {
+ if (p_property.name == "radius") {
+ if (estimate_radius) {
+ p_property.usage = PROPERTY_USAGE_NO_EDITOR;
+ }
+ }
}
void NavigationObstacle3D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_READY: {
+ initialize_agent();
+ parent_node3d = Object::cast_to<Node3D>(get_parent());
+ if (parent_node3d != nullptr) {
+ // place agent on navigation map first or else the RVO agent callback creation fails silently later
+ NavigationServer3D::get_singleton()->agent_set_map(get_rid(), parent_node3d->get_world_3d()->get_navigation_map());
+ }
set_physics_process_internal(true);
} break;
case NOTIFICATION_EXIT_TREE: {
+ parent_node3d = nullptr;
set_physics_process_internal(false);
} break;
case NOTIFICATION_PARENTED: {
parent_node3d = Object::cast_to<Node3D>(get_parent());
- update_agent_shape();
+ reevaluate_agent_radius();
} break;
case NOTIFICATION_UNPARENTED: {
parent_node3d = nullptr;
@@ -86,7 +108,22 @@ TypedArray<String> NavigationObstacle3D::get_configuration_warnings() const {
return warnings;
}
-void NavigationObstacle3D::update_agent_shape() {
+void NavigationObstacle3D::initialize_agent() {
+ NavigationServer3D::get_singleton()->agent_set_neighbor_dist(agent, 0.0);
+ NavigationServer3D::get_singleton()->agent_set_max_neighbors(agent, 0);
+ NavigationServer3D::get_singleton()->agent_set_time_horizon(agent, 0.0);
+ NavigationServer3D::get_singleton()->agent_set_max_speed(agent, 0.0);
+}
+
+void NavigationObstacle3D::reevaluate_agent_radius() {
+ if (!estimate_radius) {
+ NavigationServer3D::get_singleton()->agent_set_radius(agent, radius);
+ } else if (parent_node3d) {
+ NavigationServer3D::get_singleton()->agent_set_radius(agent, estimate_agent_radius());
+ }
+}
+
+real_t NavigationObstacle3D::estimate_agent_radius() const {
if (parent_node3d) {
// Estimate the radius of this physics body
real_t radius = 0.0;
@@ -110,15 +147,21 @@ void NavigationObstacle3D::update_agent_shape() {
Vector3 s = parent_node3d->get_global_transform().basis.get_scale();
radius *= MAX(s.x, MAX(s.y, s.z));
- if (radius == 0.0) {
- radius = 1.0; // Never a 0 radius
+ if (radius > 0.0) {
+ return radius;
}
-
- // Initialize the Agent as an object
- NavigationServer3D::get_singleton()->agent_set_neighbor_dist(agent, 0.0);
- NavigationServer3D::get_singleton()->agent_set_max_neighbors(agent, 0);
- NavigationServer3D::get_singleton()->agent_set_time_horizon(agent, 0.0);
- NavigationServer3D::get_singleton()->agent_set_radius(agent, radius);
- NavigationServer3D::get_singleton()->agent_set_max_speed(agent, 0.0);
}
+ return 1.0; // Never a 0 radius
+}
+
+void NavigationObstacle3D::set_estimate_radius(bool p_estimate_radius) {
+ estimate_radius = p_estimate_radius;
+ notify_property_list_changed();
+ reevaluate_agent_radius();
+}
+
+void NavigationObstacle3D::set_radius(real_t p_radius) {
+ ERR_FAIL_COND_MSG(p_radius <= 0.0, "Radius must be greater than 0.");
+ radius = p_radius;
+ reevaluate_agent_radius();
}
diff --git a/scene/3d/navigation_obstacle_3d.h b/scene/3d/navigation_obstacle_3d.h
index ab0b158303..12c813ab08 100644
--- a/scene/3d/navigation_obstacle_3d.h
+++ b/scene/3d/navigation_obstacle_3d.h
@@ -39,8 +39,12 @@ class NavigationObstacle3D : public Node {
Node3D *parent_node3d = nullptr;
RID agent;
+ bool estimate_radius = true;
+ real_t radius = 1.0;
+
protected:
static void _bind_methods();
+ void _validate_property(PropertyInfo &p_property) const override;
void _notification(int p_what);
public:
@@ -51,10 +55,21 @@ public:
return agent;
}
+ void set_estimate_radius(bool p_estimate_radius);
+ bool is_radius_estimated() const {
+ return estimate_radius;
+ }
+ void set_radius(real_t p_radius);
+ real_t get_radius() const {
+ return radius;
+ }
+
TypedArray<String> get_configuration_warnings() const override;
private:
- void update_agent_shape();
+ void initialize_agent();
+ void reevaluate_agent_radius();
+ real_t estimate_agent_radius() const;
};
#endif
diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp
index 1265679b36..b7b88c7135 100644
--- a/scene/3d/node_3d.cpp
+++ b/scene/3d/node_3d.cpp
@@ -943,7 +943,7 @@ void Node3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "rotation_edit_mode", PROPERTY_HINT_ENUM, "Euler,Quaternion,Basis"), "set_rotation_edit_mode", "get_rotation_edit_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "rotation_order", PROPERTY_HINT_ENUM, "XYZ,XZY,YXZ,YZX,ZXY,ZYX"), "set_rotation_order", "get_rotation_order");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "top_level"), "set_as_top_level", "is_set_as_top_level");
- ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_transform", "get_transform");
+ ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_transform", "get_transform");
ADD_GROUP("Visibility", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "visibility_parent", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "GeometryInstance3D"), "set_visibility_parent", "get_visibility_parent");
diff --git a/scene/3d/occluder_instance_3d.cpp b/scene/3d/occluder_instance_3d.cpp
index f3e174c01b..6c548edc74 100644
--- a/scene/3d/occluder_instance_3d.cpp
+++ b/scene/3d/occluder_instance_3d.cpp
@@ -130,8 +130,8 @@ void Occluder3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_indices", "indices"), &Occluder3D::set_indices);
ClassDB::bind_method(D_METHOD("get_indices"), &Occluder3D::get_indices);
- ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_vertices", "get_vertices");
- ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "indices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_indices", "get_indices");
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_vertices", "get_vertices");
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "indices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_indices", "get_indices");
}
Occluder3D::Occluder3D() {
diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp
index 4f1003839e..5cb7f431e3 100644
--- a/scene/3d/physics_body_3d.cpp
+++ b/scene/3d/physics_body_3d.cpp
@@ -174,9 +174,12 @@ bool PhysicsBody3D::test_move(const Transform3D &p_from, const Vector3 &p_linear
ERR_FAIL_COND_V(!is_inside_tree(), false);
PhysicsServer3D::MotionResult *r = nullptr;
+ PhysicsServer3D::MotionResult temp_result;
if (r_collision.is_valid()) {
// Needs const_cast because method bindings don't support non-const Ref.
r = const_cast<PhysicsServer3D::MotionResult *>(&r_collision->result);
+ } else {
+ r = &temp_result;
}
// Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky
@@ -184,7 +187,14 @@ bool PhysicsBody3D::test_move(const Transform3D &p_from, const Vector3 &p_linear
PhysicsServer3D::MotionParameters parameters(p_from, p_linear_velocity * delta, p_margin);
- return PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), parameters, r);
+ bool colliding = PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), parameters, r);
+
+ if (colliding) {
+ // Don't report collision when the whole motion is done.
+ return (r->collision_safe_fraction < 1.0);
+ } else {
+ return false;
+ }
}
void PhysicsBody3D::set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool p_lock) {
@@ -758,8 +768,26 @@ real_t RigidDynamicBody3D::get_gravity_scale() const {
return gravity_scale;
}
+void RigidDynamicBody3D::set_linear_damp_mode(DampMode p_mode) {
+ linear_damp_mode = p_mode;
+ PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_LINEAR_DAMP_MODE, linear_damp_mode);
+}
+
+RigidDynamicBody3D::DampMode RigidDynamicBody3D::get_linear_damp_mode() const {
+ return linear_damp_mode;
+}
+
+void RigidDynamicBody3D::set_angular_damp_mode(DampMode p_mode) {
+ angular_damp_mode = p_mode;
+ PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_ANGULAR_DAMP_MODE, angular_damp_mode);
+}
+
+RigidDynamicBody3D::DampMode RigidDynamicBody3D::get_angular_damp_mode() const {
+ return angular_damp_mode;
+}
+
void RigidDynamicBody3D::set_linear_damp(real_t p_linear_damp) {
- ERR_FAIL_COND(p_linear_damp < -1);
+ ERR_FAIL_COND(p_linear_damp < 0.0);
linear_damp = p_linear_damp;
PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_LINEAR_DAMP, linear_damp);
}
@@ -769,7 +797,7 @@ real_t RigidDynamicBody3D::get_linear_damp() const {
}
void RigidDynamicBody3D::set_angular_damp(real_t p_angular_damp) {
- ERR_FAIL_COND(p_angular_damp < -1);
+ ERR_FAIL_COND(p_angular_damp < 0.0);
angular_damp = p_angular_damp;
PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_ANGULAR_DAMP, angular_damp);
}
@@ -970,6 +998,12 @@ void RigidDynamicBody3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_gravity_scale", "gravity_scale"), &RigidDynamicBody3D::set_gravity_scale);
ClassDB::bind_method(D_METHOD("get_gravity_scale"), &RigidDynamicBody3D::get_gravity_scale);
+ ClassDB::bind_method(D_METHOD("set_linear_damp_mode", "linear_damp_mode"), &RigidDynamicBody3D::set_linear_damp_mode);
+ ClassDB::bind_method(D_METHOD("get_linear_damp_mode"), &RigidDynamicBody3D::get_linear_damp_mode);
+
+ ClassDB::bind_method(D_METHOD("set_angular_damp_mode", "angular_damp_mode"), &RigidDynamicBody3D::set_angular_damp_mode);
+ ClassDB::bind_method(D_METHOD("get_angular_damp_mode"), &RigidDynamicBody3D::get_angular_damp_mode);
+
ClassDB::bind_method(D_METHOD("set_linear_damp", "linear_damp"), &RigidDynamicBody3D::set_linear_damp);
ClassDB::bind_method(D_METHOD("get_linear_damp"), &RigidDynamicBody3D::get_linear_damp);
@@ -1035,10 +1069,12 @@ void RigidDynamicBody3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "freeze_mode", PROPERTY_HINT_ENUM, "Static,Kinematic"), "set_freeze_mode", "get_freeze_mode");
ADD_GROUP("Linear", "linear_");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "linear_velocity"), "set_linear_velocity", "get_linear_velocity");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "linear_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), "set_linear_damp", "get_linear_damp");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "linear_damp_mode", PROPERTY_HINT_ENUM, "Combine,Replace"), "set_linear_damp_mode", "get_linear_damp_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "linear_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_linear_damp", "get_linear_damp");
ADD_GROUP("Angular", "angular_");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "angular_velocity"), "set_angular_velocity", "get_angular_velocity");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), "set_angular_damp", "get_angular_damp");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "angular_damp_mode", PROPERTY_HINT_ENUM, "Combine,Replace"), "set_angular_damp_mode", "get_angular_damp_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_angular_damp", "get_angular_damp");
ADD_SIGNAL(MethodInfo("body_shape_entered", PropertyInfo(Variant::RID, "body_rid"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::INT, "body_shape_index"), PropertyInfo(Variant::INT, "local_shape_index")));
ADD_SIGNAL(MethodInfo("body_shape_exited", PropertyInfo(Variant::RID, "body_rid"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::INT, "body_shape_index"), PropertyInfo(Variant::INT, "local_shape_index")));
@@ -1051,12 +1087,15 @@ void RigidDynamicBody3D::_bind_methods() {
BIND_ENUM_CONSTANT(CENTER_OF_MASS_MODE_AUTO);
BIND_ENUM_CONSTANT(CENTER_OF_MASS_MODE_CUSTOM);
+
+ BIND_ENUM_CONSTANT(DAMP_MODE_COMBINE);
+ BIND_ENUM_CONSTANT(DAMP_MODE_REPLACE);
}
void RigidDynamicBody3D::_validate_property(PropertyInfo &property) const {
if (center_of_mass_mode != CENTER_OF_MASS_MODE_CUSTOM) {
if (property.name == "center_of_mass") {
- property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
}
}
PhysicsBody3D::_validate_property(property);
@@ -1116,6 +1155,10 @@ bool CharacterBody3D::move_and_slide() {
if (bs) {
Vector3 local_position = gt.origin - bs->get_transform().origin;
current_platform_velocity = bs->get_velocity_at_local_position(local_position);
+ } else {
+ // Body is removed or destroyed, invalidate floor.
+ current_platform_velocity = Vector3();
+ platform_rid = RID();
}
} else {
current_platform_velocity = Vector3();
@@ -1904,8 +1947,8 @@ void CharacterBody3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "motion_mode", PROPERTY_HINT_ENUM, "Grounded,Free", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_motion_mode", "get_motion_mode");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "up_direction"), "set_up_direction", "get_up_direction");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slide_on_ceiling"), "set_slide_on_ceiling_enabled", "is_slide_on_ceiling_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "motion_velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_motion_velocity", "get_motion_velocity");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_max_slides", "get_max_slides");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "motion_velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_motion_velocity", "get_motion_velocity");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_max_slides", "get_max_slides");
ADD_GROUP("Free Mode", "free_mode_");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wall_min_slide_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians", PROPERTY_USAGE_DEFAULT), "set_wall_min_slide_angle", "get_wall_min_slide_angle");
ADD_GROUP("Floor", "floor_");
@@ -1931,7 +1974,7 @@ void CharacterBody3D::_bind_methods() {
void CharacterBody3D::_validate_property(PropertyInfo &property) const {
if (motion_mode == MOTION_MODE_FREE) {
if (property.name.begins_with("floor_") || property.name == "up_direction" || property.name == "slide_on_ceiling") {
- property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
}
}
PhysicsBody3D::_validate_property(property);
@@ -2825,6 +2868,12 @@ void PhysicalBone3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_gravity_scale", "gravity_scale"), &PhysicalBone3D::set_gravity_scale);
ClassDB::bind_method(D_METHOD("get_gravity_scale"), &PhysicalBone3D::get_gravity_scale);
+ ClassDB::bind_method(D_METHOD("set_linear_damp_mode", "linear_damp_mode"), &PhysicalBone3D::set_linear_damp_mode);
+ ClassDB::bind_method(D_METHOD("get_linear_damp_mode"), &PhysicalBone3D::get_linear_damp_mode);
+
+ ClassDB::bind_method(D_METHOD("set_angular_damp_mode", "angular_damp_mode"), &PhysicalBone3D::set_angular_damp_mode);
+ ClassDB::bind_method(D_METHOD("get_angular_damp_mode"), &PhysicalBone3D::get_angular_damp_mode);
+
ClassDB::bind_method(D_METHOD("set_linear_damp", "linear_damp"), &PhysicalBone3D::set_linear_damp);
ClassDB::bind_method(D_METHOD("get_linear_damp"), &PhysicalBone3D::get_linear_damp);
@@ -2845,10 +2894,15 @@ void PhysicalBone3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_friction", "get_friction");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_scale", PROPERTY_HINT_RANGE, "-10,10,0.01"), "set_gravity_scale", "get_gravity_scale");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "linear_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), "set_linear_damp", "get_linear_damp");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), "set_angular_damp", "get_angular_damp");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "linear_damp_mode", PROPERTY_HINT_ENUM, "Combine,Replace"), "set_linear_damp_mode", "get_linear_damp_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "linear_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_linear_damp", "get_linear_damp");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "angular_damp_mode", PROPERTY_HINT_ENUM, "Combine,Replace"), "set_angular_damp_mode", "get_angular_damp_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_angular_damp", "get_angular_damp");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "can_sleep"), "set_can_sleep", "is_able_to_sleep");
+ BIND_ENUM_CONSTANT(DAMP_MODE_COMBINE);
+ BIND_ENUM_CONSTANT(DAMP_MODE_REPLACE);
+
BIND_ENUM_CONSTANT(JOINT_TYPE_NONE);
BIND_ENUM_CONSTANT(JOINT_TYPE_PIN);
BIND_ENUM_CONSTANT(JOINT_TYPE_CONE);
@@ -3146,8 +3200,27 @@ real_t PhysicalBone3D::get_gravity_scale() const {
return gravity_scale;
}
+void PhysicalBone3D::set_linear_damp_mode(DampMode p_mode) {
+ linear_damp_mode = p_mode;
+ PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_LINEAR_DAMP_MODE, linear_damp_mode);
+}
+
+PhysicalBone3D::DampMode PhysicalBone3D::get_linear_damp_mode() const {
+ return linear_damp_mode;
+}
+
+void PhysicalBone3D::set_angular_damp_mode(DampMode p_mode) {
+ angular_damp_mode = p_mode;
+ PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_ANGULAR_DAMP_MODE, angular_damp_mode);
+}
+
+PhysicalBone3D::DampMode PhysicalBone3D::get_angular_damp_mode() const {
+ return angular_damp_mode;
+}
+
void PhysicalBone3D::set_linear_damp(real_t p_linear_damp) {
- ERR_FAIL_COND(p_linear_damp < -1);
+ ERR_FAIL_COND(p_linear_damp < 0);
+
linear_damp = p_linear_damp;
PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_LINEAR_DAMP, linear_damp);
}
@@ -3157,7 +3230,8 @@ real_t PhysicalBone3D::get_linear_damp() const {
}
void PhysicalBone3D::set_angular_damp(real_t p_angular_damp) {
- ERR_FAIL_COND(p_angular_damp < -1);
+ ERR_FAIL_COND(p_angular_damp < 0);
+
angular_damp = p_angular_damp;
PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_ANGULAR_DAMP, angular_damp);
}
diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h
index 5677df730c..2ea796d335 100644
--- a/scene/3d/physics_body_3d.h
+++ b/scene/3d/physics_body_3d.h
@@ -143,6 +143,11 @@ public:
CENTER_OF_MASS_MODE_CUSTOM,
};
+ enum DampMode {
+ DAMP_MODE_COMBINE,
+ DAMP_MODE_REPLACE,
+ };
+
private:
bool can_sleep = true;
bool lock_rotation = false;
@@ -160,8 +165,12 @@ private:
Vector3 angular_velocity;
Basis inverse_inertia_tensor;
real_t gravity_scale = 1.0;
- real_t linear_damp = -1.0;
- real_t angular_damp = -1.0;
+
+ DampMode linear_damp_mode = DAMP_MODE_COMBINE;
+ DampMode angular_damp_mode = DAMP_MODE_COMBINE;
+
+ real_t linear_damp = 0.0;
+ real_t angular_damp = 0.0;
bool sleeping = false;
bool ccd = false;
@@ -265,6 +274,12 @@ public:
void set_gravity_scale(real_t p_gravity_scale);
real_t get_gravity_scale() const;
+ void set_linear_damp_mode(DampMode p_mode);
+ DampMode get_linear_damp_mode() const;
+
+ void set_angular_damp_mode(DampMode p_mode);
+ DampMode get_angular_damp_mode() const;
+
void set_linear_damp(real_t p_linear_damp);
real_t get_linear_damp() const;
@@ -310,6 +325,7 @@ private:
VARIANT_ENUM_CAST(RigidDynamicBody3D::FreezeMode);
VARIANT_ENUM_CAST(RigidDynamicBody3D::CenterOfMassMode);
+VARIANT_ENUM_CAST(RigidDynamicBody3D::DampMode);
class KinematicCollision3D;
@@ -494,6 +510,11 @@ class PhysicalBone3D : public PhysicsBody3D {
GDCLASS(PhysicalBone3D, PhysicsBody3D);
public:
+ enum DampMode {
+ DAMP_MODE_COMBINE,
+ DAMP_MODE_REPLACE,
+ };
+
enum JointType {
JOINT_TYPE_NONE,
JOINT_TYPE_PIN,
@@ -632,10 +653,14 @@ private:
real_t mass = 1.0;
real_t friction = 1.0;
real_t gravity_scale = 1.0;
- real_t linear_damp = -1.0;
- real_t angular_damp = -1.0;
bool can_sleep = true;
+ DampMode linear_damp_mode = DAMP_MODE_COMBINE;
+ DampMode angular_damp_mode = DAMP_MODE_COMBINE;
+
+ real_t linear_damp = 0.0;
+ real_t angular_damp = 0.0;
+
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
@@ -698,6 +723,12 @@ public:
void set_gravity_scale(real_t p_gravity_scale);
real_t get_gravity_scale() const;
+ void set_linear_damp_mode(DampMode p_mode);
+ DampMode get_linear_damp_mode() const;
+
+ void set_angular_damp_mode(DampMode p_mode);
+ DampMode get_angular_damp_mode() const;
+
void set_linear_damp(real_t p_linear_damp);
real_t get_linear_damp() const;
@@ -725,5 +756,6 @@ private:
};
VARIANT_ENUM_CAST(PhysicalBone3D::JointType);
+VARIANT_ENUM_CAST(PhysicalBone3D::DampMode);
#endif // PHYSICS_BODY__H
diff --git a/scene/3d/ray_cast_3d.cpp b/scene/3d/ray_cast_3d.cpp
index fd4c6e7416..bfa397a1f5 100644
--- a/scene/3d/ray_cast_3d.cpp
+++ b/scene/3d/ray_cast_3d.cpp
@@ -212,9 +212,17 @@ void RayCast3D::_update_raycast_state() {
to = Vector3(0, 0.01, 0);
}
- PhysicsDirectSpaceState3D::RayResult rr;
+ PhysicsDirectSpaceState3D::RayParameters ray_params;
+ ray_params.from = gt.get_origin();
+ ray_params.to = gt.xform(to);
+ ray_params.exclude = exclude;
+ ray_params.collision_mask = collision_mask;
+ ray_params.collide_with_bodies = collide_with_bodies;
+ ray_params.collide_with_areas = collide_with_areas;
+ ray_params.hit_from_inside = hit_from_inside;
- if (dss->intersect_ray(gt.get_origin(), gt.xform(to), rr, exclude, collision_mask, collide_with_bodies, collide_with_areas)) {
+ PhysicsDirectSpaceState3D::RayResult rr;
+ if (dss->intersect_ray(ray_params, rr)) {
collided = true;
against = rr.collider_id;
collision_point = rr.position;
@@ -261,22 +269,30 @@ void RayCast3D::clear_exceptions() {
exclude.clear();
}
-void RayCast3D::set_collide_with_areas(bool p_clip) {
- collide_with_areas = p_clip;
+void RayCast3D::set_collide_with_areas(bool p_enabled) {
+ collide_with_areas = p_enabled;
}
bool RayCast3D::is_collide_with_areas_enabled() const {
return collide_with_areas;
}
-void RayCast3D::set_collide_with_bodies(bool p_clip) {
- collide_with_bodies = p_clip;
+void RayCast3D::set_collide_with_bodies(bool p_enabled) {
+ collide_with_bodies = p_enabled;
}
bool RayCast3D::is_collide_with_bodies_enabled() const {
return collide_with_bodies;
}
+void RayCast3D::set_hit_from_inside(bool p_enabled) {
+ hit_from_inside = p_enabled;
+}
+
+bool RayCast3D::is_hit_from_inside_enabled() const {
+ return hit_from_inside;
+}
+
void RayCast3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &RayCast3D::set_enabled);
ClassDB::bind_method(D_METHOD("is_enabled"), &RayCast3D::is_enabled);
@@ -315,6 +331,9 @@ void RayCast3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_collide_with_bodies", "enable"), &RayCast3D::set_collide_with_bodies);
ClassDB::bind_method(D_METHOD("is_collide_with_bodies_enabled"), &RayCast3D::is_collide_with_bodies_enabled);
+ ClassDB::bind_method(D_METHOD("set_hit_from_inside", "enable"), &RayCast3D::set_hit_from_inside);
+ ClassDB::bind_method(D_METHOD("is_hit_from_inside_enabled"), &RayCast3D::is_hit_from_inside_enabled);
+
ClassDB::bind_method(D_METHOD("set_debug_shape_custom_color", "debug_shape_custom_color"), &RayCast3D::set_debug_shape_custom_color);
ClassDB::bind_method(D_METHOD("get_debug_shape_custom_color"), &RayCast3D::get_debug_shape_custom_color);
@@ -325,6 +344,7 @@ void RayCast3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "exclude_parent"), "set_exclude_parent_body", "get_exclude_parent_body");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "target_position"), "set_target_position", "get_target_position");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hit_from_inside"), "set_hit_from_inside", "is_hit_from_inside_enabled");
ADD_GROUP("Collide With", "collide_with");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_areas", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collide_with_areas", "is_collide_with_areas_enabled");
diff --git a/scene/3d/ray_cast_3d.h b/scene/3d/ray_cast_3d.h
index 3828bfb4c4..5c2a61c35b 100644
--- a/scene/3d/ray_cast_3d.h
+++ b/scene/3d/ray_cast_3d.h
@@ -65,18 +65,23 @@ class RayCast3D : public Node3D {
bool collide_with_areas = false;
bool collide_with_bodies = true;
+ bool hit_from_inside = false;
+
protected:
void _notification(int p_what);
void _update_raycast_state();
static void _bind_methods();
public:
- void set_collide_with_areas(bool p_clip);
+ void set_collide_with_areas(bool p_enabled);
bool is_collide_with_areas_enabled() const;
- void set_collide_with_bodies(bool p_clip);
+ void set_collide_with_bodies(bool p_enabled);
bool is_collide_with_bodies_enabled() const;
+ void set_hit_from_inside(bool p_enabled);
+ bool is_hit_from_inside_enabled() const;
+
void set_enabled(bool p_enabled);
bool is_enabled() const;
diff --git a/scene/3d/reflection_probe.cpp b/scene/3d/reflection_probe.cpp
index 29c382cd05..40d4d822c9 100644
--- a/scene/3d/reflection_probe.cpp
+++ b/scene/3d/reflection_probe.cpp
@@ -185,7 +185,7 @@ Vector<Face3> ReflectionProbe::get_faces(uint32_t p_usage_flags) const {
void ReflectionProbe::_validate_property(PropertyInfo &property) const {
if (property.name == "interior/ambient_color" || property.name == "interior/ambient_color_energy") {
if (ambient_mode != AMBIENT_COLOR) {
- property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
}
}
VisualInstance3D::_validate_property(property);
diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp
index e3744ad5e9..d4d3c0ebe1 100644
--- a/scene/3d/skeleton_3d.cpp
+++ b/scene/3d/skeleton_3d.cpp
@@ -155,13 +155,13 @@ 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++) {
String prep = "bones/" + itos(i) + "/";
- p_list->push_back(PropertyInfo(Variant::STRING, prep + "name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::INT, prep + "parent", PROPERTY_HINT_RANGE, "-1," + itos(bones.size() - 1) + ",1", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prep + "rest", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::BOOL, prep + "enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + "position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::QUATERNION, prep + "rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ p_list->push_back(PropertyInfo(Variant::STRING, prep + "name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::INT, prep + "parent", PROPERTY_HINT_RANGE, "-1," + itos(bones.size() - 1) + ",1", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prep + "rest", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::BOOL, prep + "enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + "position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::QUATERNION, prep + "rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
}
#ifndef _3D_DISABLED
diff --git a/scene/3d/spring_arm_3d.cpp b/scene/3d/spring_arm_3d.cpp
index 116cab19b1..0b5af8823b 100644
--- a/scene/3d/spring_arm_3d.cpp
+++ b/scene/3d/spring_arm_3d.cpp
@@ -149,10 +149,24 @@ void SpringArm3D::process_spring() {
//use camera rotation, but spring arm position
Transform3D base_transform = camera->get_global_transform();
base_transform.origin = get_global_transform().origin;
- get_world_3d()->get_direct_space_state()->cast_motion(camera->get_pyramid_shape_rid(), base_transform, motion, 0, motion_delta, motion_delta_unsafe, excluded_objects, mask);
+
+ PhysicsDirectSpaceState3D::ShapeParameters shape_params;
+ shape_params.shape_rid = camera->get_pyramid_shape_rid();
+ shape_params.transform = base_transform;
+ shape_params.motion = motion;
+ shape_params.exclude = excluded_objects;
+ shape_params.collision_mask = mask;
+
+ get_world_3d()->get_direct_space_state()->cast_motion(shape_params, motion_delta, motion_delta_unsafe);
} else {
+ PhysicsDirectSpaceState3D::RayParameters ray_params;
+ ray_params.from = get_global_transform().origin;
+ ray_params.to = get_global_transform().origin + motion;
+ ray_params.exclude = excluded_objects;
+ ray_params.collision_mask = mask;
+
PhysicsDirectSpaceState3D::RayResult r;
- bool intersected = get_world_3d()->get_direct_space_state()->intersect_ray(get_global_transform().origin, get_global_transform().origin + motion, r, excluded_objects, mask);
+ bool intersected = get_world_3d()->get_direct_space_state()->intersect_ray(ray_params, r);
if (intersected) {
real_t dist = get_global_transform().origin.distance_to(r.position);
dist -= margin;
@@ -160,7 +174,14 @@ void SpringArm3D::process_spring() {
}
}
} else {
- get_world_3d()->get_direct_space_state()->cast_motion(shape->get_rid(), get_global_transform(), motion, 0, motion_delta, motion_delta_unsafe, excluded_objects, mask);
+ PhysicsDirectSpaceState3D::ShapeParameters shape_params;
+ shape_params.shape_rid = shape->get_rid();
+ shape_params.transform = get_global_transform();
+ shape_params.motion = motion;
+ shape_params.exclude = excluded_objects;
+ shape_params.collision_mask = mask;
+
+ get_world_3d()->get_direct_space_state()->cast_motion(shape_params, motion_delta, motion_delta_unsafe);
}
current_spring_length = spring_length * motion_delta;
diff --git a/scene/3d/vehicle_body_3d.cpp b/scene/3d/vehicle_body_3d.cpp
index 61cba17cde..90db093137 100644
--- a/scene/3d/vehicle_body_3d.cpp
+++ b/scene/3d/vehicle_body_3d.cpp
@@ -407,7 +407,13 @@ real_t VehicleBody3D::_ray_cast(int p_idx, PhysicsDirectBodyState3D *s) {
PhysicsDirectSpaceState3D *ss = s->get_space_state();
- bool col = ss->intersect_ray(source, target, rr, exclude, get_collision_mask());
+ PhysicsDirectSpaceState3D::RayParameters ray_params;
+ ray_params.from = source;
+ ray_params.to = target;
+ ray_params.exclude = exclude;
+ ray_params.collision_mask = get_collision_mask();
+
+ bool col = ss->intersect_ray(ray_params, rr);
wheel.m_raycastInfo.m_groundObject = nullptr;
diff --git a/scene/3d/voxel_gi.cpp b/scene/3d/voxel_gi.cpp
index 377abd5b38..c7108cbae0 100644
--- a/scene/3d/voxel_gi.cpp
+++ b/scene/3d/voxel_gi.cpp
@@ -225,7 +225,7 @@ void VoxelGIData::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_data", "data"), &VoxelGIData::_set_data);
ClassDB::bind_method(D_METHOD("_get_data"), &VoxelGIData::_get_data);
- ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
ADD_PROPERTY(PropertyInfo(Variant::INT, "dynamic_range", PROPERTY_HINT_RANGE, "0,8,0.01"), "set_dynamic_range", "get_dynamic_range");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "energy", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_energy", "get_energy");
@@ -403,7 +403,7 @@ void VoxelGI::bake(Node *p_from_node, bool p_create_visual_debug) {
if (p_create_visual_debug) {
MultiMeshInstance3D *mmi = memnew(MultiMeshInstance3D);
mmi->set_multimesh(baker.create_debug_multimesh());
- add_child(mmi);
+ add_child(mmi, true);
#ifdef TOOLS_ENABLED
if (is_inside_tree() && get_tree()->get_edited_scene_root() == this) {
mmi->set_owner(this);
@@ -458,7 +458,7 @@ TypedArray<String> VoxelGI::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (RenderingServer::get_singleton()->is_low_end()) {
- warnings.push_back(TTR("VoxelGIs are not supported by the GLES2 video driver.\nUse a LightmapGI instead."));
+ warnings.push_back(TTR("VoxelGIs are not supported by the OpenGL video driver.\nUse a LightmapGI instead."));
} else if (probe_data.is_null()) {
warnings.push_back(TTR("No VoxelGI data set, so this node is disabled. Bake static objects to enable GI."));
}
diff --git a/scene/animation/animation_blend_space_1d.cpp b/scene/animation/animation_blend_space_1d.cpp
index 0167992baf..9a71e7bf55 100644
--- a/scene/animation/animation_blend_space_1d.cpp
+++ b/scene/animation/animation_blend_space_1d.cpp
@@ -81,14 +81,14 @@ void AnimationNodeBlendSpace1D::_bind_methods() {
ClassDB::bind_method(D_METHOD("_add_blend_point", "index", "node"), &AnimationNodeBlendSpace1D::_add_blend_point);
for (int i = 0; i < MAX_BLEND_POINTS; i++) {
- ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "blend_point_" + itos(i) + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_add_blend_point", "get_blend_point_node", i);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "blend_point_" + itos(i) + "/pos", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_blend_point_position", "get_blend_point_position", i);
+ ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "blend_point_" + itos(i) + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_add_blend_point", "get_blend_point_node", i);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "blend_point_" + itos(i) + "/pos", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_blend_point_position", "get_blend_point_position", i);
}
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "min_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_min_space", "get_min_space");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_max_space", "get_max_space");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "snap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_snap", "get_snap");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "value_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_value_label", "get_value_label");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "min_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_min_space", "get_min_space");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_max_space", "get_max_space");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "snap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_snap", "get_snap");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "value_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_value_label", "get_value_label");
}
void AnimationNodeBlendSpace1D::get_child_nodes(List<ChildNode> *r_child_nodes) {
diff --git a/scene/animation/animation_blend_space_2d.cpp b/scene/animation/animation_blend_space_2d.cpp
index 145e7c605b..e621f06ce9 100644
--- a/scene/animation/animation_blend_space_2d.cpp
+++ b/scene/animation/animation_blend_space_2d.cpp
@@ -30,6 +30,7 @@
#include "animation_blend_space_2d.h"
+#include "animation_blend_tree.h"
#include "core/math/geometry_2d.h"
void AnimationNodeBlendSpace2D::get_parameter_list(List<PropertyInfo> *r_list) const {
@@ -531,6 +532,12 @@ double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek) {
if (new_closest != closest && new_closest != -1) {
float from = 0.0;
if (blend_mode == BLEND_MODE_DISCRETE_CARRY && closest != -1) {
+ //for ping-pong loop
+ Ref<AnimationNodeAnimation> na_c = static_cast<Ref<AnimationNodeAnimation>>(blend_points[closest].node);
+ Ref<AnimationNodeAnimation> na_n = static_cast<Ref<AnimationNodeAnimation>>(blend_points[new_closest].node);
+ if (!na_c.is_null() && !na_n.is_null()) {
+ na_n->set_backward(na_c->is_backward());
+ }
//see how much animation remains
from = length_internal - blend_node(blend_points[closest].name, blend_points[closest].node, p_time, false, 0.0, FILTER_IGNORE, false);
}
@@ -639,21 +646,21 @@ void AnimationNodeBlendSpace2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("_update_triangles"), &AnimationNodeBlendSpace2D::_update_triangles);
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_triangles", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_auto_triangles", "get_auto_triangles");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_triangles", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_auto_triangles", "get_auto_triangles");
for (int i = 0; i < MAX_BLEND_POINTS; i++) {
- ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "blend_point_" + itos(i) + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_add_blend_point", "get_blend_point_node", i);
- ADD_PROPERTYI(PropertyInfo(Variant::VECTOR2, "blend_point_" + itos(i) + "/pos", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_blend_point_position", "get_blend_point_position", i);
+ ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "blend_point_" + itos(i) + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_add_blend_point", "get_blend_point_node", i);
+ ADD_PROPERTYI(PropertyInfo(Variant::VECTOR2, "blend_point_" + itos(i) + "/pos", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_blend_point_position", "get_blend_point_position", i);
}
- ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "triangles", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_triangles", "_get_triangles");
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "triangles", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_triangles", "_get_triangles");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "min_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_min_space", "get_min_space");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "max_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_max_space", "get_max_space");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "snap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_snap", "get_snap");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "x_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_x_label", "get_x_label");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "y_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_y_label", "get_y_label");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Interpolated,Discrete,Carry", PROPERTY_USAGE_NOEDITOR), "set_blend_mode", "get_blend_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "min_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_min_space", "get_min_space");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "max_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_max_space", "get_max_space");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "snap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_snap", "get_snap");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "x_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_x_label", "get_x_label");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "y_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_y_label", "get_y_label");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Interpolated,Discrete,Carry", PROPERTY_USAGE_NO_EDITOR), "set_blend_mode", "get_blend_mode");
ADD_SIGNAL(MethodInfo("triangles_updated"));
BIND_ENUM_CONSTANT(BLEND_MODE_INTERPOLATED);
diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp
index 10a66386eb..d6c5d0b51c 100644
--- a/scene/animation/animation_blend_tree.cpp
+++ b/scene/animation/animation_blend_tree.cpp
@@ -30,6 +30,7 @@
#include "animation_blend_tree.h"
+#include "scene/resources/animation.h"
#include "scene/scene_string_names.h"
void AnimationNodeAnimation::set_animation(const StringName &p_name) {
@@ -83,30 +84,55 @@ double AnimationNodeAnimation::process(double p_time, bool p_seek) {
}
Ref<Animation> anim = ap->get_animation(animation);
-
- double step;
+ double anim_size = (double)anim->get_length();
+ double step = 0.0;
+ double prev_time = time;
+ int pingponged = 0;
+ bool current_backward = signbit(p_time);
if (p_seek) {
+ step = p_time - time;
time = p_time;
- step = 0;
} else {
- time = MAX(0, time + p_time);
- step = p_time;
+ p_time *= backward ? -1.0 : 1.0;
+ if (!(time == anim_size && !current_backward) && !(time == 0 && current_backward)) {
+ time = time + p_time;
+ step = p_time;
+ }
}
- double anim_size = anim->get_length();
-
- if (anim->has_loop()) {
+ if (anim->get_loop_mode() == Animation::LoopMode::LOOP_PINGPONG) {
if (anim_size) {
- time = Math::fposmod(time, anim_size);
+ if ((int)Math::floor(abs(time - prev_time) / anim_size) % 2 == 0) {
+ if (prev_time > 0 && time <= 0) {
+ backward = !backward;
+ pingponged = -1;
+ }
+ if (prev_time < anim_size && time >= anim_size) {
+ backward = !backward;
+ pingponged = 1;
+ }
+ }
+ time = Math::pingpong(time, anim_size);
}
-
- } else if (time > anim_size) {
- time = anim_size;
+ } else {
+ if (anim->get_loop_mode() == Animation::LoopMode::LOOP_LINEAR) {
+ if (anim_size) {
+ time = Math::fposmod(time, anim_size);
+ }
+ } else if (time < 0) {
+ time = 0;
+ } else if (time > anim_size) {
+ time = anim_size;
+ }
+ backward = false;
}
- blend_animation(animation, time, step, p_seek, 1.0);
-
+ if (play_mode == PLAY_MODE_FORWARD) {
+ blend_animation(animation, time, step, p_seek, 1.0, pingponged);
+ } else {
+ blend_animation(animation, anim_size - time, -step, p_seek, 1.0, pingponged);
+ }
set_parameter(this->time, time);
return anim_size - time;
@@ -116,11 +142,34 @@ String AnimationNodeAnimation::get_caption() const {
return "Animation";
}
+void AnimationNodeAnimation::set_play_mode(PlayMode p_play_mode) {
+ play_mode = p_play_mode;
+}
+
+AnimationNodeAnimation::PlayMode AnimationNodeAnimation::get_play_mode() const {
+ return play_mode;
+}
+
+void AnimationNodeAnimation::set_backward(bool p_backward) {
+ backward = p_backward;
+}
+
+bool AnimationNodeAnimation::is_backward() const {
+ return backward;
+}
+
void AnimationNodeAnimation::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_animation", "name"), &AnimationNodeAnimation::set_animation);
ClassDB::bind_method(D_METHOD("get_animation"), &AnimationNodeAnimation::get_animation);
+ ClassDB::bind_method(D_METHOD("set_play_mode", "mode"), &AnimationNodeAnimation::set_play_mode);
+ ClassDB::bind_method(D_METHOD("get_play_mode"), &AnimationNodeAnimation::get_play_mode);
+
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "animation"), "set_animation", "get_animation");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "play_mode", PROPERTY_HINT_ENUM, "Forward,Backward"), "set_play_mode", "get_play_mode");
+
+ BIND_ENUM_CONSTANT(PLAY_MODE_FORWARD);
+ BIND_ENUM_CONSTANT(PLAY_MODE_BACKWARD);
}
AnimationNodeAnimation::AnimationNodeAnimation() {
@@ -533,7 +582,7 @@ AnimationNodeBlend3::AnimationNodeBlend3() {
/////////////////////////////////
void AnimationNodeTimeScale::get_parameter_list(List<PropertyInfo> *r_list) const {
- r_list->push_back(PropertyInfo(Variant::FLOAT, scale, PROPERTY_HINT_RANGE, "0,32,0.01,or_greater"));
+ r_list->push_back(PropertyInfo(Variant::FLOAT, scale, PROPERTY_HINT_RANGE, "-32,32,0.01,or_lesser,or_greater"));
}
Variant AnimationNodeTimeScale::get_parameter_default_value(const StringName &p_parameter) const {
@@ -1112,12 +1161,12 @@ void AnimationNodeBlendTree::_get_property_list(List<PropertyInfo> *p_list) cons
for (const StringName &E : names) {
String name = E;
if (name != "output") {
- p_list->push_back(PropertyInfo(Variant::OBJECT, "nodes/" + name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationNode", PROPERTY_USAGE_NOEDITOR));
+ p_list->push_back(PropertyInfo(Variant::OBJECT, "nodes/" + name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationNode", PROPERTY_USAGE_NO_EDITOR));
}
- p_list->push_back(PropertyInfo(Variant::VECTOR2, "nodes/" + name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ p_list->push_back(PropertyInfo(Variant::VECTOR2, "nodes/" + name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
}
- p_list->push_back(PropertyInfo(Variant::ARRAY, "node_connections", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ p_list->push_back(PropertyInfo(Variant::ARRAY, "node_connections", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
}
void AnimationNodeBlendTree::reset_state() {
@@ -1152,7 +1201,7 @@ void AnimationNodeBlendTree::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_graph_offset", "offset"), &AnimationNodeBlendTree::set_graph_offset);
ClassDB::bind_method(D_METHOD("get_graph_offset"), &AnimationNodeBlendTree::get_graph_offset);
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_graph_offset", "get_graph_offset");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_graph_offset", "get_graph_offset");
BIND_CONSTANT(CONNECTION_OK);
BIND_CONSTANT(CONNECTION_ERROR_NO_INPUT);
diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h
index 258443a999..e55dfb58ed 100644
--- a/scene/animation/animation_blend_tree.h
+++ b/scene/animation/animation_blend_tree.h
@@ -42,12 +42,12 @@ class AnimationNodeAnimation : public AnimationRootNode {
uint64_t last_version = 0;
bool skip = false;
-protected:
- void _validate_property(PropertyInfo &property) const override;
-
- static void _bind_methods();
-
public:
+ enum PlayMode {
+ PLAY_MODE_FORWARD,
+ PLAY_MODE_BACKWARD
+ };
+
void get_parameter_list(List<PropertyInfo> *r_list) const override;
static Vector<String> (*get_editable_animation_list)();
@@ -58,9 +58,25 @@ public:
void set_animation(const StringName &p_name);
StringName get_animation() const;
+ void set_play_mode(PlayMode p_play_mode);
+ PlayMode get_play_mode() const;
+
+ void set_backward(bool p_backward);
+ bool is_backward() const;
+
AnimationNodeAnimation();
+
+protected:
+ void _validate_property(PropertyInfo &property) const override;
+ static void _bind_methods();
+
+private:
+ PlayMode play_mode = PLAY_MODE_FORWARD;
+ bool backward = false;
};
+VARIANT_ENUM_CAST(AnimationNodeAnimation::PlayMode)
+
class AnimationNodeOneShot : public AnimationNode {
GDCLASS(AnimationNodeOneShot, AnimationNode);
diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp
index 81ecb50ea0..b5d7a0555e 100644
--- a/scene/animation/animation_node_state_machine.cpp
+++ b/scene/animation/animation_node_state_machine.cpp
@@ -909,14 +909,14 @@ void AnimationNodeStateMachine::_get_property_list(List<PropertyInfo> *p_list) c
names.sort_custom<StringName::AlphCompare>();
for (const StringName &name : names) {
- p_list->push_back(PropertyInfo(Variant::OBJECT, "states/" + name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationNode", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::VECTOR2, "states/" + name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ p_list->push_back(PropertyInfo(Variant::OBJECT, "states/" + name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationNode", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::VECTOR2, "states/" + name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
}
- p_list->push_back(PropertyInfo(Variant::ARRAY, "transitions", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::STRING_NAME, "start_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::STRING_NAME, "end_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ p_list->push_back(PropertyInfo(Variant::ARRAY, "transitions", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::STRING_NAME, "start_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::STRING_NAME, "end_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
}
void AnimationNodeStateMachine::reset_state() {
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 26caf826a7..93339711bd 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -174,9 +174,9 @@ void AnimationPlayer::_get_property_list(List<PropertyInfo> *p_list) const {
List<PropertyInfo> anim_names;
for (const KeyValue<StringName, AnimationData> &E : animation_set) {
- anim_names.push_back(PropertyInfo(Variant::OBJECT, "anims/" + String(E.key), PROPERTY_HINT_RESOURCE_TYPE, "Animation", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE));
+ anim_names.push_back(PropertyInfo(Variant::OBJECT, "anims/" + String(E.key), PROPERTY_HINT_RESOURCE_TYPE, "Animation", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE));
if (E.value.next != StringName()) {
- anim_names.push_back(PropertyInfo(Variant::STRING, "next/" + String(E.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
+ anim_names.push_back(PropertyInfo(Variant::STRING, "next/" + String(E.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
}
}
@@ -186,7 +186,7 @@ void AnimationPlayer::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(E);
}
- p_list->push_back(PropertyInfo(Variant::ARRAY, "blend_times", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
+ p_list->push_back(PropertyInfo(Variant::ARRAY, "blend_times", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
}
void AnimationPlayer::advance(float p_time) {
@@ -398,12 +398,13 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov
}
}
-void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double p_time, double p_delta, float p_interp, bool p_is_current, bool p_seeked, bool p_started) {
+void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double p_time, double p_delta, float p_interp, bool p_is_current, bool p_seeked, bool p_started, int p_pingponged) {
_ensure_node_caches(p_anim);
ERR_FAIL_COND(p_anim->node_cache.size() != p_anim->animation->get_track_count());
Animation *a = p_anim->animation.operator->();
bool can_call = is_inside_tree() && !Engine::get_singleton()->is_editor_hint();
+ bool backward = signbit(p_delta);
for (int i = 0; i < a->get_track_count(); i++) {
// If an animation changes this animation (or it animates itself)
@@ -557,8 +558,8 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
continue; //eeh not worth it
}
- float first_key_time = a->track_get_key_time(i, 0);
- float transition = 1.0;
+ double first_key_time = a->track_get_key_time(i, 0);
+ double transition = 1.0;
int first_key = 0;
if (first_key_time == 0.0) {
@@ -566,13 +567,13 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
if (key_count == 1) {
continue; //with one key we can't do anything
}
- transition = a->track_get_key_transition(i, 0);
+ transition = (double)a->track_get_key_transition(i, 0);
first_key_time = a->track_get_key_time(i, 1);
first_key = 1;
}
if (p_time < first_key_time) {
- float c = Math::ease(p_time / first_key_time, transition);
+ double c = Math::ease(p_time / first_key_time, transition);
Variant first_value = a->track_get_key_value(i, first_key);
Variant interp_value;
Variant::interpolate(pa->capture, first_value, c, interp_value);
@@ -614,7 +615,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
} else if (p_is_current && p_delta != 0) {
List<int> indices;
- a->value_track_get_key_indices(i, p_time, p_delta, &indices);
+ a->value_track_get_key_indices(i, p_time, p_delta, &indices, p_pingponged);
for (int &F : indices) {
Variant value = a->track_get_key_value(i, F);
@@ -673,7 +674,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
List<int> indices;
- a->method_track_get_key_indices(i, p_time, p_delta, &indices);
+ a->method_track_get_key_indices(i, p_time, p_delta, &indices, p_pingponged);
for (int &E : indices) {
StringName method = a->method_track_get_name(i, E);
@@ -728,7 +729,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
TrackNodeCache::BezierAnim *ba = &E->get();
- float bezier = a->bezier_track_interpolate(i, p_time);
+ real_t bezier = a->bezier_track_interpolate(i, p_time);
if (ba->accum_pass != accum_pass) {
ERR_CONTINUE(cache_update_bezier_size >= NODE_CACHE_UPDATE_MAX);
cache_update_bezier[cache_update_bezier_size++] = ba;
@@ -789,7 +790,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
} else {
//find stuff to play
List<int> to_play;
- a->track_get_key_indices_in_range(i, p_time, p_delta, &to_play);
+ a->track_get_key_indices_in_range(i, p_time, p_delta, &to_play, p_pingponged);
if (to_play.size()) {
int idx = to_play.back()->get();
@@ -817,12 +818,14 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
nc->audio_start = p_time;
}
} else if (nc->audio_playing) {
- bool loop = a->has_loop();
+ bool loop = a->get_loop_mode() != Animation::LoopMode::LOOP_NONE;
bool stop = false;
- if (!loop && p_time < nc->audio_start) {
- stop = true;
+ if (!loop) {
+ if ((p_time < nc->audio_start && !backward) || (p_time > nc->audio_start && backward)) {
+ stop = true;
+ }
} else if (nc->audio_len > 0) {
float len = nc->audio_start > p_time ? (a->get_length() - nc->audio_start) + p_time : p_time - nc->audio_start;
@@ -863,12 +866,23 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
Ref<Animation> anim = player->get_animation(anim_name);
- double at_anim_pos;
+ double at_anim_pos = 0.0;
- if (anim->has_loop()) {
- at_anim_pos = Math::fposmod(p_time - pos, (double)anim->get_length()); //seek to loop
- } else {
- at_anim_pos = MIN((double)anim->get_length(), p_time - pos); //seek to end
+ switch (anim->get_loop_mode()) {
+ case Animation::LoopMode::LOOP_NONE: {
+ at_anim_pos = MIN((double)anim->get_length(), p_time - pos); //seek to end
+ } break;
+
+ case Animation::LoopMode::LOOP_LINEAR: {
+ at_anim_pos = Math::fposmod(p_time - pos, (double)anim->get_length()); //seek to loop
+ } break;
+
+ case Animation::LoopMode::LOOP_PINGPONG: {
+ at_anim_pos = Math::pingpong(p_time - pos, (double)anim->get_length());
+ } break;
+
+ default:
+ break;
}
if (player->is_playing() || p_seeked) {
@@ -883,7 +897,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
} else {
//find stuff to play
List<int> to_play;
- a->track_get_key_indices_in_range(i, p_time, p_delta, &to_play);
+ a->track_get_key_indices_in_range(i, p_time, p_delta, &to_play, p_pingponged);
if (to_play.size()) {
int idx = to_play.back()->get();
@@ -913,46 +927,73 @@ void AnimationPlayer::_animation_process_data(PlaybackData &cd, double p_delta,
double next_pos = cd.pos + delta;
real_t len = cd.from->animation->get_length();
- bool loop = cd.from->animation->has_loop();
+ int pingponged = 0;
+
+ switch (cd.from->animation->get_loop_mode()) {
+ case Animation::LoopMode::LOOP_NONE: {
+ if (next_pos < 0) {
+ next_pos = 0;
+ } else if (next_pos > len) {
+ next_pos = len;
+ }
- if (!loop) {
- if (next_pos < 0) {
- next_pos = 0;
- } else if (next_pos > len) {
- next_pos = len;
- }
+ bool backwards = signbit(delta); // Negative zero means playing backwards too
+ delta = next_pos - cd.pos; // Fix delta (after determination of backwards because negative zero is lost here)
- bool backwards = signbit(delta); // Negative zero means playing backwards too
- delta = next_pos - cd.pos; // Fix delta (after determination of backwards because negative zero is lost here)
+ if (&cd == &playback.current) {
+ if (!backwards && cd.pos <= len && next_pos == len) {
+ //playback finished
+ end_reached = true;
+ end_notify = cd.pos < len; // Notify only if not already at the end
+ }
- if (&cd == &playback.current) {
- if (!backwards && cd.pos <= len && next_pos == len) {
- //playback finished
- end_reached = true;
- end_notify = cd.pos < len; // Notify only if not already at the end
+ if (backwards && cd.pos >= 0 && next_pos == 0) {
+ //playback finished
+ end_reached = true;
+ end_notify = cd.pos > 0; // Notify only if not already at the beginning
+ }
}
+ } break;
- if (backwards && cd.pos >= 0 && next_pos == 0) {
- //playback finished
- end_reached = true;
- end_notify = cd.pos > 0; // Notify only if not already at the beginning
+ case Animation::LoopMode::LOOP_LINEAR: {
+ double looped_next_pos = Math::fposmod(next_pos, (double)len);
+ if (looped_next_pos == 0 && next_pos != 0) {
+ // Loop multiples of the length to it, rather than 0
+ // so state at time=length is previewable in the editor
+ next_pos = len;
+ } else {
+ next_pos = looped_next_pos;
}
- }
+ } break;
- } else {
- double looped_next_pos = Math::fposmod(next_pos, (double)len);
- if (looped_next_pos == 0 && next_pos != 0) {
- // Loop multiples of the length to it, rather than 0
- // so state at time=length is previewable in the editor
- next_pos = len;
- } else {
- next_pos = looped_next_pos;
- }
+ case Animation::LoopMode::LOOP_PINGPONG: {
+ if ((int)Math::floor(abs(next_pos - cd.pos) / len) % 2 == 0) {
+ if (next_pos < 0 && cd.pos >= 0) {
+ cd.speed_scale *= -1.0;
+ pingponged = -1;
+ }
+ if (next_pos > len && cd.pos <= len) {
+ cd.speed_scale *= -1.0;
+ pingponged = 1;
+ }
+ }
+ double looped_next_pos = Math::pingpong(next_pos, (double)len);
+ if (looped_next_pos == 0 && next_pos != 0) {
+ // Loop multiples of the length to it, rather than 0
+ // so state at time=length is previewable in the editor
+ next_pos = len;
+ } else {
+ next_pos = looped_next_pos;
+ }
+ } break;
+
+ default:
+ break;
}
cd.pos = next_pos;
- _animation_process_animation(cd.from, cd.pos, delta, p_blend, &cd == &playback.current, p_seeked, p_started);
+ _animation_process_animation(cd.from, cd.pos, delta, p_blend, &cd == &playback.current, p_seeked, p_started, pingponged);
}
void AnimationPlayer::_animation_process2(double p_delta, bool p_started) {
@@ -1813,7 +1854,7 @@ void AnimationPlayer::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_node"), "set_root", "get_root");
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "current_animation", PROPERTY_HINT_ENUM, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ANIMATE_AS_TRIGGER), "set_current_animation", "get_current_animation");
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "assigned_animation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_assigned_animation", "get_assigned_animation");
- ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "autoplay", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_autoplay", "get_autoplay");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "autoplay", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_autoplay", "get_autoplay");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "reset_on_save", PROPERTY_HINT_NONE, ""), "set_reset_on_save_enabled", "is_reset_on_save_enabled");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "current_animation_length", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "", "get_current_animation_length");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "current_animation_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "", "get_current_animation_position");
diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h
index d9d88b5510..ea04918988 100644
--- a/scene/animation/animation_player.h
+++ b/scene/animation/animation_player.h
@@ -231,7 +231,7 @@ private:
NodePath root;
- void _animation_process_animation(AnimationData *p_anim, double p_time, double p_delta, float p_interp, bool p_is_current = true, bool p_seeked = false, bool p_started = false);
+ void _animation_process_animation(AnimationData *p_anim, double p_time, double p_delta, float p_interp, bool p_is_current = true, bool p_seeked = false, bool p_started = false, int p_pingponged = 0);
void _ensure_node_caches(AnimationData *p_anim, Node *p_root_override = nullptr);
void _animation_process_data(PlaybackData &cd, double p_delta, float p_blend, bool p_seeked, bool p_started);
diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp
index ccb5fa9472..37e754148c 100644
--- a/scene/animation/animation_tree.cpp
+++ b/scene/animation/animation_tree.cpp
@@ -32,6 +32,7 @@
#include "animation_blend_tree.h"
#include "core/config/engine.h"
+#include "scene/resources/animation.h"
#include "scene/scene_string_names.h"
#include "servers/audio/audio_stream.h"
@@ -87,7 +88,7 @@ void AnimationNode::get_child_nodes(List<ChildNode> *r_child_nodes) {
}
}
-void AnimationNode::blend_animation(const StringName &p_animation, real_t p_time, real_t p_delta, bool p_seeked, real_t p_blend) {
+void AnimationNode::blend_animation(const StringName &p_animation, real_t p_time, real_t p_delta, bool p_seeked, real_t p_blend, int p_pingponged) {
ERR_FAIL_COND(!state);
ERR_FAIL_COND(!state->player->has_animation(p_animation));
@@ -113,6 +114,7 @@ void AnimationNode::blend_animation(const StringName &p_animation, real_t p_time
anim_state.time = p_time;
anim_state.animation = animation;
anim_state.seeked = p_seeked;
+ anim_state.pingponged = p_pingponged;
state->animation_states.push_back(anim_state);
}
@@ -418,15 +420,15 @@ void AnimationNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_filters", "filters"), &AnimationNode::_set_filters);
ClassDB::bind_method(D_METHOD("_get_filters"), &AnimationNode::_get_filters);
- ClassDB::bind_method(D_METHOD("blend_animation", "animation", "time", "delta", "seeked", "blend"), &AnimationNode::blend_animation);
+ ClassDB::bind_method(D_METHOD("blend_animation", "animation", "time", "delta", "seeked", "blend", "pingponged"), &AnimationNode::blend_animation, DEFVAL(0));
ClassDB::bind_method(D_METHOD("blend_node", "name", "node", "time", "seek", "blend", "filter", "optimize"), &AnimationNode::blend_node, DEFVAL(FILTER_IGNORE), DEFVAL(true));
ClassDB::bind_method(D_METHOD("blend_input", "input_index", "time", "seek", "blend", "filter", "optimize"), &AnimationNode::blend_input, DEFVAL(FILTER_IGNORE), DEFVAL(true));
ClassDB::bind_method(D_METHOD("set_parameter", "name", "value"), &AnimationNode::set_parameter);
ClassDB::bind_method(D_METHOD("get_parameter", "name"), &AnimationNode::get_parameter);
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter_enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_filter_enabled", "is_filter_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "filters", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_filters", "_get_filters");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter_enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_filter_enabled", "is_filter_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "filters", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_filters", "_get_filters");
GDVIRTUAL_BIND(_get_child_nodes);
GDVIRTUAL_BIND(_get_parameter_list);
@@ -898,6 +900,10 @@ void AnimationTree::_process_graph(real_t p_delta) {
double delta = as.delta;
real_t weight = as.blend;
bool seeked = as.seeked;
+ int pingponged = as.pingponged;
+#ifndef _3D_DISABLED
+ bool backward = signbit(delta);
+#endif // _3D_DISABLED
for (int i = 0; i < a->get_track_count(); i++) {
NodePath path = a->track_get_path(i);
@@ -939,27 +945,63 @@ void AnimationTree::_process_graph(real_t p_delta) {
}
if (track->root_motion) {
- real_t prev_time = time - delta;
- if (prev_time < 0) {
- if (!a->has_loop()) {
- prev_time = 0;
- } else {
- prev_time = a->get_length() + prev_time;
+ double prev_time = time - delta;
+ if (!backward) {
+ if (prev_time < 0) {
+ switch (a->get_loop_mode()) {
+ case Animation::LOOP_NONE: {
+ prev_time = 0;
+ } break;
+ case Animation::LOOP_LINEAR: {
+ prev_time = Math::fposmod(prev_time, (double)a->get_length());
+ } break;
+ case Animation::LOOP_PINGPONG: {
+ prev_time = Math::pingpong(prev_time, (double)a->get_length());
+ } break;
+ default:
+ break;
+ }
+ }
+ } else {
+ if (prev_time > a->get_length()) {
+ switch (a->get_loop_mode()) {
+ case Animation::LOOP_NONE: {
+ prev_time = (double)a->get_length();
+ } break;
+ case Animation::LOOP_LINEAR: {
+ prev_time = Math::fposmod(prev_time, (double)a->get_length());
+ } break;
+ case Animation::LOOP_PINGPONG: {
+ prev_time = Math::pingpong(prev_time, (double)a->get_length());
+ } break;
+ default:
+ break;
+ }
}
}
Vector3 loc[2];
- if (prev_time > time) {
- Error err = a->position_track_interpolate(i, prev_time, &loc[0]);
- if (err != OK) {
- continue;
+ if (!backward) {
+ if (prev_time > time) {
+ Error err = a->position_track_interpolate(i, prev_time, &loc[0]);
+ if (err != OK) {
+ continue;
+ }
+ a->position_track_interpolate(i, (double)a->get_length(), &loc[1]);
+ t->loc += (loc[1] - loc[0]) * blend;
+ prev_time = 0;
+ }
+ } else {
+ if (prev_time < time) {
+ Error err = a->position_track_interpolate(i, prev_time, &loc[0]);
+ if (err != OK) {
+ continue;
+ }
+ a->position_track_interpolate(i, 0, &loc[1]);
+ t->loc += (loc[1] - loc[0]) * blend;
+ prev_time = 0;
}
-
- a->position_track_interpolate(i, a->get_length(), &loc[1]);
-
- t->loc += (loc[1] - loc[0]) * blend;
- prev_time = 0;
}
Error err = a->position_track_interpolate(i, prev_time, &loc[0]);
@@ -968,17 +1010,13 @@ void AnimationTree::_process_graph(real_t p_delta) {
}
a->position_track_interpolate(i, time, &loc[1]);
-
t->loc += (loc[1] - loc[0]) * blend;
-
- prev_time = 0;
+ prev_time = !backward ? 0 : (double)a->get_length();
} else {
Vector3 loc;
Error err = a->position_track_interpolate(i, time, &loc);
- //ERR_CONTINUE(err!=OK); //used for testing, should be removed
-
if (err != OK) {
continue;
}
@@ -1000,29 +1038,65 @@ void AnimationTree::_process_graph(real_t p_delta) {
}
if (track->root_motion) {
- real_t prev_time = time - delta;
- if (prev_time < 0) {
- if (!a->has_loop()) {
- prev_time = 0;
- } else {
- prev_time = a->get_length() + prev_time;
+ double prev_time = time - delta;
+ if (!backward) {
+ if (prev_time < 0) {
+ switch (a->get_loop_mode()) {
+ case Animation::LOOP_NONE: {
+ prev_time = 0;
+ } break;
+ case Animation::LOOP_LINEAR: {
+ prev_time = Math::fposmod(prev_time, (double)a->get_length());
+ } break;
+ case Animation::LOOP_PINGPONG: {
+ prev_time = Math::pingpong(prev_time, (double)a->get_length());
+ } break;
+ default:
+ break;
+ }
+ }
+ } else {
+ if (prev_time > a->get_length()) {
+ switch (a->get_loop_mode()) {
+ case Animation::LOOP_NONE: {
+ prev_time = (double)a->get_length();
+ } break;
+ case Animation::LOOP_LINEAR: {
+ prev_time = Math::fposmod(prev_time, (double)a->get_length());
+ } break;
+ case Animation::LOOP_PINGPONG: {
+ prev_time = Math::pingpong(prev_time, (double)a->get_length());
+ } break;
+ default:
+ break;
+ }
}
}
Quaternion rot[2];
- if (prev_time > time) {
- Error err = a->rotation_track_interpolate(i, prev_time, &rot[0]);
- if (err != OK) {
- continue;
+ if (!backward) {
+ if (prev_time > time) {
+ Error err = a->rotation_track_interpolate(i, prev_time, &rot[0]);
+ if (err != OK) {
+ continue;
+ }
+ a->rotation_track_interpolate(i, (double)a->get_length(), &rot[1]);
+ Quaternion q = Quaternion().slerp(rot[0].normalized().inverse() * rot[1].normalized(), blend).normalized();
+ t->rot = (t->rot * q).normalized();
+ prev_time = 0;
+ }
+ } else {
+ if (prev_time < time) {
+ Error err = a->rotation_track_interpolate(i, prev_time, &rot[0]);
+ if (err != OK) {
+ continue;
+ }
+ a->rotation_track_interpolate(i, 0, &rot[1]);
+ Quaternion q = Quaternion().slerp(rot[0].normalized().inverse() * rot[1].normalized(), blend).normalized();
+ t->rot = (t->rot * q).normalized();
+ prev_time = 0;
}
-
- a->rotation_track_interpolate(i, a->get_length(), &rot[1]);
-
- Quaternion q = Quaternion().slerp(rot[0].normalized().inverse() * rot[1].normalized(), blend).normalized();
- t->rot = (t->rot * q).normalized();
-
- prev_time = 0;
}
Error err = a->rotation_track_interpolate(i, prev_time, &rot[0]);
@@ -1031,18 +1105,14 @@ void AnimationTree::_process_graph(real_t p_delta) {
}
a->rotation_track_interpolate(i, time, &rot[1]);
-
Quaternion q = Quaternion().slerp(rot[0].normalized().inverse() * rot[1].normalized(), blend).normalized();
t->rot = (t->rot * q).normalized();
-
- prev_time = 0;
+ prev_time = !backward ? 0 : (double)a->get_length();
} else {
Quaternion rot;
Error err = a->rotation_track_interpolate(i, time, &rot);
- //ERR_CONTINUE(err!=OK); //used for testing, should be removed
-
if (err != OK) {
continue;
}
@@ -1071,28 +1141,63 @@ void AnimationTree::_process_graph(real_t p_delta) {
}
if (track->root_motion) {
- real_t prev_time = time - delta;
- if (prev_time < 0) {
- if (!a->has_loop()) {
- prev_time = 0;
- } else {
- prev_time = a->get_length() + prev_time;
+ double prev_time = time - delta;
+ if (!backward) {
+ if (prev_time < 0) {
+ switch (a->get_loop_mode()) {
+ case Animation::LOOP_NONE: {
+ prev_time = 0;
+ } break;
+ case Animation::LOOP_LINEAR: {
+ prev_time = Math::fposmod(prev_time, (double)a->get_length());
+ } break;
+ case Animation::LOOP_PINGPONG: {
+ prev_time = Math::pingpong(prev_time, (double)a->get_length());
+ } break;
+ default:
+ break;
+ }
+ }
+ } else {
+ if (prev_time > a->get_length()) {
+ switch (a->get_loop_mode()) {
+ case Animation::LOOP_NONE: {
+ prev_time = (double)a->get_length();
+ } break;
+ case Animation::LOOP_LINEAR: {
+ prev_time = Math::fposmod(prev_time, (double)a->get_length());
+ } break;
+ case Animation::LOOP_PINGPONG: {
+ prev_time = Math::pingpong(prev_time, (double)a->get_length());
+ } break;
+ default:
+ break;
+ }
}
}
Vector3 scale[2];
- if (prev_time > time) {
- Error err = a->scale_track_interpolate(i, prev_time, &scale[0]);
- if (err != OK) {
- continue;
+ if (!backward) {
+ if (prev_time > time) {
+ Error err = a->scale_track_interpolate(i, prev_time, &scale[0]);
+ if (err != OK) {
+ continue;
+ }
+ a->scale_track_interpolate(i, (double)a->get_length(), &scale[1]);
+ t->scale += (scale[1] - scale[0]) * blend;
+ prev_time = 0;
+ }
+ } else {
+ if (prev_time < time) {
+ Error err = a->scale_track_interpolate(i, prev_time, &scale[0]);
+ if (err != OK) {
+ continue;
+ }
+ a->scale_track_interpolate(i, 0, &scale[1]);
+ t->scale += (scale[1] - scale[0]) * blend;
+ prev_time = 0;
}
-
- a->scale_track_interpolate(i, a->get_length(), &scale[1]);
-
- t->scale += (scale[1] - scale[0]) * blend;
-
- prev_time = 0;
}
Error err = a->scale_track_interpolate(i, prev_time, &scale[0]);
@@ -1101,17 +1206,13 @@ void AnimationTree::_process_graph(real_t p_delta) {
}
a->scale_track_interpolate(i, time, &scale[1]);
-
t->scale += (scale[1] - scale[0]) * blend;
-
- prev_time = 0;
+ prev_time = !backward ? 0 : (double)a->get_length();
} else {
Vector3 scale;
Error err = a->scale_track_interpolate(i, time, &scale);
- //ERR_CONTINUE(err!=OK); //used for testing, should be removed
-
if (err != OK) {
continue;
}
@@ -1164,7 +1265,7 @@ void AnimationTree::_process_graph(real_t p_delta) {
} else {
List<int> indices;
- a->value_track_get_key_indices(i, time, delta, &indices);
+ a->value_track_get_key_indices(i, time, delta, &indices, pingponged);
for (int &F : indices) {
Variant value = a->track_get_key_value(i, F);
@@ -1181,7 +1282,7 @@ void AnimationTree::_process_graph(real_t p_delta) {
List<int> indices;
- a->method_track_get_key_indices(i, time, delta, &indices);
+ a->method_track_get_key_indices(i, time, delta, &indices, pingponged);
for (int &F : indices) {
StringName method = a->method_track_get_name(i, F);
@@ -1264,7 +1365,7 @@ void AnimationTree::_process_graph(real_t p_delta) {
} else {
//find stuff to play
List<int> to_play;
- a->track_get_key_indices_in_range(i, time, delta, &to_play);
+ a->track_get_key_indices_in_range(i, time, delta, &to_play, pingponged);
if (to_play.size()) {
int idx = to_play.back()->get();
@@ -1292,12 +1393,20 @@ void AnimationTree::_process_graph(real_t p_delta) {
t->start = time;
}
} else if (t->playing) {
- bool loop = a->has_loop();
+ bool loop = a->get_loop_mode() != Animation::LoopMode::LOOP_NONE;
bool stop = false;
- if (!loop && time < t->start) {
- stop = true;
+ if (!loop) {
+ if (delta > 0) {
+ if (time < t->start) {
+ stop = true;
+ }
+ } else if (delta < 0) {
+ if (time > t->start) {
+ stop = true;
+ }
+ }
} else if (t->len > 0) {
real_t len = t->start > time ? (a->get_length() - t->start) + time : time - t->start;
@@ -1331,7 +1440,7 @@ void AnimationTree::_process_graph(real_t p_delta) {
continue;
}
- if (delta == 0 || seeked) {
+ if (seeked) {
//seek
int idx = a->track_find_key(i, time);
if (idx < 0) {
@@ -1347,12 +1456,20 @@ void AnimationTree::_process_graph(real_t p_delta) {
Ref<Animation> anim = player2->get_animation(anim_name);
- real_t at_anim_pos;
-
- if (anim->has_loop()) {
- at_anim_pos = Math::fposmod(time - pos, (double)anim->get_length()); //seek to loop
- } else {
- at_anim_pos = MAX(anim->get_length(), time - pos); //seek to end
+ real_t at_anim_pos = 0.0;
+
+ switch (anim->get_loop_mode()) {
+ case Animation::LoopMode::LOOP_NONE: {
+ at_anim_pos = MAX((double)anim->get_length(), time - pos); //seek to end
+ } break;
+ case Animation::LoopMode::LOOP_LINEAR: {
+ at_anim_pos = Math::fposmod(time - pos, (double)anim->get_length()); //seek to loop
+ } break;
+ case Animation::LoopMode::LOOP_PINGPONG: {
+ at_anim_pos = Math::pingpong(time - pos, (double)a->get_length());
+ } break;
+ default:
+ break;
}
if (player2->is_playing() || seeked) {
@@ -1367,7 +1484,7 @@ void AnimationTree::_process_graph(real_t p_delta) {
} else {
//find stuff to play
List<int> to_play;
- a->track_get_key_indices_in_range(i, time, delta, &to_play);
+ a->track_get_key_indices_in_range(i, time, delta, &to_play, pingponged);
if (to_play.size()) {
int idx = to_play.back()->get();
diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h
index 5abea39d20..6fc051fa41 100644
--- a/scene/animation/animation_tree.h
+++ b/scene/animation/animation_tree.h
@@ -66,6 +66,7 @@ public:
const Vector<real_t> *track_blends = nullptr;
real_t blend = 0.0;
bool seeked = false;
+ int pingponged = 0;
};
struct State {
@@ -98,9 +99,10 @@ public:
real_t _blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, real_t p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true, real_t *r_max = nullptr);
protected:
- void blend_animation(const StringName &p_animation, real_t p_time, real_t p_delta, bool p_seeked, real_t p_blend);
+ void blend_animation(const StringName &p_animation, real_t p_time, real_t p_delta, bool p_seeked, real_t p_blend, int p_pingponged = 0);
real_t blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, real_t p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true);
real_t blend_input(int p_input, real_t p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true);
+
void make_invalid(const String &p_reason);
static void _bind_methods();
diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp
index 47e290beb3..da933ae02d 100644
--- a/scene/animation/tween.cpp
+++ b/scene/animation/tween.cpp
@@ -626,7 +626,7 @@ void Tween::_bind_methods() {
ClassDB::bind_method(D_METHOD("parallel"), &Tween::parallel);
ClassDB::bind_method(D_METHOD("chain"), &Tween::chain);
- ClassDB::bind_method(D_METHOD("interpolate_value", "trans_type", "ease_type", "elapsed_time", "initial_value", "delta_value", "duration"), &Tween::interpolate_variant);
+ ClassDB::bind_method(D_METHOD("interpolate_value", "initial_value", "delta_value", "elapsed_time", "duration", "trans_type", "ease_type"), &Tween::interpolate_variant);
ADD_SIGNAL(MethodInfo("step_finished", PropertyInfo(Variant::INT, "idx")));
ADD_SIGNAL(MethodInfo("loop_finished", PropertyInfo(Variant::INT, "loop_count")));
diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp
index bef1011364..9f712ed478 100644
--- a/scene/gui/base_button.cpp
+++ b/scene/gui/base_button.cpp
@@ -62,7 +62,7 @@ void BaseButton::gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseButton> mouse_button = p_event;
bool ui_accept = p_event->is_action("ui_accept") && !p_event->is_echo();
- bool button_masked = mouse_button.is_valid() && ((1 << (mouse_button->get_button_index() - 1)) & button_mask) != 0;
+ bool button_masked = mouse_button.is_valid() && (mouse_button_to_mask(mouse_button->get_button_index()) & button_mask) != MouseButton::NONE;
if (button_masked || ui_accept) {
on_action_event(p_event);
return;
@@ -313,11 +313,11 @@ BaseButton::ActionMode BaseButton::get_action_mode() const {
return action_mode;
}
-void BaseButton::set_button_mask(int p_mask) {
+void BaseButton::set_button_mask(MouseButton p_mask) {
button_mask = p_mask;
}
-int BaseButton::get_button_mask() const {
+MouseButton BaseButton::get_button_mask() const {
return button_mask;
}
diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h
index cd6e78464f..3ea59c3ff9 100644
--- a/scene/gui/base_button.h
+++ b/scene/gui/base_button.h
@@ -45,7 +45,7 @@ public:
};
private:
- int button_mask = MOUSE_BUTTON_MASK_LEFT;
+ MouseButton button_mask = MouseButton::MASK_LEFT;
bool toggle_mode = false;
bool shortcut_in_tooltip = true;
bool keep_pressed_outside = false;
@@ -118,8 +118,8 @@ public:
void set_keep_pressed_outside(bool p_on);
bool is_keep_pressed_outside() const;
- void set_button_mask(int p_mask);
- int get_button_mask() const;
+ void set_button_mask(MouseButton p_mask);
+ MouseButton get_button_mask() const;
void set_shortcut(const Ref<Shortcut> &p_shortcut);
Ref<Shortcut> get_shortcut() const;
diff --git a/scene/gui/box_container.cpp b/scene/gui/box_container.cpp
index cf2df4e1a2..cb9f13e970 100644
--- a/scene/gui/box_container.cpp
+++ b/scene/gui/box_container.cpp
@@ -354,7 +354,7 @@ MarginContainer *VBoxContainer::add_margin_child(const String &p_label, Control
add_child(l, false, INTERNAL_MODE_FRONT);
MarginContainer *mc = memnew(MarginContainer);
mc->add_theme_constant_override("margin_left", 0);
- mc->add_child(p_control);
+ mc->add_child(p_control, true);
add_child(mc, false, INTERNAL_MODE_FRONT);
if (p_expand) {
mc->set_v_size_flags(SIZE_EXPAND_FILL);
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index 046d256867..324b21c6d0 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -263,19 +263,19 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
}
switch (mb->get_button_index()) {
- case MOUSE_BUTTON_WHEEL_UP: {
+ case MouseButton::WHEEL_UP: {
if (code_completion_current_selected > 0) {
code_completion_current_selected--;
update();
}
} break;
- case MOUSE_BUTTON_WHEEL_DOWN: {
+ case MouseButton::WHEEL_DOWN: {
if (code_completion_current_selected < code_completion_options.size() - 1) {
code_completion_current_selected++;
update();
}
} break;
- case MOUSE_BUTTON_LEFT: {
+ case MouseButton::LEFT: {
code_completion_current_selected = CLAMP(code_completion_line_ofs + (mb->get_position().y - code_completion_rect.position.y) / get_line_height(), 0, code_completion_options.size() - 1);
if (mb->is_double_click()) {
confirm_code_completion();
@@ -300,7 +300,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
int line = pos.y;
int col = pos.x;
- if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb->get_button_index() == MouseButton::LEFT) {
if (is_line_folded(line)) {
int wrap_index = get_line_wrap_index_at_column(line, col);
if (wrap_index == get_line_wrap_count(line)) {
@@ -314,7 +314,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
}
}
} else {
- if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb->get_button_index() == MouseButton::LEFT) {
if (mb->is_command_pressed() && symbol_lookup_word != String()) {
Vector2i mpos = mb->get_position();
if (is_layout_rtl()) {
@@ -340,7 +340,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
}
if (symbol_lookup_on_click_enabled) {
- if (mm->is_command_pressed() && mm->get_button_mask() == 0 && !is_dragging_cursor()) {
+ if (mm->is_command_pressed() && mm->get_button_mask() == MouseButton::NONE && !is_dragging_cursor()) {
symbol_lookup_new_word = get_word_at_pos(mpos);
if (symbol_lookup_new_word != symbol_lookup_word) {
emit_signal(SNAME("symbol_validate"), symbol_lookup_new_word);
@@ -360,9 +360,9 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
/* Ctrl + Hover symbols */
#ifdef OSX_ENABLED
- if (k->get_keycode() == KEY_META) {
+ if (k->get_keycode() == Key::META) {
#else
- if (k->get_keycode() == KEY_CTRL) {
+ if (k->get_keycode() == Key::CTRL) {
#endif
if (symbol_lookup_on_click_enabled) {
if (k->is_pressed() && !is_dragging_cursor()) {
@@ -378,7 +378,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
}
/* If a modifier has been pressed, and nothing else, return. */
- if (!k->is_pressed() || k->get_keycode() == KEY_CTRL || k->get_keycode() == KEY_ALT || k->get_keycode() == KEY_SHIFT || k->get_keycode() == KEY_META) {
+ if (!k->is_pressed() || k->get_keycode() == Key::CTRL || k->get_keycode() == Key::ALT || k->get_keycode() == Key::SHIFT || k->get_keycode() == Key::META) {
return;
}
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index 894175d559..049cdb5bef 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -582,7 +582,7 @@ void ColorPicker::_update_text_value() {
void ColorPicker::_sample_input(const Ref<InputEvent> &p_event) {
const Ref<InputEventMouseButton> mb = p_event;
- if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
const Rect2 rect_old = Rect2(Point2(), Size2(sample->get_size().width * 0.5, sample->get_size().height * 0.95));
if (rect_old.has_point(mb->get_position())) {
// Revert to the old color when left-clicking the old color sample.
@@ -827,7 +827,7 @@ void ColorPicker::_uv_input(const Ref<InputEvent> &p_event, Control *c) {
Ref<InputEventMouseButton> bev = p_event;
if (bev.is_valid()) {
- if (bev->is_pressed() && bev->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (bev->is_pressed() && bev->get_button_index() == MouseButton::LEFT) {
Vector2 center = c->get_size() / 2.0;
if (picker_type == SHAPE_VHS_CIRCLE) {
real_t dist = center.distance_to(bev->get_position());
@@ -875,7 +875,7 @@ void ColorPicker::_uv_input(const Ref<InputEvent> &p_event, Control *c) {
if (!deferred_mode_enabled) {
emit_signal(SNAME("color_changed"), color);
}
- } else if (deferred_mode_enabled && !bev->is_pressed() && bev->get_button_index() == MOUSE_BUTTON_LEFT) {
+ } else if (deferred_mode_enabled && !bev->is_pressed() && bev->get_button_index() == MouseButton::LEFT) {
emit_signal(SNAME("color_changed"), color);
changing_color = false;
spinning = false;
@@ -929,7 +929,7 @@ void ColorPicker::_w_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseButton> bev = p_event;
if (bev.is_valid()) {
- if (bev->is_pressed() && bev->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (bev->is_pressed() && bev->get_button_index() == MouseButton::LEFT) {
changing_color = true;
float y = CLAMP((float)bev->get_position().y, 0, w_edit->get_size().height);
if (picker_type == SHAPE_VHS_CIRCLE) {
@@ -946,7 +946,7 @@ void ColorPicker::_w_input(const Ref<InputEvent> &p_event) {
_update_color();
if (!deferred_mode_enabled) {
emit_signal(SNAME("color_changed"), color);
- } else if (!bev->is_pressed() && bev->get_button_index() == MOUSE_BUTTON_LEFT) {
+ } else if (!bev->is_pressed() && bev->get_button_index() == MouseButton::LEFT) {
emit_signal(SNAME("color_changed"), color);
}
}
@@ -977,11 +977,11 @@ void ColorPicker::_preset_input(const Ref<InputEvent> &p_event, const Color &p_c
Ref<InputEventMouseButton> bev = p_event;
if (bev.is_valid()) {
- if (bev->is_pressed() && bev->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (bev->is_pressed() && bev->get_button_index() == MouseButton::LEFT) {
set_pick_color(p_color);
_update_color();
emit_signal(SNAME("color_changed"), p_color);
- } else if (bev->is_pressed() && bev->get_button_index() == MOUSE_BUTTON_RIGHT && presets_enabled) {
+ } else if (bev->is_pressed() && bev->get_button_index() == MouseButton::RIGHT && presets_enabled) {
erase_preset(p_color);
emit_signal(SNAME("preset_removed"), p_color);
}
@@ -994,7 +994,7 @@ void ColorPicker::_screen_input(const Ref<InputEvent> &p_event) {
}
Ref<InputEventMouseButton> bev = p_event;
- if (bev.is_valid() && bev->get_button_index() == MOUSE_BUTTON_LEFT && !bev->is_pressed()) {
+ if (bev.is_valid() && bev->get_button_index() == MouseButton::LEFT && !bev->is_pressed()) {
emit_signal(SNAME("color_changed"), color);
screen->hide();
}
diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp
index 71d2778cc3..f66d3f6835 100644
--- a/scene/gui/dialogs.cpp
+++ b/scene/gui/dialogs.cpp
@@ -44,7 +44,7 @@
void AcceptDialog::_input_from_window(const Ref<InputEvent> &p_event) {
Ref<InputEventKey> key = p_event;
- if (key.is_valid() && key->is_pressed() && key->get_keycode() == KEY_ESCAPE) {
+ if (key.is_valid() && key->is_pressed() && key->get_keycode() == Key::ESCAPE) {
_cancel_pressed();
}
}
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index 5f9f09fc50..44853fc006 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -110,7 +110,7 @@ void FileDialog::unhandled_input(const Ref<InputEvent> &p_event) {
bool handled = true;
switch (k->get_keycode()) {
- case KEY_H: {
+ case Key::H: {
if (k->is_command_pressed()) {
set_show_hidden_files(!show_hidden_files);
} else {
@@ -118,10 +118,10 @@ void FileDialog::unhandled_input(const Ref<InputEvent> &p_event) {
}
} break;
- case KEY_F5: {
+ case Key::F5: {
invalidate();
} break;
- case KEY_BACKSPACE: {
+ case Key::BACKSPACE: {
_dir_submitted("..");
} break;
default: {
diff --git a/scene/gui/gradient_edit.cpp b/scene/gui/gradient_edit.cpp
index 5d024d3be7..ae15b021a5 100644
--- a/scene/gui/gradient_edit.cpp
+++ b/scene/gui/gradient_edit.cpp
@@ -39,6 +39,10 @@ GradientEdit::GradientEdit() {
picker = memnew(ColorPicker);
popup->add_child(picker);
+ gradient_cache.instantiate();
+ preview_texture.instantiate();
+
+ preview_texture->set_width(1024);
add_child(popup, false, INTERNAL_MODE_FRONT);
}
@@ -47,7 +51,7 @@ int GradientEdit::_get_point_from_pos(int x) {
int total_w = get_size().width - get_size().height - draw_spacing;
float min_distance = 1e20;
for (int i = 0; i < points.size(); i++) {
- //Check if we clicked at point
+ // Check if we clicked at point.
float distance = ABS(x - points[i].offset * total_w);
float min = (draw_point_width / 2 * 1.7); //make it easier to grab
if (distance <= min && distance < min_distance) {
@@ -84,7 +88,7 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventKey> k = p_event;
- if (k.is_valid() && k->is_pressed() && k->get_keycode() == KEY_DELETE && grabbed != -1) {
+ if (k.is_valid() && k->is_pressed() && k->get_keycode() == Key::KEY_DELETE && grabbed != -1) {
points.remove(grabbed);
grabbed = -1;
grabbing = false;
@@ -94,15 +98,15 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) {
}
Ref<InputEventMouseButton> mb = p_event;
- //Show color picker on double click.
- if (mb.is_valid() && mb->get_button_index() == 1 && mb->is_double_click() && mb->is_pressed()) {
+ // Show color picker on double click.
+ if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_double_click() && mb->is_pressed()) {
grabbed = _get_point_from_pos(mb->get_position().x);
_show_color_picker();
accept_event();
}
- //Delete point on right click
- if (mb.is_valid() && mb->get_button_index() == 2 && mb->is_pressed()) {
+ // Delete point on right click.
+ if (mb.is_valid() && mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) {
grabbed = _get_point_from_pos(mb->get_position().x);
if (grabbed != -1) {
points.remove(grabbed);
@@ -114,20 +118,20 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) {
}
}
- //Hold alt key to duplicate selected color
- if (mb.is_valid() && mb->get_button_index() == 1 && mb->is_pressed() && mb->is_alt_pressed()) {
+ // Hold alt key to duplicate selected color.
+ if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed() && mb->is_alt_pressed()) {
int x = mb->get_position().x;
grabbed = _get_point_from_pos(x);
if (grabbed != -1) {
int total_w = get_size().width - get_size().height - draw_spacing;
- Gradient::Point newPoint = points[grabbed];
- newPoint.offset = CLAMP(x / float(total_w), 0, 1);
+ Gradient::Point new_point = points[grabbed];
+ new_point.offset = CLAMP(x / float(total_w), 0, 1);
- points.push_back(newPoint);
+ points.push_back(new_point);
points.sort();
for (int i = 0; i < points.size(); ++i) {
- if (points[i].offset == newPoint.offset) {
+ if (points[i].offset == new_point.offset) {
grabbed = i;
break;
}
@@ -138,8 +142,8 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) {
}
}
- //select
- if (mb.is_valid() && mb->get_button_index() == 1 && mb->is_pressed()) {
+ // Select.
+ if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
update();
int x = mb->get_position().x;
int total_w = get_size().width - get_size().height - draw_spacing;
@@ -158,16 +162,16 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) {
return;
}
- //insert
- Gradient::Point newPoint;
- newPoint.offset = CLAMP(x / float(total_w), 0, 1);
+ // Insert point.
+ Gradient::Point new_point;
+ new_point.offset = CLAMP(x / float(total_w), 0, 1);
Gradient::Point prev;
Gradient::Point next;
int pos = -1;
for (int i = 0; i < points.size(); i++) {
- if (points[i].offset < newPoint.offset) {
+ if (points[i].offset < new_point.offset) {
pos = i;
}
}
@@ -191,12 +195,12 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) {
prev = points[pos];
}
- newPoint.color = prev.color.lerp(next.color, (newPoint.offset - prev.offset) / (next.offset - prev.offset));
+ new_point.color = prev.color.lerp(next.color, (new_point.offset - prev.offset) / (next.offset - prev.offset));
- points.push_back(newPoint);
+ points.push_back(new_point);
points.sort();
for (int i = 0; i < points.size(); i++) {
- if (points[i].offset == newPoint.offset) {
+ if (points[i].offset == new_point.offset) {
grabbed = i;
break;
}
@@ -205,7 +209,7 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) {
emit_signal(SNAME("ramp_changed"));
}
- if (mb.is_valid() && mb->get_button_index() == 1 && !mb->is_pressed()) {
+ if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && !mb->is_pressed()) {
if (grabbing) {
grabbing = false;
emit_signal(SNAME("ramp_changed"));
@@ -223,7 +227,7 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) {
float newofs = CLAMP(x / float(total_w), 0, 1);
// Snap to "round" coordinates if holding Ctrl.
- // Be more precise if holding Shift as well
+ // Be more precise if holding Shift as well.
if (mm->is_ctrl_pressed()) {
newofs = Math::snapped(newofs, mm->is_shift_pressed() ? 0.025 : 0.1);
} else if (mm->is_shift_pressed()) {
@@ -299,57 +303,22 @@ void GradientEdit::_notification(int p_what) {
int h = get_size().y;
if (w == 0 || h == 0) {
- return; //Safety check. We have division by 'h'. And in any case there is nothing to draw with such size
+ return; // Safety check. We have division by 'h'. And in any case there is nothing to draw with such size.
}
int total_w = get_size().width - get_size().height - draw_spacing;
- //Draw checker pattern for ramp
+ // Draw checker pattern for ramp.
draw_texture_rect(get_theme_icon(SNAME("GuiMiniCheckerboard"), SNAME("EditorIcons")), Rect2(0, 0, total_w, h), true);
- //Draw color ramp
- Gradient::Point prev;
- prev.offset = 0;
- if (points.size() == 0) {
- prev.color = Color(0, 0, 0); //Draw black rectangle if we have no points
- } else {
- prev.color = points[0].color; //Extend color of first point to the beginning.
- }
+ // Draw color ramp.
- for (int i = -1; i < points.size(); i++) {
- Gradient::Point next;
- //If there is no next point
- if (i + 1 == points.size()) {
- if (points.size() == 0) {
- next.color = Color(0, 0, 0); //Draw black rectangle if we have no points
- } else {
- next.color = points[i].color; //Extend color of last point to the end.
- }
- next.offset = 1;
- } else {
- next = points[i + 1];
- }
+ gradient_cache->set_points(points);
+ gradient_cache->set_interpolation_mode(interpolation_mode);
+ preview_texture->set_gradient(gradient_cache);
+ draw_texture_rect(preview_texture, Rect2(0, 0, total_w, h));
- if (prev.offset == next.offset) {
- prev = next;
- continue;
- }
-
- Vector<Vector2> points;
- Vector<Color> colors;
- points.push_back(Vector2(prev.offset * total_w, h));
- points.push_back(Vector2(prev.offset * total_w, 0));
- points.push_back(Vector2(next.offset * total_w, 0));
- points.push_back(Vector2(next.offset * total_w, h));
- colors.push_back(prev.color);
- colors.push_back(prev.color);
- colors.push_back(next.color);
- colors.push_back(next.color);
- draw_primitive(points, colors, Vector<Point2>());
- prev = next;
- }
-
- //Draw point markers
+ // Draw point markers.
for (int i = 0; i < points.size(); i++) {
Color col = points[i].color.inverted();
col.a = 0.9;
@@ -383,7 +352,7 @@ void GradientEdit::_notification(int p_what) {
draw_line(Vector2(total_w + draw_spacing, h), Vector2(total_w + draw_spacing + h, 0), Color(1, 1, 1, 0.6));
}
- //Draw borders around color ramp if in focus
+ // Draw borders around color ramp if in focus.
if (has_focus()) {
draw_line(Vector2(-1, -1), Vector2(total_w + 1, -1), Color(1, 1, 1, 0.6));
draw_line(Vector2(total_w + 1, -1), Vector2(total_w + 1, h + 1), Color(1, 1, 1, 0.6));
@@ -448,12 +417,21 @@ void GradientEdit::set_points(Vector<Gradient::Point> &p_points) {
}
points.clear();
points = p_points;
+ points.sort();
}
Vector<Gradient::Point> &GradientEdit::get_points() {
return points;
}
+void GradientEdit::set_interpolation_mode(Gradient::InterpolationMode p_interp_mode) {
+ interpolation_mode = p_interp_mode;
+}
+
+Gradient::InterpolationMode GradientEdit::get_interpolation_mode() {
+ return interpolation_mode;
+}
+
void GradientEdit::_bind_methods() {
ADD_SIGNAL(MethodInfo("ramp_changed"));
}
diff --git a/scene/gui/gradient_edit.h b/scene/gui/gradient_edit.h
index f3a39daaf6..66b60d87c7 100644
--- a/scene/gui/gradient_edit.h
+++ b/scene/gui/gradient_edit.h
@@ -45,6 +45,10 @@ class GradientEdit : public Control {
bool grabbing = false;
int grabbed = -1;
Vector<Gradient::Point> points;
+ Gradient::InterpolationMode interpolation_mode = Gradient::GRADIENT_INTERPOLATE_LINEAR;
+
+ Ref<Gradient> gradient_cache;
+ Ref<GradientTexture1D> preview_texture;
// Make sure to use the scaled value below.
const int BASE_SPACING = 3;
@@ -69,6 +73,9 @@ public:
Vector<Color> get_colors() const;
void set_points(Vector<Gradient::Point> &p_points);
Vector<Gradient::Point> &get_points();
+ void set_interpolation_mode(Gradient::InterpolationMode p_interp_mode);
+ Gradient::InterpolationMode get_interpolation_mode();
+
virtual Size2 get_minimum_size() const override;
GradientEdit();
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index 35e31be9af..e7d98a686f 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -150,7 +150,7 @@ void GraphEditMinimap::gui_input(const Ref<InputEvent> &p_ev) {
Ref<InputEventMouseButton> mb = p_ev;
Ref<InputEventMouseMotion> mm = p_ev;
- if (mb.is_valid() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT) {
if (mb->is_pressed()) {
is_pressing = true;
@@ -501,6 +501,43 @@ void GraphEdit::_notification(int p_what) {
}
}
+void GraphEdit::_update_comment_enclosed_nodes_list(GraphNode *p_node, HashMap<StringName, Vector<GraphNode *>> &p_comment_enclosed_nodes) {
+ Rect2 comment_node_rect = p_node->get_rect();
+ Vector<GraphNode *> enclosed_nodes;
+
+ for (int i = 0; i < get_child_count(); i++) {
+ GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
+ if (!gn || gn->is_selected()) {
+ continue;
+ }
+
+ Rect2 node_rect = gn->get_rect();
+ bool included = comment_node_rect.encloses(node_rect);
+ if (included) {
+ enclosed_nodes.push_back(gn);
+ }
+ }
+
+ p_comment_enclosed_nodes.set(p_node->get_name(), enclosed_nodes);
+}
+
+void GraphEdit::_set_drag_comment_enclosed_nodes(GraphNode *p_node, HashMap<StringName, Vector<GraphNode *>> &p_comment_enclosed_nodes, bool p_drag) {
+ for (int i = 0; i < p_comment_enclosed_nodes[p_node->get_name()].size(); i++) {
+ p_comment_enclosed_nodes[p_node->get_name()][i]->set_drag(p_drag);
+ }
+}
+
+void GraphEdit::_set_position_of_comment_enclosed_nodes(GraphNode *p_node, HashMap<StringName, Vector<GraphNode *>> &p_comment_enclosed_nodes, Vector2 p_drag_accum) {
+ for (int i = 0; i < p_comment_enclosed_nodes[p_node->get_name()].size(); i++) {
+ Vector2 pos = (p_comment_enclosed_nodes[p_node->get_name()][i]->get_drag_from() * zoom + drag_accum) / zoom;
+ if (is_using_snap() ^ Input::get_singleton()->is_key_pressed(Key::CTRL)) {
+ const int snap = get_snap();
+ pos = pos.snapped(Vector2(snap, snap));
+ }
+ p_comment_enclosed_nodes[p_node->get_name()][i]->set_position_offset(pos);
+ }
+}
+
bool GraphEdit::_filter_input(const Point2 &p_point) {
Ref<Texture2D> port = get_theme_icon(SNAME("port"), SNAME("GraphNode"));
Vector2i port_size = Vector2i(port->get_width(), port->get_height());
@@ -531,7 +568,7 @@ bool GraphEdit::_filter_input(const Point2 &p_point) {
void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
Ref<InputEventMouseButton> mb = p_ev;
- if (mb.is_valid() && mb->get_button_index() == MOUSE_BUTTON_LEFT && mb->is_pressed()) {
+ if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
Ref<Texture2D> port = get_theme_icon(SNAME("port"), SNAME("GraphNode"));
Vector2i port_size = Vector2i(port->get_width(), port->get_height());
@@ -678,7 +715,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
}
}
- if (mb.is_valid() && mb->get_button_index() == MOUSE_BUTTON_LEFT && !mb->is_pressed()) {
+ if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && !mb->is_pressed()) {
if (connecting_valid) {
if (connecting && connecting_target) {
String from = connecting_from;
@@ -1031,7 +1068,7 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
ERR_FAIL_COND(p_ev.is_null());
Ref<InputEventMouseMotion> mm = p_ev;
- if (mm.is_valid() && (mm->get_button_mask() & MOUSE_BUTTON_MASK_MIDDLE || (mm->get_button_mask() & MOUSE_BUTTON_MASK_LEFT && Input::get_singleton()->is_key_pressed(KEY_SPACE)))) {
+ if (mm.is_valid() && ((mm->get_button_mask() & MouseButton::MASK_MIDDLE) != MouseButton::NONE || ((mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE && Input::get_singleton()->is_key_pressed(Key::SPACE)))) {
Vector2i relative = Input::get_singleton()->warp_mouse_motion(mm, get_global_rect());
h_scroll->set_value(h_scroll->get_value() - relative.x);
v_scroll->set_value(v_scroll->get_value() - relative.y);
@@ -1052,12 +1089,15 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
// Snapping can be toggled temporarily by holding down Ctrl.
// This is done here as to not toggle the grid when holding down Ctrl.
- if (is_using_snap() ^ Input::get_singleton()->is_key_pressed(KEY_CTRL)) {
+ if (is_using_snap() ^ Input::get_singleton()->is_key_pressed(Key::CTRL)) {
const int snap = get_snap();
pos = pos.snapped(Vector2(snap, snap));
}
gn->set_position_offset(pos);
+ if (gn->is_comment()) {
+ _set_position_of_comment_enclosed_nodes(gn, comment_enclosed_nodes, drag_accum);
+ }
}
}
}
@@ -1101,7 +1141,7 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
Ref<InputEventMouseButton> b = p_ev;
if (b.is_valid()) {
- if (b->get_button_index() == MOUSE_BUTTON_RIGHT && b->is_pressed()) {
+ if (b->get_button_index() == MouseButton::RIGHT && b->is_pressed()) {
if (box_selecting) {
box_selecting = false;
for (int i = get_child_count() - 1; i >= 0; i--) {
@@ -1131,8 +1171,8 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
}
}
- if (b->get_button_index() == MOUSE_BUTTON_LEFT && !b->is_pressed() && dragging) {
- if (!just_selected && drag_accum == Vector2() && Input::get_singleton()->is_key_pressed(KEY_CTRL)) {
+ if (b->get_button_index() == MouseButton::LEFT && !b->is_pressed() && dragging) {
+ if (!just_selected && drag_accum == Vector2() && Input::get_singleton()->is_key_pressed(Key::CTRL)) {
//deselect current node
for (int i = get_child_count() - 1; i >= 0; i--) {
GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
@@ -1153,6 +1193,9 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
if (gn && gn->is_selected()) {
gn->set_drag(false);
+ if (gn->is_comment()) {
+ _set_drag_comment_enclosed_nodes(gn, comment_enclosed_nodes, false);
+ }
}
}
}
@@ -1170,7 +1213,7 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
connections_layer->update();
}
- if (b->get_button_index() == MOUSE_BUTTON_LEFT && b->is_pressed()) {
+ if (b->get_button_index() == MouseButton::LEFT && b->is_pressed()) {
GraphNode *gn = nullptr;
for (int i = get_child_count() - 1; i >= 0; i--) {
@@ -1196,7 +1239,7 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
dragging = true;
drag_accum = Vector2();
just_selected = !gn->is_selected();
- if (!gn->is_selected() && !Input::get_singleton()->is_key_pressed(KEY_CTRL)) {
+ if (!gn->is_selected() && !Input::get_singleton()->is_key_pressed(Key::CTRL)) {
for (int i = 0; i < get_child_count(); i++) {
GraphNode *o_gn = Object::cast_to<GraphNode>(get_child(i));
if (o_gn) {
@@ -1220,6 +1263,10 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
}
if (o_gn->is_selected()) {
o_gn->set_drag(true);
+ if (o_gn->is_comment()) {
+ _update_comment_enclosed_nodes_list(o_gn, comment_enclosed_nodes);
+ _set_drag_comment_enclosed_nodes(o_gn, comment_enclosed_nodes, true);
+ }
}
}
@@ -1227,7 +1274,7 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
if (_filter_input(b->get_position())) {
return;
}
- if (Input::get_singleton()->is_key_pressed(KEY_SPACE)) {
+ if (Input::get_singleton()->is_key_pressed(Key::SPACE)) {
return;
}
@@ -1272,7 +1319,7 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
}
}
- if (b->get_button_index() == MOUSE_BUTTON_LEFT && !b->is_pressed() && box_selecting) {
+ if (b->get_button_index() == MouseButton::LEFT && !b->is_pressed() && box_selecting) {
box_selecting = false;
box_selecting_rect = Rect2();
previous_selected.clear();
@@ -1280,7 +1327,7 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
minimap->update();
}
- int scroll_direction = (b->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN) - (b->get_button_index() == MOUSE_BUTTON_WHEEL_UP);
+ int scroll_direction = (b->get_button_index() == MouseButton::WHEEL_DOWN) - (b->get_button_index() == MouseButton::WHEEL_UP);
if (scroll_direction != 0) {
if (b->is_ctrl_pressed()) {
if (b->is_shift_pressed()) {
diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h
index 44e50aa3c2..6c11f9df6a 100644
--- a/scene/gui/graph_edit.h
+++ b/scene/gui/graph_edit.h
@@ -217,6 +217,11 @@ private:
Set<int> valid_left_disconnect_types;
Set<int> valid_right_disconnect_types;
+ HashMap<StringName, Vector<GraphNode *>> comment_enclosed_nodes;
+ void _update_comment_enclosed_nodes_list(GraphNode *p_node, HashMap<StringName, Vector<GraphNode *>> &p_comment_enclosed_nodes);
+ void _set_drag_comment_enclosed_nodes(GraphNode *p_node, HashMap<StringName, Vector<GraphNode *>> &p_comment_enclosed_nodes, bool p_drag);
+ void _set_position_of_comment_enclosed_nodes(GraphNode *p_node, HashMap<StringName, Vector<GraphNode *>> &p_comment_enclosed_nodes, Vector2 p_pos);
+
HBoxContainer *zoom_hb;
friend class GraphEditFilter;
diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp
index 8462fd259e..e7094c89b1 100644
--- a/scene/gui/graph_node.cpp
+++ b/scene/gui/graph_node.cpp
@@ -894,7 +894,7 @@ void GraphNode::gui_input(const Ref<InputEvent> &p_ev) {
if (mb.is_valid()) {
ERR_FAIL_COND_MSG(get_parent_control() == nullptr, "GraphNode must be the child of a GraphEdit node.");
- if (mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
Vector2 mpos = mb->get_position();
if (close_rect.size != Size2() && close_rect.has_point(mpos)) {
//send focus to parent
@@ -917,7 +917,7 @@ void GraphNode::gui_input(const Ref<InputEvent> &p_ev) {
emit_signal(SNAME("raise_request"));
}
- if (!mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (!mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
resizing = false;
}
}
diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp
index 4215c9aff4..c0f2cdbef1 100644
--- a/scene/gui/item_list.cpp
+++ b/scene/gui/item_list.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "item_list.h"
+
#include "core/config/project_settings.h"
#include "core/os/os.h"
#include "core/string/translation.h"
@@ -546,7 +547,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseButton> mb = p_event;
- if (defer_select_single >= 0 && mb.is_valid() && mb->get_button_index() == MOUSE_BUTTON_LEFT && !mb->is_pressed()) {
+ if (defer_select_single >= 0 && mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && !mb->is_pressed()) {
select(defer_select_single, true);
emit_signal(SNAME("multi_selected"), defer_select_single, true);
@@ -554,7 +555,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
return;
}
- if (mb.is_valid() && (mb->get_button_index() == MOUSE_BUTTON_LEFT || (allow_rmb_select && mb->get_button_index() == MOUSE_BUTTON_RIGHT)) && mb->is_pressed()) {
+ if (mb.is_valid() && (mb->get_button_index() == MouseButton::LEFT || (allow_rmb_select && mb->get_button_index() == MouseButton::RIGHT)) && mb->is_pressed()) {
search_string = ""; //any mousepress cancels
Vector2 pos = mb->get_position();
Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg"));
@@ -600,16 +601,16 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
}
}
- if (mb->get_button_index() == MOUSE_BUTTON_RIGHT) {
+ if (mb->get_button_index() == MouseButton::RIGHT) {
emit_signal(SNAME("item_rmb_selected"), i, get_local_mouse_position());
}
} else {
- if (!mb->is_double_click() && !mb->is_command_pressed() && select_mode == SELECT_MULTI && items[i].selectable && !items[i].disabled && items[i].selected && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (!mb->is_double_click() && !mb->is_command_pressed() && select_mode == SELECT_MULTI && items[i].selectable && !items[i].disabled && items[i].selected && mb->get_button_index() == MouseButton::LEFT) {
defer_select_single = i;
return;
}
- if (items[i].selected && mb->get_button_index() == MOUSE_BUTTON_RIGHT) {
+ if (items[i].selected && mb->get_button_index() == MouseButton::RIGHT) {
emit_signal(SNAME("item_rmb_selected"), i, get_local_mouse_position());
} else {
bool selected = items[i].selected;
@@ -624,7 +625,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
}
}
- if (mb->get_button_index() == MOUSE_BUTTON_RIGHT) {
+ if (mb->get_button_index() == MouseButton::RIGHT) {
emit_signal(SNAME("item_rmb_selected"), i, get_local_mouse_position());
} else if (/*select_mode==SELECT_SINGLE &&*/ mb->is_double_click()) {
emit_signal(SNAME("item_activated"), i);
@@ -634,7 +635,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
return;
}
- if (mb->get_button_index() == MOUSE_BUTTON_RIGHT) {
+ if (mb->get_button_index() == MouseButton::RIGHT) {
emit_signal(SNAME("rmb_clicked"), mb->get_position());
return;
@@ -643,10 +644,10 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
// Since closest is null, more likely we clicked on empty space, so send signal to interested controls. Allows, for example, implement items deselecting.
emit_signal(SNAME("nothing_selected"));
}
- if (mb.is_valid() && mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP && mb->is_pressed()) {
+ if (mb.is_valid() && mb->get_button_index() == MouseButton::WHEEL_UP && mb->is_pressed()) {
scroll_bar->set_value(scroll_bar->get_value() - scroll_bar->get_page() * mb->get_factor() / 8);
}
- if (mb.is_valid() && mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && mb->is_pressed()) {
+ if (mb.is_valid() && mb->get_button_index() == MouseButton::WHEEL_DOWN && mb->is_pressed()) {
scroll_bar->set_value(scroll_bar->get_value() + scroll_bar->get_page() * mb->get_factor() / 8);
}
@@ -1605,7 +1606,7 @@ void ItemList::_bind_methods() {
ClassDB::bind_method(D_METHOD("move_item", "from_idx", "to_idx"), &ItemList::move_item);
- ClassDB::bind_method(D_METHOD("set_item_count"), &ItemList::set_item_count);
+ ClassDB::bind_method(D_METHOD("set_item_count", "count"), &ItemList::set_item_count);
ClassDB::bind_method(D_METHOD("get_item_count"), &ItemList::get_item_count);
ClassDB::bind_method(D_METHOD("remove_item", "idx"), &ItemList::remove_item);
diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp
index b8cb618171..50908f6a77 100644
--- a/scene/gui/label.cpp
+++ b/scene/gui/label.cpp
@@ -243,11 +243,9 @@ inline void draw_glyph_outline(const Glyph &p_gl, const RID &p_canvas, const Col
if (p_gl.font_rid != RID()) {
if (p_font_shadow_color.a > 0) {
TS->font_draw_glyph(p_gl.font_rid, p_canvas, p_gl.font_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off) + shadow_ofs, p_gl.index, p_font_shadow_color);
- if (p_shadow_outline_size > 0) {
- TS->font_draw_glyph_outline(p_gl.font_rid, p_canvas, p_gl.font_size, p_shadow_outline_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off) + Vector2(-shadow_ofs.x, shadow_ofs.y), p_gl.index, p_font_shadow_color);
- TS->font_draw_glyph_outline(p_gl.font_rid, p_canvas, p_gl.font_size, p_shadow_outline_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off) + Vector2(shadow_ofs.x, -shadow_ofs.y), p_gl.index, p_font_shadow_color);
- TS->font_draw_glyph_outline(p_gl.font_rid, p_canvas, p_gl.font_size, p_shadow_outline_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off) + Vector2(-shadow_ofs.x, -shadow_ofs.y), p_gl.index, p_font_shadow_color);
- }
+ }
+ if (p_font_shadow_color.a > 0 && p_shadow_outline_size > 0) {
+ TS->font_draw_glyph_outline(p_gl.font_rid, p_canvas, p_gl.font_size, p_shadow_outline_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off) + shadow_ofs, p_gl.index, p_font_shadow_color);
}
if (p_font_outline_color.a != 0.0 && p_outline_size > 0) {
TS->font_draw_glyph_outline(p_gl.font_rid, p_canvas, p_gl.font_size, p_outline_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off), p_gl.index, p_font_outline_color);
@@ -397,7 +395,7 @@ void Label::_notification(int p_what) {
int ellipsis_gl_size = TS->shaped_text_get_ellipsis_glyph_count(lines_rid[i]);
// Draw outline. Note: Do not merge this into the single loop with the main text, to prevent overlaps.
- if (font_shadow_color.a > 0 || (font_outline_color.a != 0.0 && outline_size > 0)) {
+ if ((outline_size > 0 && font_outline_color.a != 0) || (font_shadow_color.a != 0)) {
Vector2 offset = ofs;
// Draw RTL ellipsis string when necessary.
if (rtl && ellipsis_pos >= 0) {
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index 8c33d306a1..124d5c7821 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -225,7 +225,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
// Ignore mouse clicks in IME input mode.
return;
}
- if (b->is_pressed() && b->get_button_index() == MOUSE_BUTTON_RIGHT && context_menu_enabled) {
+ if (b->is_pressed() && b->get_button_index() == MouseButton::RIGHT && context_menu_enabled) {
_ensure_menu();
menu->set_position(get_screen_transform().xform(get_local_mouse_position()));
menu->set_size(Vector2(1, 1));
@@ -235,7 +235,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
return;
}
- if (is_middle_mouse_paste_enabled() && b->is_pressed() && b->get_button_index() == MOUSE_BUTTON_MIDDLE && is_editable() && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) {
+ if (is_middle_mouse_paste_enabled() && b->is_pressed() && b->get_button_index() == MouseButton::MIDDLE && is_editable() && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) {
String paste_buffer = DisplayServer::get_singleton()->clipboard_get_primary().strip_escapes();
deselect();
@@ -254,7 +254,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
return;
}
- if (b->get_button_index() != MOUSE_BUTTON_LEFT) {
+ if (b->get_button_index() != MouseButton::LEFT) {
return;
}
@@ -328,7 +328,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
update();
} else {
- if (selection.enabled && !pass && b->get_button_index() == MOUSE_BUTTON_LEFT && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) {
+ if (selection.enabled && !pass && b->get_button_index() == MouseButton::LEFT && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) {
DisplayServer::get_singleton()->clipboard_set_primary(text.substr(selection.begin, selection.end - selection.begin));
}
if (!text.is_empty() && is_editable() && clear_button_enabled) {
@@ -363,7 +363,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
}
}
- if (m->get_button_mask() & MOUSE_BUTTON_LEFT) {
+ if ((m->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE) {
if (selection.creating) {
set_caret_at_pixel_pos(m->get_position().x);
selection_fill_at_caret();
@@ -577,13 +577,12 @@ void LineEdit::drop_data(const Point2 &p_point, const Variant &p_data) {
if (p_data.get_type() == Variant::STRING) {
set_caret_at_pixel_pos(p_point.x);
- int selected = selection.end - selection.begin;
- text.erase(selection.begin, selected);
+ text = text.left(selection.begin) + text.substr(selection.end);
_shape();
insert_text_at_caret(p_data);
- selection.begin = caret_column - selected;
+ selection.begin = caret_column - (selection.end - selection.begin);
selection.end = caret_column;
}
}
@@ -664,7 +663,9 @@ void LineEdit::_notification(int p_what) {
}
Ref<Font> font = get_theme_font(SNAME("font"));
- style->draw(ci, Rect2(Point2(), size));
+ if (!flat) {
+ style->draw(ci, Rect2(Point2(), size));
+ }
if (has_focus()) {
get_theme_stylebox(SNAME("focus"))->draw(ci, Rect2(Point2(), size));
@@ -1242,7 +1243,7 @@ void LineEdit::delete_char() {
return;
}
- text.erase(caret_column - 1, 1);
+ text = text.left(caret_column - 1) + text.substr(caret_column);
_shape();
set_caret_column(get_caret_column() - 1);
@@ -1254,7 +1255,7 @@ void LineEdit::delete_text(int p_from_column, int p_to_column) {
ERR_FAIL_COND_MSG(p_from_column < 0 || p_from_column > p_to_column || p_to_column > text.length(),
vformat("Positional parameters (from: %d, to: %d) are inverted or outside the text length (%d).", p_from_column, p_to_column, text.length()));
- text.erase(p_from_column, p_to_column - p_from_column);
+ text = text.left(p_from_column) + text.substr(p_to_column);
_shape();
caret_column -= CLAMP(caret_column - p_from_column, 0, p_to_column - p_from_column);
@@ -1966,6 +1967,17 @@ Ref<Texture2D> LineEdit::get_right_icon() {
return right_icon;
}
+void LineEdit::set_flat(bool p_enabled) {
+ if (flat != p_enabled) {
+ flat = p_enabled;
+ update();
+ }
+}
+
+bool LineEdit::is_flat() const {
+ return flat;
+}
+
void LineEdit::_text_changed() {
_emit_text_change();
_clear_redo();
@@ -2058,25 +2070,25 @@ void LineEdit::_create_undo_state() {
undo_stack.push_back(op);
}
-int LineEdit::_get_menu_action_accelerator(const String &p_action) {
+Key LineEdit::_get_menu_action_accelerator(const String &p_action) {
const List<Ref<InputEvent>> *events = InputMap::get_singleton()->action_get_events(p_action);
if (!events) {
- return 0;
+ return Key::NONE;
}
// Use first event in the list for the accelerator.
const List<Ref<InputEvent>>::Element *first_event = events->front();
if (!first_event) {
- return 0;
+ return Key::NONE;
}
const Ref<InputEventKey> event = first_event->get();
if (event.is_null()) {
- return 0;
+ return Key::NONE;
}
// Use physical keycode if non-zero
- if (event->get_physical_keycode() != 0) {
+ if (event->get_physical_keycode() != Key::NONE) {
return event->get_physical_keycode_with_modifiers();
} else {
return event->get_keycode_with_modifiers();
@@ -2135,7 +2147,7 @@ void LineEdit::_get_property_list(List<PropertyInfo> *p_list) const {
void LineEdit::_validate_property(PropertyInfo &property) const {
if (!caret_blink_enabled && property.name == "caret_blink_speed") {
- property.usage = PROPERTY_USAGE_NOEDITOR;
+ property.usage = PROPERTY_USAGE_NO_EDITOR;
}
}
@@ -2214,6 +2226,8 @@ void LineEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_deselect_on_focus_loss_enabled"), &LineEdit::is_deselect_on_focus_loss_enabled);
ClassDB::bind_method(D_METHOD("set_right_icon", "icon"), &LineEdit::set_right_icon);
ClassDB::bind_method(D_METHOD("get_right_icon"), &LineEdit::get_right_icon);
+ ClassDB::bind_method(D_METHOD("set_flat", "enabled"), &LineEdit::set_flat);
+ ClassDB::bind_method(D_METHOD("is_flat"), &LineEdit::is_flat);
ADD_SIGNAL(MethodInfo("text_changed", PropertyInfo(Variant::STRING, "new_text")));
ADD_SIGNAL(MethodInfo("text_change_rejected", PropertyInfo(Variant::STRING, "rejected_substring")));
@@ -2269,6 +2283,7 @@ void LineEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selecting_enabled"), "set_selecting_enabled", "is_selecting_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deselect_on_focus_loss_enabled"), "set_deselect_on_focus_loss_enabled", "is_deselect_on_focus_loss_enabled");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "right_icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_right_icon", "get_right_icon");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat");
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_control_chars"), "set_draw_control_chars", "get_draw_control_chars");
@@ -2329,21 +2344,21 @@ void LineEdit::_ensure_menu() {
// Reorganize context menu.
menu->clear();
if (editable) {
- menu->add_item(RTR("Cut"), MENU_CUT, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_cut") : 0);
+ menu->add_item(RTR("Cut"), MENU_CUT, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_cut") : Key::NONE);
}
- menu->add_item(RTR("Copy"), MENU_COPY, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_copy") : 0);
+ menu->add_item(RTR("Copy"), MENU_COPY, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_copy") : Key::NONE);
if (editable) {
- menu->add_item(RTR("Paste"), MENU_PASTE, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_paste") : 0);
+ menu->add_item(RTR("Paste"), MENU_PASTE, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_paste") : Key::NONE);
}
menu->add_separator();
if (is_selecting_enabled()) {
- menu->add_item(RTR("Select All"), MENU_SELECT_ALL, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_text_select_all") : 0);
+ menu->add_item(RTR("Select All"), MENU_SELECT_ALL, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_text_select_all") : Key::NONE);
}
if (editable) {
menu->add_item(RTR("Clear"), MENU_CLEAR);
menu->add_separator();
- menu->add_item(RTR("Undo"), MENU_UNDO, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_undo") : 0);
- menu->add_item(RTR("Redo"), MENU_REDO, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_redo") : 0);
+ menu->add_item(RTR("Undo"), MENU_UNDO, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_undo") : Key::NONE);
+ menu->add_item(RTR("Redo"), MENU_REDO, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_redo") : Key::NONE);
}
menu->add_separator();
menu->add_submenu_item(RTR("Text Writing Direction"), "DirMenu");
diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h
index 3364e02e01..134d5f8f76 100644
--- a/scene/gui/line_edit.h
+++ b/scene/gui/line_edit.h
@@ -130,6 +130,7 @@ private:
bool middle_mouse_paste_enabled = true;
Ref<Texture2D> right_icon;
+ bool flat = false;
struct Selection {
int begin = 0;
@@ -169,7 +170,7 @@ private:
void _clear_redo();
void _create_undo_state();
- int _get_menu_action_accelerator(const String &p_action);
+ Key _get_menu_action_accelerator(const String &p_action);
void _shape();
void _fit_to_width();
@@ -332,6 +333,9 @@ public:
void set_right_icon(const Ref<Texture2D> &p_icon);
Ref<Texture2D> get_right_icon();
+ void set_flat(bool p_enabled);
+ bool is_flat() const;
+
virtual bool is_text_field() const override;
void show_virtual_keyboard();
diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp
index ceb2092e3a..39c7b04955 100644
--- a/scene/gui/menu_button.cpp
+++ b/scene/gui/menu_button.cpp
@@ -110,14 +110,6 @@ PopupMenu *MenuButton::get_popup() const {
return popup;
}
-void MenuButton::_set_items(const Array &p_items) {
- popup->set("items", p_items);
-}
-
-Array MenuButton::_get_items() const {
- return popup->get("items");
-}
-
void MenuButton::set_switch_on_hover(bool p_enabled) {
switch_on_hover = p_enabled;
}
@@ -126,6 +118,16 @@ bool MenuButton::is_switch_on_hover() {
return switch_on_hover;
}
+void MenuButton::set_item_count(int p_count) {
+ ERR_FAIL_COND(p_count < 0);
+ popup->set_item_count(p_count);
+ notify_property_list_changed();
+}
+
+int MenuButton::get_item_count() const {
+ return popup->get_item_count();
+}
+
void MenuButton::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_VISIBILITY_CHANGED: {
@@ -146,16 +148,66 @@ void MenuButton::_notification(int p_what) {
}
}
+bool MenuButton::_set(const StringName &p_name, const Variant &p_value) {
+ Vector<String> components = String(p_name).split("/", true, 2);
+ if (components.size() >= 2 && components[0] == "popup") {
+ bool valid;
+ popup->set(String(p_name).trim_prefix("popup/"), p_value, &valid);
+ return valid;
+ }
+ return false;
+}
+
+bool MenuButton::_get(const StringName &p_name, Variant &r_ret) const {
+ Vector<String> components = String(p_name).split("/", true, 2);
+ if (components.size() >= 2 && components[0] == "popup") {
+ bool valid;
+ r_ret = popup->get(String(p_name).trim_prefix("popup/"), &valid);
+ return valid;
+ }
+ return false;
+}
+
+void MenuButton::_get_property_list(List<PropertyInfo> *p_list) const {
+ for (int i = 0; i < popup->get_item_count(); i++) {
+ p_list->push_back(PropertyInfo(Variant::STRING, vformat("popup/item_%d/text", i)));
+
+ PropertyInfo pi = PropertyInfo(Variant::OBJECT, vformat("popup/item_%d/icon", i), PROPERTY_HINT_RESOURCE_TYPE, "Texture2D");
+ pi.usage &= ~(popup->get_item_icon(i).is_null() ? PROPERTY_USAGE_STORAGE : 0);
+ p_list->push_back(pi);
+
+ pi = PropertyInfo(Variant::INT, vformat("popup/item_%d/checkable", i), PROPERTY_HINT_ENUM, "No,As checkbox,As radio button");
+ pi.usage &= ~(!popup->is_item_checkable(i) ? PROPERTY_USAGE_STORAGE : 0);
+ p_list->push_back(pi);
+
+ pi = PropertyInfo(Variant::BOOL, vformat("popup/item_%d/checked", i));
+ pi.usage &= ~(!popup->is_item_checked(i) ? PROPERTY_USAGE_STORAGE : 0);
+ p_list->push_back(pi);
+
+ pi = PropertyInfo(Variant::INT, vformat("popup/item_%d/id", i), PROPERTY_HINT_RANGE, "1,10,1,or_greater");
+ p_list->push_back(pi);
+
+ pi = PropertyInfo(Variant::BOOL, vformat("popup/item_%d/disabled", i));
+ pi.usage &= ~(!popup->is_item_disabled(i) ? PROPERTY_USAGE_STORAGE : 0);
+ p_list->push_back(pi);
+
+ pi = PropertyInfo(Variant::BOOL, vformat("popup/item_%d/separator", i));
+ pi.usage &= ~(!popup->is_item_separator(i) ? PROPERTY_USAGE_STORAGE : 0);
+ p_list->push_back(pi);
+ }
+}
+
void MenuButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_popup"), &MenuButton::get_popup);
- ClassDB::bind_method(D_METHOD("_set_items"), &MenuButton::_set_items);
- ClassDB::bind_method(D_METHOD("_get_items"), &MenuButton::_get_items);
ClassDB::bind_method(D_METHOD("set_switch_on_hover", "enable"), &MenuButton::set_switch_on_hover);
ClassDB::bind_method(D_METHOD("is_switch_on_hover"), &MenuButton::is_switch_on_hover);
ClassDB::bind_method(D_METHOD("set_disable_shortcuts", "disabled"), &MenuButton::set_disable_shortcuts);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items");
+ ClassDB::bind_method(D_METHOD("set_item_count", "count"), &MenuButton::set_item_count);
+ ClassDB::bind_method(D_METHOD("get_item_count"), &MenuButton::get_item_count);
+
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "switch_on_hover"), "set_switch_on_hover", "is_switch_on_hover");
+ ADD_ARRAY_COUNT("Items", "items_count", "set_item_count", "get_item_count", "popup/item_");
ADD_SIGNAL(MethodInfo("about_to_popup"));
}
diff --git a/scene/gui/menu_button.h b/scene/gui/menu_button.h
index 730495b65d..455b7dc870 100644
--- a/scene/gui/menu_button.h
+++ b/scene/gui/menu_button.h
@@ -44,15 +44,15 @@ class MenuButton : public Button {
Vector2i mouse_pos_adjusted;
- Array _get_items() const;
- void _set_items(const Array &p_items);
-
virtual void gui_input(const Ref<InputEvent> &p_event) override;
void _popup_visibility_changed(bool p_visible);
protected:
void _notification(int p_what);
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
static void _bind_methods();
virtual void unhandled_key_input(const Ref<InputEvent> &p_event) override;
@@ -64,6 +64,9 @@ public:
bool is_switch_on_hover();
void set_disable_shortcuts(bool p_disabled);
+ void set_item_count(int p_count);
+ int get_item_count() const;
+
MenuButton();
~MenuButton();
};
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index c00c040048..dcf3cfeb09 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -332,7 +332,7 @@ void OptionButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_items"), &OptionButton::_set_items);
ClassDB::bind_method(D_METHOD("_get_items"), &OptionButton::_get_items);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items");
// "selected" property must come after "items", otherwise GH-10213 occurs.
ADD_PROPERTY(PropertyInfo(Variant::INT, "selected"), "_select_int", "get_selected");
ADD_SIGNAL(MethodInfo("item_selected", PropertyInfo(Variant::INT, "index")));
diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp
index be05fd5a60..a48ad0f770 100644
--- a/scene/gui/popup.cpp
+++ b/scene/gui/popup.cpp
@@ -36,7 +36,7 @@
void Popup::_input_from_window(const Ref<InputEvent> &p_event) {
Ref<InputEventKey> key = p_event;
- if (key.is_valid() && key->is_pressed() && key->get_keycode() == KEY_ESCAPE) {
+ if (key.is_valid() && key->is_pressed() && key->get_keycode() == Key::ESCAPE) {
_close_pressed();
}
}
@@ -105,8 +105,6 @@ void Popup::_close_pressed() {
_deinitialize_visible_parents();
call_deferred(SNAME("hide"));
-
- emit_signal(SNAME("cancelled"));
}
void Popup::set_as_minsize() {
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index c0a559e624..7e9b545776 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -39,7 +39,7 @@
String PopupMenu::_get_accel_text(const Item &p_item) const {
if (p_item.shortcut.is_valid()) {
return p_item.shortcut->get_as_text();
- } else if (p_item.accel) {
+ } else if (p_item.accel != Key::NONE) {
return keycode_get_string(p_item.accel);
}
return String();
@@ -74,7 +74,7 @@ Size2 PopupMenu::_get_contents_minimum_size() const {
size.width += items[i].text_buf->get_size().x;
size.height += vseparation;
- if (items[i].accel || (items[i].shortcut.is_valid() && items[i].shortcut->has_valid_event())) {
+ if (items[i].accel != Key::NONE || (items[i].shortcut.is_valid() && items[i].shortcut->has_valid_event())) {
int accel_w = hseparation * 2;
accel_w += items[i].accel_text_buf->get_size().x;
accel_max_w = MAX(accel_w, accel_max_w);
@@ -358,15 +358,15 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) {
return;
}
- int button_idx = b->get_button_index();
+ MouseButton button_idx = b->get_button_index();
if (!b->is_pressed()) {
// Activate the item on release of either the left mouse button or
// any mouse button held down when the popup was opened.
// This allows for opening the popup and triggering an action in a single mouse click.
- if (button_idx == MOUSE_BUTTON_LEFT || (initial_button_mask & (1 << (button_idx - 1)))) {
+ if (button_idx == MouseButton::LEFT || (initial_button_mask & mouse_button_to_mask(button_idx)) != MouseButton::NONE) {
bool was_during_grabbed_click = during_grabbed_click;
during_grabbed_click = false;
- initial_button_mask = 0;
+ initial_button_mask = MouseButton::NONE;
// Disable clicks under a time threshold to avoid selection right when opening the popup.
uint64_t now = OS::get_singleton()->get_ticks_msec();
@@ -637,7 +637,7 @@ void PopupMenu::_draw_items() {
}
// Accelerator / Shortcut
- if (items[i].accel || (items[i].shortcut.is_valid() && items[i].shortcut->has_valid_event())) {
+ if (items[i].accel != Key::NONE || (items[i].shortcut.is_valid() && items[i].shortcut->has_valid_event())) {
if (rtl) {
item_ofs.x = scroll_width + style->get_margin(SIDE_LEFT) + item_end_padding;
} else {
@@ -813,16 +813,17 @@ void PopupMenu::_notification(int p_what) {
item.id = p_id == -1 ? items.size() : p_id; \
item.accel = p_accel;
-void PopupMenu::add_item(const String &p_label, int p_id, uint32_t p_accel) {
+void PopupMenu::add_item(const String &p_label, int p_id, Key p_accel) {
Item item;
ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel);
items.push_back(item);
_shape_item(items.size() - 1);
control->update();
child_controls_changed();
+ notify_property_list_changed();
}
-void PopupMenu::add_icon_item(const Ref<Texture2D> &p_icon, const String &p_label, int p_id, uint32_t p_accel) {
+void PopupMenu::add_icon_item(const Ref<Texture2D> &p_icon, const String &p_label, int p_id, Key p_accel) {
Item item;
ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel);
item.icon = p_icon;
@@ -830,9 +831,10 @@ void PopupMenu::add_icon_item(const Ref<Texture2D> &p_icon, const String &p_labe
_shape_item(items.size() - 1);
control->update();
child_controls_changed();
+ notify_property_list_changed();
}
-void PopupMenu::add_check_item(const String &p_label, int p_id, uint32_t p_accel) {
+void PopupMenu::add_check_item(const String &p_label, int p_id, Key p_accel) {
Item item;
ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel);
item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
@@ -842,7 +844,7 @@ void PopupMenu::add_check_item(const String &p_label, int p_id, uint32_t p_accel
child_controls_changed();
}
-void PopupMenu::add_icon_check_item(const Ref<Texture2D> &p_icon, const String &p_label, int p_id, uint32_t p_accel) {
+void PopupMenu::add_icon_check_item(const Ref<Texture2D> &p_icon, const String &p_label, int p_id, Key p_accel) {
Item item;
ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel);
item.icon = p_icon;
@@ -853,7 +855,7 @@ void PopupMenu::add_icon_check_item(const Ref<Texture2D> &p_icon, const String &
child_controls_changed();
}
-void PopupMenu::add_radio_check_item(const String &p_label, int p_id, uint32_t p_accel) {
+void PopupMenu::add_radio_check_item(const String &p_label, int p_id, Key p_accel) {
Item item;
ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel);
item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
@@ -863,7 +865,7 @@ void PopupMenu::add_radio_check_item(const String &p_label, int p_id, uint32_t p
child_controls_changed();
}
-void PopupMenu::add_icon_radio_check_item(const Ref<Texture2D> &p_icon, const String &p_label, int p_id, uint32_t p_accel) {
+void PopupMenu::add_icon_radio_check_item(const Ref<Texture2D> &p_icon, const String &p_label, int p_id, Key p_accel) {
Item item;
ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel);
item.icon = p_icon;
@@ -874,7 +876,7 @@ void PopupMenu::add_icon_radio_check_item(const Ref<Texture2D> &p_icon, const St
child_controls_changed();
}
-void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int p_default_state, int p_id, uint32_t p_accel) {
+void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int p_default_state, int p_id, Key p_accel) {
Item item;
ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel);
item.max_states = p_max_states;
@@ -1043,7 +1045,7 @@ void PopupMenu::set_item_id(int p_idx, int p_id) {
child_controls_changed();
}
-void PopupMenu::set_item_accelerator(int p_idx, uint32_t p_accel) {
+void PopupMenu::set_item_accelerator(int p_idx, Key p_accel) {
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].accel = p_accel;
items.write[p_idx].dirty = true;
@@ -1119,8 +1121,8 @@ Ref<Texture2D> PopupMenu::get_item_icon(int p_idx) const {
return items[p_idx].icon;
}
-uint32_t PopupMenu::get_item_accelerator(int p_idx) const {
- ERR_FAIL_INDEX_V(p_idx, items.size(), 0);
+Key PopupMenu::get_item_accelerator(int p_idx) const {
+ ERR_FAIL_INDEX_V(p_idx, items.size(), Key::NONE);
return items[p_idx].accel;
}
@@ -1271,30 +1273,38 @@ int PopupMenu::get_current_index() const {
return mouse_over;
}
+void PopupMenu::set_item_count(int p_count) {
+ ERR_FAIL_COND(p_count < 0);
+ items.resize(p_count);
+ control->update();
+ child_controls_changed();
+ notify_property_list_changed();
+}
+
int PopupMenu::get_item_count() const {
return items.size();
}
bool PopupMenu::activate_item_by_event(const Ref<InputEvent> &p_event, bool p_for_global_only) {
- Key code = KEY_NONE;
+ Key code = Key::NONE;
Ref<InputEventKey> k = p_event;
if (k.is_valid()) {
code = k->get_keycode();
- if (code == KEY_NONE) {
+ if (code == Key::NONE) {
code = (Key)k->get_unicode();
}
if (k->is_ctrl_pressed()) {
- code |= KEY_MASK_CTRL;
+ code |= KeyModifierMask::CTRL;
}
if (k->is_alt_pressed()) {
- code |= KEY_MASK_ALT;
+ code |= KeyModifierMask::ALT;
}
if (k->is_meta_pressed()) {
- code |= KEY_MASK_META;
+ code |= KeyModifierMask::META;
}
if (k->is_shift_pressed()) {
- code |= KEY_MASK_SHIFT;
+ code |= KeyModifierMask::SHIFT;
}
}
@@ -1308,7 +1318,7 @@ bool PopupMenu::activate_item_by_event(const Ref<InputEvent> &p_event, bool p_fo
return true;
}
- if (code != 0 && items[i].accel == code) {
+ if (code != Key::NONE && items[i].accel == code) {
activate_item(i);
return true;
}
@@ -1420,27 +1430,7 @@ void PopupMenu::clear() {
mouse_over = -1;
control->update();
child_controls_changed();
-}
-
-Array PopupMenu::_get_items() const {
- Array items;
- for (int i = 0; i < get_item_count(); i++) {
- items.push_back(get_item_text(i));
- items.push_back(get_item_icon(i));
- // For compatibility, use false/true for no/checkbox and integers for other values
- int ct = this->items[i].checkable_type;
- items.push_back(Variant(ct <= Item::CHECKABLE_TYPE_CHECK_BOX ? is_item_checkable(i) : ct));
- items.push_back(is_item_checked(i));
- items.push_back(is_item_disabled(i));
-
- items.push_back(get_item_id(i));
- items.push_back(get_item_accelerator(i));
- items.push_back(get_item_metadata(i));
- items.push_back(get_item_submenu(i));
- items.push_back(is_item_separator(i));
- }
-
- return items;
+ notify_property_list_changed();
}
void PopupMenu::_ref_shortcut(Ref<Shortcut> p_sc) {
@@ -1461,45 +1451,6 @@ void PopupMenu::_unref_shortcut(Ref<Shortcut> p_sc) {
}
}
-void PopupMenu::_set_items(const Array &p_items) {
- ERR_FAIL_COND(p_items.size() % 10);
- clear();
-
- for (int i = 0; i < p_items.size(); i += 10) {
- String text = p_items[i + 0];
- Ref<Texture2D> icon = p_items[i + 1];
- // For compatibility, use false/true for no/checkbox and integers for other values
- bool checkable = p_items[i + 2];
- bool radio_checkable = (int)p_items[i + 2] == Item::CHECKABLE_TYPE_RADIO_BUTTON;
- bool checked = p_items[i + 3];
- bool disabled = p_items[i + 4];
-
- int id = p_items[i + 5];
- int accel = p_items[i + 6];
- Variant meta = p_items[i + 7];
- String subm = p_items[i + 8];
- bool sep = p_items[i + 9];
-
- int idx = get_item_count();
- add_item(text, id);
- set_item_icon(idx, icon);
- if (checkable) {
- if (radio_checkable) {
- set_item_as_radio_checkable(idx, true);
- } else {
- set_item_as_checkable(idx, true);
- }
- }
- set_item_checked(idx, checked);
- set_item_disabled(idx, disabled);
- set_item_id(idx, id);
- set_item_metadata(idx, meta);
- set_item_as_separator(idx, sep);
- set_item_accelerator(idx, accel);
- set_item_submenu(idx, subm);
- }
-}
-
// Hide on item selection determines whether or not the popup will close after item selection
void PopupMenu::set_hide_on_item_selection(bool p_enabled) {
hide_on_item_selection = p_enabled;
@@ -1581,6 +1532,145 @@ void PopupMenu::take_mouse_focus() {
}
}
+bool PopupMenu::_set(const StringName &p_name, const Variant &p_value) {
+ Vector<String> components = String(p_name).split("/", true, 2);
+ if (components.size() >= 2 && components[0].begins_with("item_") && components[0].trim_prefix("item_").is_valid_int()) {
+ int item_index = components[0].trim_prefix("item_").to_int();
+ String property = components[1];
+ if (property == "text") {
+ set_item_text(item_index, p_value);
+ return true;
+ } else if (property == "icon") {
+ set_item_icon(item_index, p_value);
+ return true;
+ } else if (property == "checkable") {
+ bool radio_checkable = (int)p_value == Item::CHECKABLE_TYPE_RADIO_BUTTON;
+ if (radio_checkable) {
+ set_item_as_radio_checkable(item_index, true);
+ } else {
+ bool checkable = p_value;
+ set_item_as_checkable(item_index, checkable);
+ }
+ return true;
+ } else if (property == "checked") {
+ set_item_checked(item_index, p_value);
+ return true;
+ } else if (property == "id") {
+ set_item_id(item_index, p_value);
+ return true;
+ } else if (components[1] == "disabled") {
+ set_item_disabled(item_index, p_value);
+ return true;
+ } else if (property == "separator") {
+ set_item_as_separator(item_index, p_value);
+ return true;
+ }
+ }
+#ifndef DISABLE_DEPRECATED
+ // Compatibility.
+ if (p_name == "items") {
+ Array arr = p_value;
+ ERR_FAIL_COND_V(arr.size() % 10, false);
+ clear();
+
+ for (int i = 0; i < arr.size(); i += 10) {
+ String text = arr[i + 0];
+ Ref<Texture2D> icon = arr[i + 1];
+ // For compatibility, use false/true for no/checkbox and integers for other values
+ bool checkable = arr[i + 2];
+ bool radio_checkable = (int)arr[i + 2] == Item::CHECKABLE_TYPE_RADIO_BUTTON;
+ bool checked = arr[i + 3];
+ bool disabled = arr[i + 4];
+
+ int id = arr[i + 5];
+ int accel = arr[i + 6];
+ Variant meta = arr[i + 7];
+ String subm = arr[i + 8];
+ bool sep = arr[i + 9];
+
+ int idx = get_item_count();
+ add_item(text, id);
+ set_item_icon(idx, icon);
+ if (checkable) {
+ if (radio_checkable) {
+ set_item_as_radio_checkable(idx, true);
+ } else {
+ set_item_as_checkable(idx, true);
+ }
+ }
+ set_item_checked(idx, checked);
+ set_item_disabled(idx, disabled);
+ set_item_id(idx, id);
+ set_item_metadata(idx, meta);
+ set_item_as_separator(idx, sep);
+ set_item_accelerator(idx, (Key)accel);
+ set_item_submenu(idx, subm);
+ }
+ }
+#endif
+ return false;
+}
+
+bool PopupMenu::_get(const StringName &p_name, Variant &r_ret) const {
+ Vector<String> components = String(p_name).split("/", true, 2);
+ if (components.size() >= 2 && components[0].begins_with("item_") && components[0].trim_prefix("item_").is_valid_int()) {
+ int item_index = components[0].trim_prefix("item_").to_int();
+ String property = components[1];
+ if (property == "text") {
+ r_ret = get_item_text(item_index);
+ return true;
+ } else if (property == "icon") {
+ r_ret = get_item_icon(item_index);
+ return true;
+ } else if (property == "checkable") {
+ r_ret = this->items[item_index].checkable_type;
+ return true;
+ } else if (property == "checked") {
+ r_ret = is_item_checked(item_index);
+ return true;
+ } else if (property == "id") {
+ r_ret = get_item_id(item_index);
+ return true;
+ } else if (components[1] == "disabled") {
+ r_ret = is_item_disabled(item_index);
+ return true;
+ } else if (property == "separator") {
+ r_ret = is_item_separator(item_index);
+ return true;
+ }
+ }
+ return false;
+}
+
+void PopupMenu::_get_property_list(List<PropertyInfo> *p_list) const {
+ for (int i = 0; i < items.size(); i++) {
+ p_list->push_back(PropertyInfo(Variant::STRING, vformat("item_%d/text", i)));
+
+ PropertyInfo pi = PropertyInfo(Variant::OBJECT, vformat("item_%d/icon", i), PROPERTY_HINT_RESOURCE_TYPE, "Texture2D");
+ pi.usage &= ~(get_item_icon(i).is_null() ? PROPERTY_USAGE_STORAGE : 0);
+ p_list->push_back(pi);
+
+ pi = PropertyInfo(Variant::INT, vformat("item_%d/checkable", i), PROPERTY_HINT_ENUM, "No,As checkbox,As radio button");
+ pi.usage &= ~(!is_item_checkable(i) ? PROPERTY_USAGE_STORAGE : 0);
+ p_list->push_back(pi);
+
+ pi = PropertyInfo(Variant::BOOL, vformat("item_%d/checked", i));
+ pi.usage &= ~(!is_item_checked(i) ? PROPERTY_USAGE_STORAGE : 0);
+ p_list->push_back(pi);
+
+ pi = PropertyInfo(Variant::INT, vformat("item_%d/id", i), PROPERTY_HINT_RANGE, "1,10,1,or_greater");
+ p_list->push_back(pi);
+
+ pi = PropertyInfo(Variant::BOOL, vformat("item_%d/disabled", i));
+ pi.usage &= ~(!is_item_disabled(i) ? PROPERTY_USAGE_STORAGE : 0);
+ p_list->push_back(pi);
+
+ pi = PropertyInfo(Variant::BOOL, vformat("item_%d/separator", i));
+ pi.usage &= ~(!is_item_separator(i) ? PROPERTY_USAGE_STORAGE : 0);
+ p_list->push_back(pi);
+ }
+}
+
void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_item", "label", "id", "accel"), &PopupMenu::add_item, DEFVAL(-1), DEFVAL(0));
ClassDB::bind_method(D_METHOD("add_icon_item", "texture", "label", "id", "accel"), &PopupMenu::add_icon_item, DEFVAL(-1), DEFVAL(0));
@@ -1643,6 +1733,7 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_item_shortcut", "idx"), &PopupMenu::get_item_shortcut);
ClassDB::bind_method(D_METHOD("get_current_index"), &PopupMenu::get_current_index);
+ ClassDB::bind_method(D_METHOD("set_item_count", "count"), &PopupMenu::set_item_count);
ClassDB::bind_method(D_METHOD("get_item_count"), &PopupMenu::get_item_count);
ClassDB::bind_method(D_METHOD("remove_item", "idx"), &PopupMenu::remove_item);
@@ -1650,9 +1741,6 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_separator", "label", "id"), &PopupMenu::add_separator, DEFVAL(String()), DEFVAL(-1));
ClassDB::bind_method(D_METHOD("clear"), &PopupMenu::clear);
- ClassDB::bind_method(D_METHOD("_set_items"), &PopupMenu::_set_items);
- ClassDB::bind_method(D_METHOD("_get_items"), &PopupMenu::_get_items);
-
ClassDB::bind_method(D_METHOD("set_hide_on_item_selection", "enable"), &PopupMenu::set_hide_on_item_selection);
ClassDB::bind_method(D_METHOD("is_hide_on_item_selection"), &PopupMenu::is_hide_on_item_selection);
@@ -1668,13 +1756,14 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_allow_search", "allow"), &PopupMenu::set_allow_search);
ClassDB::bind_method(D_METHOD("get_allow_search"), &PopupMenu::get_allow_search);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_on_item_selection"), "set_hide_on_item_selection", "is_hide_on_item_selection");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_on_checkable_item_selection"), "set_hide_on_checkable_item_selection", "is_hide_on_checkable_item_selection");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_on_state_item_selection"), "set_hide_on_state_item_selection", "is_hide_on_state_item_selection");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "submenu_popup_delay"), "set_submenu_popup_delay", "get_submenu_popup_delay");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_search"), "set_allow_search", "get_allow_search");
+ ADD_ARRAY_COUNT("Items", "items_count", "set_item_count", "get_item_count", "item_");
+
ADD_SIGNAL(MethodInfo("id_pressed", PropertyInfo(Variant::INT, "id")));
ADD_SIGNAL(MethodInfo("id_focused", PropertyInfo(Variant::INT, "id")));
ADD_SIGNAL(MethodInfo("index_pressed", PropertyInfo(Variant::INT, "index")));
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index 428076c6da..22912fb59c 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -66,7 +66,7 @@ class PopupMenu : public Popup {
Variant metadata;
String submenu;
String tooltip;
- uint32_t accel = 0;
+ Key accel = Key::NONE;
int _ofs_cache = 0;
int _height_cache = 0;
int h_ofs = 0;
@@ -92,7 +92,7 @@ class PopupMenu : public Popup {
Timer *submenu_timer;
List<Rect2> autohide_areas;
Vector<Item> items;
- int initial_button_mask = 0;
+ MouseButton initial_button_mask = MouseButton::NONE;
bool during_grabbed_click = false;
int mouse_over = -1;
int submenu_over = -1;
@@ -117,9 +117,6 @@ class PopupMenu : public Popup {
bool hide_on_multistate_item_selection = false;
Vector2 moved;
- Array _get_items() const;
- void _set_items(const Array &p_items);
-
Map<Ref<Shortcut>, int> shortcut_refcount;
void _ref_shortcut(Ref<Shortcut> p_sc);
@@ -141,6 +138,9 @@ class PopupMenu : public Popup {
protected:
void _notification(int p_what);
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
static void _bind_methods();
public:
@@ -148,14 +148,14 @@ public:
// this value should be updated to reflect the new size.
static const int ITEM_PROPERTY_SIZE = 10;
- void add_item(const String &p_label, int p_id = -1, uint32_t p_accel = 0);
- void add_icon_item(const Ref<Texture2D> &p_icon, const String &p_label, int p_id = -1, uint32_t p_accel = 0);
- void add_check_item(const String &p_label, int p_id = -1, uint32_t p_accel = 0);
- void add_icon_check_item(const Ref<Texture2D> &p_icon, const String &p_label, int p_id = -1, uint32_t p_accel = 0);
- void add_radio_check_item(const String &p_label, int p_id = -1, uint32_t p_accel = 0);
- void add_icon_radio_check_item(const Ref<Texture2D> &p_icon, const String &p_label, int p_id = -1, uint32_t p_accel = 0);
+ void add_item(const String &p_label, int p_id = -1, Key p_accel = Key::NONE);
+ void add_icon_item(const Ref<Texture2D> &p_icon, const String &p_label, int p_id = -1, Key p_accel = Key::NONE);
+ void add_check_item(const String &p_label, int p_id = -1, Key p_accel = Key::NONE);
+ void add_icon_check_item(const Ref<Texture2D> &p_icon, const String &p_label, int p_id = -1, Key p_accel = Key::NONE);
+ void add_radio_check_item(const String &p_label, int p_id = -1, Key p_accel = Key::NONE);
+ void add_icon_radio_check_item(const Ref<Texture2D> &p_icon, const String &p_label, int p_id = -1, Key p_accel = Key::NONE);
- void add_multistate_item(const String &p_label, int p_max_states, int p_default_state = 0, int p_id = -1, uint32_t p_accel = 0);
+ void add_multistate_item(const String &p_label, int p_max_states, int p_default_state = 0, int p_id = -1, Key p_accel = Key::NONE);
void add_shortcut(const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false);
void add_icon_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false);
@@ -175,7 +175,7 @@ public:
void set_item_icon(int p_idx, const Ref<Texture2D> &p_icon);
void set_item_checked(int p_idx, bool p_checked);
void set_item_id(int p_idx, int p_id);
- void set_item_accelerator(int p_idx, uint32_t p_accel);
+ void set_item_accelerator(int p_idx, Key p_accel);
void set_item_metadata(int p_idx, const Variant &p_meta);
void set_item_disabled(int p_idx, bool p_disabled);
void set_item_submenu(int p_idx, const String &p_submenu);
@@ -200,7 +200,7 @@ public:
bool is_item_checked(int p_idx) const;
int get_item_id(int p_idx) const;
int get_item_index(int p_id) const;
- uint32_t get_item_accelerator(int p_idx) const;
+ Key get_item_accelerator(int p_idx) const;
Variant get_item_metadata(int p_idx) const;
bool is_item_disabled(int p_idx) const;
String get_item_submenu(int p_idx) const;
@@ -213,6 +213,8 @@ public:
int get_item_state(int p_idx) const;
int get_current_index() const;
+
+ void set_item_count(int p_count);
int get_item_count() const;
bool activate_item_by_event(const Ref<InputEvent> &p_event, bool p_for_global_only = false);
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index f1efbbda98..f191dfecb4 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -36,7 +36,7 @@
#include "scene/scene_string_names.h"
#include "servers/display_server.h"
-#include "modules/modules_enabled.gen.h"
+#include "modules/modules_enabled.gen.h" // For regex.
#ifdef MODULE_REGEX_ENABLED
#include "modules/regex/regex.h"
#endif
@@ -621,7 +621,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
}
}
-int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, bool p_shadow_as_outline, const Point2 &p_shadow_ofs) {
+int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, int p_shadow_outline_size, const Point2 &p_shadow_ofs) {
Vector2 off;
ERR_FAIL_COND_V(p_frame == nullptr, 0);
@@ -804,7 +804,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
}
for (int j = 0; j < frame->lines.size(); j++) {
- _draw_line(frame, j, p_ofs + rect.position + off + Vector2(0, frame->lines[j].offset.y), rect.size.x, p_base_color, p_outline_size, p_outline_color, p_font_shadow_color, p_shadow_as_outline, p_shadow_ofs);
+ _draw_line(frame, j, p_ofs + rect.position + off + Vector2(0, frame->lines[j].offset.y), rect.size.x, p_base_color, p_outline_size, p_outline_color, p_font_shadow_color, p_shadow_outline_size, p_shadow_ofs);
}
idx++;
}
@@ -824,7 +824,8 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
Item *it = _get_item_at_pos(it_from, it_to, glyphs[i].start);
int size = _find_outline_size(it, p_outline_size);
Color font_color = _find_outline_color(it, p_outline_color);
- if (size <= 0) {
+ Color font_shadow_color = p_font_shadow_color;
+ if ((size <= 0 || font_color.a == 0) && (font_shadow_color.a == 0)) {
gloff.x += glyphs[i].advance;
continue;
}
@@ -871,9 +872,10 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
faded_visibility = faded_visibility < 0.0f ? 0.0f : faded_visibility;
}
font_color.a = faded_visibility;
+ font_shadow_color.a = faded_visibility;
}
- bool visible = (font_color.a != 0);
+ bool visible = (font_color.a != 0) || (font_shadow_color.a != 0);
for (int j = 0; j < fx_stack.size(); j++) {
ItemFX *item_fx = fx_stack[j];
@@ -942,18 +944,19 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
}
}
- Point2 shadow_ofs(get_theme_constant(SNAME("shadow_offset_x")), get_theme_constant(SNAME("shadow_offset_y")));
-
// Draw glyph outlines.
for (int j = 0; j < glyphs[i].repeat; j++) {
if (visible) {
if (frid != RID()) {
- if (p_shadow_as_outline) {
- TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, size, p_ofs + fx_offset + gloff + Vector2(-shadow_ofs.x, shadow_ofs.y), gl, p_font_shadow_color);
- TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, size, p_ofs + fx_offset + gloff + Vector2(shadow_ofs.x, -shadow_ofs.y), gl, p_font_shadow_color);
- TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, size, p_ofs + fx_offset + gloff + Vector2(-shadow_ofs.x, -shadow_ofs.y), gl, p_font_shadow_color);
+ if (font_shadow_color.a > 0) {
+ TS->font_draw_glyph(frid, ci, glyphs[i].font_size, p_ofs + fx_offset + gloff + p_shadow_ofs, gl, font_shadow_color);
+ }
+ if (font_shadow_color.a > 0 && p_shadow_outline_size > 0) {
+ TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, p_shadow_outline_size, p_ofs + fx_offset + gloff + p_shadow_ofs, gl, font_shadow_color);
+ }
+ if (font_color.a != 0.0 && size > 0) {
+ TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, size, p_ofs + fx_offset + gloff, gl, font_color);
}
- TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, size, p_ofs + fx_offset + gloff, gl, font_color);
}
}
gloff.x += glyphs[i].advance;
@@ -1470,7 +1473,7 @@ void RichTextLabel::_notification(int p_what) {
Color outline_color = get_theme_color(SNAME("font_outline_color"));
int outline_size = get_theme_constant(SNAME("outline_size"));
Color font_shadow_color = get_theme_color(SNAME("font_shadow_color"));
- bool use_outline = get_theme_constant(SNAME("shadow_as_outline"));
+ int shadow_outline_size = get_theme_constant(SNAME("shadow_outline_size"));
Point2 shadow_ofs(get_theme_constant(SNAME("shadow_offset_x")), get_theme_constant(SNAME("shadow_offset_y")));
visible_paragraph_count = 0;
@@ -1480,7 +1483,7 @@ void RichTextLabel::_notification(int p_what) {
Point2 ofs = text_rect.get_position() + Vector2(0, main->lines[from_line].offset.y - vofs);
while (ofs.y < size.height && from_line < main->lines.size()) {
visible_paragraph_count++;
- visible_line_count += _draw_line(main, from_line, ofs, text_rect.size.x, base_color, outline_size, outline_color, font_shadow_color, use_outline, shadow_ofs);
+ visible_line_count += _draw_line(main, from_line, ofs, text_rect.size.x, base_color, outline_size, outline_color, font_shadow_color, shadow_outline_size, shadow_ofs);
ofs.y += main->lines[from_line].text_buf->get_size().y + get_theme_constant(SNAME("line_separation"));
from_line++;
}
@@ -1542,7 +1545,7 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
return;
}
- if (b->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (b->get_button_index() == MouseButton::LEFT) {
if (b->is_pressed() && !b->is_double_click()) {
scroll_updated = false;
ItemFrame *c_frame = nullptr;
@@ -1633,12 +1636,12 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
}
}
- if (b->get_button_index() == MOUSE_BUTTON_WHEEL_UP) {
+ if (b->get_button_index() == MouseButton::WHEEL_UP) {
if (scroll_active) {
vscroll->set_value(vscroll->get_value() - vscroll->get_page() * b->get_factor() * 0.5 / 8);
}
}
- if (b->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN) {
+ if (b->get_button_index() == MouseButton::WHEEL_DOWN) {
if (scroll_active) {
vscroll->set_value(vscroll->get_value() + vscroll->get_page() * b->get_factor() * 0.5 / 8);
}
@@ -3798,45 +3801,32 @@ String RichTextLabel::_get_line_text(ItemFrame *p_frame, int p_line, Selection p
}
}
for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) {
+ if (it->type == ITEM_TABLE) {
+ ItemTable *table = static_cast<ItemTable *>(it);
+ for (Item *E : table->subitems) {
+ ERR_CONTINUE(E->type != ITEM_FRAME); // Children should all be frames.
+ ItemFrame *frame = static_cast<ItemFrame *>(E);
+ for (int i = 0; i < frame->lines.size(); i++) {
+ text += _get_line_text(frame, i, p_selection);
+ }
+ }
+ }
if ((p_selection.to_item != nullptr) && (p_selection.to_item->index < l.from->index)) {
- break;
+ continue;
}
if ((p_selection.from_item != nullptr) && (p_selection.from_item->index >= end_idx)) {
- break;
+ continue;
}
- switch (it->type) {
- case ITEM_NEWLINE: {
- text += "\n";
- } break;
- case ITEM_TEXT: {
- ItemText *t = (ItemText *)it;
- text += t->text;
- } break;
- case ITEM_IMAGE: {
- text += " ";
- } break;
- case ITEM_TABLE: {
- ItemTable *table = static_cast<ItemTable *>(it);
- int idx = 0;
- int col_count = table->columns.size();
- for (Item *E : table->subitems) {
- ERR_CONTINUE(E->type != ITEM_FRAME); // Children should all be frames.
- ItemFrame *frame = static_cast<ItemFrame *>(E);
- int column = idx % col_count;
-
- for (int i = 0; i < frame->lines.size(); i++) {
- text += _get_line_text(frame, i, p_selection);
- }
- if (column == col_count - 1) {
- text += "\n";
- } else {
- text += " ";
- }
- idx++;
- }
- } break;
- default:
- break;
+ if (it->type == ITEM_DROPCAP) {
+ const ItemDropcap *dc = static_cast<ItemDropcap *>(it);
+ text += dc->text;
+ } else if (it->type == ITEM_TEXT) {
+ const ItemText *t = static_cast<ItemText *>(it);
+ text += t->text;
+ } else if (it->type == ITEM_NEWLINE) {
+ text += "\n";
+ } else if (it->type == ITEM_IMAGE) {
+ text += " ";
}
}
if ((l.from != nullptr) && (p_frame == p_selection.to_frame) && (p_selection.to_item != nullptr) && (p_selection.to_item->index >= l.from->index) && (p_selection.to_item->index < end_idx)) {
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index f3c4c11cc8..5b58f14d96 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -412,7 +412,7 @@ private:
void _shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width, int *r_char_offset);
void _resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width);
- int _draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, bool p_shadow_as_outline, const Point2 &shadow_ofs);
+ int _draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, int p_shadow_outline_size, const Point2 &p_shadow_ofs);
float _find_click_in_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Point2i &p_click, ItemFrame **r_click_frame = nullptr, int *r_click_line = nullptr, Item **r_click_item = nullptr, int *r_click_char = nullptr);
String _roman(int p_num, bool p_capitalize) const;
diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp
index 4a3a6837d5..8c292e663e 100644
--- a/scene/gui/scroll_bar.cpp
+++ b/scene/gui/scroll_bar.cpp
@@ -54,17 +54,17 @@ void ScrollBar::gui_input(const Ref<InputEvent> &p_event) {
if (b.is_valid()) {
accept_event();
- if (b->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && b->is_pressed()) {
+ if (b->get_button_index() == MouseButton::WHEEL_DOWN && b->is_pressed()) {
set_value(get_value() + get_page() / 4.0);
accept_event();
}
- if (b->get_button_index() == MOUSE_BUTTON_WHEEL_UP && b->is_pressed()) {
+ if (b->get_button_index() == MouseButton::WHEEL_UP && b->is_pressed()) {
set_value(get_value() - get_page() / 4.0);
accept_event();
}
- if (b->get_button_index() != MOUSE_BUTTON_LEFT) {
+ if (b->get_button_index() != MouseButton::LEFT) {
return;
}
@@ -525,7 +525,7 @@ void ScrollBar::_drag_node_input(const Ref<InputEvent> &p_input) {
Ref<InputEventMouseButton> mb = p_input;
if (mb.is_valid()) {
- if (mb->get_button_index() != 1) {
+ if (mb->get_button_index() != MouseButton::LEFT) {
return;
}
diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp
index 0c0ec39c7f..013358e75c 100644
--- a/scene/gui/scroll_container.cpp
+++ b/scene/gui/scroll_container.cpp
@@ -92,7 +92,7 @@ void ScrollContainer::gui_input(const Ref<InputEvent> &p_gui_input) {
Ref<InputEventMouseButton> mb = p_gui_input;
if (mb.is_valid()) {
- if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP && mb->is_pressed()) {
+ if (mb->get_button_index() == MouseButton::WHEEL_UP && mb->is_pressed()) {
// only horizontal is enabled, scroll horizontally
if (h_scroll->is_visible() && (!v_scroll->is_visible() || mb->is_shift_pressed())) {
h_scroll->set_value(h_scroll->get_value() - h_scroll->get_page() / 8 * mb->get_factor());
@@ -101,7 +101,7 @@ void ScrollContainer::gui_input(const Ref<InputEvent> &p_gui_input) {
}
}
- if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && mb->is_pressed()) {
+ if (mb->get_button_index() == MouseButton::WHEEL_DOWN && mb->is_pressed()) {
// only horizontal is enabled, scroll horizontally
if (h_scroll->is_visible() && (!v_scroll->is_visible() || mb->is_shift_pressed())) {
h_scroll->set_value(h_scroll->get_value() + h_scroll->get_page() / 8 * mb->get_factor());
@@ -110,13 +110,13 @@ void ScrollContainer::gui_input(const Ref<InputEvent> &p_gui_input) {
}
}
- if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_LEFT && mb->is_pressed()) {
+ if (mb->get_button_index() == MouseButton::WHEEL_LEFT && mb->is_pressed()) {
if (h_scroll->is_visible_in_tree()) {
h_scroll->set_value(h_scroll->get_value() - h_scroll->get_page() * mb->get_factor() / 8);
}
}
- if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_RIGHT && mb->is_pressed()) {
+ if (mb->get_button_index() == MouseButton::WHEEL_RIGHT && mb->is_pressed()) {
if (h_scroll->is_visible_in_tree()) {
h_scroll->set_value(h_scroll->get_value() + h_scroll->get_page() * mb->get_factor() / 8);
}
@@ -130,7 +130,7 @@ void ScrollContainer::gui_input(const Ref<InputEvent> &p_gui_input) {
return;
}
- if (mb->get_button_index() != MOUSE_BUTTON_LEFT) {
+ if (mb->get_button_index() != MouseButton::LEFT) {
return;
}
diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp
index 352f87954e..4cc425aad3 100644
--- a/scene/gui/slider.cpp
+++ b/scene/gui/slider.cpp
@@ -55,7 +55,7 @@ void Slider::gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid()) {
- if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb->get_button_index() == MouseButton::LEFT) {
if (mb->is_pressed()) {
Ref<Texture2D> grabber = get_theme_icon(mouse_inside || has_focus() ? "grabber_highlight" : "grabber");
grab.pos = orientation == VERTICAL ? mb->get_position().y : mb->get_position().x;
@@ -74,10 +74,10 @@ void Slider::gui_input(const Ref<InputEvent> &p_event) {
grab.active = false;
}
} else if (scrollable) {
- if (mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP) {
+ if (mb->is_pressed() && mb->get_button_index() == MouseButton::WHEEL_UP) {
grab_focus();
set_value(get_value() + get_step());
- } else if (mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN) {
+ } else if (mb->is_pressed() && mb->get_button_index() == MouseButton::WHEEL_DOWN) {
grab_focus();
set_value(get_value() - get_step());
}
diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp
index 0446e1b402..4497c20772 100644
--- a/scene/gui/spin_box.cpp
+++ b/scene/gui/spin_box.cpp
@@ -85,7 +85,7 @@ void SpinBox::_line_edit_input(const Ref<InputEvent> &p_event) {
}
void SpinBox::_range_click_timeout() {
- if (!drag.enabled && Input::get_singleton()->is_mouse_button_pressed(MOUSE_BUTTON_LEFT)) {
+ if (!drag.enabled && Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT)) {
bool up = get_local_mouse_position().y < (get_size().height / 2);
set_value(get_value() + (up ? get_step() : -get_step()));
@@ -121,7 +121,7 @@ void SpinBox::gui_input(const Ref<InputEvent> &p_event) {
bool up = mb->get_position().y < (get_size().height / 2);
switch (mb->get_button_index()) {
- case MOUSE_BUTTON_LEFT: {
+ case MouseButton::LEFT: {
line_edit->grab_focus();
set_value(get_value() + (up ? get_step() : -get_step()));
@@ -133,17 +133,17 @@ void SpinBox::gui_input(const Ref<InputEvent> &p_event) {
drag.allowed = true;
drag.capture_pos = mb->get_position();
} break;
- case MOUSE_BUTTON_RIGHT: {
+ case MouseButton::RIGHT: {
line_edit->grab_focus();
set_value((up ? get_max() : get_min()));
} break;
- case MOUSE_BUTTON_WHEEL_UP: {
+ case MouseButton::WHEEL_UP: {
if (line_edit->has_focus()) {
set_value(get_value() + get_step() * mb->get_factor());
accept_event();
}
} break;
- case MOUSE_BUTTON_WHEEL_DOWN: {
+ case MouseButton::WHEEL_DOWN: {
if (line_edit->has_focus()) {
set_value(get_value() - get_step() * mb->get_factor());
accept_event();
@@ -154,7 +154,7 @@ void SpinBox::gui_input(const Ref<InputEvent> &p_event) {
}
}
- if (mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
//set_default_cursor_shape(CURSOR_ARROW);
range_click_timer->stop();
_release_mouse();
@@ -163,7 +163,7 @@ void SpinBox::gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseMotion> mm = p_event;
- if (mm.is_valid() && mm->get_button_mask() & MOUSE_BUTTON_MASK_LEFT) {
+ if (mm.is_valid() && (mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE) {
if (drag.enabled) {
drag.diff_y += mm->get_relative().y;
float diff_y = -0.01 * Math::pow(ABS(drag.diff_y), 1.8f) * SGN(drag.diff_y);
diff --git a/scene/gui/split_container.cpp b/scene/gui/split_container.cpp
index 4736a1ad37..6b53c0220e 100644
--- a/scene/gui/split_container.cpp
+++ b/scene/gui/split_container.cpp
@@ -216,7 +216,7 @@ void SplitContainer::gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid()) {
- if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb->get_button_index() == MouseButton::LEFT) {
if (mb->is_pressed()) {
int sep = get_theme_constant(SNAME("separation"));
diff --git a/scene/gui/tab_bar.cpp b/scene/gui/tab_bar.cpp
index 405fbdae75..1dda29f668 100644
--- a/scene/gui/tab_bar.cpp
+++ b/scene/gui/tab_bar.cpp
@@ -143,7 +143,7 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid()) {
- if (mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP && !mb->is_command_pressed()) {
+ if (mb->is_pressed() && mb->get_button_index() == MouseButton::WHEEL_UP && !mb->is_command_pressed()) {
if (scrolling_enabled && buttons_visible) {
if (offset > 0) {
offset--;
@@ -152,7 +152,7 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
}
}
- if (mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && !mb->is_command_pressed()) {
+ if (mb->is_pressed() && mb->get_button_index() == MouseButton::WHEEL_DOWN && !mb->is_command_pressed()) {
if (scrolling_enabled && buttons_visible) {
if (missing_right) {
offset++;
@@ -162,7 +162,7 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
}
}
- if (rb_pressing && !mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (rb_pressing && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
if (rb_hover != -1) {
// pressed
emit_signal(SNAME("tab_rmb_clicked"), rb_hover);
@@ -172,7 +172,7 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
update();
}
- if (cb_pressing && !mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (cb_pressing && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
if (cb_hover != -1) {
// pressed
emit_signal(SNAME("tab_close_pressed"), cb_hover);
@@ -182,7 +182,7 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
update();
}
- if (mb->is_pressed() && (mb->get_button_index() == MOUSE_BUTTON_LEFT || (select_with_rmb && mb->get_button_index() == MOUSE_BUTTON_RIGHT))) {
+ if (mb->is_pressed() && (mb->get_button_index() == MouseButton::LEFT || (select_with_rmb && mb->get_button_index() == MouseButton::RIGHT))) {
// clicks
Point2 pos = mb->get_position();
diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp
index c8a0501d8a..ff53d91ea3 100644
--- a/scene/gui/tab_container.cpp
+++ b/scene/gui/tab_container.cpp
@@ -78,7 +78,7 @@ void TabContainer::gui_input(const Ref<InputEvent> &p_event) {
Popup *popup = get_popup();
- if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
Point2 pos = mb->get_position();
Size2 size = get_size();
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index cb7a6c0978..8ffbe479be 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -1407,7 +1407,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
}
if (mb->is_pressed()) {
- if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP && !mb->is_command_pressed()) {
+ if (mb->get_button_index() == MouseButton::WHEEL_UP && !mb->is_command_pressed()) {
if (mb->is_shift_pressed()) {
h_scroll->set_value(h_scroll->get_value() - (100 * mb->get_factor()));
} else if (mb->is_alt_pressed()) {
@@ -1418,7 +1418,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
_scroll_up(3 * mb->get_factor());
}
}
- if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && !mb->is_command_pressed()) {
+ if (mb->get_button_index() == MouseButton::WHEEL_DOWN && !mb->is_command_pressed()) {
if (mb->is_shift_pressed()) {
h_scroll->set_value(h_scroll->get_value() + (100 * mb->get_factor()));
} else if (mb->is_alt_pressed()) {
@@ -1429,13 +1429,13 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
_scroll_down(3 * mb->get_factor());
}
}
- if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_LEFT) {
+ if (mb->get_button_index() == MouseButton::WHEEL_LEFT) {
h_scroll->set_value(h_scroll->get_value() - (100 * mb->get_factor()));
}
- if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_RIGHT) {
+ if (mb->get_button_index() == MouseButton::WHEEL_RIGHT) {
h_scroll->set_value(h_scroll->get_value() + (100 * mb->get_factor()));
}
- if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb->get_button_index() == MouseButton::LEFT) {
_reset_caret_blink_timer();
Point2i pos = get_line_column_at_pos(mpos);
@@ -1538,11 +1538,11 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
update();
}
- if (is_middle_mouse_paste_enabled() && mb->get_button_index() == MOUSE_BUTTON_MIDDLE && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) {
+ if (is_middle_mouse_paste_enabled() && mb->get_button_index() == MouseButton::MIDDLE && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) {
paste_primary_clipboard();
}
- if (mb->get_button_index() == MOUSE_BUTTON_RIGHT && context_menu_enabled) {
+ if (mb->get_button_index() == MouseButton::RIGHT && context_menu_enabled) {
_reset_caret_blink_timer();
Point2i pos = get_line_column_at_pos(mpos);
@@ -1574,7 +1574,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
grab_focus();
}
} else {
- if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb->get_button_index() == MouseButton::LEFT) {
dragging_minimap = false;
dragging_selection = false;
can_drag_minimap = false;
@@ -1613,7 +1613,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
mpos.x = get_size().x - mpos.x;
}
- if (mm->get_button_mask() & MOUSE_BUTTON_MASK_LEFT && get_viewport()->gui_get_drag_data() == Variant()) { // Ignore if dragging.
+ if ((mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE && get_viewport()->gui_get_drag_data() == Variant()) { // Ignore if dragging.
_reset_caret_blink_timer();
if (draw_minimap && !dragging_selection) {
@@ -1681,7 +1681,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
}
// If a modifier has been pressed, and nothing else, return.
- if (k->get_keycode() == KEY_CTRL || k->get_keycode() == KEY_ALT || k->get_keycode() == KEY_SHIFT || k->get_keycode() == KEY_META) {
+ if (k->get_keycode() == Key::CTRL || k->get_keycode() == Key::ALT || k->get_keycode() == Key::SHIFT || k->get_keycode() == Key::META) {
return;
}
@@ -1898,7 +1898,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
}
// Handle Unicode (if no modifiers active). Tab has a value of 0x09.
- if (allow_unicode_handling && editable && (k->get_unicode() >= 32 || k->get_keycode() == KEY_TAB)) {
+ if (allow_unicode_handling && editable && (k->get_unicode() >= 32 || k->get_keycode() == Key::TAB)) {
handle_unicode_input(k->get_unicode());
accept_event();
return;
@@ -3697,8 +3697,10 @@ void TextEdit::set_selection_mode(SelectionMode p_mode, int p_line, int p_column
if (p_line >= 0) {
ERR_FAIL_INDEX(p_line, text.size());
selection.selecting_line = p_line;
+ selection.selecting_column = CLAMP(selection.selecting_column, 0, text[selection.selecting_line].length());
}
if (p_column >= 0) {
+ ERR_FAIL_INDEX(selection.selecting_line, text.size());
ERR_FAIL_INDEX(p_column, text[selection.selecting_line].length());
selection.selecting_column = p_column;
}
@@ -5249,21 +5251,21 @@ void TextEdit::_generate_context_menu() {
// Reorganize context menu.
menu->clear();
if (editable) {
- menu->add_item(RTR("Cut"), MENU_CUT, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_cut") : 0);
+ menu->add_item(RTR("Cut"), MENU_CUT, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_cut") : Key::NONE);
}
- menu->add_item(RTR("Copy"), MENU_COPY, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_copy") : 0);
+ menu->add_item(RTR("Copy"), MENU_COPY, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_copy") : Key::NONE);
if (editable) {
- menu->add_item(RTR("Paste"), MENU_PASTE, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_paste") : 0);
+ menu->add_item(RTR("Paste"), MENU_PASTE, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_paste") : Key::NONE);
}
menu->add_separator();
if (is_selecting_enabled()) {
- menu->add_item(RTR("Select All"), MENU_SELECT_ALL, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_text_select_all") : 0);
+ menu->add_item(RTR("Select All"), MENU_SELECT_ALL, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_text_select_all") : Key::NONE);
}
if (editable) {
menu->add_item(RTR("Clear"), MENU_CLEAR);
menu->add_separator();
- menu->add_item(RTR("Undo"), MENU_UNDO, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_undo") : 0);
- menu->add_item(RTR("Redo"), MENU_REDO, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_redo") : 0);
+ menu->add_item(RTR("Undo"), MENU_UNDO, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_undo") : Key::NONE);
+ menu->add_item(RTR("Redo"), MENU_REDO, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_redo") : Key::NONE);
}
menu->add_separator();
menu->add_submenu_item(RTR("Text Writing Direction"), "DirMenu");
@@ -5284,25 +5286,25 @@ void TextEdit::_generate_context_menu() {
}
}
-int TextEdit::_get_menu_action_accelerator(const String &p_action) {
+Key TextEdit::_get_menu_action_accelerator(const String &p_action) {
const List<Ref<InputEvent>> *events = InputMap::get_singleton()->action_get_events(p_action);
if (!events) {
- return 0;
+ return Key::NONE;
}
// Use first event in the list for the accelerator.
const List<Ref<InputEvent>>::Element *first_event = events->front();
if (!first_event) {
- return 0;
+ return Key::NONE;
}
const Ref<InputEventKey> event = first_event->get();
if (event.is_null()) {
- return 0;
+ return Key::NONE;
}
// Use physical keycode if non-zero
- if (event->get_physical_keycode() != 0) {
+ if (event->get_physical_keycode() != Key::NONE) {
return event->get_physical_keycode_with_modifiers();
} else {
return event->get_keycode_with_modifiers();
@@ -5457,10 +5459,10 @@ int TextEdit::_get_column_x_offset_for_line(int p_char, int p_line) const {
/* Selection */
void TextEdit::_click_selection_held() {
- // Warning: is_mouse_button_pressed(MOUSE_BUTTON_LEFT) returns false for double+ clicks, so this doesn't work for MODE_WORD
+ // Warning: is_mouse_button_pressed(MouseButton::LEFT) returns false for double+ clicks, so this doesn't work for MODE_WORD
// and MODE_LINE. However, moving the mouse triggers _gui_input, which calls these functions too, so that's not a huge problem.
// I'm unsure if there's an actual fix that doesn't have a ton of side effects.
- if (Input::get_singleton()->is_mouse_button_pressed(MOUSE_BUTTON_LEFT) && selection.selecting_mode != SelectionMode::SELECTION_MODE_NONE) {
+ if (Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT) && selection.selecting_mode != SelectionMode::SELECTION_MODE_NONE) {
switch (selection.selecting_mode) {
case SelectionMode::SELECTION_MODE_POINTER: {
_update_selection_mode_pointer();
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index 23f80efce0..1a7dc851b5 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -280,7 +280,7 @@ private:
PopupMenu *menu_ctl = nullptr;
void _generate_context_menu();
- int _get_menu_action_accelerator(const String &p_action);
+ Key _get_menu_action_accelerator(const String &p_action);
/* Versioning */
struct TextOperation {
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 1245a37c4d..7c4cdf828b 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -1955,7 +1955,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
if (p_item->cells[i].custom_button) {
if (cache.hover_item == p_item && cache.hover_cell == i) {
- if (Input::get_singleton()->is_mouse_button_pressed(MOUSE_BUTTON_LEFT)) {
+ if (Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT)) {
draw_style_box(cache.custom_button_pressed, ir);
} else {
draw_style_box(cache.custom_button_hover, ir);
@@ -2256,7 +2256,7 @@ Rect2 Tree::search_item_rect(TreeItem *p_from, TreeItem *p_item) {
}
void Tree::_range_click_timeout() {
- if (range_item_last && !range_drag_enabled && Input::get_singleton()->is_mouse_button_pressed(MOUSE_BUTTON_LEFT)) {
+ if (range_item_last && !range_drag_enabled && Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT)) {
Point2 pos = get_local_mouse_position() - cache.bg->get_offset();
if (show_column_titles) {
pos.y -= _get_title_button_height();
@@ -2284,7 +2284,7 @@ void Tree::_range_click_timeout() {
propagate_mouse_activated = false; // done from outside, so signal handler can't clear the tree in the middle of emit (which is a common case)
blocked++;
- propagate_mouse_event(pos + cache.offset, 0, 0, x_limit + cache.offset.width, false, root, MOUSE_BUTTON_LEFT, mb);
+ propagate_mouse_event(pos + cache.offset, 0, 0, x_limit + cache.offset.width, false, root, MouseButton::LEFT, mb);
blocked--;
if (range_click_timer->is_one_shot()) {
@@ -2307,7 +2307,7 @@ void Tree::_range_click_timeout() {
}
}
-int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int x_limit, bool p_double_click, TreeItem *p_item, int p_button, const Ref<InputEventWithModifiers> &p_mod) {
+int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int x_limit, bool p_double_click, TreeItem *p_item, MouseButton p_button, const Ref<InputEventWithModifiers> &p_mod) {
int item_h = compute_item_height(p_item) + cache.vseparation;
bool skip = (p_item == root && hide_root);
@@ -2427,7 +2427,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int
col_width -= w + cache.button_margin;
}
- if (p_button == MOUSE_BUTTON_LEFT || (p_button == MOUSE_BUTTON_RIGHT && allow_rmb_select)) {
+ if (p_button == MouseButton::LEFT || (p_button == MouseButton::RIGHT && allow_rmb_select)) {
/* process selection */
if (p_double_click && (!c.editable || c.mode == TreeItem::CELL_MODE_CUSTOM || c.mode == TreeItem::CELL_MODE_ICON /*|| c.mode==TreeItem::CELL_MODE_CHECK*/)) { //it's confusing for check
@@ -2439,10 +2439,10 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int
}
if (select_mode == SELECT_MULTI && p_mod->is_command_pressed() && c.selectable) {
- if (!c.selected || p_button == MOUSE_BUTTON_RIGHT) {
+ if (!c.selected || p_button == MouseButton::RIGHT) {
p_item->select(col);
emit_signal(SNAME("multi_selected"), p_item, col, true);
- if (p_button == MOUSE_BUTTON_RIGHT) {
+ if (p_button == MouseButton::RIGHT) {
emit_signal(SNAME("item_rmb_selected"), get_local_mouse_position());
}
@@ -2459,21 +2459,21 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int
bool inrange = false;
select_single_item(p_item, root, col, selected_item, &inrange);
- if (p_button == MOUSE_BUTTON_RIGHT) {
+ if (p_button == MouseButton::RIGHT) {
emit_signal(SNAME("item_rmb_selected"), get_local_mouse_position());
}
} else {
int icount = _count_selected_items(root);
- if (select_mode == SELECT_MULTI && icount > 1 && p_button != MOUSE_BUTTON_RIGHT) {
+ if (select_mode == SELECT_MULTI && icount > 1 && p_button != MouseButton::RIGHT) {
single_select_defer = p_item;
single_select_defer_column = col;
} else {
- if (p_button != MOUSE_BUTTON_RIGHT || !c.selected) {
+ if (p_button != MouseButton::RIGHT || !c.selected) {
select_single_item(p_item, root, col);
}
- if (p_button == MOUSE_BUTTON_RIGHT) {
+ if (p_button == MouseButton::RIGHT) {
emit_signal(SNAME("item_rmb_selected"), get_local_mouse_position());
}
}
@@ -2543,7 +2543,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int
/* touching the combo */
bool up = p_pos.y < (item_h / 2);
- if (p_button == MOUSE_BUTTON_LEFT) {
+ if (p_button == MouseButton::LEFT) {
if (range_click_timer->get_time_left() == 0) {
range_item_last = p_item;
range_up_last = up;
@@ -2560,13 +2560,13 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int
item_edited(col, p_item);
- } else if (p_button == MOUSE_BUTTON_RIGHT) {
+ } else if (p_button == MouseButton::RIGHT) {
p_item->set_range(col, (up ? c.max : c.min));
item_edited(col, p_item);
- } else if (p_button == MOUSE_BUTTON_WHEEL_UP) {
+ } else if (p_button == MouseButton::WHEEL_UP) {
p_item->set_range(col, c.val + c.step);
item_edited(col, p_item);
- } else if (p_button == MOUSE_BUTTON_WHEEL_DOWN) {
+ } else if (p_button == MouseButton::WHEEL_DOWN) {
p_item->set_range(col, c.val - c.step);
item_edited(col, p_item);
}
@@ -2599,14 +2599,14 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int
}
if (!p_item->cells[col].custom_button || !on_arrow) {
- item_edited(col, p_item, p_button == MOUSE_BUTTON_LEFT);
+ item_edited(col, p_item, p_button == MouseButton::LEFT);
}
click_handled = true;
return -1;
} break;
};
- if (!bring_up_editor || p_button != MOUSE_BUTTON_LEFT) {
+ if (!bring_up_editor || p_button != MouseButton::LEFT) {
return -1;
}
@@ -2646,7 +2646,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int
item_h += child_h;
}
}
- if (p_item == root && p_button == MOUSE_BUTTON_RIGHT) {
+ if (p_item == root && p_button == MouseButton::RIGHT) {
emit_signal(SNAME("empty_rmb"), get_local_mouse_position());
}
}
@@ -2655,9 +2655,9 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int
}
void Tree::_text_editor_modal_close() {
- if (Input::get_singleton()->is_key_pressed(KEY_ESCAPE) ||
- Input::get_singleton()->is_key_pressed(KEY_KP_ENTER) ||
- Input::get_singleton()->is_key_pressed(KEY_ENTER)) {
+ if (Input::get_singleton()->is_key_pressed(Key::ESCAPE) ||
+ Input::get_singleton()->is_key_pressed(Key::KP_ENTER) ||
+ Input::get_singleton()->is_key_pressed(Key::ENTER)) {
return;
}
@@ -3048,7 +3048,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) {
return;
} else {
- if (k->get_keycode() != KEY_SHIFT) {
+ if (k->get_keycode() != Key::SHIFT) {
last_keypress = 0;
}
}
@@ -3189,7 +3189,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) {
bool rtl = is_layout_rtl();
if (!b->is_pressed()) {
- if (b->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (b->get_button_index() == MouseButton::LEFT) {
Point2 pos = b->get_position();
if (rtl) {
pos.x = get_size().width - pos.x;
@@ -3270,8 +3270,8 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) {
}
switch (b->get_button_index()) {
- case MOUSE_BUTTON_RIGHT:
- case MOUSE_BUTTON_LEFT: {
+ case MouseButton::RIGHT:
+ case MouseButton::LEFT: {
Ref<StyleBox> bg = cache.bg;
Point2 pos = b->get_position();
@@ -3284,7 +3284,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) {
pos.y -= _get_title_button_height();
if (pos.y < 0) {
- if (b->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (b->get_button_index() == MouseButton::LEFT) {
pos.x += cache.offset.x;
int len = 0;
for (int i = 0; i < columns.size(); i++) {
@@ -3302,7 +3302,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) {
}
}
if (!root || (!root->get_first_child() && hide_root)) {
- if (b->get_button_index() == MOUSE_BUTTON_RIGHT && allow_rmb_select) {
+ if (b->get_button_index() == MouseButton::RIGHT && allow_rmb_select) {
emit_signal(SNAME("empty_tree_rmb_selected"), get_local_mouse_position());
}
break;
@@ -3329,7 +3329,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) {
}
}
- if (b->get_button_index() == MOUSE_BUTTON_RIGHT) {
+ if (b->get_button_index() == MouseButton::RIGHT) {
break;
}
@@ -3352,7 +3352,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) {
set_physics_process_internal(true);
}
- if (b->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (b->get_button_index() == MouseButton::LEFT) {
if (get_item_at_position(b->get_position()) == nullptr && !b->is_shift_pressed() && !b->is_ctrl_pressed() && !b->is_command_pressed()) {
emit_signal(SNAME("nothing_selected"));
}
@@ -3365,7 +3365,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) {
}
} break;
- case MOUSE_BUTTON_WHEEL_UP: {
+ case MouseButton::WHEEL_UP: {
double prev_value = v_scroll->get_value();
v_scroll->set_value(v_scroll->get_value() - v_scroll->get_page() * b->get_factor() / 8);
if (v_scroll->get_value() != prev_value) {
@@ -3373,7 +3373,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) {
}
} break;
- case MOUSE_BUTTON_WHEEL_DOWN: {
+ case MouseButton::WHEEL_DOWN: {
double prev_value = v_scroll->get_value();
v_scroll->set_value(v_scroll->get_value() + v_scroll->get_page() * b->get_factor() / 8);
if (v_scroll->get_value() != prev_value) {
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index 6ca9458e9b..2e4e1bd364 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -462,7 +462,7 @@ private:
void draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Color &p_color, const Color &p_icon_color, int p_ol_size, const Color &p_ol_color);
int draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 &p_draw_size, TreeItem *p_item);
void select_single_item(TreeItem *p_selected, TreeItem *p_current, int p_col, TreeItem *p_prev = nullptr, bool *r_in_range = nullptr, bool p_force_deselect = false);
- int propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int x_limit, bool p_double_click, TreeItem *p_item, int p_button, const Ref<InputEventWithModifiers> &p_mod);
+ int propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int x_limit, bool p_double_click, TreeItem *p_item, MouseButton p_button, const Ref<InputEventWithModifiers> &p_mod);
void _text_editor_submit(String p_text);
void _text_editor_modal_close();
void value_editor_changed(double p_value);
diff --git a/scene/main/canvas_layer.cpp b/scene/main/canvas_layer.cpp
index 26bff4494b..cd7534f73c 100644
--- a/scene/main/canvas_layer.cpp
+++ b/scene/main/canvas_layer.cpp
@@ -256,7 +256,7 @@ void CanvasLayer::_update_follow_viewport(bool p_force_exit) {
void CanvasLayer::_validate_property(PropertyInfo &property) const {
if (!follow_viewport && property.name == "follow_viewport_scale") {
- property.usage = PROPERTY_USAGE_NOEDITOR;
+ property.usage = PROPERTY_USAGE_NO_EDITOR;
}
}
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index 189aebb47d..44420fcc31 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -908,17 +908,12 @@ void Node::set_name(const String &p_name) {
}
}
-static bool node_hrcr = false;
static SafeRefCount node_hrcr_count;
void Node::init_node_hrcr() {
node_hrcr_count.init(1);
}
-void Node::set_human_readable_collision_renaming(bool p_enabled) {
- node_hrcr = p_enabled;
-}
-
#ifdef TOOLS_ENABLED
String Node::validate_child_name(Node *p_child) {
StringName name = p_child->data.name;
@@ -930,9 +925,8 @@ String Node::validate_child_name(Node *p_child) {
void Node::_validate_child_name(Node *p_child, bool p_force_human_readable) {
/* Make sure the name is unique */
- if (node_hrcr || p_force_human_readable) {
+ if (p_force_human_readable) {
//this approach to autoset node names is human readable but very slow
- //it's turned on while running in the editor
StringName name = p_child->data.name;
_generate_serial_child_name(p_child, name);
@@ -1899,6 +1893,56 @@ Node *Node::get_deepest_editable_node(Node *p_start_node) const {
return node;
}
+#ifdef TOOLS_ENABLED
+void Node::set_property_pinned(const String &p_property, bool p_pinned) {
+ bool current_pinned = false;
+ bool has_pinned = has_meta("_edit_pinned_properties_");
+ Array pinned;
+ String psa = get_property_store_alias(p_property);
+ if (has_pinned) {
+ pinned = get_meta("_edit_pinned_properties_");
+ current_pinned = pinned.has(psa);
+ }
+
+ if (current_pinned != p_pinned) {
+ if (p_pinned) {
+ pinned.append(psa);
+ if (!has_pinned) {
+ set_meta("_edit_pinned_properties_", pinned);
+ }
+ } else {
+ pinned.erase(psa);
+ if (pinned.is_empty()) {
+ remove_meta("_edit_pinned_properties_");
+ }
+ }
+ }
+}
+
+bool Node::is_property_pinned(const StringName &p_property) const {
+ if (!has_meta("_edit_pinned_properties_")) {
+ return false;
+ }
+ Array pinned = get_meta("_edit_pinned_properties_");
+ String psa = get_property_store_alias(p_property);
+ return pinned.has(psa);
+}
+
+StringName Node::get_property_store_alias(const StringName &p_property) const {
+ return p_property;
+}
+#endif
+
+void Node::get_storable_properties(Set<StringName> &r_storable_properties) const {
+ List<PropertyInfo> pi;
+ get_property_list(&pi);
+ for (List<PropertyInfo>::Element *E = pi.front(); E; E = E->next()) {
+ if ((E->get().usage & PROPERTY_USAGE_STORAGE)) {
+ r_storable_properties.insert(E->get().name);
+ }
+ }
+}
+
String Node::to_string() {
if (get_script_instance()) {
bool valid;
@@ -2750,7 +2794,11 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_import_path", "import_path"), &Node::set_import_path);
ClassDB::bind_method(D_METHOD("_get_import_path"), &Node::get_import_path);
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "_import_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_import_path", "_get_import_path");
+#ifdef TOOLS_ENABLED
+ ClassDB::bind_method(D_METHOD("_set_property_pinned", "property", "pinned"), &Node::set_property_pinned);
+#endif
+
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "_import_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_import_path", "_get_import_path");
{
MethodInfo mi;
@@ -2841,7 +2889,7 @@ void Node::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "process_priority"), "set_process_priority", "get_process_priority");
ADD_GROUP("Editor Description", "editor_");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "editor_description", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_INTERNAL), "set_editor_description", "get_editor_description");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "editor_description", PROPERTY_HINT_MULTILINE_TEXT), "set_editor_description", "get_editor_description");
GDVIRTUAL_BIND(_process, "delta");
GDVIRTUAL_BIND(_physics_process, "delta");
diff --git a/scene/main/node.h b/scene/main/node.h
index e59a7a390a..2dd32a2e1d 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -363,6 +363,13 @@ public:
bool is_editable_instance(const Node *p_node) const;
Node *get_deepest_editable_node(Node *p_start_node) const;
+#ifdef TOOLS_ENABLED
+ void set_property_pinned(const String &p_property, bool p_pinned);
+ bool is_property_pinned(const StringName &p_property) const;
+ virtual StringName get_property_store_alias(const StringName &p_property) const;
+#endif
+ void get_storable_properties(Set<StringName> &r_storable_properties) const;
+
virtual String to_string() override;
/* NOTIFICATIONS */
@@ -437,7 +444,6 @@ public:
void queue_delete();
//hacks for speed
- static void set_human_readable_collision_renaming(bool p_enabled);
static void init_node_hrcr();
void force_parent_owned() { data.parent_owned = true; } //hack to avoid duplicate nodes
diff --git a/scene/main/resource_preloader.cpp b/scene/main/resource_preloader.cpp
index f4c90ee668..c44b55284d 100644
--- a/scene/main/resource_preloader.cpp
+++ b/scene/main/resource_preloader.cpp
@@ -147,7 +147,7 @@ void ResourcePreloader::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_resource", "name"), &ResourcePreloader::get_resource);
ClassDB::bind_method(D_METHOD("get_resource_list"), &ResourcePreloader::_get_resource_list);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "resources", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_resources", "_get_resources");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "resources", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_resources", "_get_resources");
}
ResourcePreloader::ResourcePreloader() {
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 6388b375d9..1ecc3c762a 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -584,9 +584,9 @@ void Viewport::_process_picking() {
physics_last_mouse_state.meta = mb->is_meta_pressed();
if (mb->is_pressed()) {
- physics_last_mouse_state.mouse_mask |= (MouseButton)(1 << (mb->get_button_index() - 1));
+ physics_last_mouse_state.mouse_mask |= mouse_button_to_mask(mb->get_button_index());
} else {
- physics_last_mouse_state.mouse_mask &= (MouseButton) ~(1 << (mb->get_button_index() - 1));
+ physics_last_mouse_state.mouse_mask &= ~mouse_button_to_mask(mb->get_button_index());
// If touch mouse raised, assume we don't know last mouse pos until new events come
if (mb->get_device() == InputEvent::DEVICE_ID_TOUCH_MOUSE) {
@@ -638,7 +638,13 @@ void Viewport::_process_picking() {
Vector2 point = canvas_transform.affine_inverse().xform(pos);
- int rc = ss2d->intersect_point_on_canvas(point, canvas_layer_id, res, 64, Set<RID>(), 0xFFFFFFFF, true, true, true);
+ PhysicsDirectSpaceState2D::PointParameters point_params;
+ point_params.position = point;
+ point_params.canvas_instance_id = canvas_layer_id;
+ point_params.collide_with_areas = true;
+ point_params.pick_point = true;
+
+ int rc = ss2d->intersect_point(point_params, res, 64);
for (int i = 0; i < rc; i++) {
if (res[i].collider_id.is_valid() && res[i].collider) {
CollisionObject2D *co = Object::cast_to<CollisionObject2D>(res[i].collider);
@@ -690,7 +696,7 @@ void Viewport::_process_picking() {
if (co && camera_3d) {
_collision_object_3d_input_event(co, camera_3d, ev, Vector3(), Vector3(), 0);
captured = true;
- if (mb.is_valid() && mb->get_button_index() == 1 && !mb->is_pressed()) {
+ if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && !mb->is_pressed()) {
physics_object_capture = ObjectID();
}
@@ -706,7 +712,7 @@ void Viewport::_process_picking() {
if (ObjectDB::get_instance(last_id) && last_object) {
// Good, exists.
_collision_object_3d_input_event(last_object, camera_3d, ev, result.position, result.normal, result.shape);
- if (last_object->get_capture_input_on_drag() && mb.is_valid() && mb->get_button_index() == 1 && mb->is_pressed()) {
+ if (last_object->get_capture_input_on_drag() && mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
physics_object_capture = last_id;
}
}
@@ -715,10 +721,17 @@ void Viewport::_process_picking() {
if (camera_3d) {
Vector3 from = camera_3d->project_ray_origin(pos);
Vector3 dir = camera_3d->project_ray_normal(pos);
+ real_t far = camera_3d->far;
PhysicsDirectSpaceState3D *space = PhysicsServer3D::get_singleton()->space_get_direct_state(find_world_3d()->get_space());
if (space) {
- bool col = space->intersect_ray(from, from + dir * 10000, result, Set<RID>(), 0xFFFFFFFF, true, true, true);
+ PhysicsDirectSpaceState3D::RayParameters ray_params;
+ ray_params.from = from;
+ ray_params.to = from + dir * far;
+ ray_params.collide_with_areas = true;
+ ray_params.pick_ray = true;
+
+ bool col = space->intersect_ray(ray_params, result);
ObjectID new_collider;
if (col) {
CollisionObject3D *co = Object::cast_to<CollisionObject3D>(result.collider);
@@ -727,7 +740,7 @@ void Viewport::_process_picking() {
last_object = co;
last_id = result.collider_id;
new_collider = last_id;
- if (co->get_capture_input_on_drag() && mb.is_valid() && mb->get_button_index() == 1 && mb->is_pressed()) {
+ if (co->get_capture_input_on_drag() && mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
physics_object_capture = last_id;
}
}
@@ -1246,10 +1259,10 @@ void Viewport::_gui_call_input(Control *p_control, const Ref<InputEvent> &p_inpu
Ref<InputEventMouseButton> mb = p_input;
bool cant_stop_me_now = (mb.is_valid() &&
- (mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN ||
- mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP ||
- mb->get_button_index() == MOUSE_BUTTON_WHEEL_LEFT ||
- mb->get_button_index() == MOUSE_BUTTON_WHEEL_RIGHT));
+ (mb->get_button_index() == MouseButton::WHEEL_DOWN ||
+ mb->get_button_index() == MouseButton::WHEEL_UP ||
+ mb->get_button_index() == MouseButton::WHEEL_LEFT ||
+ mb->get_button_index() == MouseButton::WHEEL_RIGHT));
Ref<InputEventPanGesture> pn = p_input;
cant_stop_me_now = pn.is_valid() || cant_stop_me_now;
@@ -1432,21 +1445,21 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
gui.last_mouse_pos = mpos;
if (mb->is_pressed()) {
Size2 pos = mpos;
- if (gui.mouse_focus_mask) {
+ if (gui.mouse_focus_mask != MouseButton::NONE) {
// Do not steal mouse focus and stuff while a focus mask exists.
- gui.mouse_focus_mask |= 1 << (mb->get_button_index() - 1); // Add the button to the mask.
+ gui.mouse_focus_mask |= mouse_button_to_mask(mb->get_button_index());
} else {
gui.mouse_focus = gui_find_control(pos);
gui.last_mouse_focus = gui.mouse_focus;
if (!gui.mouse_focus) {
- gui.mouse_focus_mask = 0;
+ gui.mouse_focus_mask = MouseButton::NONE;
return;
}
- gui.mouse_focus_mask = 1 << (mb->get_button_index() - 1);
+ gui.mouse_focus_mask = mouse_button_to_mask(mb->get_button_index());
- if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb->get_button_index() == MouseButton::LEFT) {
gui.drag_accum = Vector2();
gui.drag_attempted = false;
}
@@ -1469,7 +1482,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
}
#endif
- if (mb->get_button_index() == MOUSE_BUTTON_LEFT) { // Assign focus.
+ if (mb->get_button_index() == MouseButton::LEFT) { // Assign focus.
CanvasItem *ci = gui.mouse_focus;
while (ci) {
Control *control = Object::cast_to<Control>(ci);
@@ -1500,7 +1513,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
set_input_as_handled();
- if (gui.drag_data.get_type() != Variant::NIL && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (gui.drag_data.get_type() != Variant::NIL && mb->get_button_index() == MouseButton::LEFT) {
// Alternate drop use (when using force_drag(), as proposed by #5342).
if (gui.mouse_focus) {
_gui_drop(gui.mouse_focus, pos, false);
@@ -1520,7 +1533,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
_gui_cancel_tooltip();
} else {
- if (gui.drag_data.get_type() != Variant::NIL && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (gui.drag_data.get_type() != Variant::NIL && mb->get_button_index() == MouseButton::LEFT) {
if (gui.drag_mouse_over) {
_gui_drop(gui.drag_mouse_over, gui.drag_mouse_over_pos, false);
}
@@ -1538,7 +1551,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
// Change mouse accordingly.
}
- gui.mouse_focus_mask &= ~(1 << (mb->get_button_index() - 1)); // Remove from mask.
+ gui.mouse_focus_mask &= ~mouse_button_to_mask(mb->get_button_index()); // Remove from mask.
if (!gui.mouse_focus) {
// Release event is only sent if a mouse focus (previously pressed button) exists.
@@ -1557,7 +1570,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
// Disable mouse focus if needed before calling input,
// this makes popups on mouse press event work better,
// as the release will never be received otherwise.
- if (gui.mouse_focus_mask == 0) {
+ if (gui.mouse_focus_mask == MouseButton::NONE) {
gui.mouse_focus = nullptr;
gui.forced_mouse_focus = false;
}
@@ -1577,7 +1590,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
over = gui_find_control(mpos);
}
- if (gui.mouse_focus_mask == 0 && over != gui.mouse_over) {
+ if (gui.mouse_focus_mask == MouseButton::NONE && over != gui.mouse_over) {
if (gui.mouse_over) {
_gui_call_notification(gui.mouse_over, Control::NOTIFICATION_MOUSE_EXIT);
}
@@ -1605,7 +1618,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
Control *over = nullptr;
// Drag & drop.
- if (!gui.drag_attempted && gui.mouse_focus && mm->get_button_mask() & MOUSE_BUTTON_MASK_LEFT) {
+ if (!gui.drag_attempted && gui.mouse_focus && (mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE) {
gui.drag_accum += mm->get_relative();
float len = gui.drag_accum.length();
if (len > 10) {
@@ -1619,7 +1632,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
if (gui.drag_data.get_type() != Variant::NIL) {
gui.mouse_focus = nullptr;
gui.forced_mouse_focus = false;
- gui.mouse_focus_mask = 0;
+ gui.mouse_focus_mask = MouseButton::NONE;
break;
} else {
Control *drag_preview = _gui_get_drag_preview();
@@ -1688,7 +1701,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
mm->set_speed(speed);
mm->set_relative(rel);
- if (mm->get_button_mask() == 0) {
+ if (mm->get_button_mask() == MouseButton::NONE) {
// Nothing pressed.
bool can_tooltip = true;
@@ -1741,7 +1754,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
Control *c = over;
Vector2 cpos = pos;
while (c) {
- if (gui.mouse_focus_mask != 0 || c->has_point(cpos)) {
+ if (gui.mouse_focus_mask != MouseButton::NONE || c->has_point(cpos)) {
cursor_shape = c->get_cursor_shape(cpos);
} else {
cursor_shape = Control::CURSOR_ARROW;
@@ -1854,7 +1867,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
Transform2D localizer = gui.drag_mouse_over->get_global_transform_with_canvas().affine_inverse();
gui.drag_mouse_over_pos = localizer.xform(viewport_pos);
- if (mm->get_button_mask() & MOUSE_BUTTON_MASK_LEFT) {
+ if ((mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE) {
bool can_drop = _gui_drop(gui.drag_mouse_over, gui.drag_mouse_over_pos, true);
if (!can_drop) {
@@ -2095,7 +2108,7 @@ void Viewport::_gui_remove_control(Control *p_control) {
if (gui.mouse_focus == p_control) {
gui.mouse_focus = nullptr;
gui.forced_mouse_focus = false;
- gui.mouse_focus_mask = 0;
+ gui.mouse_focus_mask = MouseButton::NONE;
}
if (gui.last_mouse_focus == p_control) {
gui.last_mouse_focus = nullptr;
@@ -2165,13 +2178,13 @@ void Viewport::_gui_accept_event() {
void Viewport::_drop_mouse_focus() {
Control *c = gui.mouse_focus;
- int mask = gui.mouse_focus_mask;
+ MouseButton mask = gui.mouse_focus_mask;
gui.mouse_focus = nullptr;
gui.forced_mouse_focus = false;
- gui.mouse_focus_mask = 0;
+ gui.mouse_focus_mask = MouseButton::NONE;
for (int i = 0; i < 3; i++) {
- if (mask & (1 << i)) {
+ if ((int)mask & (1 << i)) {
Ref<InputEventMouseButton> mb;
mb.instantiate();
mb->set_position(c->get_local_mouse_position());
@@ -2280,11 +2293,11 @@ void Viewport::_post_gui_grab_click_focus() {
return;
}
- int mask = gui.mouse_focus_mask;
+ MouseButton mask = gui.mouse_focus_mask;
Point2 click = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse().xform(gui.last_mouse_pos);
for (int i = 0; i < 3; i++) {
- if (mask & (1 << i)) {
+ if ((int)mask & (1 << i)) {
Ref<InputEventMouseButton> mb;
mb.instantiate();
@@ -2302,7 +2315,7 @@ void Viewport::_post_gui_grab_click_focus() {
click = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse().xform(gui.last_mouse_pos);
for (int i = 0; i < 3; i++) {
- if (mask & (1 << i)) {
+ if ((int)mask & (1 << i)) {
Ref<InputEventMouseButton> mb;
mb.instantiate();
@@ -2399,7 +2412,7 @@ bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND_V(gui.subwindow_focused == nullptr, false);
Ref<InputEventMouseButton> mb = p_event;
- if (mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
if (gui.subwindow_drag == SUB_WINDOW_DRAG_CLOSE) {
if (gui.subwindow_drag_close_rect.has_point(mb->get_position())) {
// Close window.
@@ -2524,7 +2537,7 @@ bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseButton> mb = p_event;
// If the event is a mouse button, we need to check whether another window was clicked.
- if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
bool click_on_window = false;
for (int i = gui.sub_windows.size() - 1; i >= 0; i--) {
SubWindow &sw = gui.sub_windows.write[i];
@@ -3028,7 +3041,7 @@ void Viewport::pass_mouse_focus_to(Viewport *p_viewport, Control *p_control) {
gui.mouse_focus = nullptr;
gui.forced_mouse_focus = false;
- gui.mouse_focus_mask = 0;
+ gui.mouse_focus_mask = MouseButton::NONE;
}
}
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index 1f19ff04c9..5320aea02a 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -244,7 +244,7 @@ private:
bool control = false;
bool shift = false;
bool meta = false;
- int mouse_mask = 0;
+ MouseButton mouse_mask = MouseButton::NONE;
} physics_last_mouse_state;
@@ -327,7 +327,7 @@ private:
Control *mouse_focus = nullptr;
Control *last_mouse_focus = nullptr;
Control *mouse_click_grabber = nullptr;
- int mouse_focus_mask = 0;
+ MouseButton mouse_focus_mask = MouseButton::NONE;
Control *key_focus = nullptr;
Control *mouse_over = nullptr;
Control *drag_mouse_over = nullptr;
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index a0f62c853f..5da5a183f7 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -895,7 +895,7 @@ void Window::_window_input(const Ref<InputEvent> &p_ev) {
if (EngineDebugger::is_active()) {
//quit from game window using F8
Ref<InputEventKey> k = p_ev;
- if (k.is_valid() && k->is_pressed() && !k->is_echo() && k->get_keycode() == KEY_F8) {
+ if (k.is_valid() && k->is_pressed() && !k->is_echo() && k->get_keycode() == Key::F8) {
EngineDebugger::get_singleton()->send_message("request_quit", Array());
}
}
diff --git a/scene/property_utils.cpp b/scene/property_utils.cpp
new file mode 100644
index 0000000000..7df601492b
--- /dev/null
+++ b/scene/property_utils.cpp
@@ -0,0 +1,188 @@
+/*************************************************************************/
+/* property_utils.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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 "property_utils.h"
+
+#include "core/config/engine.h"
+#include "core/templates/local_vector.h"
+#include "scene/resources/packed_scene.h"
+
+#ifdef TOOLS_ENABLED
+#include "editor/editor_node.h"
+#endif // TOOLS_ENABLED
+
+bool PropertyUtils::is_property_value_different(const Variant &p_a, const Variant &p_b) {
+ if (p_a.get_type() == Variant::FLOAT && p_b.get_type() == Variant::FLOAT) {
+ //this must be done because, as some scenes save as text, there might be a tiny difference in floats due to numerical error
+ return !Math::is_equal_approx((float)p_a, (float)p_b);
+ } else {
+ // For our purposes, treating null object as NIL is the right thing to do
+ const Variant &a = p_a.get_type() == Variant::OBJECT && (Object *)p_a == nullptr ? Variant() : p_a;
+ const Variant &b = p_b.get_type() == Variant::OBJECT && (Object *)p_b == nullptr ? Variant() : p_b;
+ return a != b;
+ }
+}
+
+Variant PropertyUtils::get_property_default_value(const Object *p_object, const StringName &p_property, const Vector<SceneState::PackState> *p_states_stack_cache, bool p_update_exports, const Node *p_owner, bool *r_is_class_default) {
+ // This function obeys the way property values are set when an object is instantiated,
+ // which is the following (the latter wins):
+ // 1. Default value from builtin class
+ // 2. Default value from script exported variable (from the topmost script)
+ // 3. Value overrides from the instantiation/inheritance stack
+
+ if (r_is_class_default) {
+ *r_is_class_default = false;
+ }
+
+ Ref<Script> topmost_script;
+
+ if (const Node *node = Object::cast_to<Node>(p_object)) {
+ // Check inheritance/instantiation ancestors
+ const Vector<SceneState::PackState> &states_stack = p_states_stack_cache ? *p_states_stack_cache : PropertyUtils::get_node_states_stack(node, p_owner);
+ for (int i = 0; i < states_stack.size(); ++i) {
+ const SceneState::PackState &ia = states_stack[i];
+ bool found = false;
+ Variant value_in_ancestor = ia.state->get_property_value(ia.node, p_property, found);
+ if (found) {
+ return value_in_ancestor;
+ }
+ // Save script for later
+ bool has_script = false;
+ Variant script = ia.state->get_property_value(ia.node, SNAME("script"), has_script);
+ if (has_script) {
+ Ref<Script> scr = script;
+ if (scr.is_valid()) {
+ topmost_script = scr;
+ }
+ }
+ }
+ }
+
+ // Let's see what default is set by the topmost script having a default, if any
+ if (topmost_script.is_null()) {
+ topmost_script = p_object->get_script();
+ }
+ if (topmost_script.is_valid()) {
+ // Should be called in the editor only and not at runtime,
+ // otherwise it can cause problems because of missing instance state support
+ if (p_update_exports && Engine::get_singleton()->is_editor_hint()) {
+ topmost_script->update_exports();
+ }
+ Variant default_value;
+ if (topmost_script->get_property_default_value(p_property, default_value)) {
+ return default_value;
+ }
+ }
+
+ // Fall back to the default from the native class
+ if (r_is_class_default) {
+ *r_is_class_default = true;
+ }
+ return ClassDB::class_get_default_property_value(p_object->get_class_name(), p_property);
+}
+
+// Like SceneState::PackState, but using a raw pointer to avoid the cost of
+// updating the reference count during the internal work of the functions below
+namespace {
+struct _FastPackState {
+ SceneState *state = nullptr;
+ int node = -1;
+};
+} // namespace
+
+static bool _collect_inheritance_chain(const Ref<SceneState> &p_state, const NodePath &p_path, LocalVector<_FastPackState> &r_states_stack) {
+ bool found = false;
+
+ LocalVector<_FastPackState> inheritance_states;
+
+ Ref<SceneState> state = p_state;
+ while (state.is_valid()) {
+ int node = state->find_node_by_path(p_path);
+ if (node >= 0) {
+ // This one has state for this node
+ inheritance_states.push_back({ state.ptr(), node });
+ found = true;
+ }
+ state = state->get_base_scene_state();
+ }
+
+ for (int i = inheritance_states.size() - 1; i >= 0; --i) {
+ r_states_stack.push_back(inheritance_states[i]);
+ }
+
+ return found;
+}
+
+Vector<SceneState::PackState> PropertyUtils::get_node_states_stack(const Node *p_node, const Node *p_owner, bool *r_instantiated_by_owner) {
+ if (r_instantiated_by_owner) {
+ *r_instantiated_by_owner = true;
+ }
+
+ LocalVector<_FastPackState> states_stack;
+ {
+ const Node *owner = p_owner;
+#ifdef TOOLS_ENABLED
+ if (!p_owner && Engine::get_singleton()->is_editor_hint()) {
+ owner = EditorNode::get_singleton()->get_edited_scene();
+ }
+#endif
+
+ const Node *n = p_node;
+ while (n) {
+ if (n == owner) {
+ const Ref<SceneState> &state = n->get_scene_inherited_state();
+ if (_collect_inheritance_chain(state, n->get_path_to(p_node), states_stack)) {
+ if (r_instantiated_by_owner) {
+ *r_instantiated_by_owner = false;
+ }
+ }
+ break;
+ } else if (n->get_scene_file_path() != String()) {
+ const Ref<SceneState> &state = n->get_scene_instance_state();
+ _collect_inheritance_chain(state, n->get_path_to(p_node), states_stack);
+ }
+ n = n->get_owner();
+ }
+ }
+
+ // Convert to the proper type for returning, inverting the vector on the go
+ // (it was more convenient to fill the vector in reverse order)
+ Vector<SceneState::PackState> states_stack_ret;
+ {
+ states_stack_ret.resize(states_stack.size());
+ _FastPackState *ps = states_stack.ptr();
+ for (int i = states_stack.size() - 1; i >= 0; --i) {
+ states_stack_ret.write[i].state.reference_ptr(ps->state);
+ states_stack_ret.write[i].node = ps->node;
+ ++ps;
+ }
+ }
+ return states_stack_ret;
+}
diff --git a/scene/property_utils.h b/scene/property_utils.h
new file mode 100644
index 0000000000..fde9163548
--- /dev/null
+++ b/scene/property_utils.h
@@ -0,0 +1,51 @@
+/*************************************************************************/
+/* property_utils.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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 PROPERTY_UTILS_H
+#define PROPERTY_UTILS_H
+
+#include "scene/main/node.h"
+#include "scene/resources/packed_scene.h"
+
+class PropertyUtils {
+public:
+ static bool is_property_value_different(const Variant &p_a, const Variant &p_b);
+ // Gets the most pure default value, the one that would be set when the node has just been instantiated
+ static Variant get_property_default_value(const Object *p_object, const StringName &p_property, const Vector<SceneState::PackState> *p_states_stack_cache = nullptr, bool p_update_exports = false, const Node *p_owner = nullptr, bool *r_is_class_default = nullptr);
+
+ // Gets the instance/inheritance states of this node, in order of precedence,
+ // that is, from the topmost (the most able to override values) to the lowermost
+ // (Note that in nested instancing the one with the greatest precedence is the furthest
+ // in the tree, since every owner found while traversing towards the root gets a chance
+ // to override property values.)
+ static Vector<SceneState::PackState> get_node_states_stack(const Node *p_node, const Node *p_owner = nullptr, bool *r_instantiated_by_owner = nullptr);
+};
+
+#endif // PROPERTY_UTILS_H
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index d44cd7ca63..056ace5e4e 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -63,6 +63,7 @@
#include "scene/2d/position_2d.h"
#include "scene/2d/ray_cast_2d.h"
#include "scene/2d/remote_transform_2d.h"
+#include "scene/2d/shape_cast_2d.h"
#include "scene/2d/skeleton_2d.h"
#include "scene/2d/sprite_2d.h"
#include "scene/2d/tile_map.h"
@@ -628,6 +629,7 @@ void register_scene_types() {
GDREGISTER_CLASS(VisualShaderNodeParticleSphereEmitter);
GDREGISTER_CLASS(VisualShaderNodeParticleBoxEmitter);
GDREGISTER_CLASS(VisualShaderNodeParticleRingEmitter);
+ GDREGISTER_CLASS(VisualShaderNodeParticleMeshEmitter);
GDREGISTER_CLASS(VisualShaderNodeParticleMultiplyByAxisAngle);
GDREGISTER_CLASS(VisualShaderNodeParticleConeVelocity);
GDREGISTER_CLASS(VisualShaderNodeParticleRandomness);
@@ -665,6 +667,7 @@ void register_scene_types() {
GDREGISTER_CLASS(CollisionShape2D);
GDREGISTER_CLASS(CollisionPolygon2D);
GDREGISTER_CLASS(RayCast2D);
+ GDREGISTER_CLASS(ShapeCast2D);
GDREGISTER_CLASS(VisibleOnScreenNotifier2D);
GDREGISTER_CLASS(VisibleOnScreenEnabler2D);
GDREGISTER_CLASS(Polygon2D);
@@ -791,7 +794,7 @@ void register_scene_types() {
GDREGISTER_CLASS(MeshTexture);
GDREGISTER_CLASS(CurveTexture);
GDREGISTER_CLASS(CurveXYZTexture);
- GDREGISTER_CLASS(GradientTexture);
+ GDREGISTER_CLASS(GradientTexture1D);
GDREGISTER_CLASS(GradientTexture2D);
GDREGISTER_CLASS(ProxyTexture);
GDREGISTER_CLASS(AnimatedTexture);
@@ -922,6 +925,7 @@ void register_scene_types() {
ClassDB::add_compatibility_class("EditorSpatialGizmo", "EditorNode3DGizmo");
ClassDB::add_compatibility_class("EditorSpatialGizmoPlugin", "EditorNode3DGizmoPlugin");
ClassDB::add_compatibility_class("Generic6DOFJoint", "Generic6DOFJoint3D");
+ ClassDB::add_compatibility_class("GradientTexture", "GradientTexture1D");
ClassDB::add_compatibility_class("HeightMapShape", "HeightMapShape3D");
ClassDB::add_compatibility_class("HingeJoint", "HingeJoint3D");
ClassDB::add_compatibility_class("Joint", "Joint3D");
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index 06ce993cc7..3700e87839 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -449,8 +449,8 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
return true;
} else if (name == "length") {
r_ret = length;
- } else if (name == "loop") {
- r_ret = loop;
+ } else if (name == "loop_mode") {
+ r_ret = loop_mode;
} else if (name == "step") {
r_ret = step;
} else if (name.begins_with("tracks/")) {
@@ -799,19 +799,19 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
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_NOEDITOR | PROPERTY_USAGE_INTERNAL));
+ p_list->push_back(PropertyInfo(Variant::DICTIONARY, "_compression", 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_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/imported", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::NODE_PATH, "tracks/" + itos(i) + "/path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
+ 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));
+ p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
+ p_list->push_back(PropertyInfo(Variant::NODE_PATH, "tracks/" + itos(i) + "/path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
if (track_is_compressed(i)) {
- p_list->push_back(PropertyInfo(Variant::INT, "tracks/" + itos(i) + "/compressed_track", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
+ p_list->push_back(PropertyInfo(Variant::INT, "tracks/" + itos(i) + "/compressed_track", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
} else {
- p_list->push_back(PropertyInfo(Variant::INT, "tracks/" + itos(i) + "/interp", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/loop_wrap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::ARRAY, "tracks/" + itos(i) + "/keys", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
+ p_list->push_back(PropertyInfo(Variant::INT, "tracks/" + itos(i) + "/interp", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
+ p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/loop_wrap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
+ p_list->push_back(PropertyInfo(Variant::ARRAY, "tracks/" + itos(i) + "/keys", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
}
}
}
@@ -2231,7 +2231,7 @@ void Animation::track_set_key_transition(int p_track, int p_key_idx, real_t p_tr
}
template <class K>
-int Animation::_find(const Vector<K> &p_keys, double p_time) const {
+int Animation::_find(const Vector<K> &p_keys, double p_time, bool p_backward) const {
int len = p_keys.size();
if (len == 0) {
return -2;
@@ -2261,8 +2261,14 @@ int Animation::_find(const Vector<K> &p_keys, double p_time) const {
}
}
- if (keys[middle].time > p_time) {
- middle--;
+ if (!p_backward) {
+ if (keys[middle].time > p_time) {
+ middle--;
+ }
+ } else {
+ if (keys[middle].time < p_time) {
+ middle++;
+ }
}
return middle;
@@ -2385,7 +2391,7 @@ real_t Animation::_cubic_interpolate(const real_t &p_pre_a, const real_t &p_a, c
}
template <class T>
-T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, InterpolationType p_interp, bool p_loop_wrap, bool *p_ok) const {
+T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, InterpolationType p_interp, bool p_loop_wrap, bool *p_ok, bool p_backward) const {
int len = _find(p_keys, length) + 1; // try to find last key (there may be more past the end)
if (len <= 0) {
@@ -2403,7 +2409,7 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, Interpol
return p_keys[0].value;
}
- int idx = _find(p_keys, p_time);
+ int idx = _find(p_keys, p_time, p_backward);
ERR_FAIL_COND_V(idx == -2, T());
@@ -2412,24 +2418,42 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, Interpol
real_t c = 0.0;
// prepare for all cases of interpolation
- if (loop && p_loop_wrap) {
+ if ((loop_mode == LOOP_LINEAR || loop_mode == LOOP_PINGPONG) && p_loop_wrap) {
// loop
- if (idx >= 0) {
- if ((idx + 1) < len) {
- next = idx + 1;
- real_t delta = p_keys[next].time - p_keys[idx].time;
- real_t from = p_time - p_keys[idx].time;
-
- if (Math::is_zero_approx(delta)) {
- c = 0;
+ if (!p_backward) {
+ // no backward
+ if (idx >= 0) {
+ if (idx < len - 1) {
+ next = idx + 1;
+ real_t delta = p_keys[next].time - p_keys[idx].time;
+ real_t from = p_time - p_keys[idx].time;
+
+ if (Math::is_zero_approx(delta)) {
+ c = 0;
+ } else {
+ c = from / delta;
+ }
} else {
- c = from / delta;
- }
+ next = 0;
+ real_t delta = (length - p_keys[idx].time) + p_keys[next].time;
+ real_t from = p_time - p_keys[idx].time;
+ if (Math::is_zero_approx(delta)) {
+ c = 0;
+ } else {
+ c = from / delta;
+ }
+ }
} else {
+ // on loop, behind first key
+ idx = len - 1;
next = 0;
- real_t delta = (length - p_keys[idx].time) + p_keys[next].time;
- real_t from = p_time - p_keys[idx].time;
+ real_t endtime = (length - p_keys[idx].time);
+ if (endtime < 0) { // may be keys past the end
+ endtime = 0;
+ }
+ real_t delta = endtime + p_keys[next].time;
+ real_t from = endtime + p_time;
if (Math::is_zero_approx(delta)) {
c = 0;
@@ -2437,49 +2461,81 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, Interpol
c = from / delta;
}
}
-
} else {
- // on loop, behind first key
- idx = len - 1;
- next = 0;
- real_t endtime = (length - p_keys[idx].time);
- if (endtime < 0) { // may be keys past the end
- endtime = 0;
- }
- real_t delta = endtime + p_keys[next].time;
- real_t from = endtime + p_time;
-
- if (Math::is_zero_approx(delta)) {
- c = 0;
+ // backward
+ if (idx <= len - 1) {
+ if (idx > 0) {
+ next = idx - 1;
+ real_t delta = (length - p_keys[next].time) - (length - p_keys[idx].time);
+ real_t from = (length - p_time) - (length - p_keys[idx].time);
+
+ if (Math::is_zero_approx(delta))
+ c = 0;
+ else
+ c = from / delta;
+ } else {
+ next = len - 1;
+ real_t delta = p_keys[idx].time + (length - p_keys[next].time);
+ real_t from = (length - p_time) - (length - p_keys[idx].time);
+
+ if (Math::is_zero_approx(delta))
+ c = 0;
+ else
+ c = from / delta;
+ }
} else {
- c = from / delta;
+ // on loop, in front of last key
+ idx = 0;
+ next = len - 1;
+ real_t endtime = p_keys[idx].time;
+ if (endtime > length) // may be keys past the end
+ endtime = length;
+ real_t delta = p_keys[next].time - endtime;
+ real_t from = p_time - endtime;
+
+ if (Math::is_zero_approx(delta))
+ c = 0;
+ else
+ c = from / delta;
}
}
-
} else { // no loop
-
- if (idx >= 0) {
- if ((idx + 1) < len) {
- next = idx + 1;
- real_t delta = p_keys[next].time - p_keys[idx].time;
- real_t from = p_time - p_keys[idx].time;
-
- if (Math::is_zero_approx(delta)) {
- c = 0;
+ if (!p_backward) {
+ if (idx >= 0) {
+ if (idx < len - 1) {
+ next = idx + 1;
+ real_t delta = p_keys[next].time - p_keys[idx].time;
+ real_t from = p_time - p_keys[idx].time;
+
+ if (Math::is_zero_approx(delta)) {
+ c = 0;
+ } else {
+ c = from / delta;
+ }
} else {
- c = from / delta;
+ next = idx;
}
-
} else {
- next = idx;
+ idx = next = 0;
}
-
} else {
- // only allow extending first key to anim start if looping
- if (loop) {
- idx = next = 0;
+ if (idx <= len - 1) {
+ if (idx > 0) {
+ next = idx - 1;
+ real_t delta = (length - p_keys[next].time) - (length - p_keys[idx].time);
+ real_t from = (length - p_time) - (length - p_keys[idx].time);
+
+ if (Math::is_zero_approx(delta)) {
+ c = 0;
+ } else {
+ c = from / delta;
+ }
+
+ } else {
+ next = idx;
+ }
} else {
- result = false;
+ idx = next = len - 1;
}
}
}
@@ -2583,7 +2639,7 @@ void Animation::_value_track_get_key_indices_in_range(const ValueTrack *vt, doub
}
}
-void Animation::value_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices) const {
+void Animation::value_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices, int p_pingponged) const {
ERR_FAIL_INDEX(p_track, tracks.size());
Track *t = tracks[p_track];
ERR_FAIL_COND(t->type != TYPE_VALUE);
@@ -2597,30 +2653,50 @@ void Animation::value_track_get_key_indices(int p_track, double p_time, double p
SWAP(from_time, to_time);
}
- if (loop) {
- from_time = Math::fposmod(from_time, length);
- to_time = Math::fposmod(to_time, length);
+ switch (loop_mode) {
+ case LOOP_NONE: {
+ if (from_time < 0) {
+ from_time = 0;
+ }
+ if (from_time > length) {
+ from_time = length;
+ }
- if (from_time > to_time) {
- // handle loop by splitting
- _value_track_get_key_indices_in_range(vt, from_time, length, p_indices);
- _value_track_get_key_indices_in_range(vt, 0, to_time, p_indices);
- return;
- }
- } else {
- if (from_time < 0) {
- from_time = 0;
- }
- if (from_time > length) {
- from_time = length;
- }
+ if (to_time < 0) {
+ to_time = 0;
+ }
+ if (to_time > length) {
+ to_time = length;
+ }
+ } break;
+ case LOOP_LINEAR: {
+ from_time = Math::fposmod(from_time, length);
+ to_time = Math::fposmod(to_time, length);
- if (to_time < 0) {
- to_time = 0;
- }
- if (to_time > length) {
- to_time = length;
- }
+ if (from_time > to_time) {
+ // handle loop by splitting
+ _value_track_get_key_indices_in_range(vt, from_time, length, p_indices);
+ _value_track_get_key_indices_in_range(vt, 0, to_time, p_indices);
+ return;
+ }
+ } break;
+ case LOOP_PINGPONG: {
+ from_time = Math::pingpong(from_time, length);
+ to_time = Math::pingpong(to_time, length);
+
+ if (p_pingponged == -1) {
+ // handle loop by splitting
+ _value_track_get_key_indices_in_range(vt, 0, from_time, p_indices);
+ _value_track_get_key_indices_in_range(vt, 0, to_time, p_indices);
+ return;
+ }
+ if (p_pingponged == 1) {
+ // handle loop by splitting
+ _value_track_get_key_indices_in_range(vt, from_time, length, p_indices);
+ _value_track_get_key_indices_in_range(vt, to_time, length, p_indices);
+ return;
+ }
+ } break;
}
_value_track_get_key_indices_in_range(vt, from_time, to_time, p_indices);
@@ -2679,7 +2755,7 @@ void Animation::_track_get_key_indices_in_range(const Vector<T> &p_array, double
}
}
-void Animation::track_get_key_indices_in_range(int p_track, double p_time, double p_delta, List<int> *p_indices) const {
+void Animation::track_get_key_indices_in_range(int p_track, double p_time, double p_delta, List<int> *p_indices, int p_pingponged) const {
ERR_FAIL_INDEX(p_track, tracks.size());
const Track *t = tracks[p_track];
@@ -2690,114 +2766,255 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
SWAP(from_time, to_time);
}
- if (loop) {
- if (from_time > length || from_time < 0) {
- from_time = Math::fposmod(from_time, length);
- }
-
- if (to_time > length || to_time < 0) {
- to_time = Math::fposmod(to_time, length);
- }
-
- if (from_time > to_time) {
- // handle loop by splitting
-
- switch (t->type) {
- case TYPE_POSITION_3D: {
- const PositionTrack *tt = static_cast<const PositionTrack *>(t);
- if (tt->compressed_track >= 0) {
- _get_compressed_key_indices_in_range<3>(tt->compressed_track, from_time, length, p_indices);
- _get_compressed_key_indices_in_range<3>(tt->compressed_track, 0, to_time, p_indices);
-
- } else {
- _track_get_key_indices_in_range(tt->positions, from_time, length, p_indices);
- _track_get_key_indices_in_range(tt->positions, 0, to_time, p_indices);
- }
-
- } break;
- case TYPE_ROTATION_3D: {
- const RotationTrack *rt = static_cast<const RotationTrack *>(t);
- if (rt->compressed_track >= 0) {
- _get_compressed_key_indices_in_range<3>(rt->compressed_track, from_time, length, p_indices);
- _get_compressed_key_indices_in_range<3>(rt->compressed_track, 0, to_time, p_indices);
+ switch (loop_mode) {
+ case LOOP_NONE: {
+ if (from_time < 0) {
+ from_time = 0;
+ }
+ if (from_time > length) {
+ from_time = length;
+ }
- } else {
- _track_get_key_indices_in_range(rt->rotations, from_time, length, p_indices);
- _track_get_key_indices_in_range(rt->rotations, 0, to_time, p_indices);
- }
+ if (to_time < 0) {
+ to_time = 0;
+ }
+ if (to_time > length) {
+ to_time = length;
+ }
+ } break;
+ case LOOP_LINEAR: {
+ if (from_time > length || from_time < 0) {
+ from_time = Math::fposmod(from_time, length);
+ }
+ if (to_time > length || to_time < 0) {
+ to_time = Math::fposmod(to_time, length);
+ }
- } break;
- case TYPE_SCALE_3D: {
- const ScaleTrack *st = static_cast<const ScaleTrack *>(t);
- if (st->compressed_track >= 0) {
- _get_compressed_key_indices_in_range<3>(st->compressed_track, from_time, length, p_indices);
- _get_compressed_key_indices_in_range<3>(st->compressed_track, 0, to_time, p_indices);
+ if (from_time > to_time) {
+ // handle loop by splitting
+ switch (t->type) {
+ case TYPE_POSITION_3D: {
+ const PositionTrack *tt = static_cast<const PositionTrack *>(t);
+ if (tt->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(tt->compressed_track, from_time, length, p_indices);
+ _get_compressed_key_indices_in_range<3>(tt->compressed_track, 0, to_time, p_indices);
+ } else {
+ _track_get_key_indices_in_range(tt->positions, from_time, length, p_indices);
+ _track_get_key_indices_in_range(tt->positions, 0, to_time, p_indices);
+ }
+ } break;
+ case TYPE_ROTATION_3D: {
+ const RotationTrack *rt = static_cast<const RotationTrack *>(t);
+ if (rt->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(rt->compressed_track, from_time, length, p_indices);
+ _get_compressed_key_indices_in_range<3>(rt->compressed_track, 0, to_time, p_indices);
+ } else {
+ _track_get_key_indices_in_range(rt->rotations, from_time, length, p_indices);
+ _track_get_key_indices_in_range(rt->rotations, 0, to_time, p_indices);
+ }
+ } break;
+ case TYPE_SCALE_3D: {
+ const ScaleTrack *st = static_cast<const ScaleTrack *>(t);
+ if (st->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(st->compressed_track, from_time, length, p_indices);
+ _get_compressed_key_indices_in_range<3>(st->compressed_track, 0, to_time, p_indices);
+ } else {
+ _track_get_key_indices_in_range(st->scales, from_time, length, p_indices);
+ _track_get_key_indices_in_range(st->scales, 0, to_time, p_indices);
+ }
+ } break;
+ case TYPE_BLEND_SHAPE: {
+ const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t);
+ if (bst->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<1>(bst->compressed_track, from_time, length, p_indices);
+ _get_compressed_key_indices_in_range<1>(bst->compressed_track, 0, to_time, p_indices);
+ } else {
+ _track_get_key_indices_in_range(bst->blend_shapes, from_time, length, p_indices);
+ _track_get_key_indices_in_range(bst->blend_shapes, 0, to_time, p_indices);
+ }
+ } break;
+ case TYPE_VALUE: {
+ const ValueTrack *vt = static_cast<const ValueTrack *>(t);
+ _track_get_key_indices_in_range(vt->values, from_time, length, p_indices);
+ _track_get_key_indices_in_range(vt->values, 0, to_time, p_indices);
+ } break;
+ case TYPE_METHOD: {
+ const MethodTrack *mt = static_cast<const MethodTrack *>(t);
+ _track_get_key_indices_in_range(mt->methods, from_time, length, p_indices);
+ _track_get_key_indices_in_range(mt->methods, 0, to_time, p_indices);
+ } break;
+ case TYPE_BEZIER: {
+ const BezierTrack *bz = static_cast<const BezierTrack *>(t);
+ _track_get_key_indices_in_range(bz->values, from_time, length, p_indices);
+ _track_get_key_indices_in_range(bz->values, 0, to_time, p_indices);
+ } break;
+ case TYPE_AUDIO: {
+ const AudioTrack *ad = static_cast<const AudioTrack *>(t);
+ _track_get_key_indices_in_range(ad->values, from_time, length, p_indices);
+ _track_get_key_indices_in_range(ad->values, 0, to_time, p_indices);
+ } break;
+ case TYPE_ANIMATION: {
+ const AnimationTrack *an = static_cast<const AnimationTrack *>(t);
+ _track_get_key_indices_in_range(an->values, from_time, length, p_indices);
+ _track_get_key_indices_in_range(an->values, 0, to_time, p_indices);
+ } break;
+ }
+ return;
+ }
+ } break;
+ case LOOP_PINGPONG: {
+ if (from_time > length || from_time < 0) {
+ from_time = Math::pingpong(from_time, length);
+ }
+ if (to_time > length || to_time < 0) {
+ to_time = Math::pingpong(to_time, length);
+ }
- } else {
- _track_get_key_indices_in_range(st->scales, from_time, length, p_indices);
- _track_get_key_indices_in_range(st->scales, 0, to_time, p_indices);
+ if ((int)Math::floor(abs(p_delta) / length) % 2 == 0) {
+ if (p_pingponged == -1) {
+ // handle loop by splitting
+ switch (t->type) {
+ case TYPE_POSITION_3D: {
+ const PositionTrack *tt = static_cast<const PositionTrack *>(t);
+ if (tt->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(tt->compressed_track, 0, from_time, p_indices);
+ _get_compressed_key_indices_in_range<3>(tt->compressed_track, 0, to_time, p_indices);
+ } else {
+ _track_get_key_indices_in_range(tt->positions, 0, from_time, p_indices);
+ _track_get_key_indices_in_range(tt->positions, 0, to_time, p_indices);
+ }
+ } break;
+ case TYPE_ROTATION_3D: {
+ const RotationTrack *rt = static_cast<const RotationTrack *>(t);
+ if (rt->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(rt->compressed_track, 0, from_time, p_indices);
+ _get_compressed_key_indices_in_range<3>(rt->compressed_track, 0, to_time, p_indices);
+ } else {
+ _track_get_key_indices_in_range(rt->rotations, 0, from_time, p_indices);
+ _track_get_key_indices_in_range(rt->rotations, 0, to_time, p_indices);
+ }
+ } break;
+ case TYPE_SCALE_3D: {
+ const ScaleTrack *st = static_cast<const ScaleTrack *>(t);
+ if (st->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(st->compressed_track, 0, from_time, p_indices);
+ _get_compressed_key_indices_in_range<3>(st->compressed_track, 0, to_time, p_indices);
+ } else {
+ _track_get_key_indices_in_range(st->scales, 0, from_time, p_indices);
+ _track_get_key_indices_in_range(st->scales, 0, to_time, p_indices);
+ }
+ } break;
+ case TYPE_BLEND_SHAPE: {
+ const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t);
+ if (bst->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<1>(bst->compressed_track, 0, from_time, p_indices);
+ _get_compressed_key_indices_in_range<1>(bst->compressed_track, 0, to_time, p_indices);
+ } else {
+ _track_get_key_indices_in_range(bst->blend_shapes, 0, from_time, p_indices);
+ _track_get_key_indices_in_range(bst->blend_shapes, 0, to_time, p_indices);
+ }
+ } break;
+ case TYPE_VALUE: {
+ const ValueTrack *vt = static_cast<const ValueTrack *>(t);
+ _track_get_key_indices_in_range(vt->values, 0, from_time, p_indices);
+ _track_get_key_indices_in_range(vt->values, 0, to_time, p_indices);
+ } break;
+ case TYPE_METHOD: {
+ const MethodTrack *mt = static_cast<const MethodTrack *>(t);
+ _track_get_key_indices_in_range(mt->methods, 0, from_time, p_indices);
+ _track_get_key_indices_in_range(mt->methods, 0, to_time, p_indices);
+ } break;
+ case TYPE_BEZIER: {
+ const BezierTrack *bz = static_cast<const BezierTrack *>(t);
+ _track_get_key_indices_in_range(bz->values, 0, from_time, p_indices);
+ _track_get_key_indices_in_range(bz->values, 0, to_time, p_indices);
+ } break;
+ case TYPE_AUDIO: {
+ const AudioTrack *ad = static_cast<const AudioTrack *>(t);
+ _track_get_key_indices_in_range(ad->values, 0, from_time, p_indices);
+ _track_get_key_indices_in_range(ad->values, 0, to_time, p_indices);
+ } break;
+ case TYPE_ANIMATION: {
+ const AnimationTrack *an = static_cast<const AnimationTrack *>(t);
+ _track_get_key_indices_in_range(an->values, 0, from_time, p_indices);
+ _track_get_key_indices_in_range(an->values, 0, to_time, p_indices);
+ } break;
}
-
- } break;
- case TYPE_BLEND_SHAPE: {
- const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t);
- if (bst->compressed_track >= 0) {
- _get_compressed_key_indices_in_range<1>(bst->compressed_track, from_time, length, p_indices);
- _get_compressed_key_indices_in_range<1>(bst->compressed_track, 0, to_time, p_indices);
-
- } else {
- _track_get_key_indices_in_range(bst->blend_shapes, from_time, length, p_indices);
- _track_get_key_indices_in_range(bst->blend_shapes, 0, to_time, p_indices);
+ return;
+ }
+ if (p_pingponged == 1) {
+ // handle loop by splitting
+ switch (t->type) {
+ case TYPE_POSITION_3D: {
+ const PositionTrack *tt = static_cast<const PositionTrack *>(t);
+ if (tt->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(tt->compressed_track, from_time, length, p_indices);
+ _get_compressed_key_indices_in_range<3>(tt->compressed_track, to_time, length, p_indices);
+ } else {
+ _track_get_key_indices_in_range(tt->positions, from_time, length, p_indices);
+ _track_get_key_indices_in_range(tt->positions, to_time, length, p_indices);
+ }
+ } break;
+ case TYPE_ROTATION_3D: {
+ const RotationTrack *rt = static_cast<const RotationTrack *>(t);
+ if (rt->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(rt->compressed_track, from_time, length, p_indices);
+ _get_compressed_key_indices_in_range<3>(rt->compressed_track, to_time, length, p_indices);
+ } else {
+ _track_get_key_indices_in_range(rt->rotations, from_time, length, p_indices);
+ _track_get_key_indices_in_range(rt->rotations, to_time, length, p_indices);
+ }
+ } break;
+ case TYPE_SCALE_3D: {
+ const ScaleTrack *st = static_cast<const ScaleTrack *>(t);
+ if (st->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(st->compressed_track, from_time, length, p_indices);
+ _get_compressed_key_indices_in_range<3>(st->compressed_track, to_time, length, p_indices);
+ } else {
+ _track_get_key_indices_in_range(st->scales, from_time, length, p_indices);
+ _track_get_key_indices_in_range(st->scales, to_time, length, p_indices);
+ }
+ } break;
+ case TYPE_BLEND_SHAPE: {
+ const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t);
+ if (bst->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<1>(bst->compressed_track, from_time, length, p_indices);
+ _get_compressed_key_indices_in_range<1>(bst->compressed_track, to_time, length, p_indices);
+ } else {
+ _track_get_key_indices_in_range(bst->blend_shapes, from_time, length, p_indices);
+ _track_get_key_indices_in_range(bst->blend_shapes, to_time, length, p_indices);
+ }
+ } break;
+ case TYPE_VALUE: {
+ const ValueTrack *vt = static_cast<const ValueTrack *>(t);
+ _track_get_key_indices_in_range(vt->values, from_time, length, p_indices);
+ _track_get_key_indices_in_range(vt->values, to_time, length, p_indices);
+ } break;
+ case TYPE_METHOD: {
+ const MethodTrack *mt = static_cast<const MethodTrack *>(t);
+ _track_get_key_indices_in_range(mt->methods, from_time, length, p_indices);
+ _track_get_key_indices_in_range(mt->methods, to_time, length, p_indices);
+ } break;
+ case TYPE_BEZIER: {
+ const BezierTrack *bz = static_cast<const BezierTrack *>(t);
+ _track_get_key_indices_in_range(bz->values, from_time, length, p_indices);
+ _track_get_key_indices_in_range(bz->values, to_time, length, p_indices);
+ } break;
+ case TYPE_AUDIO: {
+ const AudioTrack *ad = static_cast<const AudioTrack *>(t);
+ _track_get_key_indices_in_range(ad->values, from_time, length, p_indices);
+ _track_get_key_indices_in_range(ad->values, to_time, length, p_indices);
+ } break;
+ case TYPE_ANIMATION: {
+ const AnimationTrack *an = static_cast<const AnimationTrack *>(t);
+ _track_get_key_indices_in_range(an->values, from_time, length, p_indices);
+ _track_get_key_indices_in_range(an->values, to_time, length, p_indices);
+ } break;
}
-
- } break;
- case TYPE_VALUE: {
- const ValueTrack *vt = static_cast<const ValueTrack *>(t);
- _track_get_key_indices_in_range(vt->values, from_time, length, p_indices);
- _track_get_key_indices_in_range(vt->values, 0, to_time, p_indices);
-
- } break;
- case TYPE_METHOD: {
- const MethodTrack *mt = static_cast<const MethodTrack *>(t);
- _track_get_key_indices_in_range(mt->methods, from_time, length, p_indices);
- _track_get_key_indices_in_range(mt->methods, 0, to_time, p_indices);
-
- } break;
- case TYPE_BEZIER: {
- const BezierTrack *bz = static_cast<const BezierTrack *>(t);
- _track_get_key_indices_in_range(bz->values, from_time, length, p_indices);
- _track_get_key_indices_in_range(bz->values, 0, to_time, p_indices);
-
- } break;
- case TYPE_AUDIO: {
- const AudioTrack *ad = static_cast<const AudioTrack *>(t);
- _track_get_key_indices_in_range(ad->values, from_time, length, p_indices);
- _track_get_key_indices_in_range(ad->values, 0, to_time, p_indices);
-
- } break;
- case TYPE_ANIMATION: {
- const AnimationTrack *an = static_cast<const AnimationTrack *>(t);
- _track_get_key_indices_in_range(an->values, from_time, length, p_indices);
- _track_get_key_indices_in_range(an->values, 0, to_time, p_indices);
-
- } break;
+ return;
+ }
}
- return;
- }
- } else {
- if (from_time < 0) {
- from_time = 0;
- }
- if (from_time > length) {
- from_time = length;
- }
-
- if (to_time < 0) {
- to_time = 0;
- }
- if (to_time > length) {
- to_time = length;
- }
+ } break;
}
switch (t->type) {
@@ -2808,7 +3025,6 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
} else {
_track_get_key_indices_in_range(tt->positions, from_time, to_time, p_indices);
}
-
} break;
case TYPE_ROTATION_3D: {
const RotationTrack *rt = static_cast<const RotationTrack *>(t);
@@ -2817,7 +3033,6 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
} else {
_track_get_key_indices_in_range(rt->rotations, from_time, to_time, p_indices);
}
-
} break;
case TYPE_SCALE_3D: {
const ScaleTrack *st = static_cast<const ScaleTrack *>(t);
@@ -2826,7 +3041,6 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
} else {
_track_get_key_indices_in_range(st->scales, from_time, to_time, p_indices);
}
-
} break;
case TYPE_BLEND_SHAPE: {
const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t);
@@ -2835,32 +3049,26 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
} else {
_track_get_key_indices_in_range(bst->blend_shapes, from_time, to_time, p_indices);
}
-
} break;
case TYPE_VALUE: {
const ValueTrack *vt = static_cast<const ValueTrack *>(t);
_track_get_key_indices_in_range(vt->values, from_time, to_time, p_indices);
-
} break;
case TYPE_METHOD: {
const MethodTrack *mt = static_cast<const MethodTrack *>(t);
_track_get_key_indices_in_range(mt->methods, from_time, to_time, p_indices);
-
} break;
case TYPE_BEZIER: {
const BezierTrack *bz = static_cast<const BezierTrack *>(t);
_track_get_key_indices_in_range(bz->values, from_time, to_time, p_indices);
-
} break;
case TYPE_AUDIO: {
const AudioTrack *ad = static_cast<const AudioTrack *>(t);
_track_get_key_indices_in_range(ad->values, from_time, to_time, p_indices);
-
} break;
case TYPE_ANIMATION: {
const AnimationTrack *an = static_cast<const AnimationTrack *>(t);
_track_get_key_indices_in_range(an->values, from_time, to_time, p_indices);
-
} break;
}
}
@@ -2898,7 +3106,7 @@ void Animation::_method_track_get_key_indices_in_range(const MethodTrack *mt, do
}
}
-void Animation::method_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices) const {
+void Animation::method_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices, int p_pingponged) const {
ERR_FAIL_INDEX(p_track, tracks.size());
Track *t = tracks[p_track];
ERR_FAIL_COND(t->type != TYPE_METHOD);
@@ -2912,35 +3120,58 @@ void Animation::method_track_get_key_indices(int p_track, double p_time, double
SWAP(from_time, to_time);
}
- if (loop) {
- if (from_time > length || from_time < 0) {
- from_time = Math::fposmod(from_time, length);
- }
+ switch (loop_mode) {
+ case LOOP_NONE: {
+ if (from_time < 0) {
+ from_time = 0;
+ }
+ if (from_time > length) {
+ from_time = length;
+ }
- if (to_time > length || to_time < 0) {
- to_time = Math::fposmod(to_time, length);
- }
+ if (to_time < 0) {
+ to_time = 0;
+ }
+ if (to_time > length) {
+ to_time = length;
+ }
+ } break;
+ case LOOP_LINEAR: {
+ if (from_time > length || from_time < 0) {
+ from_time = Math::fposmod(from_time, length);
+ }
+ if (to_time > length || to_time < 0) {
+ to_time = Math::fposmod(to_time, length);
+ }
- if (from_time > to_time) {
- // handle loop by splitting
- _method_track_get_key_indices_in_range(mt, from_time, length, p_indices);
- _method_track_get_key_indices_in_range(mt, 0, to_time, p_indices);
- return;
- }
- } else {
- if (from_time < 0) {
- from_time = 0;
- }
- if (from_time > length) {
- from_time = length;
- }
+ if (from_time > to_time) {
+ // handle loop by splitting
+ _method_track_get_key_indices_in_range(mt, from_time, length, p_indices);
+ _method_track_get_key_indices_in_range(mt, 0, to_time, p_indices);
+ return;
+ }
+ } break;
+ case LOOP_PINGPONG: {
+ if (from_time > length || from_time < 0) {
+ from_time = Math::pingpong(from_time, length);
+ }
+ if (to_time > length || to_time < 0) {
+ to_time = Math::pingpong(to_time, length);
+ }
- if (to_time < 0) {
- to_time = 0;
- }
- if (to_time > length) {
- to_time = length;
- }
+ if (p_pingponged == -1) {
+ _method_track_get_key_indices_in_range(mt, 0, from_time, p_indices);
+ _method_track_get_key_indices_in_range(mt, 0, to_time, p_indices);
+ return;
+ }
+ if (p_pingponged == 1) {
+ _method_track_get_key_indices_in_range(mt, from_time, length, p_indices);
+ _method_track_get_key_indices_in_range(mt, to_time, length, p_indices);
+ return;
+ }
+ } break;
+ default:
+ break;
}
_method_track_get_key_indices_in_range(mt, from_time, to_time, p_indices);
@@ -3326,13 +3557,13 @@ real_t Animation::get_length() const {
return length;
}
-void Animation::set_loop(bool p_enabled) {
- loop = p_enabled;
+void Animation::set_loop_mode(Animation::LoopMode p_loop_mode) {
+ loop_mode = p_loop_mode;
emit_changed();
}
-bool Animation::has_loop() const {
- return loop;
+Animation::LoopMode Animation::get_loop_mode() const {
+ return loop_mode;
}
void Animation::track_set_imported(int p_track, bool p_imported) {
@@ -3514,8 +3745,8 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_length", "time_sec"), &Animation::set_length);
ClassDB::bind_method(D_METHOD("get_length"), &Animation::get_length);
- ClassDB::bind_method(D_METHOD("set_loop", "enabled"), &Animation::set_loop);
- ClassDB::bind_method(D_METHOD("has_loop"), &Animation::has_loop);
+ ClassDB::bind_method(D_METHOD("set_loop_mode", "loop_mode"), &Animation::set_loop_mode);
+ ClassDB::bind_method(D_METHOD("get_loop_mode"), &Animation::get_loop_mode);
ClassDB::bind_method(D_METHOD("set_step", "size_sec"), &Animation::set_step);
ClassDB::bind_method(D_METHOD("get_step"), &Animation::get_step);
@@ -3526,7 +3757,7 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("compress", "page_size", "fps", "split_tolerance"), &Animation::compress, DEFVAL(8192), DEFVAL(120), DEFVAL(4.0));
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length", PROPERTY_HINT_RANGE, "0.001,99999,0.001"), "set_length", "get_length");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_mode"), "set_loop_mode", "get_loop_mode");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "step", PROPERTY_HINT_RANGE, "0,4096,0.001"), "set_step", "get_step");
ADD_SIGNAL(MethodInfo("tracks_changed"));
@@ -3549,6 +3780,10 @@ void Animation::_bind_methods() {
BIND_ENUM_CONSTANT(UPDATE_DISCRETE);
BIND_ENUM_CONSTANT(UPDATE_TRIGGER);
BIND_ENUM_CONSTANT(UPDATE_CAPTURE);
+
+ BIND_ENUM_CONSTANT(LOOP_NONE);
+ BIND_ENUM_CONSTANT(LOOP_LINEAR);
+ BIND_ENUM_CONSTANT(LOOP_PINGPONG);
}
void Animation::clear() {
@@ -3556,7 +3791,7 @@ void Animation::clear() {
memdelete(tracks[i]);
}
tracks.clear();
- loop = false;
+ loop_mode = LOOP_NONE;
length = 1;
compression.enabled = false;
compression.bounds.clear();
diff --git a/scene/resources/animation.h b/scene/resources/animation.h
index ee07fb19d3..510d6c8323 100644
--- a/scene/resources/animation.h
+++ b/scene/resources/animation.h
@@ -64,7 +64,12 @@ public:
UPDATE_DISCRETE,
UPDATE_TRIGGER,
UPDATE_CAPTURE,
+ };
+ enum LoopMode {
+ LOOP_NONE,
+ LOOP_LINEAR,
+ LOOP_PINGPONG,
};
private:
@@ -208,7 +213,8 @@ private:
int _insert(double p_time, T &p_keys, const V &p_value);
template <class K>
- inline int _find(const Vector<K> &p_keys, double p_time) const;
+
+ inline int _find(const Vector<K> &p_keys, double p_time, bool p_backward = false) const;
_FORCE_INLINE_ Vector3 _interpolate(const Vector3 &p_a, const Vector3 &p_b, real_t p_c) const;
_FORCE_INLINE_ Quaternion _interpolate(const Quaternion &p_a, const Quaternion &p_b, real_t p_c) const;
@@ -221,7 +227,7 @@ private:
_FORCE_INLINE_ real_t _cubic_interpolate(const real_t &p_pre_a, const real_t &p_a, const real_t &p_b, const real_t &p_post_b, real_t p_c) const;
template <class T>
- _FORCE_INLINE_ T _interpolate(const Vector<TKey<T>> &p_keys, double p_time, InterpolationType p_interp, bool p_loop_wrap, bool *p_ok) const;
+ _FORCE_INLINE_ T _interpolate(const Vector<TKey<T>> &p_keys, double p_time, InterpolationType p_interp, bool p_loop_wrap, bool *p_ok, bool p_backward = false) const;
template <class T>
_FORCE_INLINE_ void _track_get_key_indices_in_range(const Vector<T> &p_array, double from_time, double to_time, List<int> *p_indices) const;
@@ -231,7 +237,8 @@ private:
double length = 1.0;
real_t step = 0.1;
- bool loop = false;
+ LoopMode loop_mode = LOOP_NONE;
+ int pingponged = 0;
/* Animation compression page format (version 1):
*
@@ -438,23 +445,23 @@ public:
bool track_get_interpolation_loop_wrap(int p_track) const;
Variant value_track_interpolate(int p_track, double p_time) const;
- void value_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices) const;
+ void value_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices, int p_pingponged = 0) const;
void value_track_set_update_mode(int p_track, UpdateMode p_mode);
UpdateMode value_track_get_update_mode(int p_track) const;
- void method_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices) const;
+ void method_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices, int p_pingponged = 0) const;
Vector<Variant> method_track_get_params(int p_track, int p_key_idx) const;
StringName method_track_get_name(int p_track, int p_key_idx) const;
void copy_track(int p_track, Ref<Animation> p_to_animation);
- void track_get_key_indices_in_range(int p_track, double p_time, double p_delta, List<int> *p_indices) const;
+ void track_get_key_indices_in_range(int p_track, double p_time, double p_delta, List<int> *p_indices, int p_pingponged = 0) const;
void set_length(real_t p_length);
real_t get_length() const;
- void set_loop(bool p_enabled);
- bool has_loop() const;
+ void set_loop_mode(LoopMode p_loop_mode);
+ LoopMode get_loop_mode() const;
void set_step(real_t p_step);
real_t get_step() const;
@@ -471,5 +478,6 @@ public:
VARIANT_ENUM_CAST(Animation::TrackType);
VARIANT_ENUM_CAST(Animation::InterpolationType);
VARIANT_ENUM_CAST(Animation::UpdateMode);
+VARIANT_ENUM_CAST(Animation::LoopMode);
#endif
diff --git a/scene/resources/audio_stream_sample.cpp b/scene/resources/audio_stream_sample.cpp
index d018103e64..d3fab802c5 100644
--- a/scene/resources/audio_stream_sample.cpp
+++ b/scene/resources/audio_stream_sample.cpp
@@ -299,7 +299,7 @@ int AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, int
if (loop_format != AudioStreamSample::LOOP_DISABLED && offset < loop_begin_fp) {
/* loopstart reached */
- if (loop_format == AudioStreamSample::LOOP_PING_PONG) {
+ if (loop_format == AudioStreamSample::LOOP_PINGPONG) {
/* bounce ping pong */
offset = loop_begin_fp + (loop_begin_fp - offset);
increment = -increment;
@@ -320,7 +320,7 @@ int AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, int
if (loop_format != AudioStreamSample::LOOP_DISABLED && offset >= loop_end_fp) {
/* loopend reached */
- if (loop_format == AudioStreamSample::LOOP_PING_PONG) {
+ if (loop_format == AudioStreamSample::LOOP_PINGPONG) {
/* bounce ping pong */
offset = loop_end_fp - (offset - loop_end_fp);
increment = -increment;
@@ -636,7 +636,7 @@ void AudioStreamSample::_bind_methods() {
ClassDB::bind_method(D_METHOD("save_to_wav", "path"), &AudioStreamSample::save_to_wav);
- ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_data", "get_data");
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_data", "get_data");
ADD_PROPERTY(PropertyInfo(Variant::INT, "format", PROPERTY_HINT_ENUM, "8-Bit,16-Bit,IMA-ADPCM"), "set_format", "get_format");
ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_mode", PROPERTY_HINT_ENUM, "Disabled,Forward,Ping-Pong,Backward"), "set_loop_mode", "get_loop_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_begin"), "set_loop_begin", "get_loop_begin");
@@ -650,7 +650,7 @@ void AudioStreamSample::_bind_methods() {
BIND_ENUM_CONSTANT(LOOP_DISABLED);
BIND_ENUM_CONSTANT(LOOP_FORWARD);
- BIND_ENUM_CONSTANT(LOOP_PING_PONG);
+ BIND_ENUM_CONSTANT(LOOP_PINGPONG);
BIND_ENUM_CONSTANT(LOOP_BACKWARD);
}
diff --git a/scene/resources/audio_stream_sample.h b/scene/resources/audio_stream_sample.h
index 24198e3c98..0eb34be9bf 100644
--- a/scene/resources/audio_stream_sample.h
+++ b/scene/resources/audio_stream_sample.h
@@ -92,7 +92,7 @@ public:
enum LoopMode {
LOOP_DISABLED,
LOOP_FORWARD,
- LOOP_PING_PONG,
+ LOOP_PINGPONG,
LOOP_BACKWARD
};
diff --git a/scene/resources/bit_map.cpp b/scene/resources/bit_map.cpp
index 49ed9dcb82..16f1507c34 100644
--- a/scene/resources/bit_map.cpp
+++ b/scene/resources/bit_map.cpp
@@ -674,7 +674,7 @@ void BitMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("grow_mask", "pixels", "rect"), &BitMap::grow_mask);
ClassDB::bind_method(D_METHOD("opaque_to_polygons", "rect", "epsilon"), &BitMap::_opaque_to_polygons_bind, DEFVAL(2.0));
- ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
}
BitMap::BitMap() {}
diff --git a/scene/resources/camera_effects.cpp b/scene/resources/camera_effects.cpp
index b633196424..0df372ea1b 100644
--- a/scene/resources/camera_effects.cpp
+++ b/scene/resources/camera_effects.cpp
@@ -149,7 +149,7 @@ void CameraEffects::_validate_property(PropertyInfo &property) const {
if ((!dof_blur_far_enabled && (property.name == "dof_blur_far_distance" || property.name == "dof_blur_far_transition")) ||
(!dof_blur_near_enabled && (property.name == "dof_blur_near_distance" || property.name == "dof_blur_near_transition")) ||
(!override_exposure_enabled && property.name == "override_exposure")) {
- property.usage = PROPERTY_USAGE_NOEDITOR;
+ property.usage = PROPERTY_USAGE_NO_EDITOR;
}
}
diff --git a/scene/resources/concave_polygon_shape_3d.cpp b/scene/resources/concave_polygon_shape_3d.cpp
index 3fed700383..03854683bd 100644
--- a/scene/resources/concave_polygon_shape_3d.cpp
+++ b/scene/resources/concave_polygon_shape_3d.cpp
@@ -108,7 +108,7 @@ void ConcavePolygonShape3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_backface_collision_enabled", "enabled"), &ConcavePolygonShape3D::set_backface_collision_enabled);
ClassDB::bind_method(D_METHOD("is_backface_collision_enabled"), &ConcavePolygonShape3D::is_backface_collision_enabled);
- ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_faces", "get_faces");
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_faces", "get_faces");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "backface_collision"), "set_backface_collision_enabled", "is_backface_collision_enabled");
}
diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp
index 9dc76dcf44..b530a72033 100644
--- a/scene/resources/curve.cpp
+++ b/scene/resources/curve.cpp
@@ -522,7 +522,7 @@ void Curve::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "min_value", PROPERTY_HINT_RANGE, "-1024,1024,0.01"), "set_min_value", "get_min_value");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_value", PROPERTY_HINT_RANGE, "-1024,1024,0.01"), "set_max_value", "get_max_value");
ADD_PROPERTY(PropertyInfo(Variant::INT, "bake_resolution", PROPERTY_HINT_RANGE, "1,1000,1"), "set_bake_resolution", "get_bake_resolution");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
ADD_SIGNAL(MethodInfo(SIGNAL_RANGE_CHANGED));
@@ -1006,7 +1006,7 @@ void Curve2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_data"), &Curve2D::_set_data);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bake_interval", PROPERTY_HINT_RANGE, "0.01,512,0.01"), "set_bake_interval", "get_bake_interval");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
}
Curve2D::Curve2D() {
@@ -1699,7 +1699,7 @@ void Curve3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_data"), &Curve3D::_set_data);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bake_interval", PROPERTY_HINT_RANGE, "0.01,512,0.01"), "set_bake_interval", "get_bake_interval");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
ADD_GROUP("Up Vector", "up_vector_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "up_vector_enabled"), "set_up_vector_enabled", "is_up_vector_enabled");
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index f21a070133..a1d76ef352 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -940,7 +940,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("shadow_offset_x", "RichTextLabel", 1 * scale);
theme->set_constant("shadow_offset_y", "RichTextLabel", 1 * scale);
- theme->set_constant("shadow_as_outline", "RichTextLabel", 0 * scale);
+ theme->set_constant("shadow_outline_size", "RichTextLabel", 1 * scale);
theme->set_constant("line_separation", "RichTextLabel", 0 * scale);
theme->set_constant("table_hseparation", "RichTextLabel", 3 * scale);
diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp
index 9f8e89564d..4c25ed589b 100644
--- a/scene/resources/environment.cpp
+++ b/scene/resources/environment.cpp
@@ -917,7 +917,7 @@ float Environment::get_adjustment_saturation() const {
void Environment::set_adjustment_color_correction(Ref<Texture> p_color_correction) {
adjustment_color_correction = p_color_correction;
- Ref<GradientTexture> grad_tex = p_color_correction;
+ Ref<GradientTexture1D> grad_tex = p_color_correction;
if (grad_tex.is_valid()) {
if (!grad_tex->is_connected(CoreStringNames::get_singleton()->changed, callable_mp(this, &Environment::_update_adjustment))) {
grad_tex->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Environment::_update_adjustment));
@@ -954,39 +954,39 @@ void Environment::_update_adjustment() {
void Environment::_validate_property(PropertyInfo &property) const {
if (property.name == "sky" || property.name == "sky_custom_fov" || property.name == "sky_rotation" || property.name == "ambient_light/sky_contribution") {
if (bg_mode != BG_SKY && ambient_source != AMBIENT_SOURCE_SKY && reflection_source != REFLECTION_SOURCE_SKY) {
- property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
}
}
if (property.name == "fog_aerial_perspective") {
if (bg_mode != BG_SKY) {
- property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
}
}
if (property.name == "glow_intensity" && glow_blend_mode == GLOW_BLEND_MODE_MIX) {
- property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
}
if (property.name == "glow_mix" && glow_blend_mode != GLOW_BLEND_MODE_MIX) {
- property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
}
if (property.name == "background_color") {
if (bg_mode != BG_COLOR && ambient_source != AMBIENT_SOURCE_COLOR) {
- property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
}
}
if (property.name == "background_canvas_max_layer") {
if (bg_mode != BG_CANVAS) {
- property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
}
}
if (property.name == "background_camera_feed_id") {
if (bg_mode != BG_CAMERA_FEED) {
- property.usage = PROPERTY_USAGE_NOEDITOR;
+ property.usage = PROPERTY_USAGE_NO_EDITOR;
}
}
@@ -1018,7 +1018,7 @@ void Environment::_validate_property(PropertyInfo &property) const {
String enabled = prefix + "enabled";
if (property.name.begins_with(prefix) && property.name != enabled && !bool(get(enabled))) {
- property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
return;
}
@@ -1031,7 +1031,7 @@ void Environment::_validate_property(PropertyInfo &property) const {
String prefix = String(*prefixes);
if (property.name.begins_with(prefix)) {
- property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
return;
}
diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp
index 819ae95715..d9de47afc7 100644
--- a/scene/resources/font.cpp
+++ b/scene/resources/font.cpp
@@ -70,6 +70,15 @@ void FontData::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_antialiased", "antialiased"), &FontData::set_antialiased);
ClassDB::bind_method(D_METHOD("is_antialiased"), &FontData::is_antialiased);
+ ClassDB::bind_method(D_METHOD("set_font_name", "name"), &FontData::set_font_name);
+ ClassDB::bind_method(D_METHOD("get_font_name"), &FontData::get_font_name);
+
+ ClassDB::bind_method(D_METHOD("set_font_style_name", "name"), &FontData::set_font_style_name);
+ ClassDB::bind_method(D_METHOD("get_font_style_name"), &FontData::get_font_style_name);
+
+ ClassDB::bind_method(D_METHOD("set_font_style", "style"), &FontData::set_font_style);
+ ClassDB::bind_method(D_METHOD("get_font_style"), &FontData::get_font_style);
+
ClassDB::bind_method(D_METHOD("set_multichannel_signed_distance_field", "msdf"), &FontData::set_multichannel_signed_distance_field);
ClassDB::bind_method(D_METHOD("is_multichannel_signed_distance_field"), &FontData::is_multichannel_signed_distance_field);
@@ -190,6 +199,15 @@ bool FontData::_set(const StringName &p_name, const Variant &p_value) {
} else if (tokens[0] == "antialiased") {
set_antialiased(p_value);
return true;
+ } else if (tokens[0] == "font_name") {
+ set_font_name(p_value);
+ return true;
+ } else if (tokens[0] == "style_name") {
+ set_font_style_name(p_value);
+ return true;
+ } else if (tokens[0] == "font_style") {
+ set_font_style(p_value);
+ return true;
} else if (tokens[0] == "multichannel_signed_distance_field") {
set_multichannel_signed_distance_field(p_value);
return true;
@@ -295,6 +313,15 @@ bool FontData::_get(const StringName &p_name, Variant &r_ret) const {
} else if (tokens[0] == "antialiased") {
r_ret = is_antialiased();
return true;
+ } else if (tokens[0] == "font_name") {
+ r_ret = get_font_name();
+ return true;
+ } else if (tokens[0] == "style_name") {
+ r_ret = get_font_style_name();
+ return true;
+ } else if (tokens[0] == "font_style") {
+ r_ret = get_font_style();
+ return true;
} else if (tokens[0] == "multichannel_signed_distance_field") {
r_ret = is_multichannel_signed_distance_field();
return true;
@@ -394,6 +421,9 @@ bool FontData::_get(const StringName &p_name, Variant &r_ret) const {
void FontData::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::STRING, "font_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::STRING, "style_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::INT, "font_style", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
p_list->push_back(PropertyInfo(Variant::BOOL, "antialiased", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
p_list->push_back(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
p_list->push_back(PropertyInfo(Variant::INT, "msdf_pixel_range", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
@@ -510,6 +540,36 @@ PackedByteArray FontData::get_data() const {
return data;
}
+void FontData::set_font_name(const String &p_name) {
+ _ensure_rid(0);
+ TS->font_set_name(cache[0], p_name);
+}
+
+String FontData::get_font_name() const {
+ _ensure_rid(0);
+ return TS->font_get_name(cache[0]);
+}
+
+void FontData::set_font_style_name(const String &p_name) {
+ _ensure_rid(0);
+ TS->font_set_style_name(cache[0], p_name);
+}
+
+String FontData::get_font_style_name() const {
+ _ensure_rid(0);
+ return TS->font_get_style_name(cache[0]);
+}
+
+void FontData::set_font_style(uint32_t p_style) {
+ _ensure_rid(0);
+ TS->font_set_style(cache[0], p_style);
+}
+
+uint32_t FontData::get_font_style() const {
+ _ensure_rid(0);
+ return TS->font_get_style(cache[0]);
+}
+
void FontData::set_antialiased(bool p_antialiased) {
if (antialiased != p_antialiased) {
antialiased = p_antialiased;
diff --git a/scene/resources/font.h b/scene/resources/font.h
index e1f1f6d742..4d9ee72c84 100644
--- a/scene/resources/font.h
+++ b/scene/resources/font.h
@@ -79,6 +79,15 @@ public:
virtual PackedByteArray get_data() const;
// Common properties.
+ virtual void set_font_name(const String &p_name);
+ virtual String get_font_name() const;
+
+ virtual void set_font_style_name(const String &p_name);
+ virtual String get_font_style_name() const;
+
+ virtual void set_font_style(uint32_t p_style);
+ virtual uint32_t get_font_style() const;
+
virtual void set_antialiased(bool p_antialiased);
virtual bool is_antialiased() const;
diff --git a/scene/resources/gradient.cpp b/scene/resources/gradient.cpp
index 7b9b942142..4559b4ce0a 100644
--- a/scene/resources/gradient.cpp
+++ b/scene/resources/gradient.cpp
@@ -51,6 +51,8 @@ void Gradient::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_offset", "point", "offset"), &Gradient::set_offset);
ClassDB::bind_method(D_METHOD("get_offset", "point"), &Gradient::get_offset);
+ ClassDB::bind_method(D_METHOD("reverse"), &Gradient::reverse);
+
ClassDB::bind_method(D_METHOD("set_color", "point", "color"), &Gradient::set_color);
ClassDB::bind_method(D_METHOD("get_color", "point"), &Gradient::get_color);
@@ -64,8 +66,18 @@ void Gradient::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_colors", "colors"), &Gradient::set_colors);
ClassDB::bind_method(D_METHOD("get_colors"), &Gradient::get_colors);
+ ClassDB::bind_method(D_METHOD("set_interpolation_mode", "interpolation_mode"), &Gradient::set_interpolation_mode);
+ ClassDB::bind_method(D_METHOD("get_interpolation_mode"), &Gradient::get_interpolation_mode);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "interpolation_mode", PROPERTY_HINT_ENUM, "Linear,Constant,Cubic"), "set_interpolation_mode", "get_interpolation_mode");
+
+ ADD_GROUP("Raw data", "");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT32_ARRAY, "offsets"), "set_offsets", "get_offsets");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "colors"), "set_colors", "get_colors");
+
+ BIND_ENUM_CONSTANT(GRADIENT_INTERPOLATE_LINEAR);
+ BIND_ENUM_CONSTANT(GRADIENT_INTERPOLATE_CONSTANT);
+ BIND_ENUM_CONSTANT(GRADIENT_INTERPOLATE_CUBIC);
}
Vector<float> Gradient::get_offsets() const {
@@ -86,6 +98,15 @@ Vector<Color> Gradient::get_colors() const {
return colors;
}
+void Gradient::set_interpolation_mode(Gradient::InterpolationMode p_interp_mode) {
+ interpolation_mode = p_interp_mode;
+ emit_signal(CoreStringNames::get_singleton()->changed);
+}
+
+Gradient::InterpolationMode Gradient::get_interpolation_mode() {
+ return interpolation_mode;
+}
+
void Gradient::set_offsets(const Vector<float> &p_offsets) {
points.resize(p_offsets.size());
for (int i = 0; i < points.size(); i++) {
@@ -127,6 +148,15 @@ void Gradient::remove_point(int p_index) {
emit_signal(CoreStringNames::get_singleton()->changed);
}
+void Gradient::reverse() {
+ for (int i = 0; i < points.size(); i++) {
+ points.write[i].offset = 1.0 - points[i].offset;
+ }
+
+ _update_sorting();
+ emit_signal(CoreStringNames::get_singleton()->changed);
+}
+
void Gradient::set_points(Vector<Gradient::Point> &p_points) {
points = p_points;
is_sorted = false;
diff --git a/scene/resources/gradient.h b/scene/resources/gradient.h
index cf5b179c45..eb438d0bba 100644
--- a/scene/resources/gradient.h
+++ b/scene/resources/gradient.h
@@ -38,6 +38,12 @@ class Gradient : public Resource {
OBJ_SAVE_TYPE(Gradient);
public:
+ enum InterpolationMode {
+ GRADIENT_INTERPOLATE_LINEAR,
+ GRADIENT_INTERPOLATE_CONSTANT,
+ GRADIENT_INTERPOLATE_CUBIC,
+ };
+
struct Point {
float offset = 0.0;
Color color;
@@ -49,6 +55,8 @@ public:
private:
Vector<Point> points;
bool is_sorted = true;
+ InterpolationMode interpolation_mode = GRADIENT_INTERPOLATE_LINEAR;
+
_FORCE_INLINE_ void _update_sorting() {
if (!is_sorted) {
points.sort();
@@ -65,9 +73,9 @@ public:
void add_point(float p_offset, const Color &p_color);
void remove_point(int p_index);
-
void set_points(Vector<Point> &p_points);
Vector<Point> &get_points();
+ void reverse();
void set_offset(int pos, const float offset);
float get_offset(int pos);
@@ -81,6 +89,13 @@ public:
void set_colors(const Vector<Color> &p_colors);
Vector<Color> get_colors() const;
+ void set_interpolation_mode(InterpolationMode p_interp_mode);
+ InterpolationMode get_interpolation_mode();
+
+ _FORCE_INLINE_ float cubic_interpolate(float p0, float p1, float p2, float p3, float x) {
+ return p1 + 0.5 * x * (p2 - p0 + x * (2.0 * p0 - 5.0 * p1 + 4.0 * p2 - p3 + x * (3.0 * (p1 - p2) + p3 - p0)));
+ }
+
_FORCE_INLINE_ Color get_color_at_offset(float p_offset) {
if (points.is_empty()) {
return Color(0, 0, 0, 1);
@@ -88,7 +103,7 @@ public:
_update_sorting();
- //binary search
+ // Binary search.
int low = 0;
int high = points.size() - 1;
int middle = 0;
@@ -111,7 +126,7 @@ public:
}
}
- //return interpolated value
+ // Return interpolated value.
if (points[middle].offset > p_offset) {
middle--;
}
@@ -125,10 +140,44 @@ public:
}
const Point &pointFirst = points[first];
const Point &pointSecond = points[second];
- return pointFirst.color.lerp(pointSecond.color, (p_offset - pointFirst.offset) / (pointSecond.offset - pointFirst.offset));
+
+ switch (interpolation_mode) {
+ case GRADIENT_INTERPOLATE_LINEAR: {
+ return pointFirst.color.lerp(pointSecond.color, (p_offset - pointFirst.offset) / (pointSecond.offset - pointFirst.offset));
+ } break;
+ case GRADIENT_INTERPOLATE_CONSTANT: {
+ return pointFirst.color;
+ } break;
+ case GRADIENT_INTERPOLATE_CUBIC: {
+ int p0 = first - 1;
+ int p3 = second + 1;
+ if (p3 >= points.size()) {
+ p3 = second;
+ }
+ if (p0 < 0) {
+ p0 = first;
+ }
+ const Point &pointP0 = points[p0];
+ const Point &pointP3 = points[p3];
+
+ float x = (p_offset - pointFirst.offset) / (pointSecond.offset - pointFirst.offset);
+ float r = cubic_interpolate(pointP0.color.r, pointFirst.color.r, pointSecond.color.r, pointP3.color.r, x);
+ float g = cubic_interpolate(pointP0.color.g, pointFirst.color.g, pointSecond.color.g, pointP3.color.g, x);
+ float b = cubic_interpolate(pointP0.color.b, pointFirst.color.b, pointSecond.color.b, pointP3.color.b, x);
+ float a = cubic_interpolate(pointP0.color.a, pointFirst.color.a, pointSecond.color.a, pointP3.color.a, x);
+
+ return Color(r, g, b, a);
+ } break;
+ default: {
+ // Fallback to linear interpolation.
+ return pointFirst.color.lerp(pointSecond.color, (p_offset - pointFirst.offset) / (pointSecond.offset - pointFirst.offset));
+ }
+ }
}
int get_points_count() const;
};
+VARIANT_ENUM_CAST(Gradient::InterpolationMode);
+
#endif // GRADIENT_H
diff --git a/scene/resources/importer_mesh.cpp b/scene/resources/importer_mesh.cpp
index 076b8312b6..7afa4c91f0 100644
--- a/scene/resources/importer_mesh.cpp
+++ b/scene/resources/importer_mesh.cpp
@@ -1243,5 +1243,5 @@ void ImporterMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_lightmap_size_hint", "size"), &ImporterMesh::set_lightmap_size_hint);
ClassDB::bind_method(D_METHOD("get_lightmap_size_hint"), &ImporterMesh::get_lightmap_size_hint);
- ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_data", "_get_data");
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "_set_data", "_get_data");
}
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index e01be7cc84..8399b14a56 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -1695,7 +1695,7 @@ BaseMaterial3D::TextureFilter BaseMaterial3D::get_texture_filter() const {
void BaseMaterial3D::_validate_feature(const String &text, Feature feature, PropertyInfo &property) const {
if (property.name.begins_with(text) && property.name != text + "_enabled" && !features[feature]) {
- property.usage = PROPERTY_USAGE_NOEDITOR;
+ property.usage = PROPERTY_USAGE_NO_EDITOR;
}
}
@@ -1729,23 +1729,23 @@ void BaseMaterial3D::_validate_property(PropertyInfo &property) const {
}
if (property.name == "billboard_keep_scale" && billboard_mode == BILLBOARD_DISABLED) {
- property.usage = PROPERTY_USAGE_NOEDITOR;
+ property.usage = PROPERTY_USAGE_NO_EDITOR;
}
if (property.name == "grow_amount" && !grow_enabled) {
- property.usage = PROPERTY_USAGE_NOEDITOR;
+ property.usage = PROPERTY_USAGE_NO_EDITOR;
}
if (property.name == "point_size" && !flags[FLAG_USE_POINT_SIZE]) {
- property.usage = PROPERTY_USAGE_NOEDITOR;
+ property.usage = PROPERTY_USAGE_NO_EDITOR;
}
if (property.name == "proximity_fade_distance" && !proximity_fade_enabled) {
- property.usage = PROPERTY_USAGE_NOEDITOR;
+ property.usage = PROPERTY_USAGE_NO_EDITOR;
}
if ((property.name == "distance_fade_max_distance" || property.name == "distance_fade_min_distance") && distance_fade == DISTANCE_FADE_DISABLED) {
- property.usage = PROPERTY_USAGE_NOEDITOR;
+ property.usage = PROPERTY_USAGE_NO_EDITOR;
}
// you can only enable anti-aliasing (in materials) on alpha scissor and alpha hash
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index 7ffe0b03e1..51b4e1fbd8 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -1842,8 +1842,8 @@ void ArrayMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_surfaces", "surfaces"), &ArrayMesh::_set_surfaces);
ClassDB::bind_method(D_METHOD("_get_surfaces"), &ArrayMesh::_get_surfaces);
- ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "_blend_shape_names", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_blend_shape_names", "_get_blend_shape_names");
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "_surfaces", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_surfaces", "_get_surfaces");
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "_blend_shape_names", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_blend_shape_names", "_get_blend_shape_names");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "_surfaces", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_surfaces", "_get_surfaces");
ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_shape_mode", PROPERTY_HINT_ENUM, "Normalized,Relative"), "set_blend_shape_mode", "get_blend_shape_mode");
ADD_PROPERTY(PropertyInfo(Variant::AABB, "custom_aabb", PROPERTY_HINT_NONE, ""), "set_custom_aabb", "get_custom_aabb");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shadow_mesh", PROPERTY_HINT_RESOURCE_TYPE, "ArrayMesh"), "set_shadow_mesh", "get_shadow_mesh");
diff --git a/scene/resources/mesh_data_tool.cpp b/scene/resources/mesh_data_tool.cpp
index 04b2437ae8..9ecd8ec2f3 100644
--- a/scene/resources/mesh_data_tool.cpp
+++ b/scene/resources/mesh_data_tool.cpp
@@ -421,6 +421,7 @@ Vector<int> MeshDataTool::get_vertex_bones(int p_idx) const {
void MeshDataTool::set_vertex_bones(int p_idx, const Vector<int> &p_bones) {
ERR_FAIL_INDEX(p_idx, vertices.size());
+ ERR_FAIL_COND(p_bones.size() != 4);
vertices.write[p_idx].bones = p_bones;
format |= Mesh::ARRAY_FORMAT_BONES;
}
@@ -432,6 +433,7 @@ Vector<float> MeshDataTool::get_vertex_weights(int p_idx) const {
void MeshDataTool::set_vertex_weights(int p_idx, const Vector<float> &p_weights) {
ERR_FAIL_INDEX(p_idx, vertices.size());
+ ERR_FAIL_COND(p_weights.size() != 4);
vertices.write[p_idx].weights = p_weights;
format |= Mesh::ARRAY_FORMAT_WEIGHTS;
}
diff --git a/scene/resources/navigation_mesh.cpp b/scene/resources/navigation_mesh.cpp
index d87056f55d..db091ec37b 100644
--- a/scene/resources/navigation_mesh.cpp
+++ b/scene/resources/navigation_mesh.cpp
@@ -477,8 +477,8 @@ void NavigationMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_polygons", "polygons"), &NavigationMesh::_set_polygons);
ClassDB::bind_method(D_METHOD("_get_polygons"), &NavigationMesh::_get_polygons);
- ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_vertices", "get_vertices");
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_polygons", "_get_polygons");
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_vertices", "get_vertices");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_polygons", "_get_polygons");
ADD_PROPERTY(PropertyInfo(Variant::INT, "sample_partition_type/sample_partition_type", PROPERTY_HINT_ENUM, "Watershed,Monotone,Layers"), "set_sample_partition_type", "get_sample_partition_type");
ADD_PROPERTY(PropertyInfo(Variant::INT, "geometry/parsed_geometry_type", PROPERTY_HINT_ENUM, "Mesh Instances,Static Colliders,Both"), "set_parsed_geometry_type", "get_parsed_geometry_type");
diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp
index 60cda637ca..f43715a463 100644
--- a/scene/resources/packed_scene.cpp
+++ b/scene/resources/packed_scene.cpp
@@ -34,10 +34,12 @@
#include "core/config/project_settings.h"
#include "core/core_string_names.h"
#include "core/io/resource_loader.h"
+#include "editor/editor_inspector.h"
#include "scene/2d/node_2d.h"
#include "scene/3d/node_3d.h"
#include "scene/gui/control.h"
#include "scene/main/instance_placeholder.h"
+#include "scene/property_utils.h"
#define PACKED_SCENE_VERSION 2
@@ -45,6 +47,30 @@ bool SceneState::can_instantiate() const {
return nodes.size() > 0;
}
+static Array _sanitize_node_pinned_properties(Node *p_node) {
+ if (!p_node->has_meta("_edit_pinned_properties_")) {
+ return Array();
+ }
+ Array pinned = p_node->get_meta("_edit_pinned_properties_");
+ if (pinned.is_empty()) {
+ return Array();
+ }
+ Set<StringName> storable_properties;
+ p_node->get_storable_properties(storable_properties);
+ int i = 0;
+ do {
+ if (storable_properties.has(pinned[i])) {
+ i++;
+ } else {
+ pinned.remove(i);
+ }
+ } while (i < pinned.size());
+ if (pinned.is_empty()) {
+ p_node->remove_meta("_edit_pinned_properties_");
+ }
+ return pinned;
+}
+
Node *SceneState::instantiate(GenEditState p_edit_state) const {
// nodes where instancing failed (because something is missing)
List<Node *> stray_instances;
@@ -227,7 +253,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
} else {
Node *base = i == 0 ? node : ret_nodes[0];
- if (p_edit_state == GEN_EDIT_STATE_MAIN) {
+ if (p_edit_state == GEN_EDIT_STATE_MAIN || p_edit_state == GEN_EDIT_STATE_MAIN_INHERITED) {
//for the main scene, use the resource as is
res->configure_for_local_scene(base, resources_local_to_scene);
resources_local_to_scene[res] = res;
@@ -291,6 +317,13 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
}
}
+ // we only want to deal with pinned flag if instancing as pure main (no instance, no inheriting)
+ if (p_edit_state == GEN_EDIT_STATE_MAIN) {
+ _sanitize_node_pinned_properties(node);
+ } else {
+ node->remove_meta("_edit_pinned_properties_");
+ }
+
ret_nodes[i] = node;
if (node && gen_node_path_cache && ret_nodes[0]) {
@@ -415,61 +448,22 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
// with the instance states, we can query for identical properties/groups
// and only save what has changed
- List<PackState> pack_state_stack;
-
- bool instantiated_by_owner = true;
-
- {
- Node *n = p_node;
-
- while (n) {
- if (n == p_owner) {
- Ref<SceneState> state = n->get_scene_inherited_state();
- if (state.is_valid()) {
- int node = state->find_node_by_path(n->get_path_to(p_node));
- if (node >= 0) {
- //this one has state for this node, save
- PackState ps;
- ps.node = node;
- ps.state = state;
- pack_state_stack.push_back(ps);
- instantiated_by_owner = false;
- }
- }
-
- if (p_node->get_scene_file_path() != String() && p_node->get_owner() == p_owner && instantiated_by_owner) {
- if (p_node->get_scene_instance_load_placeholder()) {
- //it's a placeholder, use the placeholder path
- nd.instance = _vm_get_variant(p_node->get_scene_file_path(), variant_map);
- nd.instance |= FLAG_INSTANCE_IS_PLACEHOLDER;
- } else {
- //must instance ourselves
- Ref<PackedScene> instance = ResourceLoader::load(p_node->get_scene_file_path());
- if (!instance.is_valid()) {
- return ERR_CANT_OPEN;
- }
+ bool instantiated_by_owner = false;
+ Vector<SceneState::PackState> states_stack = PropertyUtils::get_node_states_stack(p_node, p_owner, &instantiated_by_owner);
- nd.instance = _vm_get_variant(instance, variant_map);
- }
- }
- n = nullptr;
- } else {
- if (n->get_scene_file_path() != String()) {
- //is an instance
- Ref<SceneState> state = n->get_scene_instance_state();
- if (state.is_valid()) {
- int node = state->find_node_by_path(n->get_path_to(p_node));
- if (node >= 0) {
- //this one has state for this node, save
- PackState ps;
- ps.node = node;
- ps.state = state;
- pack_state_stack.push_back(ps);
- }
- }
- }
- n = n->get_owner();
+ if (p_node->get_scene_file_path() != String() && p_node->get_owner() == p_owner && instantiated_by_owner) {
+ if (p_node->get_scene_instance_load_placeholder()) {
+ //it's a placeholder, use the placeholder path
+ nd.instance = _vm_get_variant(p_node->get_scene_file_path(), variant_map);
+ nd.instance |= FLAG_INSTANCE_IS_PLACEHOLDER;
+ } else {
+ //must instance ourselves
+ Ref<PackedScene> instance = ResourceLoader::load(p_node->get_scene_file_path());
+ if (!instance.is_valid()) {
+ return ERR_CANT_OPEN;
}
+
+ nd.instance = _vm_get_variant(instance, variant_map);
}
}
@@ -478,88 +472,37 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
List<PropertyInfo> plist;
p_node->get_property_list(&plist);
- StringName type = p_node->get_class();
- Ref<Script> script = p_node->get_script();
- if (Engine::get_singleton()->is_editor_hint() && script.is_valid()) {
- // Should be called in the editor only and not at runtime,
- // otherwise it can cause problems because of missing instance state support.
- script->update_exports();
- }
+ Array pinned_props = _sanitize_node_pinned_properties(p_node);
for (const PropertyInfo &E : plist) {
if (!(E.usage & PROPERTY_USAGE_STORAGE)) {
continue;
}
- String name = E.name;
- Variant value = p_node->get(E.name);
-
- bool isdefault = false;
- Variant default_value = ClassDB::class_get_default_property_value(type, name);
-
- if (default_value.get_type() != Variant::NIL) {
- isdefault = bool(Variant::evaluate(Variant::OP_EQUAL, value, default_value));
- }
+ Variant forced_value;
- if (!isdefault && script.is_valid() && script->get_property_default_value(name, default_value)) {
- isdefault = bool(Variant::evaluate(Variant::OP_EQUAL, value, default_value));
- }
- // the version above makes more sense, because it does not rely on placeholder or usage flag
- // in the script, just the default value function.
- // if (E.usage & PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE) {
- // isdefault = true; //is script default value
- // }
-
- if (pack_state_stack.size()) {
- // we are on part of an instantiated subscene
- // or part of instantiated scene.
- // only save what has been changed
- // only save changed properties in instance
-
- if ((E.usage & PROPERTY_USAGE_NO_INSTANCE_STATE) || E.name == "__meta__") {
- //property has requested that no instance state is saved, sorry
- //also, meta won't be overridden or saved
+ // If instance or inheriting, not saving if property requested so, or it's meta
+ if (states_stack.size()) {
+ if ((E.usage & PROPERTY_USAGE_NO_INSTANCE_STATE)) {
continue;
}
-
- bool exists = false;
- Variant original;
-
- for (List<PackState>::Element *F = pack_state_stack.back(); F; F = F->prev()) {
- //check all levels of pack to see if the property exists somewhere
- const PackState &ps = F->get();
-
- original = ps.state->get_property_value(ps.node, E.name, exists);
- if (exists) {
- break;
- }
- }
-
- if (exists) {
- //check if already exists and did not change
- if (value.get_type() == Variant::FLOAT && original.get_type() == Variant::FLOAT) {
- //this must be done because, as some scenes save as text, there might be a tiny difference in floats due to numerical error
- float a = value;
- float b = original;
-
- if (Math::is_equal_approx(a, b)) {
- continue;
- }
- } else if (bool(Variant::evaluate(Variant::OP_EQUAL, value, original))) {
- continue;
+ // Meta is normally not saved in instances/inherited (see GH-12838), but we need to save the pinned list
+ if (E.name == "__meta__") {
+ if (pinned_props.size()) {
+ Dictionary meta_override;
+ meta_override["_edit_pinned_properties_"] = pinned_props;
+ forced_value = meta_override;
}
}
+ }
- if (!exists && isdefault) {
- //does not exist in original node, but it's the default value
- //so safe to skip too.
- continue;
- }
+ StringName name = E.name;
+ Variant value = forced_value.get_type() == Variant::NIL ? p_node->get(name) : forced_value;
- } else {
- if (isdefault) {
- //it's the default value, no point in saving it
+ if (!pinned_props.has(name) && forced_value.get_type() == Variant::NIL) {
+ Variant default_value = PropertyUtils::get_property_default_value(p_node, name, &states_stack, true);
+ if (!PropertyUtils::is_property_value_different(value, default_value)) {
continue;
}
}
@@ -585,10 +528,9 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
*/
bool skip = false;
- for (const PackState &F : pack_state_stack) {
+ for (const SceneState::PackState &ia : states_stack) {
//check all levels of pack to see if the group was added somewhere
- const PackState &ps = F;
- if (ps.state->is_node_in_group(ps.node, gi.name)) {
+ if (ia.state->is_node_in_group(ia.node, gi.name)) {
skip = true;
break;
}
@@ -618,7 +560,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
// Save the right type. If this node was created by an instance
// then flag that the node should not be created but reused
- if (pack_state_stack.is_empty() && !is_editable_instance) {
+ if (states_stack.is_empty() && !is_editable_instance) {
//this node is not part of an instancing process, so save the type
nd.type = _nm_get_string(p_node->get_class(), name_map);
} else {
@@ -635,7 +577,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
bool save_node = nd.properties.size() || nd.groups.size(); // some local properties or groups exist
save_node = save_node || p_node == p_owner; // owner is always saved
- save_node = save_node || (p_node->get_owner() == p_owner && instantiated_by_owner); //part of scene and not instantiated
+ save_node = save_node || (p_node->get_owner() == p_owner && instantiated_by_owner); //part of scene and not instanced
int idx = nodes.size();
int parent_node = NO_PARENT_SAVED;
@@ -932,7 +874,7 @@ void SceneState::clear() {
base_scene_idx = -1;
}
-Ref<SceneState> SceneState::_get_base_scene_state() const {
+Ref<SceneState> SceneState::get_base_scene_state() const {
if (base_scene_idx >= 0) {
Ref<PackedScene> ps = variants[base_scene_idx];
if (ps.is_valid()) {
@@ -947,8 +889,8 @@ int SceneState::find_node_by_path(const NodePath &p_node) const {
ERR_FAIL_COND_V_MSG(node_path_cache.size() == 0, -1, "This operation requires the node cache to have been built.");
if (!node_path_cache.has(p_node)) {
- if (_get_base_scene_state().is_valid()) {
- int idx = _get_base_scene_state()->find_node_by_path(p_node);
+ if (get_base_scene_state().is_valid()) {
+ int idx = get_base_scene_state()->find_node_by_path(p_node);
if (idx != -1) {
int rkey = _find_base_scene_node_remap_key(idx);
if (rkey == -1) {
@@ -963,11 +905,11 @@ int SceneState::find_node_by_path(const NodePath &p_node) const {
int nid = node_path_cache[p_node];
- if (_get_base_scene_state().is_valid() && !base_scene_node_remap.has(nid)) {
+ if (get_base_scene_state().is_valid() && !base_scene_node_remap.has(nid)) {
//for nodes that _do_ exist in current scene, still try to look for
//the node in the instantiated scene, as a property may be missing
//from the local one
- int idx = _get_base_scene_state()->find_node_by_path(p_node);
+ int idx = get_base_scene_state()->find_node_by_path(p_node);
if (idx != -1) {
base_scene_node_remap[nid] = idx;
}
@@ -1007,7 +949,7 @@ Variant SceneState::get_property_value(int p_node, const StringName &p_property,
//property not found, try on instance
if (base_scene_node_remap.has(p_node)) {
- return _get_base_scene_state()->get_property_value(base_scene_node_remap[p_node], p_property, found);
+ return get_base_scene_state()->get_property_value(base_scene_node_remap[p_node], p_property, found);
}
return Variant();
@@ -1026,7 +968,7 @@ bool SceneState::is_node_in_group(int p_node, const StringName &p_group) const {
}
if (base_scene_node_remap.has(p_node)) {
- return _get_base_scene_state()->is_node_in_group(base_scene_node_remap[p_node], p_group);
+ return get_base_scene_state()->is_node_in_group(base_scene_node_remap[p_node], p_group);
}
return false;
@@ -1065,7 +1007,7 @@ bool SceneState::is_connection(int p_node, const StringName &p_signal, int p_to_
}
if (base_scene_node_remap.has(p_node) && base_scene_node_remap.has(p_to_node)) {
- return _get_base_scene_state()->is_connection(base_scene_node_remap[p_node], p_signal, base_scene_node_remap[p_to_node], p_to_method);
+ return get_base_scene_state()->is_connection(base_scene_node_remap[p_node], p_signal, base_scene_node_remap[p_to_node], p_to_method);
}
return false;
@@ -1488,7 +1430,7 @@ bool SceneState::has_connection(const NodePath &p_node_from, const StringName &p
}
}
- ss = ss->_get_base_scene_state();
+ ss = ss->get_base_scene_state();
} while (ss.is_valid());
return false;
@@ -1610,6 +1552,7 @@ void SceneState::_bind_methods() {
BIND_ENUM_CONSTANT(GEN_EDIT_STATE_DISABLED);
BIND_ENUM_CONSTANT(GEN_EDIT_STATE_INSTANCE);
BIND_ENUM_CONSTANT(GEN_EDIT_STATE_MAIN);
+ BIND_ENUM_CONSTANT(GEN_EDIT_STATE_MAIN_INHERITED);
}
SceneState::SceneState() {
@@ -1651,7 +1594,7 @@ Node *PackedScene::instantiate(GenEditState p_edit_state) const {
s->set_scene_instance_state(state);
}
- if (get_path() != "" && get_path().find("::") == -1) {
+ if (!is_built_in()) {
s->set_scene_file_path(get_path());
}
@@ -1701,6 +1644,7 @@ void PackedScene::_bind_methods() {
BIND_ENUM_CONSTANT(GEN_EDIT_STATE_DISABLED);
BIND_ENUM_CONSTANT(GEN_EDIT_STATE_INSTANCE);
BIND_ENUM_CONSTANT(GEN_EDIT_STATE_MAIN);
+ BIND_ENUM_CONSTANT(GEN_EDIT_STATE_MAIN_INHERITED);
}
PackedScene::PackedScene() {
diff --git a/scene/resources/packed_scene.h b/scene/resources/packed_scene.h
index 55708f7914..a03da558e4 100644
--- a/scene/resources/packed_scene.h
+++ b/scene/resources/packed_scene.h
@@ -69,11 +69,6 @@ class SceneState : public RefCounted {
Vector<int> groups;
};
- struct PackState {
- Ref<SceneState> state;
- int node = -1;
- };
-
Vector<NodeData> nodes;
struct ConnectionData {
@@ -94,8 +89,6 @@ class SceneState : public RefCounted {
uint64_t last_modified_time = 0;
- _FORCE_INLINE_ Ref<SceneState> _get_base_scene_state() const;
-
static bool disable_placeholders;
Vector<String> _get_node_groups(int p_idx) const;
@@ -117,6 +110,12 @@ public:
GEN_EDIT_STATE_DISABLED,
GEN_EDIT_STATE_INSTANCE,
GEN_EDIT_STATE_MAIN,
+ GEN_EDIT_STATE_MAIN_INHERITED,
+ };
+
+ struct PackState {
+ Ref<SceneState> state;
+ int node = -1;
};
static void set_disable_placeholders(bool p_disable);
@@ -139,6 +138,8 @@ public:
bool can_instantiate() const;
Node *instantiate(GenEditState p_edit_state) const;
+ Ref<SceneState> get_base_scene_state() const;
+
//unbuild API
int get_node_count() const;
@@ -207,6 +208,7 @@ public:
GEN_EDIT_STATE_DISABLED,
GEN_EDIT_STATE_INSTANCE,
GEN_EDIT_STATE_MAIN,
+ GEN_EDIT_STATE_MAIN_INHERITED,
};
Error pack(Node *p_scene);
diff --git a/scene/resources/particles_material.cpp b/scene/resources/particles_material.cpp
index d9ec0bfd69..e77f5a0be7 100644
--- a/scene/resources/particles_material.cpp
+++ b/scene/resources/particles_material.cpp
@@ -1413,7 +1413,7 @@ void ParticlesMaterial::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "scale_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture,CurveXYZTexture"), "set_param_texture", "get_param_texture", PARAM_SCALE);
ADD_GROUP("Color", "");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "GradientTexture"), "set_color_ramp", "get_color_ramp");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "GradientTexture1D"), "set_color_ramp", "get_color_ramp");
ADD_GROUP("Hue Variation", "hue_");
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "hue_variation_min", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_param_min", "get_param_min", PARAM_HUE_VARIATION);
diff --git a/scene/resources/polygon_path_finder.cpp b/scene/resources/polygon_path_finder.cpp
index 4dd3c874cb..ec2022ed2f 100644
--- a/scene/resources/polygon_path_finder.cpp
+++ b/scene/resources/polygon_path_finder.cpp
@@ -556,7 +556,7 @@ void PolygonPathFinder::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_data"), &PolygonPathFinder::_set_data);
ClassDB::bind_method(D_METHOD("_get_data"), &PolygonPathFinder::_get_data);
- ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
}
PolygonPathFinder::PolygonPathFinder() {
diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp
index 77d915aef9..ea3b72af1b 100644
--- a/scene/resources/resource_format_text.cpp
+++ b/scene/resources/resource_format_text.cpp
@@ -1492,7 +1492,7 @@ String ResourceFormatSaverTextInstance::_write_resource(const RES &res) {
} else {
if (internal_resources.has(res)) {
return "SubResource( \"" + internal_resources[res] + "\" )";
- } else if (res->get_path().length() && res->get_path().find("::") == -1) {
+ } else if (!res->is_built_in()) {
if (res->get_path() == local_path) { //circular reference attempt
return "null";
}
@@ -1515,7 +1515,7 @@ void ResourceFormatSaverTextInstance::_find_resources(const Variant &p_variant,
return;
}
- if (!p_main && (!bundle_resources) && res->get_path().length() && res->get_path().find("::") == -1) {
+ if (!p_main && (!bundle_resources) && !res->is_built_in()) {
if (res->get_path() == local_path) {
ERR_PRINT("Circular reference to resource being saved found: '" + local_path + "' will be null next time it's loaded.");
return;
@@ -1728,7 +1728,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
for (List<RES>::Element *E = saved_resources.front(); E; E = E->next()) {
RES res = E->get();
- if (E->next() && (res->get_path() == "" || res->get_path().find("::") != -1)) {
+ if (E->next() && res->is_built_in()) {
if (res->get_scene_unique_id() != "") {
if (used_unique_ids.has(res->get_scene_unique_id())) {
res->set_scene_unique_id(""); // Repeated.
diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp
index 4ba8d4d494..84fa07e4f4 100644
--- a/scene/resources/shader.cpp
+++ b/scene/resources/shader.cpp
@@ -97,28 +97,36 @@ RID Shader::get_rid() const {
return shader;
}
-void Shader::set_default_texture_param(const StringName &p_param, const Ref<Texture2D> &p_texture) {
+void Shader::set_default_texture_param(const StringName &p_param, const Ref<Texture2D> &p_texture, int p_index) {
if (p_texture.is_valid()) {
- default_textures[p_param] = p_texture;
- RS::get_singleton()->shader_set_default_texture_param(shader, p_param, p_texture->get_rid());
+ if (!default_textures.has(p_param)) {
+ default_textures[p_param] = Map<int, Ref<Texture2D>>();
+ }
+ default_textures[p_param][p_index] = p_texture;
+ RS::get_singleton()->shader_set_default_texture_param(shader, p_param, p_texture->get_rid(), p_index);
} else {
- default_textures.erase(p_param);
- RS::get_singleton()->shader_set_default_texture_param(shader, p_param, RID());
+ if (default_textures.has(p_param) && default_textures[p_param].has(p_index)) {
+ default_textures[p_param].erase(p_index);
+
+ if (default_textures[p_param].is_empty()) {
+ default_textures.erase(p_param);
+ }
+ }
+ RS::get_singleton()->shader_set_default_texture_param(shader, p_param, RID(), p_index);
}
emit_changed();
}
-Ref<Texture2D> Shader::get_default_texture_param(const StringName &p_param) const {
- if (default_textures.has(p_param)) {
- return default_textures[p_param];
- } else {
- return Ref<Texture2D>();
+Ref<Texture2D> Shader::get_default_texture_param(const StringName &p_param, int p_index) const {
+ if (default_textures.has(p_param) && default_textures[p_param].has(p_index)) {
+ return default_textures[p_param][p_index];
}
+ return Ref<Texture2D>();
}
void Shader::get_default_texture_param_list(List<StringName> *r_textures) const {
- for (const KeyValue<StringName, Ref<Texture2D>> &E : default_textures) {
+ for (const KeyValue<StringName, Map<int, Ref<Texture2D>>> &E : default_textures) {
r_textures->push_back(E.key);
}
}
@@ -140,12 +148,12 @@ void Shader::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_code", "code"), &Shader::set_code);
ClassDB::bind_method(D_METHOD("get_code"), &Shader::get_code);
- ClassDB::bind_method(D_METHOD("set_default_texture_param", "param", "texture"), &Shader::set_default_texture_param);
- ClassDB::bind_method(D_METHOD("get_default_texture_param", "param"), &Shader::get_default_texture_param);
+ ClassDB::bind_method(D_METHOD("set_default_texture_param", "param", "texture", "index"), &Shader::set_default_texture_param, DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("get_default_texture_param", "param", "index"), &Shader::get_default_texture_param, DEFVAL(0));
ClassDB::bind_method(D_METHOD("has_param", "name"), &Shader::has_param);
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "code", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_code", "get_code");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "code", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_code", "get_code");
BIND_ENUM_CONSTANT(MODE_SPATIAL);
BIND_ENUM_CONSTANT(MODE_CANVAS_ITEM);
diff --git a/scene/resources/shader.h b/scene/resources/shader.h
index c0dc07b403..c688dc1bab 100644
--- a/scene/resources/shader.h
+++ b/scene/resources/shader.h
@@ -59,7 +59,7 @@ private:
// conversion fast and save memory.
mutable bool params_cache_dirty = true;
mutable Map<StringName, StringName> params_cache; //map a shader param to a material param..
- Map<StringName, Ref<Texture2D>> default_textures;
+ Map<StringName, Map<int, Ref<Texture2D>>> default_textures;
virtual void _update_shader() const; //used for visual shader
protected:
@@ -75,8 +75,8 @@ public:
void get_param_list(List<PropertyInfo> *p_params) const;
bool has_param(const StringName &p_param) const;
- void set_default_texture_param(const StringName &p_param, const Ref<Texture2D> &p_texture);
- Ref<Texture2D> get_default_texture_param(const StringName &p_param) const;
+ void set_default_texture_param(const StringName &p_param, const Ref<Texture2D> &p_texture, int p_index = 0);
+ Ref<Texture2D> get_default_texture_param(const StringName &p_param, int p_index = 0) const;
void get_default_texture_param_list(List<StringName> *r_textures) const;
virtual bool is_text_shader() const;
diff --git a/scene/resources/skeleton_modification_2d_jiggle.cpp b/scene/resources/skeleton_modification_2d_jiggle.cpp
index 84abc9d020..31045455a3 100644
--- a/scene/resources/skeleton_modification_2d_jiggle.cpp
+++ b/scene/resources/skeleton_modification_2d_jiggle.cpp
@@ -194,9 +194,13 @@ void SkeletonModification2DJiggle::_execute_jiggle_joint(int p_joint_idx, Node2D
PhysicsDirectSpaceState2D *space_state = PhysicsServer2D::get_singleton()->space_get_direct_state(world_2d->get_space());
PhysicsDirectSpaceState2D::RayResult ray_result;
+ PhysicsDirectSpaceState2D::RayParameters ray_params;
+ ray_params.from = operation_bone_trans.get_origin();
+ ray_params.to = jiggle_data_chain[p_joint_idx].dynamic_position;
+ ray_params.collision_mask = collision_mask;
+
// Add exception support?
- bool ray_hit = space_state->intersect_ray(operation_bone_trans.get_origin(), jiggle_data_chain[p_joint_idx].dynamic_position,
- ray_result, Set<RID>(), collision_mask);
+ bool ray_hit = space_state->intersect_ray(ray_params, ray_result);
if (ray_hit) {
jiggle_data_chain.write[p_joint_idx].dynamic_position = jiggle_data_chain[p_joint_idx].last_noncollision_position;
diff --git a/scene/resources/skeleton_modification_3d_jiggle.cpp b/scene/resources/skeleton_modification_3d_jiggle.cpp
index a6bcb0176a..2535f2b987 100644
--- a/scene/resources/skeleton_modification_3d_jiggle.cpp
+++ b/scene/resources/skeleton_modification_3d_jiggle.cpp
@@ -206,8 +206,12 @@ void SkeletonModification3DJiggle::_execute_jiggle_joint(int p_joint_idx, Node3D
Transform3D new_bone_trans_world = stack->skeleton->global_pose_to_world_transform(new_bone_trans);
Transform3D dynamic_position_world = stack->skeleton->global_pose_to_world_transform(Transform3D(Basis(), jiggle_data_chain[p_joint_idx].dynamic_position));
- bool ray_hit = space_state->intersect_ray(new_bone_trans_world.origin, dynamic_position_world.get_origin(),
- ray_result, Set<RID>(), collision_mask);
+ PhysicsDirectSpaceState3D::RayParameters ray_params;
+ ray_params.from = new_bone_trans_world.origin;
+ ray_params.to = dynamic_position_world.get_origin();
+ ray_params.collision_mask = collision_mask;
+
+ bool ray_hit = space_state->intersect_ray(ray_params, ray_result);
if (ray_hit) {
jiggle_data_chain[p_joint_idx].dynamic_position = jiggle_data_chain[p_joint_idx].last_noncollision_position;
diff --git a/scene/resources/skin.cpp b/scene/resources/skin.cpp
index 710612ae05..15cdb86bab 100644
--- a/scene/resources/skin.cpp
+++ b/scene/resources/skin.cpp
@@ -133,7 +133,7 @@ void Skin::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::INT, "bind_count", PROPERTY_HINT_RANGE, "0,16384,1,or_greater"));
for (int i = 0; i < get_bind_count(); i++) {
p_list->push_back(PropertyInfo(Variant::STRING_NAME, "bind/" + itos(i) + "/name"));
- p_list->push_back(PropertyInfo(Variant::INT, "bind/" + itos(i) + "/bone", PROPERTY_HINT_RANGE, "0,16384,1,or_greater", get_bind_name(i) != StringName() ? PROPERTY_USAGE_NOEDITOR : PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::INT, "bind/" + itos(i) + "/bone", PROPERTY_HINT_RANGE, "0,16384,1,or_greater", get_bind_name(i) != StringName() ? PROPERTY_USAGE_NO_EDITOR : PROPERTY_USAGE_DEFAULT));
p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, "bind/" + itos(i) + "/pose"));
}
}
diff --git a/scene/resources/sprite_frames.cpp b/scene/resources/sprite_frames.cpp
index 01afb00283..5524d59dc7 100644
--- a/scene/resources/sprite_frames.cpp
+++ b/scene/resources/sprite_frames.cpp
@@ -233,7 +233,7 @@ void SpriteFrames::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_animations"), &SpriteFrames::_set_animations);
ClassDB::bind_method(D_METHOD("_get_animations"), &SpriteFrames::_get_animations);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "animations", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_animations", "_get_animations"); //compatibility
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "animations", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_animations", "_get_animations"); //compatibility
}
SpriteFrames::SpriteFrames() {
diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp
index 3381043d29..b960944d99 100644
--- a/scene/resources/style_box.cpp
+++ b/scene/resources/style_box.cpp
@@ -790,7 +790,7 @@ float StyleBoxFlat::get_style_margin(Side p_side) const {
void StyleBoxFlat::_validate_property(PropertyInfo &property) const {
if (!anti_aliased && property.name == "anti_aliasing_size") {
- property.usage = PROPERTY_USAGE_NOEDITOR;
+ property.usage = PROPERTY_USAGE_NO_EDITOR;
}
}
diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp
index fae1de94d3..1b7fc64267 100644
--- a/scene/resources/text_paragraph.cpp
+++ b/scene/resources/text_paragraph.cpp
@@ -38,6 +38,11 @@ void TextParagraph::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "direction", PROPERTY_HINT_ENUM, "Auto,Light-to-right,Right-to-left"), "set_direction", "get_direction");
+ ClassDB::bind_method(D_METHOD("set_custom_punctuation", "custom_punctuation"), &TextParagraph::set_custom_punctuation);
+ ClassDB::bind_method(D_METHOD("get_custom_punctuation"), &TextParagraph::get_custom_punctuation);
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "custom_punctuation"), "set_custom_punctuation", "get_custom_punctuation");
+
ClassDB::bind_method(D_METHOD("set_orientation", "orientation"), &TextParagraph::set_orientation);
ClassDB::bind_method(D_METHOD("get_orientation"), &TextParagraph::get_orientation);
@@ -304,6 +309,15 @@ TextServer::Direction TextParagraph::get_direction() const {
return TS->shaped_text_get_direction(rid);
}
+void TextParagraph::set_custom_punctuation(const String &p_punct) {
+ TS->shaped_text_set_custom_punctuation(rid, p_punct);
+ lines_dirty = true;
+}
+
+String TextParagraph::get_custom_punctuation() const {
+ return TS->shaped_text_get_custom_punctuation(rid);
+}
+
void TextParagraph::set_orientation(TextServer::Orientation p_orientation) {
TS->shaped_text_set_orientation(rid, p_orientation);
TS->shaped_text_set_orientation(dropcap_rid, p_orientation);
diff --git a/scene/resources/text_paragraph.h b/scene/resources/text_paragraph.h
index 701c9a17cd..4c4af43d14 100644
--- a/scene/resources/text_paragraph.h
+++ b/scene/resources/text_paragraph.h
@@ -96,6 +96,9 @@ public:
void set_bidi_override(const Array &p_override);
+ void set_custom_punctuation(const String &p_punct);
+ String get_custom_punctuation() const;
+
bool set_dropcap(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Rect2 &p_dropcap_margins = Rect2(), const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "");
void clear_dropcap();
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index dafc32b490..311bd9524b 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -1503,6 +1503,7 @@ Ref<Curve> CurveTexture::get_curve() const {
}
void CurveTexture::set_texture_mode(TextureMode p_mode) {
+ ERR_FAIL_COND(p_mode < TEXTURE_MODE_RGB || p_mode > TEXTURE_MODE_RED);
if (texture_mode == p_mode) {
return;
}
@@ -1728,53 +1729,53 @@ CurveXYZTexture::~CurveXYZTexture() {
//////////////////
-GradientTexture::GradientTexture() {
+GradientTexture1D::GradientTexture1D() {
_queue_update();
}
-GradientTexture::~GradientTexture() {
+GradientTexture1D::~GradientTexture1D() {
if (texture.is_valid()) {
RS::get_singleton()->free(texture);
}
}
-void GradientTexture::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_gradient", "gradient"), &GradientTexture::set_gradient);
- ClassDB::bind_method(D_METHOD("get_gradient"), &GradientTexture::get_gradient);
+void GradientTexture1D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_gradient", "gradient"), &GradientTexture1D::set_gradient);
+ ClassDB::bind_method(D_METHOD("get_gradient"), &GradientTexture1D::get_gradient);
- ClassDB::bind_method(D_METHOD("set_width", "width"), &GradientTexture::set_width);
+ ClassDB::bind_method(D_METHOD("set_width", "width"), &GradientTexture1D::set_width);
// The `get_width()` method is already exposed by the parent class Texture2D.
- ClassDB::bind_method(D_METHOD("set_use_hdr", "enabled"), &GradientTexture::set_use_hdr);
- ClassDB::bind_method(D_METHOD("is_using_hdr"), &GradientTexture::is_using_hdr);
+ ClassDB::bind_method(D_METHOD("set_use_hdr", "enabled"), &GradientTexture1D::set_use_hdr);
+ ClassDB::bind_method(D_METHOD("is_using_hdr"), &GradientTexture1D::is_using_hdr);
- ClassDB::bind_method(D_METHOD("_update"), &GradientTexture::_update);
+ ClassDB::bind_method(D_METHOD("_update"), &GradientTexture1D::_update);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gradient", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_gradient", "get_gradient");
ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,4096"), "set_width", "get_width");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_hdr"), "set_use_hdr", "is_using_hdr");
}
-void GradientTexture::set_gradient(Ref<Gradient> p_gradient) {
+void GradientTexture1D::set_gradient(Ref<Gradient> p_gradient) {
if (p_gradient == gradient) {
return;
}
if (gradient.is_valid()) {
- gradient->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &GradientTexture::_update));
+ gradient->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &GradientTexture1D::_update));
}
gradient = p_gradient;
if (gradient.is_valid()) {
- gradient->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &GradientTexture::_update));
+ gradient->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &GradientTexture1D::_update));
}
_update();
emit_changed();
}
-Ref<Gradient> GradientTexture::get_gradient() const {
+Ref<Gradient> GradientTexture1D::get_gradient() const {
return gradient;
}
-void GradientTexture::_queue_update() {
+void GradientTexture1D::_queue_update() {
if (update_pending) {
return;
}
@@ -1783,7 +1784,7 @@ void GradientTexture::_queue_update() {
call_deferred(SNAME("_update"));
}
-void GradientTexture::_update() {
+void GradientTexture1D::_update() {
update_pending = false;
if (gradient.is_null()) {
@@ -1838,17 +1839,17 @@ void GradientTexture::_update() {
emit_changed();
}
-void GradientTexture::set_width(int p_width) {
+void GradientTexture1D::set_width(int p_width) {
ERR_FAIL_COND(p_width <= 0);
width = p_width;
_queue_update();
}
-int GradientTexture::get_width() const {
+int GradientTexture1D::get_width() const {
return width;
}
-void GradientTexture::set_use_hdr(bool p_enabled) {
+void GradientTexture1D::set_use_hdr(bool p_enabled) {
if (p_enabled == use_hdr) {
return;
}
@@ -1857,11 +1858,11 @@ void GradientTexture::set_use_hdr(bool p_enabled) {
_queue_update();
}
-bool GradientTexture::is_using_hdr() const {
+bool GradientTexture1D::is_using_hdr() const {
return use_hdr;
}
-Ref<Image> GradientTexture::get_image() const {
+Ref<Image> GradientTexture1D::get_image() const {
if (!texture.is_valid()) {
return Ref<Image>();
}
@@ -2870,15 +2871,6 @@ RID CameraTexture::get_rid() const {
}
}
-void CameraTexture::set_flags(uint32_t p_flags) {
- // not supported
-}
-
-uint32_t CameraTexture::get_flags() const {
- // not supported
- return 0;
-}
-
Ref<Image> CameraTexture::get_image() const {
// not (yet) supported
return Ref<Image>();
diff --git a/scene/resources/texture.h b/scene/resources/texture.h
index 51567124c6..5b69711de6 100644
--- a/scene/resources/texture.h
+++ b/scene/resources/texture.h
@@ -669,8 +669,8 @@ public:
~CurveXYZTexture();
};
-class GradientTexture : public Texture2D {
- GDCLASS(GradientTexture, Texture2D);
+class GradientTexture1D : public Texture2D {
+ GDCLASS(GradientTexture1D, Texture2D);
public:
struct Point {
@@ -710,8 +710,8 @@ public:
virtual Ref<Image> get_image() const override;
- GradientTexture();
- virtual ~GradientTexture();
+ GradientTexture1D();
+ virtual ~GradientTexture1D();
};
class GradientTexture2D : public Texture2D {
@@ -900,9 +900,6 @@ public:
virtual RID get_rid() const override;
virtual bool has_alpha() const override;
- virtual void set_flags(uint32_t p_flags);
- virtual uint32_t get_flags() const;
-
virtual Ref<Image> get_image() const override;
void set_camera_feed_id(int p_new_id);
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index 141e9e1b0e..8c92445338 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -203,7 +203,7 @@ bool TileMapPattern::_get(const StringName &p_name, Variant &r_ret) const {
}
void TileMapPattern::_get_property_list(List<PropertyInfo> *p_list) const {
- p_list->push_back(PropertyInfo(Variant::OBJECT, "tile_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
+ p_list->push_back(PropertyInfo(Variant::OBJECT, "tile_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
}
void TileMapPattern::_bind_methods() {
@@ -212,7 +212,7 @@ void TileMapPattern::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_cell", "coords", "source_id", "atlas_coords", "alternative_tile"), &TileMapPattern::set_cell, DEFVAL(TileSet::INVALID_SOURCE), DEFVAL(TileSetSource::INVALID_ATLAS_COORDS), DEFVAL(TileSetSource::INVALID_TILE_ALTERNATIVE));
ClassDB::bind_method(D_METHOD("has_cell", "coords"), &TileMapPattern::has_cell);
- ClassDB::bind_method(D_METHOD("remove_cell", "coords"), &TileMapPattern::remove_cell);
+ ClassDB::bind_method(D_METHOD("remove_cell", "coords", "update_size"), &TileMapPattern::remove_cell);
ClassDB::bind_method(D_METHOD("get_cell_source_id", "coords"), &TileMapPattern::get_cell_source_id);
ClassDB::bind_method(D_METHOD("get_cell_atlas_coords", "coords"), &TileMapPattern::get_cell_atlas_coords);
ClassDB::bind_method(D_METHOD("get_cell_alternative_tile", "coords"), &TileMapPattern::get_cell_alternative_tile);
@@ -225,6 +225,90 @@ void TileMapPattern::_bind_methods() {
/////////////////////////////// TileSet //////////////////////////////////////
+bool TileSet::TerrainsPattern::is_valid() const {
+ return valid;
+}
+
+bool TileSet::TerrainsPattern::is_erase_pattern() const {
+ return not_empty_terrains_count == 0;
+}
+
+bool TileSet::TerrainsPattern::operator<(const TerrainsPattern &p_terrains_pattern) const {
+ for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ if (is_valid_bit[i] != p_terrains_pattern.is_valid_bit[i]) {
+ return is_valid_bit[i] < p_terrains_pattern.is_valid_bit[i];
+ }
+ }
+ for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ if (is_valid_bit[i] && bits[i] != p_terrains_pattern.bits[i]) {
+ return bits[i] < p_terrains_pattern.bits[i];
+ }
+ }
+ return false;
+}
+
+bool TileSet::TerrainsPattern::operator==(const TerrainsPattern &p_terrains_pattern) const {
+ for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ if (is_valid_bit[i] != p_terrains_pattern.is_valid_bit[i]) {
+ return false;
+ }
+ if (is_valid_bit[i] && bits[i] != p_terrains_pattern.bits[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void TileSet::TerrainsPattern::set_terrain(TileSet::CellNeighbor p_peering_bit, int p_terrain) {
+ ERR_FAIL_COND(p_peering_bit == TileSet::CELL_NEIGHBOR_MAX);
+ ERR_FAIL_COND(!is_valid_bit[p_peering_bit]);
+ ERR_FAIL_COND(p_terrain < -1);
+
+ // Update the "is_erase_pattern" status.
+ if (p_terrain >= 0 && bits[p_peering_bit] < 0) {
+ not_empty_terrains_count++;
+ } else if (p_terrain < 0 && bits[p_peering_bit] >= 0) {
+ not_empty_terrains_count--;
+ }
+
+ bits[p_peering_bit] = p_terrain;
+}
+
+int TileSet::TerrainsPattern::get_terrain(TileSet::CellNeighbor p_peering_bit) const {
+ ERR_FAIL_COND_V(p_peering_bit == TileSet::CELL_NEIGHBOR_MAX, -1);
+ ERR_FAIL_COND_V(!is_valid_bit[p_peering_bit], -1);
+ return bits[p_peering_bit];
+}
+
+void TileSet::TerrainsPattern::set_terrains_from_array(Array p_terrains) {
+ int in_array_index = 0;
+ for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ if (is_valid_bit[i]) {
+ ERR_FAIL_COND(in_array_index >= p_terrains.size());
+ set_terrain(TileSet::CellNeighbor(i), p_terrains[in_array_index]);
+ in_array_index++;
+ }
+ }
+}
+
+Array TileSet::TerrainsPattern::get_terrains_as_array() const {
+ Array output;
+ for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ if (is_valid_bit[i]) {
+ output.push_back(bits[i]);
+ }
+ }
+ return output;
+}
+TileSet::TerrainsPattern::TerrainsPattern(const TileSet *p_tile_set, int p_terrain_set) {
+ ERR_FAIL_COND(p_terrain_set < 0);
+ for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ is_valid_bit[i] = (p_tile_set->is_valid_peering_bit_terrain(p_terrain_set, TileSet::CellNeighbor(i)));
+ bits[i] = -1;
+ }
+ valid = true;
+}
+
const int TileSet::INVALID_SOURCE = -1;
const char *TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[] = {
@@ -330,10 +414,13 @@ void TileSet::_update_terrains_cache() {
TileSet::TerrainsPattern terrains_pattern = tile_data->get_terrains_pattern();
// Terrain bits.
- for (int i = 0; i < terrains_pattern.size(); i++) {
- int terrain = terrains_pattern[i];
- if (terrain >= 0) {
- per_terrain_pattern_tiles[terrain_set][terrains_pattern].insert(cell);
+ for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ CellNeighbor bit = CellNeighbor(i);
+ if (is_valid_peering_bit_terrain(terrain_set, bit)) {
+ int terrain = terrains_pattern.get_terrain(bit);
+ if (terrain >= 0) {
+ per_terrain_pattern_tiles[terrain_set][terrains_pattern].insert(cell);
+ }
}
}
}
@@ -344,12 +431,7 @@ void TileSet::_update_terrains_cache() {
// Add the empty cell in the possible patterns and cells.
for (int i = 0; i < terrain_sets.size(); i++) {
- TileSet::TerrainsPattern empty_pattern;
- for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
- if (is_valid_peering_bit_terrain(i, TileSet::CellNeighbor(j))) {
- empty_pattern.push_back(-1);
- }
- }
+ TileSet::TerrainsPattern empty_pattern(this, i);
TileMapCell empty_cell;
empty_cell.source_id = TileSet::INVALID_SOURCE;
@@ -1283,7 +1365,7 @@ Set<TileMapCell> TileSet::get_tiles_for_terrains_pattern(int p_terrain_set, Terr
return per_terrain_pattern_tiles[p_terrain_set][p_terrain_tile_pattern];
}
-TileMapCell TileSet::get_random_tile_from_pattern(int p_terrain_set, TileSet::TerrainsPattern p_terrain_tile_pattern) {
+TileMapCell TileSet::get_random_tile_from_terrains_pattern(int p_terrain_set, TileSet::TerrainsPattern p_terrain_tile_pattern) {
ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), TileMapCell());
_update_terrains_cache();
@@ -3042,19 +3124,19 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const {
// Sources.
// Note: sources have to be listed in at the end as some TileData rely on the TileSet properties being initialized first.
for (const KeyValue<int, Ref<TileSetSource>> &E_source : sources) {
- p_list->push_back(PropertyInfo(Variant::INT, vformat("sources/%d", E_source.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ p_list->push_back(PropertyInfo(Variant::INT, vformat("sources/%d", E_source.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
}
// Tile Proxies.
// Note: proxies need to be set after sources are set.
p_list->push_back(PropertyInfo(Variant::NIL, "Tile Proxies", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
- p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/source_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/coords_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/alternative_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/source_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/coords_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/alternative_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
// Patterns.
for (unsigned int pattern_index = 0; pattern_index < patterns.size(); pattern_index++) {
- p_list->push_back(PropertyInfo(Variant::OBJECT, vformat("pattern_%d", pattern_index), PROPERTY_HINT_RESOURCE_TYPE, "TileMapPattern", PROPERTY_USAGE_NOEDITOR));
+ p_list->push_back(PropertyInfo(Variant::OBJECT, vformat("pattern_%d", pattern_index), PROPERTY_HINT_RESOURCE_TYPE, "TileMapPattern", PROPERTY_USAGE_NO_EDITOR));
}
}
@@ -3277,6 +3359,10 @@ void TileSetAtlasSource::set_tile_set(const TileSet *p_tile_set) {
}
}
+const TileSet *TileSetAtlasSource::get_tile_set() const {
+ return tile_set;
+}
+
void TileSetAtlasSource::notify_tile_data_properties_should_change() {
// Set the TileSet on all TileData.
for (KeyValue<Vector2i, TileAlternativesData> &E_tile : tiles) {
@@ -3440,9 +3526,18 @@ void TileSetAtlasSource::reset_state() {
}
void TileSetAtlasSource::set_texture(Ref<Texture2D> p_texture) {
+ if (texture.is_valid()) {
+ texture->disconnect(SNAME("changed"), callable_mp(this, &TileSetAtlasSource::_queue_update_padded_texture));
+ }
+
texture = p_texture;
+ if (texture.is_valid()) {
+ texture->connect(SNAME("changed"), callable_mp(this, &TileSetAtlasSource::_queue_update_padded_texture));
+ }
+
_clear_tiles_outside_texture();
+ _queue_update_padded_texture();
emit_changed();
}
@@ -3459,8 +3554,10 @@ void TileSetAtlasSource::set_margins(Vector2i p_margins) {
}
_clear_tiles_outside_texture();
+ _queue_update_padded_texture();
emit_changed();
}
+
Vector2i TileSetAtlasSource::get_margins() const {
return margins;
}
@@ -3474,8 +3571,10 @@ void TileSetAtlasSource::set_separation(Vector2i p_separation) {
}
_clear_tiles_outside_texture();
+ _queue_update_padded_texture();
emit_changed();
}
+
Vector2i TileSetAtlasSource::get_separation() const {
return separation;
}
@@ -3489,12 +3588,27 @@ void TileSetAtlasSource::set_texture_region_size(Vector2i p_tile_size) {
}
_clear_tiles_outside_texture();
+ _queue_update_padded_texture();
emit_changed();
}
+
Vector2i TileSetAtlasSource::get_texture_region_size() const {
return texture_region_size;
}
+void TileSetAtlasSource::set_use_texture_padding(bool p_use_padding) {
+ if (use_texture_padding == p_use_padding) {
+ return;
+ }
+ use_texture_padding = p_use_padding;
+ _queue_update_padded_texture();
+ emit_changed();
+}
+
+bool TileSetAtlasSource::get_use_texture_padding() const {
+ return use_texture_padding;
+}
+
Vector2i TileSetAtlasSource::get_atlas_grid_size() const {
Ref<Texture2D> texture = get_texture();
if (!texture.is_valid()) {
@@ -3655,35 +3769,35 @@ void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const {
List<PropertyInfo> tile_property_list;
// size_in_atlas
- property_info = PropertyInfo(Variant::VECTOR2I, "size_in_atlas", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR);
+ property_info = PropertyInfo(Variant::VECTOR2I, "size_in_atlas", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR);
if (E_tile.value.size_in_atlas == Vector2i(1, 1)) {
property_info.usage ^= PROPERTY_USAGE_STORAGE;
}
tile_property_list.push_back(property_info);
// next_alternative_id
- property_info = PropertyInfo(Variant::INT, "next_alternative_id", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR);
+ property_info = PropertyInfo(Variant::INT, "next_alternative_id", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR);
if (E_tile.value.next_alternative_id == 1) {
property_info.usage ^= PROPERTY_USAGE_STORAGE;
}
tile_property_list.push_back(property_info);
// animation_columns.
- property_info = PropertyInfo(Variant::INT, "animation_columns", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR);
+ property_info = PropertyInfo(Variant::INT, "animation_columns", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR);
if (E_tile.value.animation_columns == 0) {
property_info.usage ^= PROPERTY_USAGE_STORAGE;
}
tile_property_list.push_back(property_info);
// animation_separation.
- property_info = PropertyInfo(Variant::INT, "animation_separation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR);
+ property_info = PropertyInfo(Variant::INT, "animation_separation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR);
if (E_tile.value.animation_separation == Vector2i()) {
property_info.usage ^= PROPERTY_USAGE_STORAGE;
}
tile_property_list.push_back(property_info);
// animation_speed.
- property_info = PropertyInfo(Variant::FLOAT, "animation_speed", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR);
+ property_info = PropertyInfo(Variant::FLOAT, "animation_speed", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR);
if (E_tile.value.animation_speed == 1.0) {
property_info.usage ^= PROPERTY_USAGE_STORAGE;
}
@@ -3695,7 +3809,7 @@ void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const {
// animation_frame_*.
bool store_durations = tiles[E_tile.key].animation_frames_durations.size() >= 2;
for (int i = 0; i < (int)tiles[E_tile.key].animation_frames_durations.size(); i++) {
- property_info = PropertyInfo(Variant::FLOAT, vformat("animation_frame_%d/duration", i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR);
+ property_info = PropertyInfo(Variant::FLOAT, vformat("animation_frame_%d/duration", i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR);
if (!store_durations) {
property_info.usage ^= PROPERTY_USAGE_STORAGE;
}
@@ -3704,7 +3818,7 @@ void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const {
for (const KeyValue<int, TileData *> &E_alternative : E_tile.value.alternatives) {
// Add a dummy property to show the alternative exists.
- tile_property_list.push_back(PropertyInfo(Variant::INT, vformat("%d", E_alternative.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ tile_property_list.push_back(PropertyInfo(Variant::INT, vformat("%d", E_alternative.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
// Get the alternative tile's properties and append them to the list of properties.
List<PropertyInfo> alternative_property_list;
@@ -3753,6 +3867,7 @@ void TileSetAtlasSource::create_tile(const Vector2i p_atlas_coords, const Vector
tiles_ids.sort();
_create_coords_mapping_cache(p_atlas_coords);
+ _queue_update_padded_texture();
emit_signal(SNAME("changed"));
}
@@ -3773,6 +3888,8 @@ void TileSetAtlasSource::remove_tile(Vector2i p_atlas_coords) {
tiles_ids.erase(p_atlas_coords);
tiles_ids.sort();
+ _queue_update_padded_texture();
+
emit_signal(SNAME("changed"));
}
@@ -3801,6 +3918,7 @@ void TileSetAtlasSource::set_tile_animation_columns(const Vector2i p_atlas_coord
tiles[p_atlas_coords].animation_columns = p_frame_columns;
_create_coords_mapping_cache(p_atlas_coords);
+ _queue_update_padded_texture();
emit_signal(SNAME("changed"));
}
@@ -3823,6 +3941,7 @@ void TileSetAtlasSource::set_tile_animation_separation(const Vector2i p_atlas_co
tiles[p_atlas_coords].animation_separation = p_separation;
_create_coords_mapping_cache(p_atlas_coords);
+ _queue_update_padded_texture();
emit_signal(SNAME("changed"));
}
@@ -3850,19 +3969,24 @@ void TileSetAtlasSource::set_tile_animation_frames_count(const Vector2i p_atlas_
ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords)));
ERR_FAIL_COND(p_frames_count < 1);
+ int old_size = tiles[p_atlas_coords].animation_frames_durations.size();
+ if (p_frames_count == old_size) {
+ return;
+ }
+
TileAlternativesData &tad = tiles[p_atlas_coords];
bool room_for_tile = has_room_for_tile(p_atlas_coords, tad.size_in_atlas, tad.animation_columns, tad.animation_separation, p_frames_count, p_atlas_coords);
ERR_FAIL_COND_MSG(!room_for_tile, "Cannot set animation columns count, tiles are already present in the space the tile would cover.");
_clear_coords_mapping_cache(p_atlas_coords);
- int old_size = tiles[p_atlas_coords].animation_frames_durations.size();
tiles[p_atlas_coords].animation_frames_durations.resize(p_frames_count);
for (int i = old_size; i < p_frames_count; i++) {
tiles[p_atlas_coords].animation_frames_durations[i] = 1.0;
}
_create_coords_mapping_cache(p_atlas_coords);
+ _queue_update_padded_texture();
notify_property_list_changed();
@@ -4001,6 +4125,31 @@ Vector2i TileSetAtlasSource::get_tile_effective_texture_offset(Vector2i p_atlas_
return effective_texture_offset;
}
+// Getters for texture and tile region (padded or not)
+Ref<Texture2D> TileSetAtlasSource::get_runtime_texture() const {
+ if (use_texture_padding) {
+ return padded_texture;
+ } else {
+ return texture;
+ }
+}
+
+Rect2i TileSetAtlasSource::get_runtime_tile_texture_region(Vector2i p_atlas_coords, int p_frame) const {
+ ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Rect2i(), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
+ ERR_FAIL_INDEX_V(p_frame, (int)tiles[p_atlas_coords].animation_frames_durations.size(), Rect2i());
+
+ Rect2i src_rect = get_tile_texture_region(p_atlas_coords, p_frame);
+ if (use_texture_padding) {
+ const TileAlternativesData &tad = tiles[p_atlas_coords];
+ Vector2i frame_coords = p_atlas_coords + (tad.size_in_atlas + tad.animation_separation) * ((tad.animation_columns > 0) ? Vector2i(p_frame % tad.animation_columns, p_frame / tad.animation_columns) : Vector2i(p_frame, 0));
+ Vector2i base_pos = frame_coords * (texture_region_size + Vector2i(2, 2)) + Vector2i(1, 1);
+
+ return Rect2i(base_pos, src_rect.size);
+ } else {
+ return src_rect;
+ }
+}
+
void TileSetAtlasSource::move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords, Vector2i p_new_size) {
ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
@@ -4031,6 +4180,7 @@ void TileSetAtlasSource::move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_
tiles[new_atlas_coords].size_in_atlas = new_size;
_create_coords_mapping_cache(new_atlas_coords);
+ _queue_update_padded_texture();
emit_signal(SNAME("changed"));
}
@@ -4122,11 +4272,14 @@ void TileSetAtlasSource::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_separation"), &TileSetAtlasSource::get_separation);
ClassDB::bind_method(D_METHOD("set_texture_region_size", "texture_region_size"), &TileSetAtlasSource::set_texture_region_size);
ClassDB::bind_method(D_METHOD("get_texture_region_size"), &TileSetAtlasSource::get_texture_region_size);
+ ClassDB::bind_method(D_METHOD("set_use_texture_padding", "use_texture_padding"), &TileSetAtlasSource::set_use_texture_padding);
+ ClassDB::bind_method(D_METHOD("get_use_texture_padding"), &TileSetAtlasSource::get_use_texture_padding);
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_NOEDITOR), "set_texture", "get_texture");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "margins", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_margins", "get_margins");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "separation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_separation", "get_separation");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "texture_region_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_texture_region_size", "get_texture_region_size");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_NO_EDITOR), "set_texture", "get_texture");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "margins", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_margins", "get_margins");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "separation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_separation", "get_separation");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "texture_region_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_texture_region_size", "get_texture_region_size");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_texture_padding", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_use_texture_padding", "get_use_texture_padding");
// Base tiles
ClassDB::bind_method(D_METHOD("create_tile", "atlas_coords", "size"), &TileSetAtlasSource::create_tile, DEFVAL(Vector2i(1, 1)));
@@ -4161,6 +4314,11 @@ void TileSetAtlasSource::_bind_methods() {
// Helpers.
ClassDB::bind_method(D_METHOD("get_atlas_grid_size"), &TileSetAtlasSource::get_atlas_grid_size);
ClassDB::bind_method(D_METHOD("get_tile_texture_region", "atlas_coords", "frame"), &TileSetAtlasSource::get_tile_texture_region, DEFVAL(0));
+
+ // Getters for texture and tile region (padded or not)
+ ClassDB::bind_method(D_METHOD("_update_padded_texture"), &TileSetAtlasSource::_update_padded_texture);
+ ClassDB::bind_method(D_METHOD("get_runtime_texture"), &TileSetAtlasSource::get_runtime_texture);
+ ClassDB::bind_method(D_METHOD("get_runtime_tile_texture_region", "atlas_coords", "frame"), &TileSetAtlasSource::get_runtime_tile_texture_region);
}
TileSetAtlasSource::~TileSetAtlasSource() {
@@ -4247,6 +4405,69 @@ void TileSetAtlasSource::_clear_tiles_outside_texture() {
}
}
+void TileSetAtlasSource::_queue_update_padded_texture() {
+ padded_texture_needs_update = true;
+ call_deferred("_update_padded_texture");
+}
+
+void TileSetAtlasSource::_update_padded_texture() {
+ if (!padded_texture_needs_update) {
+ return;
+ }
+ padded_texture_needs_update = false;
+ padded_texture = Ref<ImageTexture>();
+
+ if (!texture.is_valid()) {
+ return;
+ }
+
+ if (!use_texture_padding) {
+ return;
+ }
+
+ Size2 size = get_atlas_grid_size() * (texture_region_size + Vector2i(2, 2));
+
+ Ref<Image> src = texture->get_image();
+
+ Ref<Image> image;
+ image.instantiate();
+ image->create(size.x, size.y, false, Image::FORMAT_RGBA8);
+
+ for (KeyValue<Vector2i, TileAlternativesData> kv : tiles) {
+ for (int frame = 0; frame < (int)kv.value.animation_frames_durations.size(); frame++) {
+ // Compute the source rects.
+ Rect2i src_rect = get_tile_texture_region(kv.key, frame);
+
+ Rect2i top_src_rect = Rect2i(src_rect.position, Vector2i(src_rect.size.x, 1));
+ Rect2i bottom_src_rect = Rect2i(src_rect.position + Vector2i(0, src_rect.size.y - 1), Vector2i(src_rect.size.x, 1));
+ Rect2i left_src_rect = Rect2i(src_rect.position, Vector2i(1, src_rect.size.y));
+ Rect2i right_src_rect = Rect2i(src_rect.position + Vector2i(src_rect.size.x - 1, 0), Vector2i(1, src_rect.size.y));
+
+ // Copy the tile and the paddings.
+ Vector2i frame_coords = kv.key + (kv.value.size_in_atlas + kv.value.animation_separation) * ((kv.value.animation_columns > 0) ? Vector2i(frame % kv.value.animation_columns, frame / kv.value.animation_columns) : Vector2i(frame, 0));
+ Vector2i base_pos = frame_coords * (texture_region_size + Vector2i(2, 2)) + Vector2i(1, 1);
+
+ image->blit_rect(*src, src_rect, base_pos);
+
+ image->blit_rect(*src, top_src_rect, base_pos + Vector2i(0, -1));
+ image->blit_rect(*src, bottom_src_rect, base_pos + Vector2i(0, src_rect.size.y));
+ image->blit_rect(*src, left_src_rect, base_pos + Vector2i(-1, 0));
+ image->blit_rect(*src, right_src_rect, base_pos + Vector2i(src_rect.size.x, 0));
+
+ image->set_pixelv(base_pos + Vector2i(-1, -1), src->get_pixelv(src_rect.position));
+ image->set_pixelv(base_pos + Vector2i(src_rect.size.x, -1), src->get_pixelv(src_rect.position + Vector2i(src_rect.size.x - 1, 0)));
+ image->set_pixelv(base_pos + Vector2i(-1, src_rect.size.y), src->get_pixelv(src_rect.position + Vector2i(0, src_rect.size.y - 1)));
+ image->set_pixelv(base_pos + Vector2i(src_rect.size.x, src_rect.size.y), src->get_pixelv(src_rect.position + Vector2i(src_rect.size.x - 1, src_rect.size.y - 1)));
+ }
+ }
+
+ if (!padded_texture.is_valid()) {
+ padded_texture.instantiate();
+ }
+ padded_texture->create_from_image(image);
+ emit_changed();
+}
+
/////////////////////////////// TileSetScenesCollectionSource //////////////////////////////////////
void TileSetScenesCollectionSource::_compute_next_alternative_id() {
@@ -4776,6 +4997,9 @@ real_t TileData::get_constant_angular_velocity(int p_layer_id) const {
void TileData::set_collision_polygons_count(int p_layer_id, int p_polygons_count) {
ERR_FAIL_INDEX(p_layer_id, physics.size());
ERR_FAIL_COND(p_polygons_count < 0);
+ if (p_polygons_count == physics.write[p_layer_id].polygons.size()) {
+ return;
+ }
physics.write[p_layer_id].polygons.resize(p_polygons_count);
notify_property_list_changed();
emit_signal(SNAME("changed"));
@@ -4864,8 +5088,8 @@ int TileData::get_collision_polygon_shapes_count(int p_layer_id, int p_polygon_i
Ref<ConvexPolygonShape2D> TileData::get_collision_polygon_shape(int p_layer_id, int p_polygon_index, int shape_index) const {
ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0);
ERR_FAIL_INDEX_V(p_polygon_index, physics[p_layer_id].polygons.size(), Ref<ConvexPolygonShape2D>());
- ERR_FAIL_INDEX_V(shape_index, (int)physics[p_layer_id].polygons[shape_index].shapes.size(), Ref<ConvexPolygonShape2D>());
- return physics[p_layer_id].polygons[shape_index].shapes[shape_index];
+ ERR_FAIL_INDEX_V(shape_index, (int)physics[p_layer_id].polygons[p_polygon_index].shapes.size(), Ref<ConvexPolygonShape2D>());
+ return physics[p_layer_id].polygons[p_polygon_index].shapes[shape_index];
}
// Terrain
@@ -4915,10 +5139,10 @@ bool TileData::is_valid_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit)
TileSet::TerrainsPattern TileData::get_terrains_pattern() const {
ERR_FAIL_COND_V(!tile_set, TileSet::TerrainsPattern());
- TileSet::TerrainsPattern output;
+ TileSet::TerrainsPattern output(tile_set, terrain_set);
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
if (tile_set->is_valid_peering_bit_terrain(terrain_set, TileSet::CellNeighbor(i))) {
- output.push_back(get_peering_bit_terrain(TileSet::CellNeighbor(i)));
+ output.set_terrain(TileSet::CellNeighbor(i), get_peering_bit_terrain(TileSet::CellNeighbor(i)));
}
}
return output;
@@ -4981,9 +5205,6 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) {
ERR_FAIL_COND_V(layer_index < 0, false);
if (components[1] == "polygon") {
Ref<OccluderPolygon2D> polygon = p_value;
- if (!polygon.is_valid()) {
- return false;
- }
if (layer_index >= occluders.size()) {
if (tile_set) {
@@ -5055,9 +5276,6 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) {
ERR_FAIL_COND_V(layer_index < 0, false);
if (components[1] == "polygon") {
Ref<NavigationPolygon> polygon = p_value;
- if (!polygon.is_valid()) {
- return false;
- }
if (layer_index >= navigation.size()) {
if (tile_set) {
diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h
index 077315e58d..d2238c26d2 100644
--- a/scene/resources/tile_set.h
+++ b/scene/resources/tile_set.h
@@ -253,7 +253,30 @@ public:
Ref<PackedScene> scene;
Vector2 offset;
};
- typedef Array TerrainsPattern;
+
+ class TerrainsPattern {
+ bool valid = false;
+ int bits[TileSet::CELL_NEIGHBOR_MAX];
+ bool is_valid_bit[TileSet::CELL_NEIGHBOR_MAX];
+
+ int not_empty_terrains_count = 0;
+
+ public:
+ bool is_valid() const;
+ bool is_erase_pattern() const;
+
+ bool operator<(const TerrainsPattern &p_terrains_pattern) const;
+ bool operator==(const TerrainsPattern &p_terrains_pattern) const;
+
+ void set_terrain(TileSet::CellNeighbor p_peering_bit, int p_terrain);
+ int get_terrain(TileSet::CellNeighbor p_peering_bit) const;
+
+ void set_terrains_from_array(Array p_terrains);
+ Array get_terrains_as_array() const;
+
+ TerrainsPattern(const TileSet *p_tile_set, int p_terrain_set);
+ TerrainsPattern() {}
+ };
protected:
bool _set(const StringName &p_name, const Variant &p_value);
@@ -478,7 +501,7 @@ public:
// Terrains.
Set<TerrainsPattern> get_terrains_pattern_set(int p_terrain_set);
Set<TileMapCell> get_tiles_for_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern);
- TileMapCell get_random_tile_from_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern);
+ TileMapCell get_random_tile_from_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern);
// Helpers
Vector<Vector2> get_tile_shape_polygon();
@@ -580,6 +603,12 @@ private:
void _clear_tiles_outside_texture();
+ bool use_texture_padding = true;
+ Ref<ImageTexture> padded_texture;
+ bool padded_texture_needs_update = false;
+ void _queue_update_padded_texture();
+ void _update_padded_texture();
+
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
@@ -590,6 +619,7 @@ protected:
public:
// Not exposed.
virtual void set_tile_set(const TileSet *p_tile_set) override;
+ const TileSet *get_tile_set() const;
virtual void notify_tile_data_properties_should_change() override;
virtual void add_occlusion_layer(int p_index) override;
virtual void move_occlusion_layer(int p_from_index, int p_to_pos) override;
@@ -621,6 +651,10 @@ public:
void set_texture_region_size(Vector2i p_tile_size);
Vector2i get_texture_region_size() const;
+ // Padding.
+ void set_use_texture_padding(bool p_use_padding);
+ bool get_use_texture_padding() const;
+
// Base tiles.
void create_tile(const Vector2i p_atlas_coords, const Vector2i p_size = Vector2i(1, 1));
void remove_tile(Vector2i p_atlas_coords);
@@ -666,6 +700,10 @@ public:
Rect2i get_tile_texture_region(Vector2i p_atlas_coords, int p_frame = 0) const;
Vector2i get_tile_effective_texture_offset(Vector2i p_atlas_coords, int p_alternative_tile) const;
+ // Getters for texture and tile region (padded or not)
+ Ref<Texture2D> get_runtime_texture() const;
+ Rect2i get_runtime_tile_texture_region(Vector2i p_atlas_coords, int p_frame = 0) const;
+
~TileSetAtlasSource();
};
@@ -743,7 +781,7 @@ private:
};
Vector2 linear_velocity;
- float angular_velocity = 0.0;
+ double angular_velocity = 0.0;
Vector<PolygonShapeTileData> polygons;
};
Vector<PhysicsLayerTileData> physics;
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index fd785631a8..0bdc81330e 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -36,6 +36,11 @@
#include "visual_shader_particle_nodes.h"
#include "visual_shader_sdf_nodes.h"
+String make_unique_id(VisualShader::Type p_type, int p_id, const String &p_name) {
+ static const char *typepf[VisualShader::TYPE_MAX] = { "vtx", "frg", "lgt", "start", "process", "collide", "start_custom", "process_custom", "sky", "fog" };
+ return p_name + "_" + String(typepf[p_type]) + "_" + itos(p_id);
+}
+
bool VisualShaderNode::is_simple_decl() const {
return simple_decl;
}
@@ -199,6 +204,10 @@ Vector<StringName> VisualShaderNode::get_editable_properties() const {
return Vector<StringName>();
}
+Map<StringName, String> VisualShaderNode::get_editable_properties_names() const {
+ return Map<StringName, String>();
+}
+
Array VisualShaderNode::get_default_input_values() const {
Array ret;
for (const KeyValue<int, Variant> &E : default_input_values) {
@@ -246,8 +255,8 @@ void VisualShaderNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_default_input_values"), &VisualShaderNode::get_default_input_values);
ADD_PROPERTY(PropertyInfo(Variant::INT, "output_port_for_preview"), "set_output_port_for_preview", "get_output_port_for_preview");
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "default_input_values", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_default_input_values", "get_default_input_values");
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "expanded_output_ports", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_output_ports_expanded", "_get_output_ports_expanded");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "default_input_values", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_default_input_values", "get_default_input_values");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "expanded_output_ports", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_output_ports_expanded", "_get_output_ports_expanded");
ADD_SIGNAL(MethodInfo("editor_refresh_request"));
BIND_ENUM_CONSTANT(PORT_TYPE_SCALAR);
@@ -350,7 +359,7 @@ String VisualShaderNodeCustom::generate_code(Shader::Mode p_mode, VisualShader::
}
String code = " {\n";
String _code;
- GDVIRTUAL_CALL(_get_code, input_vars, output_vars, (int)p_mode, (int)p_type, _code);
+ GDVIRTUAL_CALL(_get_code, input_vars, output_vars, p_mode, p_type, _code);
bool nend = _code.ends_with("\n");
_code = _code.insert(0, " ");
_code = _code.replace("\n", "\n ");
@@ -367,7 +376,7 @@ String VisualShaderNodeCustom::generate_code(Shader::Mode p_mode, VisualShader::
String VisualShaderNodeCustom::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
String ret;
- if (GDVIRTUAL_CALL(_get_global_code, (int)p_mode, ret)) {
+ if (GDVIRTUAL_CALL(_get_global_code, p_mode, ret)) {
String code = "// " + get_caption() + "\n";
code += ret;
code += "\n";
@@ -431,7 +440,7 @@ void VisualShaderNodeCustom::_bind_methods() {
ClassDB::bind_method(D_METHOD("_is_initialized"), &VisualShaderNodeCustom::_is_initialized);
ClassDB::bind_method(D_METHOD("_set_input_port_default_value", "port", "value"), &VisualShaderNodeCustom::_set_input_port_default_value);
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "initialized", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_initialized", "_is_initialized");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "initialized", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_initialized", "_is_initialized");
}
VisualShaderNodeCustom::VisualShaderNodeCustom() {
@@ -1291,20 +1300,20 @@ void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const {
prop_name += "/" + itos(E.key);
if (E.key != NODE_ID_OUTPUT) {
- p_list->push_back(PropertyInfo(Variant::OBJECT, prop_name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "VisualShaderNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE));
+ p_list->push_back(PropertyInfo(Variant::OBJECT, prop_name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "VisualShaderNode", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE));
}
- p_list->push_back(PropertyInfo(Variant::VECTOR2, prop_name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ p_list->push_back(PropertyInfo(Variant::VECTOR2, prop_name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
if (Object::cast_to<VisualShaderNodeGroupBase>(E.value.node.ptr()) != nullptr) {
- p_list->push_back(PropertyInfo(Variant::VECTOR2, prop_name + "/size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/input_ports", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/output_ports", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ p_list->push_back(PropertyInfo(Variant::VECTOR2, prop_name + "/size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/input_ports", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/output_ports", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
}
if (Object::cast_to<VisualShaderNodeExpression>(E.value.node.ptr()) != nullptr) {
- p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/expression", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/expression", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
}
}
- p_list->push_back(PropertyInfo(Variant::PACKED_INT32_ARRAY, "nodes/" + String(type_string[i]) + "/connections", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ p_list->push_back(PropertyInfo(Variant::PACKED_INT32_ARRAY, "nodes/" + String(type_string[i]) + "/connections", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
}
}
@@ -1825,6 +1834,7 @@ void VisualShader::_update_shader() const {
code += " vec3 __vec3_buff2;\n";
code += " float __scalar_buff1;\n";
code += " float __scalar_buff2;\n";
+ code += " int __scalar_ibuff;\n";
code += " vec3 __ndiff = normalize(__diff);\n\n";
}
if (has_start) {
@@ -1922,6 +1932,10 @@ void VisualShader::_update_shader() const {
global_compute_code += " return mat4(vec4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0), vec4(oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0), vec4(oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0), vec4(0, 0, 0, 1));\n";
global_compute_code += "}\n\n";
+ global_compute_code += "vec2 __get_random_unit_vec2(inout uint seed) {\n";
+ global_compute_code += " return normalize(vec2(__rand_from_seed_m1_p1(seed), __rand_from_seed_m1_p1(seed)));\n";
+ global_compute_code += "}\n\n";
+
global_compute_code += "vec3 __get_random_unit_vec3(inout uint seed) {\n";
global_compute_code += " return normalize(vec3(__rand_from_seed_m1_p1(seed), __rand_from_seed_m1_p1(seed), __rand_from_seed_m1_p1(seed)));\n";
global_compute_code += "}\n\n";
@@ -1948,7 +1962,9 @@ void VisualShader::_update_shader() const {
const_cast<VisualShader *>(this)->set_code(final_code);
for (int i = 0; i < default_tex_params.size(); i++) {
- const_cast<VisualShader *>(this)->set_default_texture_param(default_tex_params[i].name, default_tex_params[i].param);
+ for (int j = 0; j < default_tex_params[i].params.size(); j++) {
+ const_cast<VisualShader *>(this)->set_default_texture_param(default_tex_params[i].name, default_tex_params[i].params[j], j);
+ }
}
if (previous_code != final_code) {
const_cast<VisualShader *>(this)->emit_signal(SNAME("changed"));
@@ -2017,8 +2033,8 @@ void VisualShader::_bind_methods() {
ClassDB::bind_method(D_METHOD("_update_shader"), &VisualShader::_update_shader);
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_graph_offset", "get_graph_offset");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "engine_version", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_engine_version", "get_engine_version");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_graph_offset", "get_graph_offset");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "engine_version", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_engine_version", "get_engine_version");
ADD_PROPERTY_DEFAULT("code", ""); // Inherited from Shader, prevents showing default code as override in docs.
@@ -2850,7 +2866,7 @@ void VisualShaderNodeUniformRef::_bind_methods() {
ClassDB::bind_method(D_METHOD("_get_uniform_type"), &VisualShaderNodeUniformRef::_get_uniform_type);
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "uniform_name", PROPERTY_HINT_ENUM, ""), "set_uniform_name", "get_uniform_name");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "uniform_type", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_uniform_type", "_get_uniform_type");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "uniform_type", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_uniform_type", "_get_uniform_type");
}
Vector<StringName> VisualShaderNodeUniformRef::get_editable_properties() const {
@@ -3436,7 +3452,7 @@ void VisualShaderNodeGroupBase::add_input_port(int p_id, int p_type, const Strin
count++;
}
- inputs.erase(index, count);
+ inputs = inputs.left(index) + inputs.substr(index + count);
inputs = inputs.insert(index, itos(i));
index += inputs_strings[i].size();
}
@@ -3459,7 +3475,7 @@ void VisualShaderNodeGroupBase::remove_input_port(int p_id) {
}
index += inputs_strings[i].size();
}
- inputs.erase(index, count);
+ inputs = inputs.left(index) + inputs.substr(index + count);
inputs_strings = inputs.split(";", false);
inputs = inputs.substr(0, index);
@@ -3512,7 +3528,7 @@ void VisualShaderNodeGroupBase::add_output_port(int p_id, int p_type, const Stri
count++;
}
- outputs.erase(index, count);
+ outputs = outputs.left(index) + outputs.substr(index + count);
outputs = outputs.insert(index, itos(i));
index += outputs_strings[i].size();
}
@@ -3535,7 +3551,7 @@ void VisualShaderNodeGroupBase::remove_output_port(int p_id) {
}
index += outputs_strings[i].size();
}
- outputs.erase(index, count);
+ outputs = outputs.left(index) + outputs.substr(index + count);
outputs_strings = outputs.split(";", false);
outputs = outputs.substr(0, index);
@@ -3587,8 +3603,7 @@ void VisualShaderNodeGroupBase::set_input_port_type(int p_id, int p_type) {
index += inputs_strings[i].size();
}
- inputs.erase(index, count);
-
+ inputs = inputs.left(index) + inputs.substr(index + count);
inputs = inputs.insert(index, itos(p_type));
_apply_port_changes();
@@ -3623,8 +3638,7 @@ void VisualShaderNodeGroupBase::set_input_port_name(int p_id, const String &p_na
index += inputs_strings[i].size();
}
- inputs.erase(index, count);
-
+ inputs = inputs.left(index) + inputs.substr(index + count);
inputs = inputs.insert(index, p_name);
_apply_port_changes();
@@ -3659,7 +3673,7 @@ void VisualShaderNodeGroupBase::set_output_port_type(int p_id, int p_type) {
index += output_strings[i].size();
}
- outputs.erase(index, count);
+ outputs = outputs.left(index) + outputs.substr(index + count);
outputs = outputs.insert(index, itos(p_type));
@@ -3695,7 +3709,7 @@ void VisualShaderNodeGroupBase::set_output_port_name(int p_id, const String &p_n
index += output_strings[i].size();
}
- outputs.erase(index, count);
+ outputs = outputs.left(index) + outputs.substr(index + count);
outputs = outputs.insert(index, p_name);
diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h
index 19530e5a34..5bb9d45f39 100644
--- a/scene/resources/visual_shader.h
+++ b/scene/resources/visual_shader.h
@@ -70,7 +70,7 @@ public:
struct DefaultTextureParam {
StringName name;
- Ref<Texture2D> param;
+ List<Ref<Texture2D>> params;
};
private:
@@ -272,6 +272,7 @@ public:
void set_disabled(bool p_disabled = true);
virtual Vector<StringName> get_editable_properties() const;
+ virtual Map<StringName, String> get_editable_properties_names() const;
virtual Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const;
virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const;
@@ -327,8 +328,8 @@ protected:
GDVIRTUAL0RC(int, _get_output_port_count)
GDVIRTUAL1RC(int, _get_output_port_type, int)
GDVIRTUAL1RC(String, _get_output_port_name, int)
- GDVIRTUAL4RC(String, _get_code, Vector<String>, TypedArray<String>, int, int)
- GDVIRTUAL1RC(String, _get_global_code, int)
+ GDVIRTUAL4RC(String, _get_code, Vector<String>, TypedArray<String>, Shader::Mode, VisualShader::Type)
+ GDVIRTUAL1RC(String, _get_global_code, Shader::Mode)
GDVIRTUAL0RC(bool, _is_highend)
protected:
@@ -696,4 +697,6 @@ public:
VisualShaderNodeGlobalExpression();
};
+extern String make_unique_id(VisualShader::Type p_type, int p_id, const String &p_name);
+
#endif // VISUAL_SHADER_H
diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp
index c3d9ef7b04..951870fe34 100644
--- a/scene/resources/visual_shader_nodes.cpp
+++ b/scene/resources/visual_shader_nodes.cpp
@@ -494,15 +494,10 @@ String VisualShaderNodeTexture::get_input_port_default_hint(int p_port) const {
return "";
}
-static String make_unique_id(VisualShader::Type p_type, int p_id, const String &p_name) {
- static const char *typepf[VisualShader::TYPE_MAX] = { "vtx", "frg", "lgt" };
- return p_name + "_" + String(typepf[p_type]) + "_" + itos(p_id);
-}
-
Vector<VisualShader::DefaultTextureParam> VisualShaderNodeTexture::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const {
VisualShader::DefaultTextureParam dtp;
dtp.name = make_unique_id(p_type, p_id, "tex");
- dtp.param = texture;
+ dtp.params.push_back(texture);
Vector<VisualShader::DefaultTextureParam> ret;
ret.push_back(dtp);
return ret;
@@ -900,7 +895,7 @@ String VisualShaderNodeCurveTexture::generate_code(Shader::Mode p_mode, VisualSh
Vector<VisualShader::DefaultTextureParam> VisualShaderNodeCurveTexture::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const {
VisualShader::DefaultTextureParam dtp;
dtp.name = make_unique_id(p_type, p_id, "curve");
- dtp.param = texture;
+ dtp.params.push_back(texture);
Vector<VisualShader::DefaultTextureParam> ret;
ret.push_back(dtp);
return ret;
@@ -985,7 +980,7 @@ String VisualShaderNodeCurveXYZTexture::generate_code(Shader::Mode p_mode, Visua
Vector<VisualShader::DefaultTextureParam> VisualShaderNodeCurveXYZTexture::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const {
VisualShader::DefaultTextureParam dtp;
dtp.name = make_unique_id(p_type, p_id, "curve3d");
- dtp.param = texture;
+ dtp.params.push_back(texture);
Vector<VisualShader::DefaultTextureParam> ret;
ret.push_back(dtp);
return ret;
@@ -1167,7 +1162,7 @@ String VisualShaderNodeTexture2DArray::get_input_port_name(int p_port) const {
Vector<VisualShader::DefaultTextureParam> VisualShaderNodeTexture2DArray::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const {
VisualShader::DefaultTextureParam dtp;
dtp.name = make_unique_id(p_type, p_id, "tex3d");
- dtp.param = texture_array;
+ dtp.params.push_back(texture_array);
Vector<VisualShader::DefaultTextureParam> ret;
ret.push_back(dtp);
return ret;
@@ -1224,7 +1219,7 @@ String VisualShaderNodeTexture3D::get_input_port_name(int p_port) const {
Vector<VisualShader::DefaultTextureParam> VisualShaderNodeTexture3D::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const {
VisualShader::DefaultTextureParam dtp;
dtp.name = make_unique_id(p_type, p_id, "tex3d");
- dtp.param = texture;
+ dtp.params.push_back(texture);
Vector<VisualShader::DefaultTextureParam> ret;
ret.push_back(dtp);
return ret;
@@ -1323,7 +1318,7 @@ bool VisualShaderNodeCubemap::is_output_port_expandable(int p_port) const {
Vector<VisualShader::DefaultTextureParam> VisualShaderNodeCubemap::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const {
VisualShader::DefaultTextureParam dtp;
dtp.name = make_unique_id(p_type, p_id, "cube");
- dtp.param = cube_map;
+ dtp.params.push_back(cube_map);
Vector<VisualShader::DefaultTextureParam> ret;
ret.push_back(dtp);
return ret;
diff --git a/scene/resources/visual_shader_particle_nodes.cpp b/scene/resources/visual_shader_particle_nodes.cpp
index 18b933e5cf..7dd4eed15b 100644
--- a/scene/resources/visual_shader_particle_nodes.cpp
+++ b/scene/resources/visual_shader_particle_nodes.cpp
@@ -30,6 +30,8 @@
#include "visual_shader_particle_nodes.h"
+#include "core/core_string_names.h"
+
// VisualShaderNodeParticleEmitter
int VisualShaderNodeParticleEmitter::get_output_port_count() const {
@@ -51,6 +53,38 @@ bool VisualShaderNodeParticleEmitter::has_output_port_preview(int p_port) const
return false;
}
+void VisualShaderNodeParticleEmitter::set_mode_2d(bool p_enabled) {
+ mode_2d = p_enabled;
+ emit_changed();
+}
+
+bool VisualShaderNodeParticleEmitter::is_mode_2d() const {
+ return mode_2d;
+}
+
+Vector<StringName> VisualShaderNodeParticleEmitter::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("mode_2d");
+ return props;
+}
+
+Map<StringName, String> VisualShaderNodeParticleEmitter::get_editable_properties_names() const {
+ Map<StringName, String> names;
+ names.insert("mode_2d", TTR("2D Mode"));
+ return names;
+}
+
+bool VisualShaderNodeParticleEmitter::is_show_prop_names() const {
+ return true;
+}
+
+void VisualShaderNodeParticleEmitter::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_mode_2d", "enabled"), &VisualShaderNodeParticleEmitter::set_mode_2d);
+ ClassDB::bind_method(D_METHOD("is_mode_2d"), &VisualShaderNodeParticleEmitter::is_mode_2d);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "mode_2d"), "set_mode_2d", "is_mode_2d");
+}
+
VisualShaderNodeParticleEmitter::VisualShaderNodeParticleEmitter() {
}
@@ -79,15 +113,27 @@ String VisualShaderNodeParticleSphereEmitter::get_input_port_name(int p_port) co
String VisualShaderNodeParticleSphereEmitter::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
String code;
+
+ code += "vec2 __get_random_point_in_circle(inout uint seed, float radius, float inner_radius) {\n";
+ code += " return __get_random_unit_vec2(seed) * __randf_range(seed, inner_radius, radius);\n";
+ code += "}\n\n";
+
code += "vec3 __get_random_point_in_sphere(inout uint seed, float radius, float inner_radius) {\n";
code += " return __get_random_unit_vec3(seed) * __randf_range(seed, inner_radius, radius);\n";
code += "}\n\n";
+
return code;
}
String VisualShaderNodeParticleSphereEmitter::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 += " " + p_output_vars[0] + " = __get_random_point_in_sphere(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ", " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ");\n";
+
+ if (mode_2d) {
+ code += " " + p_output_vars[0] + " = vec3(__get_random_point_in_circle(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ", " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + "), 0.0);\n";
+ } else {
+ code += " " + p_output_vars[0] + " = __get_random_point_in_sphere(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ", " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ");\n";
+ }
+
return code;
}
@@ -122,16 +168,27 @@ String VisualShaderNodeParticleBoxEmitter::get_input_port_name(int p_port) const
String VisualShaderNodeParticleBoxEmitter::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
String code;
- code += "vec3 __get_random_point_in_box(inout uint seed, vec3 extents) {\n";
+
+ code += "vec2 __get_random_point_in_box2d(inout uint seed, vec2 extents) {\n";
+ code += " vec2 half_extents = extents / 2.0;\n";
+ code += " return vec2(__randf_range(seed, -half_extents.x, half_extents.x), __randf_range(seed, -half_extents.y, half_extents.y));\n";
+ code += "}\n\n";
+
+ code += "vec3 __get_random_point_in_box3d(inout uint seed, vec3 extents) {\n";
code += " vec3 half_extents = extents / 2.0;\n";
code += " return vec3(__randf_range(seed, -half_extents.x, half_extents.x), __randf_range(seed, -half_extents.y, half_extents.y), __randf_range(seed, -half_extents.z, half_extents.z));\n";
code += "}\n\n";
+
return code;
}
String VisualShaderNodeParticleBoxEmitter::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 += " " + p_output_vars[0] + " = __get_random_point_in_box(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ");\n";
+ if (mode_2d) {
+ code += " " + p_output_vars[0] + " = vec3(__get_random_point_in_box2d(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ".xy), 0.0);\n";
+ } else {
+ code += " " + p_output_vars[0] + " = __get_random_point_in_box3d(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ");\n";
+ }
return code;
}
@@ -166,17 +223,31 @@ String VisualShaderNodeParticleRingEmitter::get_input_port_name(int p_port) cons
String VisualShaderNodeParticleRingEmitter::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
String code;
- code += "vec3 __get_random_point_on_ring(inout uint seed, float radius, float inner_radius, float height) {\n";
- code += " float angle = __rand_from_seed(seed) * PI * 2.0;\n";
+
+ code += "vec2 __get_random_point_on_ring2d(inout uint seed, float radius, float inner_radius) {\n";
+ code += " float angle = __rand_from_seed(seed) * TAU;\n";
+ code += " vec2 ring = vec2(sin(angle), cos(angle)) * __randf_range(seed, inner_radius, radius);\n";
+ code += " return vec2(ring.x, ring.y);\n";
+ code += "}\n\n";
+
+ code += "vec3 __get_random_point_on_ring3d(inout uint seed, float radius, float inner_radius, float height) {\n";
+ code += " float angle = __rand_from_seed(seed) * TAU;\n";
code += " vec2 ring = vec2(sin(angle), cos(angle)) * __randf_range(seed, inner_radius, radius);\n";
code += " return vec3(ring.x, __randf_range(seed, min(0.0, height), max(0.0, height)), ring.y);\n";
code += "}\n\n";
+
return code;
}
String VisualShaderNodeParticleRingEmitter::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 = " " + p_output_vars[0] + " = __get_random_point_on_ring(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ", " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ", " + (p_input_vars[2].is_empty() ? (String)get_input_port_default_value(2) : p_input_vars[2]) + ");\n";
+
+ if (mode_2d) {
+ code = " " + p_output_vars[0] + " = vec3(__get_random_point_on_ring2d(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ", " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + "), 0.0);\n";
+ } else {
+ code = " " + p_output_vars[0] + " = __get_random_point_on_ring3d(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ", " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ", " + (p_input_vars[2].is_empty() ? (String)get_input_port_default_value(2) : p_input_vars[2]) + ");\n";
+ }
+
return code;
}
@@ -186,6 +257,283 @@ VisualShaderNodeParticleRingEmitter::VisualShaderNodeParticleRingEmitter() {
set_input_port_default_value(2, 0.0);
}
+// VisualShaderNodeParticleMeshEmitter
+
+String VisualShaderNodeParticleMeshEmitter::get_caption() const {
+ return "MeshEmitter";
+}
+
+int VisualShaderNodeParticleMeshEmitter::get_output_port_count() const {
+ return 2;
+}
+
+VisualShaderNodeParticleBoxEmitter::PortType VisualShaderNodeParticleMeshEmitter::get_output_port_type(int p_port) const {
+ switch (p_port) {
+ case 0:
+ return PORT_TYPE_VECTOR; // position
+ case 1:
+ return PORT_TYPE_VECTOR; // normal
+ }
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeParticleMeshEmitter::get_output_port_name(int p_port) const {
+ switch (p_port) {
+ case 0:
+ return "position";
+ case 1:
+ return "normal";
+ }
+ return String();
+}
+
+int VisualShaderNodeParticleMeshEmitter::get_input_port_count() const {
+ return 0;
+}
+
+VisualShaderNodeParticleBoxEmitter::PortType VisualShaderNodeParticleMeshEmitter::get_input_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeParticleMeshEmitter::get_input_port_name(int p_port) const {
+ return String();
+}
+
+String VisualShaderNodeParticleMeshEmitter::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+ String code;
+
+ if (mesh.is_valid()) {
+ code += "uniform sampler2D " + make_unique_id(p_type, p_id, "mesh_vx") + ";\n";
+ code += "uniform sampler2D " + make_unique_id(p_type, p_id, "mesh_nm") + ";\n";
+ }
+
+ return code;
+}
+
+String VisualShaderNodeParticleMeshEmitter::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 += " __scalar_ibuff = int(__rand_from_seed(__seed) * 65535.0) % " + itos(position_texture->get_width()) + ";\n";
+
+ if (position_texture->get_width() == 0) {
+ code += " " + p_output_vars[0] + " = vec3(0.0);\n";
+ } else {
+ if (mode_2d) {
+ code += " " + p_output_vars[0] + " = vec3(";
+ code += "texelFetch(";
+ code += make_unique_id(p_type, p_id, "mesh_vx") + ", ";
+ code += "ivec2(__scalar_ibuff, 0), 0).xy, 0.0);\n";
+ } else {
+ code += " " + p_output_vars[0] + " = texelFetch(";
+ code += make_unique_id(p_type, p_id, "mesh_vx") + ", ";
+ code += "ivec2(__scalar_ibuff, 0), 0).xyz;\n";
+ }
+ }
+
+ if (normal_texture->get_width() == 0) {
+ code += " " + p_output_vars[1] + " = vec3(0.0);\n";
+ } else {
+ if (mode_2d) {
+ code += " " + p_output_vars[1] + " = vec3(";
+ code += "texelFetch(";
+ code += make_unique_id(p_type, p_id, "mesh_nm") + ", ";
+ code += "ivec2(__scalar_ibuff, 0), 0).xy, 0.0);\n";
+ } else {
+ code += " " + p_output_vars[1] + " = texelFetch(";
+ code += make_unique_id(p_type, p_id, "mesh_nm") + ", ";
+ code += "ivec2(__scalar_ibuff, 0), 0).xyz;\n";
+ }
+ }
+
+ return code;
+}
+
+Vector<VisualShader::DefaultTextureParam> VisualShaderNodeParticleMeshEmitter::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const {
+ VisualShader::DefaultTextureParam dtp_vx;
+ dtp_vx.name = make_unique_id(p_type, p_id, "mesh_vx");
+ dtp_vx.params.push_back(position_texture);
+
+ VisualShader::DefaultTextureParam dtp_nm;
+ dtp_nm.name = make_unique_id(p_type, p_id, "mesh_nm");
+ dtp_nm.params.push_back(normal_texture);
+
+ Vector<VisualShader::DefaultTextureParam> ret;
+ ret.push_back(dtp_vx);
+ ret.push_back(dtp_nm);
+ return ret;
+}
+
+void VisualShaderNodeParticleMeshEmitter::update_texture() {
+ if (!mesh.is_valid()) {
+ return;
+ }
+
+ Vector<Vector3> vertices;
+ Vector<Vector3> normals;
+
+ if (use_all_surfaces) {
+ for (int i = 0; i < max_surface_index; i++) {
+ Array vertex_array = mesh->surface_get_arrays(i)[Mesh::ARRAY_VERTEX];
+ for (int j = 0; j < vertex_array.size(); j++) {
+ vertices.push_back((Vector3)vertex_array[j]);
+ }
+
+ Array normal_array = mesh->surface_get_arrays(i)[Mesh::ARRAY_NORMAL];
+ for (int j = 0; j < vertex_array.size(); j++) {
+ normals.push_back((Vector3)vertex_array[j]);
+ }
+ }
+ } else {
+ Array vertex_array = mesh->surface_get_arrays(surface_index)[Mesh::ARRAY_VERTEX];
+ for (int i = 0; i < vertex_array.size(); i++) {
+ vertices.push_back((Vector3)vertex_array[i]);
+ }
+
+ Array normal_array = mesh->surface_get_arrays(surface_index)[Mesh::ARRAY_NORMAL];
+ for (int i = 0; i < normal_array.size(); i++) {
+ normals.push_back((Vector3)normal_array[i]);
+ }
+ }
+
+ // vertices
+ {
+ Ref<Image> image;
+ image.instantiate();
+ image->create(vertices.size(), 1, false, Image::Format::FORMAT_RGBF);
+
+ for (int i = 0; i < vertices.size(); i++) {
+ Vector3 v = vertices[i];
+ image->set_pixel(i, 0, Color(v.x, v.y, v.z));
+ }
+ if (position_texture->get_width() != vertices.size()) {
+ position_texture->create_from_image(image);
+ } else {
+ position_texture->update(image);
+ }
+ }
+
+ // normals
+ {
+ Ref<Image> image;
+ image.instantiate();
+ image->create(normals.size(), 1, false, Image::Format::FORMAT_RGBF);
+
+ for (int i = 0; i < normals.size(); i++) {
+ Vector3 v = normals[i];
+ image->set_pixel(i, 0, Color(v.x, v.y, v.z));
+ }
+ if (normal_texture->get_width() != normals.size()) {
+ normal_texture->create_from_image(image);
+ } else {
+ normal_texture->update(image);
+ }
+ }
+}
+
+void VisualShaderNodeParticleMeshEmitter::set_mesh(Ref<Mesh> p_mesh) {
+ if (mesh == p_mesh) {
+ return;
+ }
+
+ if (p_mesh.is_valid()) {
+ max_surface_index = p_mesh->get_surface_count();
+ } else {
+ max_surface_index = 0;
+ }
+
+ if (mesh.is_valid()) {
+ Callable callable = callable_mp(this, &VisualShaderNodeParticleMeshEmitter::update_texture);
+
+ if (mesh->is_connected(CoreStringNames::get_singleton()->changed, callable)) {
+ mesh->disconnect(CoreStringNames::get_singleton()->changed, callable);
+ }
+ }
+
+ mesh = p_mesh;
+
+ if (mesh.is_valid()) {
+ Callable callable = callable_mp(this, &VisualShaderNodeParticleMeshEmitter::update_texture);
+
+ if (!mesh->is_connected(CoreStringNames::get_singleton()->changed, callable)) {
+ mesh->connect(CoreStringNames::get_singleton()->changed, callable);
+ }
+ }
+
+ emit_changed();
+}
+
+Ref<Mesh> VisualShaderNodeParticleMeshEmitter::get_mesh() const {
+ return mesh;
+}
+
+void VisualShaderNodeParticleMeshEmitter::set_use_all_surfaces(bool p_enabled) {
+ if (use_all_surfaces == p_enabled) {
+ return;
+ }
+ use_all_surfaces = p_enabled;
+ emit_changed();
+}
+
+bool VisualShaderNodeParticleMeshEmitter::is_use_all_surfaces() const {
+ return use_all_surfaces;
+}
+
+void VisualShaderNodeParticleMeshEmitter::set_surface_index(int p_surface_index) {
+ if (p_surface_index == surface_index || p_surface_index < 0 || p_surface_index >= max_surface_index) {
+ return;
+ }
+ surface_index = p_surface_index;
+ emit_changed();
+}
+
+int VisualShaderNodeParticleMeshEmitter::get_surface_index() const {
+ return surface_index;
+}
+
+Vector<StringName> VisualShaderNodeParticleMeshEmitter::get_editable_properties() const {
+ Vector<StringName> props = VisualShaderNodeParticleEmitter::get_editable_properties();
+
+ props.push_back("mesh");
+ props.push_back("use_all_surfaces");
+ if (!use_all_surfaces) {
+ props.push_back("surface_index");
+ }
+
+ return props;
+}
+
+Map<StringName, String> VisualShaderNodeParticleMeshEmitter::get_editable_properties_names() const {
+ Map<StringName, String> names = VisualShaderNodeParticleEmitter::get_editable_properties_names();
+
+ names.insert("mesh", TTR("Mesh"));
+ names.insert("use_all_surfaces", TTR("Use All Surfaces"));
+ if (!use_all_surfaces) {
+ names.insert("surface_index", TTR("Surface Index"));
+ }
+
+ return names;
+}
+
+void VisualShaderNodeParticleMeshEmitter::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_mesh", "mesh"), &VisualShaderNodeParticleMeshEmitter::set_mesh);
+ ClassDB::bind_method(D_METHOD("get_mesh"), &VisualShaderNodeParticleMeshEmitter::get_mesh);
+ ClassDB::bind_method(D_METHOD("set_use_all_surfaces", "enabled"), &VisualShaderNodeParticleMeshEmitter::set_use_all_surfaces);
+ ClassDB::bind_method(D_METHOD("is_use_all_surfaces"), &VisualShaderNodeParticleMeshEmitter::is_use_all_surfaces);
+ ClassDB::bind_method(D_METHOD("set_surface_index", "surface_index"), &VisualShaderNodeParticleMeshEmitter::set_surface_index);
+ ClassDB::bind_method(D_METHOD("get_surface_index"), &VisualShaderNodeParticleMeshEmitter::get_surface_index);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_all_surfaces"), "set_use_all_surfaces", "is_use_all_surfaces");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "surface_index"), "set_surface_index", "get_surface_index");
+}
+
+VisualShaderNodeParticleMeshEmitter::VisualShaderNodeParticleMeshEmitter() {
+ connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &VisualShaderNodeParticleMeshEmitter::update_texture));
+
+ position_texture.instantiate();
+ normal_texture.instantiate();
+}
+
// VisualShaderNodeParticleMultiplyByAxisAngle
void VisualShaderNodeParticleMultiplyByAxisAngle::_bind_methods() {
@@ -265,7 +613,6 @@ bool VisualShaderNodeParticleMultiplyByAxisAngle::is_degrees_mode() const {
Vector<StringName> VisualShaderNodeParticleMultiplyByAxisAngle::get_editable_properties() const {
Vector<StringName> props;
props.push_back("degrees_mode");
- props.push_back("axis_amount");
return props;
}
diff --git a/scene/resources/visual_shader_particle_nodes.h b/scene/resources/visual_shader_particle_nodes.h
index b8bc7992cc..0d0f21e4bf 100644
--- a/scene/resources/visual_shader_particle_nodes.h
+++ b/scene/resources/visual_shader_particle_nodes.h
@@ -38,12 +38,23 @@
class VisualShaderNodeParticleEmitter : public VisualShaderNode {
GDCLASS(VisualShaderNodeParticleEmitter, VisualShaderNode);
+protected:
+ bool mode_2d = false;
+ static void _bind_methods();
+
public:
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
virtual bool has_output_port_preview(int p_port) const override;
+ void set_mode_2d(bool p_enabled);
+ bool is_mode_2d() const;
+
+ Vector<StringName> get_editable_properties() const override;
+ virtual Map<StringName, String> get_editable_properties_names() const override;
+ bool is_show_prop_names() const override;
+
VisualShaderNodeParticleEmitter();
};
@@ -95,6 +106,51 @@ public:
VisualShaderNodeParticleRingEmitter();
};
+class VisualShaderNodeParticleMeshEmitter : public VisualShaderNodeParticleEmitter {
+ GDCLASS(VisualShaderNodeParticleMeshEmitter, VisualShaderNodeParticleEmitter);
+ Ref<Mesh> mesh;
+ bool use_all_surfaces = true;
+ int surface_index = 0;
+ int max_surface_index = 0;
+
+ Ref<ImageTexture> position_texture;
+ Ref<ImageTexture> normal_texture;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const override;
+
+ virtual int get_output_port_count() const override;
+ virtual PortType get_output_port_type(int p_port) const override;
+ virtual String get_output_port_name(int p_port) const override;
+
+ virtual int get_input_port_count() const override;
+ virtual PortType get_input_port_type(int p_port) const override;
+ virtual String get_input_port_name(int p_port) const override;
+
+ virtual String generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) 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;
+
+ void update_texture();
+
+ void set_mesh(Ref<Mesh> p_mesh);
+ Ref<Mesh> get_mesh() const;
+
+ void set_use_all_surfaces(bool p_enabled);
+ bool is_use_all_surfaces() const;
+
+ void set_surface_index(int p_surface_index);
+ int get_surface_index() const;
+
+ Vector<StringName> get_editable_properties() const override;
+ Map<StringName, String> get_editable_properties_names() const override;
+ Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const override;
+
+ VisualShaderNodeParticleMeshEmitter();
+};
+
class VisualShaderNodeParticleMultiplyByAxisAngle : public VisualShaderNode {
GDCLASS(VisualShaderNodeParticleMultiplyByAxisAngle, VisualShaderNode);
bool degrees_mode = true;
diff --git a/scene/resources/world_3d.cpp b/scene/resources/world_3d.cpp
index 42047f104f..0e1b343eac 100644
--- a/scene/resources/world_3d.cpp
+++ b/scene/resources/world_3d.cpp
@@ -144,9 +144,9 @@ World3D::World3D() {
PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_GRAVITY, GLOBAL_DEF("physics/3d/default_gravity", 9.8));
PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_GRAVITY_VECTOR, GLOBAL_DEF("physics/3d/default_gravity_vector", Vector3(0, -1, 0)));
PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_LINEAR_DAMP, GLOBAL_DEF("physics/3d/default_linear_damp", 0.1));
- ProjectSettings::get_singleton()->set_custom_property_info("physics/3d/default_linear_damp", PropertyInfo(Variant::FLOAT, "physics/3d/default_linear_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"));
+ ProjectSettings::get_singleton()->set_custom_property_info("physics/3d/default_linear_damp", PropertyInfo(Variant::FLOAT, "physics/3d/default_linear_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"));
PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_ANGULAR_DAMP, GLOBAL_DEF("physics/3d/default_angular_damp", 0.1));
- ProjectSettings::get_singleton()->set_custom_property_info("physics/3d/default_angular_damp", PropertyInfo(Variant::FLOAT, "physics/3d/default_angular_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"));
+ ProjectSettings::get_singleton()->set_custom_property_info("physics/3d/default_angular_damp", PropertyInfo(Variant::FLOAT, "physics/3d/default_angular_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"));
navigation_map = NavigationServer3D::get_singleton()->map_create();
NavigationServer3D::get_singleton()->map_set_active(navigation_map, true);