summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/animated_sprite.cpp3
-rw-r--r--scene/2d/audio_stream_player_2d.cpp35
-rw-r--r--scene/2d/audio_stream_player_2d.h5
-rw-r--r--scene/2d/navigation_2d.cpp12
-rw-r--r--scene/2d/physics_body_2d.cpp3
-rw-r--r--scene/3d/audio_stream_player_3d.cpp41
-rw-r--r--scene/3d/audio_stream_player_3d.h6
-rw-r--r--scene/3d/camera.cpp46
-rw-r--r--scene/3d/camera.h8
-rw-r--r--scene/3d/collision_shape.cpp28
-rw-r--r--scene/3d/collision_shape.h4
-rw-r--r--scene/3d/cpu_particles.cpp1
-rw-r--r--scene/3d/path.cpp8
-rw-r--r--scene/3d/physics_body.cpp3
-rw-r--r--scene/3d/vehicle_body.cpp2
-rw-r--r--scene/3d/visual_instance.cpp8
-rw-r--r--scene/3d/visual_instance.h2
-rw-r--r--scene/3d/voxel_light_baker.cpp6
-rw-r--r--scene/audio/audio_stream_player.cpp138
-rw-r--r--scene/audio/audio_stream_player.h8
-rw-r--r--scene/gui/base_button.cpp2
-rw-r--r--scene/gui/color_picker.cpp4
-rw-r--r--scene/gui/graph_edit.cpp2
-rw-r--r--scene/gui/line_edit.cpp10
-rw-r--r--scene/gui/menu_button.cpp8
-rw-r--r--scene/gui/option_button.cpp2
-rw-r--r--scene/gui/popup_menu.cpp2
-rw-r--r--scene/gui/rich_text_label.cpp17
-rw-r--r--scene/gui/text_edit.cpp138
-rw-r--r--scene/gui/text_edit.h21
-rw-r--r--scene/gui/tree.cpp1
-rw-r--r--scene/main/http_request.cpp2
-rw-r--r--scene/main/node.cpp9
-rw-r--r--scene/main/node.h2
-rw-r--r--scene/main/scene_tree.cpp2
-rw-r--r--scene/main/viewport.cpp4
-rw-r--r--scene/register_scene_types.cpp4
-rw-r--r--scene/resources/animation.cpp38
-rw-r--r--scene/resources/box_shape.cpp1
-rw-r--r--scene/resources/capsule_shape.cpp1
-rw-r--r--scene/resources/concave_polygon_shape.cpp1
-rw-r--r--scene/resources/convex_polygon_shape.cpp2
-rw-r--r--scene/resources/curve.cpp2
-rw-r--r--scene/resources/cylinder_shape.cpp1
-rw-r--r--scene/resources/default_theme/default_theme.cpp4
-rw-r--r--scene/resources/multimesh.cpp14
-rw-r--r--scene/resources/multimesh.h4
-rw-r--r--scene/resources/packed_scene.cpp2
-rw-r--r--scene/resources/plane_shape.cpp1
-rw-r--r--scene/resources/ray_shape.cpp2
-rw-r--r--scene/resources/shape.cpp5
-rw-r--r--scene/resources/shape.h2
-rw-r--r--scene/resources/sphere_shape.cpp1
-rw-r--r--scene/resources/visual_shader.cpp3
54 files changed, 492 insertions, 189 deletions
diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp
index 932db8f001..25ad6bd5c9 100644
--- a/scene/2d/animated_sprite.cpp
+++ b/scene/2d/animated_sprite.cpp
@@ -645,6 +645,9 @@ void AnimatedSprite::_reset_timeout() {
void AnimatedSprite::set_animation(const StringName &p_animation) {
+ ERR_EXPLAIN(vformat("There is no animation with name '%s'.", p_animation));
+ ERR_FAIL_COND(frames->get_animation_names().find(p_animation) == -1);
+
if (animation == p_animation)
return;
diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp
index faefd85968..73f583111b 100644
--- a/scene/2d/audio_stream_player_2d.cpp
+++ b/scene/2d/audio_stream_player_2d.cpp
@@ -37,7 +37,7 @@
void AudioStreamPlayer2D::_mix_audio() {
if (!stream_playback.is_valid() || !active ||
- (stream_paused && !stream_fade_out)) {
+ (stream_paused && !stream_paused_fade_out)) {
return;
}
@@ -50,7 +50,7 @@ void AudioStreamPlayer2D::_mix_audio() {
AudioFrame *buffer = mix_buffer.ptrw();
int buffer_size = mix_buffer.size();
- if (stream_fade_out) {
+ if (stream_paused_fade_out) {
// Short fadeout ramp
buffer_size = MIN(buffer_size, 128);
}
@@ -84,10 +84,10 @@ void AudioStreamPlayer2D::_mix_audio() {
}
//mix!
- AudioFrame target_volume = stream_fade_out ? AudioFrame(0.f, 0.f) : current.vol;
- AudioFrame vol_prev = stream_fade_in ? AudioFrame(0.f, 0.f) : prev_outputs[i].vol;
+ AudioFrame target_volume = stream_paused_fade_out ? AudioFrame(0.f, 0.f) : current.vol;
+ AudioFrame vol_prev = stream_paused_fade_in ? AudioFrame(0.f, 0.f) : prev_outputs[i].vol;
AudioFrame vol_inc = (target_volume - vol_prev) / float(buffer_size);
- AudioFrame vol = stream_fade_in ? AudioFrame(0.f, 0.f) : current.vol;
+ AudioFrame vol = stream_paused_fade_in ? AudioFrame(0.f, 0.f) : current.vol;
int cc = AudioServer::get_singleton()->get_channel_count();
@@ -139,15 +139,9 @@ void AudioStreamPlayer2D::_mix_audio() {
active = false;
}
- if (stream_stop) {
- active = false;
- set_physics_process_internal(false);
- setplay = -1;
- }
-
output_ready = false;
- stream_fade_in = false;
- stream_fade_out = false;
+ stream_paused_fade_in = false;
+ stream_paused_fade_out = false;
}
void AudioStreamPlayer2D::_notification(int p_what) {
@@ -329,7 +323,6 @@ void AudioStreamPlayer2D::play(float p_from_pos) {
}
if (stream_playback.is_valid()) {
- stream_stop = false;
active = true;
setplay = p_from_pos;
output_ready = false;
@@ -347,8 +340,9 @@ void AudioStreamPlayer2D::seek(float p_seconds) {
void AudioStreamPlayer2D::stop() {
if (stream_playback.is_valid()) {
- stream_stop = true;
- stream_fade_out = true;
+ active = false;
+ set_physics_process_internal(false);
+ setplay = -1;
}
}
@@ -463,8 +457,8 @@ void AudioStreamPlayer2D::set_stream_paused(bool p_pause) {
if (p_pause != stream_paused) {
stream_paused = p_pause;
- stream_fade_in = p_pause ? false : true;
- stream_fade_out = p_pause ? true : false;
+ stream_paused_fade_in = p_pause ? false : true;
+ stream_paused_fade_out = p_pause ? true : false;
}
}
@@ -549,9 +543,8 @@ AudioStreamPlayer2D::AudioStreamPlayer2D() {
output_ready = false;
area_mask = 1;
stream_paused = false;
- stream_fade_in = false;
- stream_fade_out = false;
- stream_stop = false;
+ stream_paused_fade_in = false;
+ stream_paused_fade_out = false;
AudioServer::get_singleton()->connect("bus_layout_changed", this, "_bus_layout_changed");
}
diff --git a/scene/2d/audio_stream_player_2d.h b/scene/2d/audio_stream_player_2d.h
index 0cd18fb93b..e9cdfa2303 100644
--- a/scene/2d/audio_stream_player_2d.h
+++ b/scene/2d/audio_stream_player_2d.h
@@ -73,9 +73,8 @@ private:
float pitch_scale;
bool autoplay;
bool stream_paused;
- bool stream_fade_in;
- bool stream_fade_out;
- bool stream_stop;
+ bool stream_paused_fade_in;
+ bool stream_paused_fade_out;
StringName bus;
void _mix_audio();
diff --git a/scene/2d/navigation_2d.cpp b/scene/2d/navigation_2d.cpp
index 57e0a5b118..72b5f2fb12 100644
--- a/scene/2d/navigation_2d.cpp
+++ b/scene/2d/navigation_2d.cpp
@@ -542,7 +542,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect
if (CLOCK_TANGENT(apex_point, portal_left, left) >= 0) {
//process
- if (portal_left.distance_squared_to(apex_point) < CMP_EPSILON || CLOCK_TANGENT(apex_point, left, portal_right) > 0) {
+ if (Math::is_zero_approx(portal_left.distance_squared_to(apex_point)) || CLOCK_TANGENT(apex_point, left, portal_right) > 0) {
left_poly = p;
portal_left = left;
} else {
@@ -552,7 +552,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect
left_poly = p;
portal_left = apex_point;
portal_right = apex_point;
- if (!path.size() || path[path.size() - 1].distance_to(apex_point) > CMP_EPSILON)
+ if (!path.size() || !Math::is_zero_approx(path[path.size() - 1].distance_to(apex_point)))
path.push_back(apex_point);
skip = true;
}
@@ -560,7 +560,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect
if (!skip && CLOCK_TANGENT(apex_point, portal_right, right) <= 0) {
//process
- if (portal_right.distance_squared_to(apex_point) < CMP_EPSILON || CLOCK_TANGENT(apex_point, right, portal_left) < 0) {
+ if (Math::is_zero_approx(portal_right.distance_squared_to(apex_point)) || CLOCK_TANGENT(apex_point, right, portal_left) < 0) {
right_poly = p;
portal_right = right;
} else {
@@ -570,7 +570,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect
right_poly = p;
portal_right = apex_point;
portal_left = apex_point;
- if (!path.size() || path[path.size() - 1].distance_to(apex_point) > CMP_EPSILON)
+ if (!path.size() || !Math::is_zero_approx(path[path.size() - 1].distance_to(apex_point)))
path.push_back(apex_point);
}
}
@@ -596,7 +596,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect
}
}
- if (!path.size() || path[path.size() - 1].distance_squared_to(begin_point) > CMP_EPSILON) {
+ if (!path.size() || !Math::is_zero_approx(path[path.size() - 1].distance_squared_to(begin_point))) {
path.push_back(begin_point); // Add the begin point
} else {
path.write[path.size() - 1] = begin_point; // Replace first midpoint by the exact begin point
@@ -604,7 +604,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect
path.invert();
- if (path.size() <= 1 || path[path.size() - 1].distance_squared_to(end_point) > CMP_EPSILON) {
+ if (path.size() <= 1 || !Math::is_zero_approx(path[path.size() - 1].distance_squared_to(end_point))) {
path.push_back(end_point); // Add the end point
} else {
path.write[path.size() - 1] = end_point; // Replace last midpoint by the exact end point
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index 690f1d4b4a..578c9aa5f9 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -1275,9 +1275,6 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const
if (collided) {
found_collision = true;
- }
-
- if (collided) {
colliders.push_back(collision);
motion = collision.remainder;
diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp
index 4d5ffc31b3..ff8c218575 100644
--- a/scene/3d/audio_stream_player_3d.cpp
+++ b/scene/3d/audio_stream_player_3d.cpp
@@ -38,7 +38,7 @@
void AudioStreamPlayer3D::_mix_audio() {
if (!stream_playback.is_valid() || !active ||
- (stream_paused && !stream_fade_out)) {
+ (stream_paused && !stream_paused_fade_out)) {
return;
}
@@ -53,7 +53,7 @@ void AudioStreamPlayer3D::_mix_audio() {
AudioFrame *buffer = mix_buffer.ptrw();
int buffer_size = mix_buffer.size();
- if (stream_fade_out) {
+ if (stream_paused_fade_out) {
// Short fadeout ramp
buffer_size = MIN(buffer_size, 128);
}
@@ -109,10 +109,10 @@ void AudioStreamPlayer3D::_mix_audio() {
int buffers = AudioServer::get_singleton()->get_channel_count();
for (int k = 0; k < buffers; k++) {
- AudioFrame target_volume = stream_fade_out ? AudioFrame(0.f, 0.f) : current.vol[k];
- AudioFrame vol_prev = stream_fade_in ? AudioFrame(0.f, 0.f) : prev_outputs[i].vol[k];
+ AudioFrame target_volume = stream_paused_fade_out ? AudioFrame(0.f, 0.f) : current.vol[k];
+ AudioFrame vol_prev = stream_paused_fade_in ? AudioFrame(0.f, 0.f) : prev_outputs[i].vol[k];
AudioFrame vol_inc = (target_volume - vol_prev) / float(buffer_size);
- AudioFrame vol = stream_fade_in ? AudioFrame(0.f, 0.f) : current.vol[k];
+ AudioFrame vol = stream_paused_fade_in ? AudioFrame(0.f, 0.f) : current.vol[k];
if (!AudioServer::get_singleton()->thread_has_channel_mix_buffer(current.bus_index, k))
continue; //may have been deleted, will be updated on process
@@ -198,15 +198,9 @@ void AudioStreamPlayer3D::_mix_audio() {
active = false;
}
- if (stream_stop) {
- active = false;
- set_physics_process_internal(false);
- setplay = -1;
- }
-
output_ready = false;
- stream_fade_in = false;
- stream_fade_out = false;
+ stream_paused_fade_in = false;
+ stream_paused_fade_out = false;
}
float AudioStreamPlayer3D::_get_attenuation_db(float p_distance) const {
@@ -224,6 +218,7 @@ float AudioStreamPlayer3D::_get_attenuation_db(float p_distance) const {
case ATTENUATION_LOGARITHMIC: {
att = -20 * Math::log(p_distance / unit_size + CMP_EPSILON);
} break;
+ case ATTENUATION_DISABLED: break;
default: {
ERR_PRINT("Unknown attenuation type");
break;
@@ -662,7 +657,6 @@ float AudioStreamPlayer3D::get_pitch_scale() const {
void AudioStreamPlayer3D::play(float p_from_pos) {
if (stream_playback.is_valid()) {
- stream_stop = false;
active = true;
setplay = p_from_pos;
output_ready = false;
@@ -680,8 +674,9 @@ void AudioStreamPlayer3D::seek(float p_seconds) {
void AudioStreamPlayer3D::stop() {
if (stream_playback.is_valid()) {
- stream_stop = true;
- stream_fade_out = true;
+ active = false;
+ set_physics_process_internal(false);
+ setplay = -1;
}
}
@@ -831,7 +826,7 @@ float AudioStreamPlayer3D::get_attenuation_filter_db() const {
}
void AudioStreamPlayer3D::set_attenuation_model(AttenuationModel p_model) {
- ERR_FAIL_INDEX(p_model, 3);
+ ERR_FAIL_INDEX((int)p_model, 4);
attenuation_model = p_model;
}
@@ -877,8 +872,8 @@ void AudioStreamPlayer3D::set_stream_paused(bool p_pause) {
if (p_pause != stream_paused) {
stream_paused = p_pause;
- stream_fade_in = stream_paused ? false : true;
- stream_fade_out = stream_paused ? true : false;
+ stream_paused_fade_in = stream_paused ? false : true;
+ stream_paused_fade_out = stream_paused ? true : false;
}
}
@@ -962,7 +957,7 @@ void AudioStreamPlayer3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("_bus_layout_changed"), &AudioStreamPlayer3D::_bus_layout_changed);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "attenuation_model", PROPERTY_HINT_ENUM, "Inverse,InverseSquare,Log"), "set_attenuation_model", "get_attenuation_model");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "attenuation_model", PROPERTY_HINT_ENUM, "Inverse,InverseSquare,Log,Disabled"), "set_attenuation_model", "get_attenuation_model");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "unit_db", PROPERTY_HINT_RANGE, "-80,80"), "set_unit_db", "get_unit_db");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "unit_size", PROPERTY_HINT_RANGE, "0.1,100,0.1"), "set_unit_size", "get_unit_size");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_db", PROPERTY_HINT_RANGE, "-24,6"), "set_max_db", "get_max_db");
@@ -987,6 +982,7 @@ void AudioStreamPlayer3D::_bind_methods() {
BIND_ENUM_CONSTANT(ATTENUATION_INVERSE_DISTANCE);
BIND_ENUM_CONSTANT(ATTENUATION_INVERSE_SQUARE_DISTANCE);
BIND_ENUM_CONSTANT(ATTENUATION_LOGARITHMIC);
+ BIND_ENUM_CONSTANT(ATTENUATION_DISABLED);
BIND_ENUM_CONSTANT(OUT_OF_RANGE_MIX);
BIND_ENUM_CONSTANT(OUT_OF_RANGE_PAUSE);
@@ -1022,9 +1018,8 @@ AudioStreamPlayer3D::AudioStreamPlayer3D() {
out_of_range_mode = OUT_OF_RANGE_MIX;
doppler_tracking = DOPPLER_TRACKING_DISABLED;
stream_paused = false;
- stream_fade_in = false;
- stream_fade_out = false;
- stream_stop = false;
+ stream_paused_fade_in = false;
+ stream_paused_fade_out = false;
velocity_tracker.instance();
AudioServer::get_singleton()->connect("bus_layout_changed", this, "_bus_layout_changed");
diff --git a/scene/3d/audio_stream_player_3d.h b/scene/3d/audio_stream_player_3d.h
index 1175547028..98bc74b2e4 100644
--- a/scene/3d/audio_stream_player_3d.h
+++ b/scene/3d/audio_stream_player_3d.h
@@ -46,6 +46,7 @@ public:
ATTENUATION_INVERSE_DISTANCE,
ATTENUATION_INVERSE_SQUARE_DISTANCE,
ATTENUATION_LOGARITHMIC,
+ ATTENUATION_DISABLED,
};
enum OutOfRangeMode {
@@ -109,9 +110,8 @@ private:
float pitch_scale;
bool autoplay;
bool stream_paused;
- bool stream_fade_in;
- bool stream_fade_out;
- bool stream_stop;
+ bool stream_paused_fade_in;
+ bool stream_paused_fade_out;
StringName bus;
void _mix_audio();
diff --git a/scene/3d/camera.cpp b/scene/3d/camera.cpp
index 368cebeeab..54d7681a3a 100644
--- a/scene/3d/camera.cpp
+++ b/scene/3d/camera.cpp
@@ -55,16 +55,23 @@ void Camera::_update_camera_mode() {
case PROJECTION_ORTHOGONAL: {
set_orthogonal(size, near, far);
} break;
+ case PROJECTION_FRUSTUM: {
+ set_frustum(size, frustum_offset, near, far);
+ } break;
}
}
void Camera::_validate_property(PropertyInfo &p_property) const {
if (p_property.name == "fov") {
- if (mode == PROJECTION_ORTHOGONAL) {
+ if (mode != PROJECTION_PERSPECTIVE) {
p_property.usage = PROPERTY_USAGE_NOEDITOR;
}
} else if (p_property.name == "size") {
- if (mode == PROJECTION_PERSPECTIVE) {
+ if (mode != PROJECTION_ORTHOGONAL && mode != PROJECTION_FRUSTUM) {
+ p_property.usage = PROPERTY_USAGE_NOEDITOR;
+ }
+ } else if (p_property.name == "frustum_offset") {
+ if (mode != PROJECTION_FRUSTUM) {
p_property.usage = PROPERTY_USAGE_NOEDITOR;
}
}
@@ -177,8 +184,24 @@ void Camera::set_orthogonal(float p_size, float p_z_near, float p_z_far) {
update_gizmo();
}
+void Camera::set_frustum(float p_size, Vector2 p_offset, float p_z_near, float p_z_far) {
+ if (!force_change && size == p_size && frustum_offset == p_offset && p_z_near == near && p_z_far == far && mode == PROJECTION_FRUSTUM)
+ return;
+
+ size = p_size;
+ frustum_offset = p_offset;
+
+ near = p_z_near;
+ far = p_z_far;
+ mode = PROJECTION_FRUSTUM;
+ force_change = false;
+
+ VisualServer::get_singleton()->camera_set_frustum(camera, size, frustum_offset, near, far);
+ update_gizmo();
+}
+
void Camera::set_projection(Camera::Projection p_mode) {
- if (p_mode == PROJECTION_PERSPECTIVE || p_mode == PROJECTION_ORTHOGONAL) {
+ if (p_mode == PROJECTION_PERSPECTIVE || p_mode == PROJECTION_ORTHOGONAL || p_mode == PROJECTION_FRUSTUM) {
mode = p_mode;
_update_camera_mode();
_change_notify();
@@ -470,16 +493,19 @@ void Camera::_bind_methods() {
ClassDB::bind_method(D_METHOD("project_position", "screen_point"), &Camera::project_position);
ClassDB::bind_method(D_METHOD("set_perspective", "fov", "z_near", "z_far"), &Camera::set_perspective);
ClassDB::bind_method(D_METHOD("set_orthogonal", "size", "z_near", "z_far"), &Camera::set_orthogonal);
+ ClassDB::bind_method(D_METHOD("set_frustum", "size", "offset", "z_near", "z_far"), &Camera::set_frustum);
ClassDB::bind_method(D_METHOD("make_current"), &Camera::make_current);
ClassDB::bind_method(D_METHOD("clear_current", "enable_next"), &Camera::clear_current, DEFVAL(true));
ClassDB::bind_method(D_METHOD("set_current"), &Camera::set_current);
ClassDB::bind_method(D_METHOD("is_current"), &Camera::is_current);
ClassDB::bind_method(D_METHOD("get_camera_transform"), &Camera::get_camera_transform);
ClassDB::bind_method(D_METHOD("get_fov"), &Camera::get_fov);
+ ClassDB::bind_method(D_METHOD("get_frustum_offset"), &Camera::get_frustum_offset);
ClassDB::bind_method(D_METHOD("get_size"), &Camera::get_size);
ClassDB::bind_method(D_METHOD("get_zfar"), &Camera::get_zfar);
ClassDB::bind_method(D_METHOD("get_znear"), &Camera::get_znear);
ClassDB::bind_method(D_METHOD("set_fov"), &Camera::set_fov);
+ ClassDB::bind_method(D_METHOD("set_frustum_offset"), &Camera::set_frustum_offset);
ClassDB::bind_method(D_METHOD("set_size"), &Camera::set_size);
ClassDB::bind_method(D_METHOD("set_zfar"), &Camera::set_zfar);
ClassDB::bind_method(D_METHOD("set_znear"), &Camera::set_znear);
@@ -510,15 +536,17 @@ void Camera::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "h_offset"), "set_h_offset", "get_h_offset");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "v_offset"), "set_v_offset", "get_v_offset");
ADD_PROPERTY(PropertyInfo(Variant::INT, "doppler_tracking", PROPERTY_HINT_ENUM, "Disabled,Idle,Physics"), "set_doppler_tracking", "get_doppler_tracking");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "projection", PROPERTY_HINT_ENUM, "Perspective,Orthogonal"), "set_projection", "get_projection");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "projection", PROPERTY_HINT_ENUM, "Perspective,Orthogonal,Frustum"), "set_projection", "get_projection");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "current"), "set_current", "is_current");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "fov", PROPERTY_HINT_RANGE, "1,179,0.1"), "set_fov", "get_fov");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "size", PROPERTY_HINT_RANGE, "0.1,16384,0.01"), "set_size", "get_size");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "frustum_offset"), "set_frustum_offset", "get_frustum_offset");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "near", PROPERTY_HINT_EXP_RANGE, "0.01,8192,0.01,or_greater"), "set_znear", "get_znear");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "far", PROPERTY_HINT_EXP_RANGE, "0.1,8192,0.1,or_greater"), "set_zfar", "get_zfar");
BIND_ENUM_CONSTANT(PROJECTION_PERSPECTIVE);
BIND_ENUM_CONSTANT(PROJECTION_ORTHOGONAL);
+ BIND_ENUM_CONSTANT(PROJECTION_FRUSTUM);
BIND_ENUM_CONSTANT(KEEP_WIDTH);
BIND_ENUM_CONSTANT(KEEP_HEIGHT);
@@ -543,6 +571,10 @@ float Camera::get_znear() const {
return near;
}
+Vector2 Camera::get_frustum_offset() const {
+ return frustum_offset;
+}
+
float Camera::get_zfar() const {
return far;
@@ -570,6 +602,11 @@ void Camera::set_znear(float p_znear) {
_update_camera_mode();
}
+void Camera::set_frustum_offset(Vector2 p_offset) {
+ frustum_offset = p_offset;
+ _update_camera_mode();
+}
+
void Camera::set_zfar(float p_zfar) {
far = p_zfar;
_update_camera_mode();
@@ -648,6 +685,7 @@ Camera::Camera() {
camera = VisualServer::get_singleton()->camera_create();
size = 1;
fov = 0;
+ frustum_offset = Vector2();
near = 0;
far = 0;
current = false;
diff --git a/scene/3d/camera.h b/scene/3d/camera.h
index a531324a85..fe8cb84f0d 100644
--- a/scene/3d/camera.h
+++ b/scene/3d/camera.h
@@ -46,7 +46,8 @@ public:
enum Projection {
PROJECTION_PERSPECTIVE,
- PROJECTION_ORTHOGONAL
+ PROJECTION_ORTHOGONAL,
+ PROJECTION_FRUSTUM
};
enum KeepAspect {
@@ -68,6 +69,7 @@ private:
float fov;
float size;
+ Vector2 frustum_offset;
float near, far;
float v_offset;
float h_offset;
@@ -110,6 +112,7 @@ public:
void set_perspective(float p_fovy_degrees, float p_z_near, float p_z_far);
void set_orthogonal(float p_size, float p_z_near, float p_z_far);
+ void set_frustum(float p_size, Vector2 p_offset, float p_near, float p_far);
void set_projection(Camera::Projection p_mode);
void make_current();
@@ -123,12 +126,15 @@ public:
float get_size() const;
float get_zfar() const;
float get_znear() const;
+ Vector2 get_frustum_offset() const;
+
Projection get_projection() const;
void set_fov(float p_fov);
void set_size(float p_size);
void set_zfar(float p_zfar);
void set_znear(float p_znear);
+ void set_frustum_offset(Vector2 p_offset);
virtual Transform get_camera_transform() const;
diff --git a/scene/3d/collision_shape.cpp b/scene/3d/collision_shape.cpp
index ac33e2b714..6bb2b547c7 100644
--- a/scene/3d/collision_shape.cpp
+++ b/scene/3d/collision_shape.cpp
@@ -91,7 +91,7 @@ void CollisionShape::_notification(int p_what) {
_update_in_shape_owner();
}
if (get_tree()->is_debugging_collisions_hint()) {
- _create_debug_shape();
+ _update_debug_shape();
}
} break;
case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
@@ -142,17 +142,24 @@ void CollisionShape::_bind_methods() {
ClassDB::bind_method(D_METHOD("make_convex_from_brothers"), &CollisionShape::make_convex_from_brothers);
ClassDB::set_method_flags("CollisionShape", "make_convex_from_brothers", METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR);
+ ClassDB::bind_method(D_METHOD("_shape_changed"), &CollisionShape::_shape_changed);
+ ClassDB::bind_method(D_METHOD("_update_debug_shape"), &CollisionShape::_update_debug_shape);
+
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape"), "set_shape", "get_shape");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled");
}
void CollisionShape::set_shape(const Ref<Shape> &p_shape) {
- if (!shape.is_null())
+ if (!shape.is_null()) {
shape->unregister_owner(this);
+ shape->disconnect("changed", this, "_shape_changed");
+ }
shape = p_shape;
- if (!shape.is_null())
+ if (!shape.is_null()) {
shape->register_owner(this);
+ shape->connect("changed", this, "_shape_changed");
+ }
update_gizmo();
if (parent) {
parent->shape_owner_clear_shapes(owner_id);
@@ -161,6 +168,8 @@ void CollisionShape::set_shape(const Ref<Shape> &p_shape) {
}
}
+ if (is_inside_tree())
+ _shape_changed();
update_configuration_warning();
}
@@ -199,7 +208,8 @@ CollisionShape::~CollisionShape() {
//VisualServer::get_singleton()->free(indicator);
}
-void CollisionShape::_create_debug_shape() {
+void CollisionShape::_update_debug_shape() {
+ debug_shape_dirty = false;
if (debug_shape) {
debug_shape->queue_delete();
@@ -207,15 +217,19 @@ void CollisionShape::_create_debug_shape() {
}
Ref<Shape> s = get_shape();
-
if (s.is_null())
return;
Ref<Mesh> mesh = s->get_debug_mesh();
-
MeshInstance *mi = memnew(MeshInstance);
mi->set_mesh(mesh);
-
add_child(mi);
debug_shape = mi;
}
+
+void CollisionShape::_shape_changed() {
+ if (get_tree()->is_debugging_collisions_hint() && !debug_shape_dirty) {
+ debug_shape_dirty = true;
+ call_deferred("_update_debug_shape");
+ }
+}
diff --git a/scene/3d/collision_shape.h b/scene/3d/collision_shape.h
index 0c8e383a7f..98427b8590 100644
--- a/scene/3d/collision_shape.h
+++ b/scene/3d/collision_shape.h
@@ -45,12 +45,14 @@ class CollisionShape : public Spatial {
CollisionObject *parent;
Node *debug_shape;
+ bool debug_shape_dirty;
void resource_changed(RES res);
bool disabled;
protected:
- void _create_debug_shape();
+ void _update_debug_shape();
+ void _shape_changed();
void _update_in_shape_owner(bool p_xform_only = false);
diff --git a/scene/3d/cpu_particles.cpp b/scene/3d/cpu_particles.cpp
index 469a1e87db..d4e242dcb7 100644
--- a/scene/3d/cpu_particles.cpp
+++ b/scene/3d/cpu_particles.cpp
@@ -1394,6 +1394,7 @@ CPUParticles::CPUParticles() {
redraw = false;
multimesh = VisualServer::get_singleton()->multimesh_create();
+ VisualServer::get_singleton()->multimesh_set_visible_instances(multimesh, 0);
set_base(multimesh);
set_emitting(true);
diff --git a/scene/3d/path.cpp b/scene/3d/path.cpp
index 190967d76c..84078911cb 100644
--- a/scene/3d/path.cpp
+++ b/scene/3d/path.cpp
@@ -173,7 +173,7 @@ void PathFollow::_update_transform() {
float dot = t_prev.dot(t_cur);
float angle = Math::acos(CLAMP(dot, -1, 1));
- if (likely(Math::abs(angle) > CMP_EPSILON)) {
+ if (likely(!Math::is_zero_approx(angle))) {
if (rotation_mode == ROTATION_Y) {
// assuming we're referring to global Y-axis. is this correct?
axis.x = 0;
@@ -184,7 +184,7 @@ void PathFollow::_update_transform() {
// all components are allowed
}
- if (likely(axis.length() > CMP_EPSILON)) {
+ if (likely(!Math::is_zero_approx(axis.length()))) {
t.rotate_basis(axis.normalized(), angle);
}
}
@@ -193,7 +193,7 @@ void PathFollow::_update_transform() {
float tilt_angle = c->interpolate_baked_tilt(o);
Vector3 tilt_axis = t_cur; // not sure what tilt is supposed to do, is this correct??
- if (likely(Math::abs(tilt_angle) > CMP_EPSILON)) {
+ if (likely(!Math::is_zero_approx(Math::abs(tilt_angle)))) {
if (rotation_mode == ROTATION_Y) {
tilt_axis.x = 0;
tilt_axis.z = 0;
@@ -203,7 +203,7 @@ void PathFollow::_update_transform() {
// all components are allowed
}
- if (likely(tilt_axis.length() > CMP_EPSILON)) {
+ if (likely(!Math::is_zero_approx(tilt_axis.length()))) {
t.rotate_basis(tilt_axis.normalized(), tilt_angle);
}
}
diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp
index 05214ed669..e2dc89aa6e 100644
--- a/scene/3d/physics_body.cpp
+++ b/scene/3d/physics_body.cpp
@@ -1203,9 +1203,6 @@ Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Ve
if (collided) {
found_collision = true;
- }
-
- if (collided) {
colliders.push_back(collision);
motion = collision.remainder;
diff --git a/scene/3d/vehicle_body.cpp b/scene/3d/vehicle_body.cpp
index c7f7b14a8f..fde135c972 100644
--- a/scene/3d/vehicle_body.cpp
+++ b/scene/3d/vehicle_body.cpp
@@ -716,7 +716,7 @@ void VehicleBody::_update_friction(PhysicsDirectBodyState *s) {
real_t rollingFriction = 0.f;
if (wheelInfo.m_raycastInfo.m_isInContact) {
- if (engine_force != 0.f) {
+ if (engine_force != 0.f && wheelInfo.engine_traction != false) {
rollingFriction = -engine_force * s->get_step();
} else {
real_t defaultRollingFrictionImpulse = 0.f;
diff --git a/scene/3d/visual_instance.cpp b/scene/3d/visual_instance.cpp
index 1bbf1b7bc7..1aded826c0 100644
--- a/scene/3d/visual_instance.cpp
+++ b/scene/3d/visual_instance.cpp
@@ -123,6 +123,8 @@ void VisualInstance::_bind_methods() {
ClassDB::bind_method(D_METHOD("_get_visual_instance_rid"), &VisualInstance::_get_visual_instance_rid);
ClassDB::bind_method(D_METHOD("set_base", "base"), &VisualInstance::set_base);
+ ClassDB::bind_method(D_METHOD("get_base"), &VisualInstance::get_base);
+ ClassDB::bind_method(D_METHOD("get_instance"), &VisualInstance::get_instance);
ClassDB::bind_method(D_METHOD("set_layer_mask", "mask"), &VisualInstance::set_layer_mask);
ClassDB::bind_method(D_METHOD("get_layer_mask"), &VisualInstance::get_layer_mask);
ClassDB::bind_method(D_METHOD("set_layer_mask_bit", "layer", "enabled"), &VisualInstance::set_layer_mask_bit);
@@ -136,6 +138,12 @@ void VisualInstance::_bind_methods() {
void VisualInstance::set_base(const RID &p_base) {
VisualServer::get_singleton()->instance_set_base(instance, p_base);
+ base = p_base;
+}
+
+RID VisualInstance::get_base() const {
+
+ return base;
}
VisualInstance::VisualInstance() {
diff --git a/scene/3d/visual_instance.h b/scene/3d/visual_instance.h
index 3b6fccf65f..f5b7479bb1 100644
--- a/scene/3d/visual_instance.h
+++ b/scene/3d/visual_instance.h
@@ -43,6 +43,7 @@ class VisualInstance : public Spatial {
GDCLASS(VisualInstance, Spatial);
OBJ_CATEGORY("3D Visual Nodes");
+ RID base;
RID instance;
uint32_t layers;
@@ -69,6 +70,7 @@ public:
virtual AABB get_transformed_aabb() const; // helper
void set_base(const RID &p_base);
+ RID get_base() const;
void set_layer_mask(uint32_t p_mask);
uint32_t get_layer_mask() const;
diff --git a/scene/3d/voxel_light_baker.cpp b/scene/3d/voxel_light_baker.cpp
index 750ed97ae6..75b419ca58 100644
--- a/scene/3d/voxel_light_baker.cpp
+++ b/scene/3d/voxel_light_baker.cpp
@@ -835,7 +835,7 @@ void VoxelLightBaker::plot_light_directional(const Vector3 &p_direction, const C
for (int i = 0; i < 3; i++) {
- if (ABS(light_axis[i]) < CMP_EPSILON)
+ if (Math::is_zero_approx(light_axis[i]))
continue;
clip[clip_planes].normal[i] = 1.0;
@@ -978,7 +978,7 @@ void VoxelLightBaker::plot_light_omni(const Vector3 &p_pos, const Color &p_color
for (int c = 0; c < 3; c++) {
- if (ABS(light_axis[c]) < CMP_EPSILON)
+ if (Math::is_zero_approx(light_axis[c]))
continue;
clip[clip_planes].normal[c] = 1.0;
@@ -1113,7 +1113,7 @@ void VoxelLightBaker::plot_light_spot(const Vector3 &p_pos, const Vector3 &p_axi
for (int c = 0; c < 3; c++) {
- if (ABS(light_axis[c]) < CMP_EPSILON)
+ if (Math::is_zero_approx(light_axis[c]))
continue;
clip[clip_planes].normal[c] = 1.0;
diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp
index 47278b8fc1..144e58d8b9 100644
--- a/scene/audio/audio_stream_player.cpp
+++ b/scene/audio/audio_stream_player.cpp
@@ -32,33 +32,10 @@
#include "core/engine.h"
-void AudioStreamPlayer::_mix_internal(bool p_fadeout) {
-
- int bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus);
-
- //get data
- AudioFrame *buffer = mix_buffer.ptrw();
- int buffer_size = mix_buffer.size();
-
- if (p_fadeout) {
- // Short fadeout ramp
- buffer_size = MIN(buffer_size, 128);
- }
-
- stream_playback->mix(buffer, pitch_scale, buffer_size);
-
- //multiply volume interpolating to avoid clicks if this changes
- float target_volume = p_fadeout ? -80.0 : volume_db;
- float vol = Math::db2linear(mix_volume_db);
- float vol_inc = (Math::db2linear(target_volume) - vol) / float(buffer_size);
- for (int i = 0; i < buffer_size; i++) {
- buffer[i] *= vol;
- vol += vol_inc;
- }
+void AudioStreamPlayer::_mix_to_bus(const AudioFrame *p_frames,int p_amount) {
- //set volume for next mix
- mix_volume_db = target_volume;
+ int bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus);
AudioFrame *targets[4] = { NULL, NULL, NULL, NULL };
@@ -83,42 +60,84 @@ void AudioStreamPlayer::_mix_internal(bool p_fadeout) {
for (int c = 0; c < 4; c++) {
if (!targets[c])
break;
- for (int i = 0; i < buffer_size; i++) {
- targets[c][i] += buffer[i];
+ for (int i = 0; i < p_amount; i++) {
+ targets[c][i] += p_frames[i];
}
}
}
+
+void AudioStreamPlayer::_mix_internal(bool p_fadeout) {
+
+
+ //get data
+ AudioFrame *buffer = mix_buffer.ptrw();
+ int buffer_size = mix_buffer.size();
+
+ if (p_fadeout) {
+ // Short fadeout ramp
+ buffer_size = MIN(buffer_size, 128);
+ }
+
+ stream_playback->mix(buffer, pitch_scale, buffer_size);
+
+ //multiply volume interpolating to avoid clicks if this changes
+ float target_volume = p_fadeout ? -80.0 : volume_db;
+ float vol = Math::db2linear(mix_volume_db);
+ float vol_inc = (Math::db2linear(target_volume) - vol) / float(buffer_size);
+
+ for (int i = 0; i < buffer_size; i++) {
+ buffer[i] *= vol;
+ vol += vol_inc;
+ }
+
+ //set volume for next mix
+ mix_volume_db = target_volume;
+
+ _mix_to_bus(buffer,buffer_size);
+
+}
+
void AudioStreamPlayer::_mix_audio() {
+ if (use_fadeout) {
+ _mix_to_bus(fadeout_buffer.ptr(),fadeout_buffer.size());
+ use_fadeout=false;
+ }
+
if (!stream_playback.is_valid() || !active ||
- (stream_paused && !stream_fade)) {
+ (stream_paused && !stream_paused_fade)) {
return;
}
- if (stream_fade) {
- _mix_internal(true);
- stream_fade = false;
-
- if (stream_stop) {
- stream_playback->stop();
- active = false;
- set_process_internal(false);
+ if (stream_paused) {
+ if (stream_paused_fade) {
+ _mix_internal(true);
+ stream_paused_fade = false;
}
return;
}
- if (setseek >= 0.0) {
+ if (setstop) {
+ _mix_internal(true);
+ stream_playback->stop();
+ setstop=false;
+ }
+
+ if (setseek >= 0.0 && !stop_has_priority) {
if (stream_playback->is_playing()) {
//fade out to avoid pops
_mix_internal(true);
}
+
stream_playback->start(setseek);
setseek = -1.0; //reset seek
mix_volume_db = volume_db; //reset ramp
}
+ stop_has_priority = false;
+
_mix_internal(false);
}
@@ -135,7 +154,7 @@ void AudioStreamPlayer::_notification(int p_what) {
if (p_what == NOTIFICATION_INTERNAL_PROCESS) {
if (!active || (setseek < 0 && !stream_playback->is_playing())) {
- active = false;
+ active = false;
set_process_internal(false);
emit_signal("finished");
}
@@ -162,6 +181,28 @@ void AudioStreamPlayer::set_stream(Ref<AudioStream> p_stream) {
AudioServer::get_singleton()->lock();
+ if (active && stream_playback.is_valid() && !stream_paused) {
+ //changing streams out of the blue is not a great idea, but at least
+ //lets try to somehow avoid a click
+
+ AudioFrame *buffer = fadeout_buffer.ptrw();
+ int buffer_size = fadeout_buffer.size();
+
+ stream_playback->mix(buffer, pitch_scale, buffer_size);
+
+ //multiply volume interpolating to avoid clicks if this changes
+ float target_volume = -80.0;
+ float vol = Math::db2linear(mix_volume_db);
+ float vol_inc = (Math::db2linear(target_volume) - vol) / float(buffer_size);
+
+ for (int i = 0; i < buffer_size; i++) {
+ buffer[i] *= vol;
+ vol += vol_inc;
+ }
+
+ use_fadeout=true;
+ }
+
mix_buffer.resize(AudioServer::get_singleton()->thread_get_mix_buffer_size());
if (stream_playback.is_valid()) {
@@ -169,6 +210,7 @@ void AudioStreamPlayer::set_stream(Ref<AudioStream> p_stream) {
stream.unref();
active = false;
setseek = -1;
+ setstop = false;
}
if (p_stream.is_valid()) {
@@ -209,8 +251,8 @@ void AudioStreamPlayer::play(float p_from_pos) {
if (stream_playback.is_valid()) {
//mix_volume_db = volume_db; do not reset volume ramp here, can cause clicks
- stream_stop = false;
setseek = p_from_pos;
+ stop_has_priority=false;
active = true;
set_process_internal(true);
}
@@ -225,16 +267,16 @@ void AudioStreamPlayer::seek(float p_seconds) {
void AudioStreamPlayer::stop() {
- if (stream_playback.is_valid()) {
- stream_stop = true;
- stream_fade = true;
+ if (stream_playback.is_valid() && active) {
+ setstop=true;
+ stop_has_priority=true;
}
}
bool AudioStreamPlayer::is_playing() const {
if (stream_playback.is_valid()) {
- return active; //&& stream_playback->is_playing();
+ return active && !setstop; //&& stream_playback->is_playing();
}
return false;
@@ -301,7 +343,7 @@ void AudioStreamPlayer::set_stream_paused(bool p_pause) {
if (p_pause != stream_paused) {
stream_paused = p_pause;
- stream_fade = p_pause ? true : false;
+ stream_paused_fade = p_pause ? true : false;
}
}
@@ -315,7 +357,7 @@ void AudioStreamPlayer::_validate_property(PropertyInfo &property) const {
if (property.name == "bus") {
String options;
- for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) {
+ for (int i = 0; i <AudioServer::get_singleton()->get_bus_count(); i++) {
if (i > 0)
options += ",";
String name = AudioServer::get_singleton()->get_bus_name(i);
@@ -397,9 +439,11 @@ AudioStreamPlayer::AudioStreamPlayer() {
setseek = -1;
active = false;
stream_paused = false;
- stream_fade = false;
- stream_stop = false;
+ stream_paused_fade = false;
mix_target = MIX_TARGET_STEREO;
+ fadeout_buffer.resize(512);
+ setstop=false;
+ use_fadeout=false;
AudioServer::get_singleton()->connect("bus_layout_changed", this, "_bus_layout_changed");
}
diff --git a/scene/audio/audio_stream_player.h b/scene/audio/audio_stream_player.h
index 590bef95b0..0b782b67e7 100644
--- a/scene/audio/audio_stream_player.h
+++ b/scene/audio/audio_stream_player.h
@@ -49,17 +49,20 @@ private:
Ref<AudioStreamPlayback> stream_playback;
Ref<AudioStream> stream;
Vector<AudioFrame> mix_buffer;
+ Vector<AudioFrame> fadeout_buffer;
+ bool use_fadeout;
volatile float setseek;
volatile bool active;
+ volatile bool setstop;
+ volatile bool stop_has_priority;
float mix_volume_db;
float pitch_scale;
float volume_db;
bool autoplay;
bool stream_paused;
- bool stream_fade;
- bool stream_stop;
+ bool stream_paused_fade;
StringName bus;
MixTarget mix_target;
@@ -72,6 +75,7 @@ private:
bool _is_active() const;
void _bus_layout_changed();
+ void _mix_to_bus(const AudioFrame *p_frames, int p_amount);
protected:
void _validate_property(PropertyInfo &property) const;
diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp
index 806c8afa5b..d68cdd5f8d 100644
--- a/scene/gui/base_button.cpp
+++ b/scene/gui/base_button.cpp
@@ -59,7 +59,7 @@ void BaseButton::_gui_input(Ref<InputEvent> p_event) {
Ref<InputEventMouseButton> b = p_event;
if (b.is_valid()) {
- if (status.disabled || ((1 << (b->get_button_index() - 1)) & button_mask) == 0)
+ if (((1 << (b->get_button_index() - 1)) & button_mask) == 0)
return;
if (status.pressing_button)
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index 7aca6acd00..c78f29dd50 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -825,6 +825,8 @@ void ColorPickerButton::_update_picker() {
add_child(popup);
picker->connect("color_changed", this, "_color_changed");
popup->connect("modal_closed", this, "_modal_closed");
+ popup->connect("about_to_show", this, "set_pressed", varray(true));
+ popup->connect("popup_hide", this, "set_pressed", varray(false));
picker->set_pick_color(color);
picker->set_edit_alpha(edit_alpha);
}
@@ -855,4 +857,6 @@ ColorPickerButton::ColorPickerButton() {
picker = NULL;
popup = NULL;
edit_alpha = true;
+
+ set_toggle_mode(true);
}
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index 30ad81bb2e..dabff08fea 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -1053,7 +1053,7 @@ void GraphEdit::set_connection_activity(const StringName &p_from, int p_from_por
if (E->get().from == p_from && E->get().from_port == p_from_port && E->get().to == p_to && E->get().to_port == p_to_port) {
- if (ABS(E->get().activity - p_activity) < CMP_EPSILON) {
+ if (Math::is_equal_approx(E->get().activity, p_activity)) {
//update only if changed
top_layer->update();
connections_layer->update();
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index e83ef95db6..1e8d73b6a4 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -722,6 +722,8 @@ void LineEdit::_notification(int p_what) {
} else {
x_ofs = MAX(style->get_margin(MARGIN_LEFT), x_ofs - r_icon->get_width() - style->get_margin(MARGIN_RIGHT));
}
+
+ ofs_max -= r_icon->get_width();
}
int caret_height = font->get_height() > y_area ? y_area : font->get_height();
@@ -918,6 +920,10 @@ void LineEdit::undo() {
TextOperation op = undo_stack_pos->get();
text = op.text;
set_cursor_position(op.cursor_pos);
+
+ if (expand_to_text_length)
+ minimum_size_changed();
+
_emit_text_change();
}
@@ -932,6 +938,10 @@ void LineEdit::redo() {
TextOperation op = undo_stack_pos->get();
text = op.text;
set_cursor_position(op.cursor_pos);
+
+ if (expand_to_text_length)
+ minimum_size_changed();
+
_emit_text_change();
}
diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp
index b67d8c00d6..a011b793de 100644
--- a/scene/gui/menu_button.cpp
+++ b/scene/gui/menu_button.cpp
@@ -116,15 +116,19 @@ MenuButton::MenuButton() {
switch_on_hover = false;
set_flat(true);
+ set_toggle_mode(true);
set_disable_shortcuts(false);
set_enabled_focus_mode(FOCUS_NONE);
+ set_process_unhandled_key_input(true);
+ set_action_mode(ACTION_MODE_BUTTON_PRESS);
+
popup = memnew(PopupMenu);
popup->hide();
add_child(popup);
popup->set_as_toplevel(true);
popup->set_pass_on_modal_close_click(false);
- set_process_unhandled_key_input(true);
- set_action_mode(ACTION_MODE_BUTTON_PRESS);
+ popup->connect("about_to_show", this, "set_pressed", varray(true)); // For when switching from another MenuButton.
+ popup->connect("popup_hide", this, "set_pressed", varray(false));
}
MenuButton::~MenuButton() {
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index b9b270ce0c..cb5fb0263b 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -340,6 +340,7 @@ void OptionButton::_bind_methods() {
OptionButton::OptionButton() {
current = -1;
+ set_toggle_mode(true);
set_text_align(ALIGN_LEFT);
set_action_mode(ACTION_MODE_BUTTON_PRESS);
@@ -350,6 +351,7 @@ OptionButton::OptionButton() {
popup->set_pass_on_modal_close_click(false);
popup->connect("id_pressed", this, "_selected");
popup->connect("id_focused", this, "_focused");
+ popup->connect("popup_hide", this, "set_pressed", varray(false));
}
OptionButton::~OptionButton() {
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index 94c73b2e42..23c61f37d6 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -239,7 +239,7 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
for (int i = search_from; i >= 0; i--) {
- if (i < 0 || i >= items.size())
+ if (i >= items.size())
continue;
if (!items[i].separator && !items[i].disabled) {
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 3fbf809012..101eb2ac88 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -753,6 +753,8 @@ void RichTextLabel::_scroll_changed(double) {
else
scroll_following = false;
+ scroll_updated = true;
+
update();
}
@@ -778,7 +780,6 @@ void RichTextLabel::_update_scroll() {
main->first_invalid_line = 0; //invalidate ALL
_validate_line_caches(main);
}
- scroll_updated = true;
}
void RichTextLabel::_notification(int p_what) {
@@ -901,13 +902,21 @@ void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item
Control::CursorShape RichTextLabel::get_cursor_shape(const Point2 &p_pos) const {
- if (selection.click)
+ if (!underline_meta || selection.click)
return CURSOR_ARROW;
if (main->first_invalid_line < main->lines.size())
return CURSOR_ARROW; //invalid
- return get_default_cursor_shape();
+ int line = 0;
+ Item *item = NULL;
+
+ ((RichTextLabel *)(this))->_find_click(main, p_pos, &item, &line);
+
+ if (item && ((RichTextLabel *)(this))->_find_meta(item, NULL))
+ return CURSOR_POINTING_HAND;
+
+ return CURSOR_ARROW;
}
void RichTextLabel::_gui_input(Ref<InputEvent> p_event) {
@@ -1997,7 +2006,7 @@ bool RichTextLabel::search(const String &p_string, bool p_from_selection, bool p
Item *it = main;
int charidx = 0;
- if (p_from_selection && selection.active && selection.enabled) {
+ if (p_from_selection && selection.active) {
it = selection.to;
charidx = selection.to_char + 1;
}
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 4e724759c9..9c6fa703fa 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -350,6 +350,10 @@ void TextEdit::_update_scrollbars() {
total_width += cache.breakpoint_gutter_width;
}
+ if (draw_info_gutter) {
+ total_width += cache.info_gutter_width;
+ }
+
if (draw_fold_gutter) {
total_width += cache.fold_gutter_width;
}
@@ -608,6 +612,13 @@ void TextEdit::_notification(int p_what) {
cache.breakpoint_gutter_width = 0;
}
+ if (draw_info_gutter) {
+ info_gutter_width = (get_row_height());
+ cache.info_gutter_width = info_gutter_width;
+ } else {
+ cache.info_gutter_width = 0;
+ }
+
if (draw_fold_gutter) {
fold_gutter_width = (get_row_height() * 55) / 100;
cache.fold_gutter_width = fold_gutter_width;
@@ -637,7 +648,7 @@ void TextEdit::_notification(int p_what) {
RID ci = get_canvas_item();
VisualServer::get_singleton()->canvas_item_set_clip(get_canvas_item(), true);
- int xmargin_beg = cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width;
+ int xmargin_beg = cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width + cache.info_gutter_width;
int xmargin_end = size.width - cache.style_normal->get_margin(MARGIN_RIGHT);
//let's do it easy for now:
cache.style_normal->draw(ci, Rect2(Point2(), size));
@@ -952,10 +963,35 @@ void TextEdit::_notification(int p_what) {
}
}
+ // draw info icons
+ if (draw_info_gutter && text.has_info_icon(line)) {
+ int vertical_gap = (get_row_height() * 40) / 100;
+ int horizontal_gap = (cache.info_gutter_width * 30) / 100;
+ int gutter_left = cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width;
+
+ Ref<Texture> info_icon = text.get_info_icon(line);
+ // ensure the icon fits the gutter size
+ Size2i icon_size = info_icon->get_size();
+ if (icon_size.width > cache.info_gutter_width - horizontal_gap) {
+ icon_size.width = cache.info_gutter_width - horizontal_gap;
+ }
+ if (icon_size.height > get_row_height() - horizontal_gap) {
+ icon_size.height = get_row_height() - horizontal_gap;
+ }
+
+ Size2i icon_pos;
+ int xofs = horizontal_gap - (info_icon->get_width() / 4);
+ int yofs = vertical_gap - (info_icon->get_height() / 4);
+ icon_pos.x = gutter_left + xofs + ofs_x;
+ icon_pos.y = ofs_y + yofs;
+
+ draw_texture_rect(info_icon, Rect2(icon_pos, icon_size));
+ }
+
// draw fold markers
if (draw_fold_gutter) {
int horizontal_gap = (cache.fold_gutter_width * 30) / 100;
- int gutter_left = cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + cache.line_number_w;
+ int gutter_left = cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + cache.line_number_w + cache.info_gutter_width;
if (is_folded(line)) {
int xofs = horizontal_gap - (cache.can_fold_icon->get_width()) / 2;
int yofs = (get_row_height() - cache.folded_icon->get_height()) / 2;
@@ -975,7 +1011,7 @@ void TextEdit::_notification(int p_what) {
fc = line_num_padding + fc;
}
- cache.font->draw(ci, Point2(cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + ofs_x, yofs + cache.font->get_ascent()), fc, text.is_safe(line) ? cache.safe_line_number_color : cache.line_number_color);
+ cache.font->draw(ci, Point2(cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + cache.info_gutter_width + ofs_x, yofs + cache.font->get_ascent()), fc, text.is_safe(line) ? cache.safe_line_number_color : cache.line_number_color);
}
}
@@ -1566,6 +1602,10 @@ void TextEdit::backspace_at_cursor() {
set_line_as_breakpoint(prev_line, true);
}
+ if (text.has_info_icon(cursor.line)) {
+ set_line_info_icon(prev_line, text.get_info_icon(cursor.line), text.get_info(cursor.line));
+ }
+
if (auto_brace_completion_enabled &&
cursor.column > 0 &&
_is_pair_left_symbol(text[cursor.line][cursor.column - 1])) {
@@ -1718,7 +1758,7 @@ void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) co
col = text[row].size();
} else {
- int colx = p_mouse.x - (cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width);
+ int colx = p_mouse.x - (cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width + cache.info_gutter_width);
colx += cursor.x_ofs;
col = get_char_pos_for_line(colx, row, wrap_index);
if (is_wrap_enabled() && wrap_index < times_line_wraps(row)) {
@@ -1816,18 +1856,28 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
// toggle breakpoint on gutter click
if (draw_breakpoint_gutter) {
int gutter = cache.style_normal->get_margin(MARGIN_LEFT);
- if (mb->get_position().x > gutter && mb->get_position().x <= gutter + cache.breakpoint_gutter_width + 3) {
+ if (mb->get_position().x > gutter - 6 && mb->get_position().x <= gutter + cache.breakpoint_gutter_width - 3) {
set_line_as_breakpoint(row, !is_line_set_as_breakpoint(row));
emit_signal("breakpoint_toggled", row);
return;
}
}
+ // emit info clicked
+ if (draw_info_gutter && text.has_info_icon(row)) {
+ int left_margin = cache.style_normal->get_margin(MARGIN_LEFT);
+ int gutter_left = left_margin + cache.breakpoint_gutter_width;
+ if (mb->get_position().x > gutter_left - 6 && mb->get_position().x <= gutter_left + cache.info_gutter_width - 3) {
+ emit_signal("info_clicked", row, text.get_info(row));
+ return;
+ }
+ }
+
// toggle fold on gutter click if can
if (draw_fold_gutter) {
int left_margin = cache.style_normal->get_margin(MARGIN_LEFT);
- int gutter_left = left_margin + cache.breakpoint_gutter_width + cache.line_number_w;
+ int gutter_left = left_margin + cache.breakpoint_gutter_width + cache.line_number_w + cache.info_gutter_width;
if (mb->get_position().x > gutter_left - 6 && mb->get_position().x <= gutter_left + cache.fold_gutter_width - 3) {
if (is_folded(row)) {
unfold_line(row);
@@ -1841,7 +1891,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
// unfold on folded icon click
if (is_folded(row)) {
int line_width = text.get_line_width(row);
- line_width += cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width - cursor.x_ofs;
+ line_width += cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.info_gutter_width + cache.fold_gutter_width - cursor.x_ofs;
if (mb->get_position().x > line_width - 3 && mb->get_position().x <= line_width + cache.folded_eol_icon->get_width() + 3) {
unfold_line(row);
return;
@@ -3374,8 +3424,11 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i
if (shift_first_line) {
text.set_breakpoint(p_line + 1, text.is_breakpoint(p_line));
text.set_hidden(p_line + 1, text.is_hidden(p_line));
+ text.set_info_icon(p_line + 1, text.get_info_icon(p_line), text.get_info(p_line));
+
text.set_breakpoint(p_line, false);
text.set_hidden(p_line, false);
+ text.set_info_icon(p_line, NULL, "");
}
text.set_line_wrap_amount(p_line, -1);
@@ -3628,7 +3681,7 @@ int TextEdit::get_total_visible_rows() const {
void TextEdit::_update_wrap_at() {
- wrap_at = get_size().width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width - wrap_right_offset;
+ wrap_at = get_size().width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width - cache.info_gutter_width - wrap_right_offset;
update_cursor_wrap_offset();
text.clear_wrap_cache();
@@ -3662,7 +3715,7 @@ void TextEdit::adjust_viewport_to_cursor() {
set_line_as_last_visible(cur_line, cur_wrap);
}
- int visible_width = get_size().width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width;
+ int visible_width = get_size().width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width - cache.info_gutter_width;
if (v_scroll->is_visible_in_tree())
visible_width -= v_scroll->get_combined_minimum_size().width;
visible_width -= 20; // give it a little more space
@@ -3693,7 +3746,7 @@ void TextEdit::center_viewport_to_cursor() {
unfold_line(cursor.line);
set_line_as_center_visible(cursor.line, get_cursor_wrap_index());
- int visible_width = get_size().width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width;
+ int visible_width = get_size().width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width - cache.info_gutter_width;
if (v_scroll->is_visible_in_tree())
visible_width -= v_scroll->get_combined_minimum_size().width;
visible_width -= 20; // give it a little more space
@@ -4132,7 +4185,7 @@ Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const {
if (highlighted_word != String())
return CURSOR_POINTING_HAND;
- int gutter = cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width;
+ int gutter = cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width + cache.info_gutter_width;
if ((completion_active && completion_rect.has_point(p_pos))) {
return CURSOR_ARROW;
}
@@ -4143,18 +4196,27 @@ Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const {
int left_margin = cache.style_normal->get_margin(MARGIN_LEFT);
// breakpoint icon
- if (draw_breakpoint_gutter && p_pos.x > left_margin && p_pos.x <= left_margin + cache.breakpoint_gutter_width + 3) {
+ if (draw_breakpoint_gutter && p_pos.x > left_margin - 6 && p_pos.x <= left_margin + cache.breakpoint_gutter_width - 3) {
return CURSOR_POINTING_HAND;
}
+ // info icons
+ int gutter_left = left_margin + cache.breakpoint_gutter_width + cache.info_gutter_width;
+ if (draw_info_gutter && p_pos.x > left_margin + cache.breakpoint_gutter_width - 6 && p_pos.x <= gutter_left - 3) {
+ if (text.has_info_icon(row)) {
+ return CURSOR_POINTING_HAND;
+ }
+ return CURSOR_ARROW;
+ }
+
// fold icon
- int gutter_left = left_margin + cache.breakpoint_gutter_width + cache.line_number_w;
- if (draw_fold_gutter && p_pos.x > gutter_left - 6 && p_pos.x <= gutter_left + cache.fold_gutter_width - 3) {
+ if (draw_fold_gutter && p_pos.x > gutter_left + cache.line_number_w - 6 && p_pos.x <= gutter_left + cache.line_number_w + cache.fold_gutter_width - 3) {
if (is_folded(row) || can_fold(row))
return CURSOR_POINTING_HAND;
else
return CURSOR_ARROW;
}
+
return CURSOR_ARROW;
} else {
int row, col;
@@ -4162,7 +4224,7 @@ Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const {
// eol fold icon
if (is_folded(row)) {
int line_width = text.get_line_width(row);
- line_width += cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width - cursor.x_ofs;
+ line_width += cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width + cache.info_gutter_width - cursor.x_ofs;
if (p_pos.x > line_width - 3 && p_pos.x <= line_width + cache.folded_eol_icon->get_width() + 3) {
return CURSOR_POINTING_HAND;
}
@@ -4366,8 +4428,8 @@ void TextEdit::_update_caches() {
#endif
cache.row_height = cache.font->get_height() + cache.line_spacing;
cache.tab_icon = get_icon("tab");
- cache.folded_icon = get_icon("GuiTreeArrowRight", "EditorIcons");
- cache.can_fold_icon = get_icon("GuiTreeArrowDown", "EditorIcons");
+ cache.folded_icon = get_icon("folded");
+ cache.can_fold_icon = get_icon("fold");
cache.folded_eol_icon = get_icon("GuiEllipsis", "EditorIcons");
text.set_font(cache.font);
@@ -4973,6 +5035,19 @@ void TextEdit::remove_breakpoints() {
}
}
+void TextEdit::set_line_info_icon(int p_line, Ref<Texture> p_icon, String p_info) {
+ ERR_FAIL_INDEX(p_line, text.size());
+ text.set_info_icon(p_line, p_icon, p_info);
+ update();
+}
+
+void TextEdit::clear_info_icons() {
+ for (int i = 0; i < text.size(); i++) {
+ text.set_info_icon(i, NULL, "");
+ }
+ update();
+}
+
void TextEdit::set_line_as_hidden(int p_line, bool p_hidden) {
ERR_FAIL_INDEX(p_line, text.size());
@@ -5469,6 +5544,7 @@ int TextEdit::get_indent_size() {
void TextEdit::set_draw_tabs(bool p_draw) {
draw_tabs = p_draw;
+ update();
}
bool TextEdit::is_drawing_tabs() const {
@@ -6013,6 +6089,24 @@ int TextEdit::get_fold_gutter_width() const {
return cache.fold_gutter_width;
}
+void TextEdit::set_draw_info_gutter(bool p_draw) {
+ draw_info_gutter = p_draw;
+ update();
+}
+
+bool TextEdit::is_drawing_info_gutter() const {
+ return draw_info_gutter;
+}
+
+void TextEdit::set_info_gutter_width(int p_gutter_width) {
+ info_gutter_width = p_gutter_width;
+ update();
+}
+
+int TextEdit::get_info_gutter_width() const {
+ return info_gutter_width;
+}
+
void TextEdit::set_hiding_enabled(int p_enabled) {
if (!p_enabled)
unhide_all_lines();
@@ -6169,8 +6263,12 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_show_line_numbers", "enable"), &TextEdit::set_show_line_numbers);
ClassDB::bind_method(D_METHOD("is_show_line_numbers_enabled"), &TextEdit::is_show_line_numbers_enabled);
+ ClassDB::bind_method(D_METHOD("set_draw_tabs"), &TextEdit::set_draw_tabs);
+ ClassDB::bind_method(D_METHOD("is_drawing_tabs"), &TextEdit::is_drawing_tabs);
ClassDB::bind_method(D_METHOD("set_breakpoint_gutter_enabled", "enable"), &TextEdit::set_breakpoint_gutter_enabled);
ClassDB::bind_method(D_METHOD("is_breakpoint_gutter_enabled"), &TextEdit::is_breakpoint_gutter_enabled);
+ ClassDB::bind_method(D_METHOD("set_draw_fold_gutter"), &TextEdit::set_draw_fold_gutter);
+ ClassDB::bind_method(D_METHOD("is_drawing_fold_gutter"), &TextEdit::is_drawing_fold_gutter);
ClassDB::bind_method(D_METHOD("set_hiding_enabled", "enable"), &TextEdit::set_hiding_enabled);
ClassDB::bind_method(D_METHOD("is_hiding_enabled"), &TextEdit::is_hiding_enabled);
@@ -6217,7 +6315,9 @@ void TextEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_current_line"), "set_highlight_current_line", "is_highlight_current_line_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "syntax_highlighting"), "set_syntax_coloring", "is_syntax_coloring_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_line_numbers"), "set_show_line_numbers", "is_show_line_numbers_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_tabs"), "set_draw_tabs", "is_drawing_tabs");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "breakpoint_gutter"), "set_breakpoint_gutter_enabled", "is_breakpoint_gutter_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fold_gutter"), "set_draw_fold_gutter", "is_drawing_fold_gutter");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_all_occurrences"), "set_highlight_all_occurrences", "is_highlight_all_occurrences_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_selected_font_color"), "set_override_selected_font_color", "is_overriding_selected_font_color");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled");
@@ -6238,6 +6338,7 @@ void TextEdit::_bind_methods() {
ADD_SIGNAL(MethodInfo("request_completion"));
ADD_SIGNAL(MethodInfo("breakpoint_toggled", PropertyInfo(Variant::INT, "row")));
ADD_SIGNAL(MethodInfo("symbol_lookup", PropertyInfo(Variant::STRING, "symbol"), PropertyInfo(Variant::INT, "row"), PropertyInfo(Variant::INT, "column")));
+ ADD_SIGNAL(MethodInfo("info_clicked", PropertyInfo(Variant::INT, "row"), PropertyInfo(Variant::STRING, "info")));
BIND_ENUM_CONSTANT(MENU_CUT);
BIND_ENUM_CONSTANT(MENU_COPY);
@@ -6273,6 +6374,8 @@ TextEdit::TextEdit() {
breakpoint_gutter_width = 0;
cache.fold_gutter_width = 0;
fold_gutter_width = 0;
+ info_gutter_width = 0;
+ cache.info_gutter_width = 0;
set_default_cursor_shape(CURSOR_IBEAM);
indent_size = 4;
@@ -6345,6 +6448,7 @@ TextEdit::TextEdit() {
line_length_guideline_col = 80;
draw_breakpoint_gutter = false;
draw_fold_gutter = false;
+ draw_info_gutter = false;
hiding_enabled = false;
next_operation_is_complex = false;
scroll_past_end_of_file_enabled = false;
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index 503bead785..e854032159 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -79,6 +79,8 @@ public:
bool safe : 1;
int wrap_amount_cache : 24;
Map<int, ColorRegionInfo> region_info;
+ Ref<Texture> info_icon;
+ String info;
String data;
};
@@ -109,6 +111,13 @@ public:
bool is_hidden(int p_line) const { return text[p_line].hidden; }
void set_safe(int p_line, bool p_safe) { text.write[p_line].safe = p_safe; }
bool is_safe(int p_line) const { return text[p_line].safe; }
+ void set_info_icon(int p_line, Ref<Texture> p_icon, String p_info) {
+ text.write[p_line].info_icon = p_icon;
+ text.write[p_line].info = p_info;
+ }
+ bool has_info_icon(int p_line) const { return text[p_line].info_icon.is_valid(); }
+ const Ref<Texture> &get_info_icon(int p_line) const { return text[p_line].info_icon; }
+ const String &get_info(int p_line) const { return text[p_line].info; }
void insert(int p_at, const String &p_text);
void remove(int p_at);
int size() const { return text.size(); }
@@ -193,6 +202,7 @@ private:
int line_number_w;
int breakpoint_gutter_width;
int fold_gutter_width;
+ int info_gutter_width;
} cache;
Map<int, int> color_region_cache;
@@ -291,6 +301,8 @@ private:
bool draw_fold_gutter;
int fold_gutter_width;
bool hiding_enabled;
+ bool draw_info_gutter;
+ int info_gutter_width;
bool highlight_all_occurrences;
bool scroll_past_end_of_file_enabled;
@@ -480,6 +492,9 @@ public:
Array get_breakpoints_array() const;
void remove_breakpoints();
+ void set_line_info_icon(int p_line, Ref<Texture> p_icon, String p_info = "");
+ void clear_info_icons();
+
void set_line_as_hidden(int p_line, bool p_hidden);
bool is_line_hidden(int p_line) const;
void fold_all_lines();
@@ -651,6 +666,12 @@ public:
void set_fold_gutter_width(int p_gutter_width);
int get_fold_gutter_width() const;
+ void set_draw_info_gutter(bool p_draw);
+ bool is_drawing_info_gutter() const;
+
+ void set_info_gutter_width(int p_gutter_width);
+ int get_info_gutter_width() const;
+
void set_hiding_enabled(int p_enabled);
int is_hiding_enabled() const;
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 7e59606d2b..f22fe5b6a5 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -3938,7 +3938,6 @@ Tree::Tree() {
cache.click_item = NULL;
cache.click_column = 0;
cache.hover_cell = -1;
- cache.hover_index = -1;
last_keypress = 0;
focus_in_id = 0;
diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp
index 8b68b3215c..e65314644e 100644
--- a/scene/main/http_request.cpp
+++ b/scene/main/http_request.cpp
@@ -286,7 +286,7 @@ bool HTTPRequest::_update_connection() {
call_deferred("_request_done", RESULT_SUCCESS, response_code, response_headers, PoolByteArray());
return true;
}
- if (got_response && body_len < 0) {
+ if (body_len < 0) {
// Chunked transfer is done
call_deferred("_request_done", RESULT_SUCCESS, response_code, response_headers, body);
return true;
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index cbe700c826..0465ffe442 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -41,6 +41,8 @@
VARIANT_ENUM_CAST(Node::PauseMode);
+int Node::orphan_node_count = 0;
+
void Node::_notification(int p_notification) {
switch (p_notification) {
@@ -84,11 +86,14 @@ void Node::_notification(int p_notification) {
add_to_group("_vp_unhandled_key_input" + itos(get_viewport()->get_instance_id()));
get_tree()->node_count++;
+ orphan_node_count--;
} break;
case NOTIFICATION_EXIT_TREE: {
get_tree()->node_count--;
+ orphan_node_count++;
+
if (data.input)
remove_from_group("_vp_input" + itos(get_viewport()->get_instance_id()));
if (data.unhandled_input)
@@ -2938,6 +2943,8 @@ Node::Node() {
data.use_placeholder = false;
data.display_folded = false;
data.ready_first = true;
+
+ orphan_node_count++;
}
Node::~Node() {
@@ -2948,6 +2955,8 @@ Node::~Node() {
ERR_FAIL_COND(data.parent);
ERR_FAIL_COND(data.children.size());
+
+ orphan_node_count--;
}
////////////////////////////////
diff --git a/scene/main/node.h b/scene/main/node.h
index b490db37c5..9b9ca06455 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -75,6 +75,8 @@ public:
bool operator()(const Node *p_a, const Node *p_b) const { return p_b->data.process_priority == p_a->data.process_priority ? p_b->is_greater_than(p_a) : p_b->data.process_priority > p_a->data.process_priority; }
};
+ static int orphan_node_count;
+
private:
struct GroupData {
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index 81c38cec89..125e0a2882 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -1160,7 +1160,7 @@ void SceneTree::_update_root_rect() {
WARN_PRINT("Font oversampling only works with the resize modes 'Keep Width', 'Keep Height', and 'Expand'.");
}
- if (stretch_aspect == STRETCH_ASPECT_IGNORE || ABS(viewport_aspect - video_mode_aspect) < CMP_EPSILON) {
+ if (stretch_aspect == STRETCH_ASPECT_IGNORE || Math::is_equal_approx(viewport_aspect, video_mode_aspect)) {
//same aspect or ignore aspect
viewport_size = desired_res;
screen_size = video_mode;
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 52f63ddc1d..ae2c571201 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -2482,11 +2482,7 @@ void Viewport::_gui_hid_control(Control *p_control) {
if (gui.mouse_over == p_control)
gui.mouse_over = NULL;
if (gui.tooltip == p_control)
- gui.tooltip = NULL;
- if (gui.tooltip == p_control) {
- gui.tooltip = NULL;
_gui_cancel_tooltip();
- }
}
void Viewport::_gui_remove_control(Control *p_control) {
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 9face3e476..47f5b152f0 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -782,7 +782,11 @@ void unregister_scene_types() {
ResourceLoader::remove_resource_format_loader(resource_loader_bmfont);
resource_loader_bmfont.unref();
+ //SpatialMaterial is not initialised when 3D is disabled, so it shouldn't be cleaned up either
+#ifndef _3D_DISABLED
SpatialMaterial::finish_shaders();
+#endif // _3D_DISABLED
+
ParticlesMaterial::finish_shaders();
CanvasItemMaterial::finish_shaders();
SceneStringNames::free();
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index e58ec9d71e..9c79b2ba3b 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -1477,7 +1477,7 @@ int Animation::_find(const Vector<K> &p_keys, float p_time) const {
middle = (low + high) / 2;
- if (Math::abs(p_time - keys[middle].time) < CMP_EPSILON) { //match
+ if (Math::is_equal_approx(p_time, keys[middle].time)) { //match
return middle;
} else if (p_time < keys[middle].time)
high = middle - 1; //search low end of array
@@ -1680,10 +1680,10 @@ T Animation::_interpolate(const Vector<TKey<T> > &p_keys, float p_time, Interpol
float delta = p_keys[next].time - p_keys[idx].time;
float from = p_time - p_keys[idx].time;
- if (Math::absf(delta) > CMP_EPSILON)
- c = from / delta;
- else
+ if (Math::is_zero_approx(delta))
c = 0;
+ else
+ c = from / delta;
} else {
@@ -1691,10 +1691,10 @@ T Animation::_interpolate(const Vector<TKey<T> > &p_keys, float p_time, Interpol
float delta = (length - p_keys[idx].time) + p_keys[next].time;
float from = p_time - p_keys[idx].time;
- if (Math::absf(delta) > CMP_EPSILON)
- c = from / delta;
- else
+ if (Math::is_zero_approx(delta))
c = 0;
+ else
+ c = from / delta;
}
} else {
@@ -1707,10 +1707,10 @@ T Animation::_interpolate(const Vector<TKey<T> > &p_keys, float p_time, Interpol
float delta = endtime + p_keys[next].time;
float from = endtime + p_time;
- if (Math::absf(delta) > CMP_EPSILON)
- c = from / delta;
- else
+ if (Math::is_zero_approx(delta))
c = 0;
+ else
+ c = from / delta;
}
} else { // no loop
@@ -1723,10 +1723,10 @@ T Animation::_interpolate(const Vector<TKey<T> > &p_keys, float p_time, Interpol
float delta = p_keys[next].time - p_keys[idx].time;
float from = p_time - p_keys[idx].time;
- if (Math::absf(delta) > CMP_EPSILON)
- c = from / delta;
- else
+ if (Math::is_zero_approx(delta))
c = 0;
+ else
+ c = from / delta;
} else {
@@ -2774,9 +2774,9 @@ bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, cons
const Vector3 &v1 = t1.value.loc;
const Vector3 &v2 = t2.value.loc;
- if (v0.distance_to(v2) < CMP_EPSILON) {
+ if (Math::is_zero_approx(v0.distance_to(v2))) {
//0 and 2 are close, let's see if 1 is close
- if (v0.distance_to(v1) > CMP_EPSILON) {
+ if (!Math::is_zero_approx(v0.distance_to(v1))) {
//not close, not optimizable
return false;
}
@@ -2813,9 +2813,9 @@ bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, cons
//localize both to rotation from q0
- if ((q0 - q2).length() < CMP_EPSILON) {
+ if (Math::is_zero_approx((q0 - q2).length())) {
- if ((q0 - q1).length() > CMP_EPSILON)
+ if (!Math::is_zero_approx((q0 - q1).length()))
return false;
} else {
@@ -2863,9 +2863,9 @@ bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, cons
const Vector3 &v1 = t1.value.scale;
const Vector3 &v2 = t2.value.scale;
- if (v0.distance_to(v2) < CMP_EPSILON) {
+ if (Math::is_zero_approx(v0.distance_to(v2))) {
//0 and 2 are close, let's see if 1 is close
- if (v0.distance_to(v1) > CMP_EPSILON) {
+ if (!Math::is_zero_approx(v0.distance_to(v1))) {
//not close, not optimizable
return false;
}
diff --git a/scene/resources/box_shape.cpp b/scene/resources/box_shape.cpp
index d93754076c..d819e9f776 100644
--- a/scene/resources/box_shape.cpp
+++ b/scene/resources/box_shape.cpp
@@ -51,6 +51,7 @@ Vector<Vector3> BoxShape::_gen_debug_mesh_lines() {
void BoxShape::_update_shape() {
PhysicsServer::get_singleton()->shape_set_data(get_shape(), extents);
+ Shape::_update_shape();
}
void BoxShape::set_extents(const Vector3 &p_extents) {
diff --git a/scene/resources/capsule_shape.cpp b/scene/resources/capsule_shape.cpp
index 3f7bf1e0ec..669b261bfe 100644
--- a/scene/resources/capsule_shape.cpp
+++ b/scene/resources/capsule_shape.cpp
@@ -75,6 +75,7 @@ void CapsuleShape::_update_shape() {
d["radius"] = radius;
d["height"] = height;
PhysicsServer::get_singleton()->shape_set_data(get_shape(), d);
+ Shape::_update_shape();
}
void CapsuleShape::set_radius(float p_radius) {
diff --git a/scene/resources/concave_polygon_shape.cpp b/scene/resources/concave_polygon_shape.cpp
index b192d088d8..b4cc38c8c0 100644
--- a/scene/resources/concave_polygon_shape.cpp
+++ b/scene/resources/concave_polygon_shape.cpp
@@ -65,6 +65,7 @@ Vector<Vector3> ConcavePolygonShape::_gen_debug_mesh_lines() {
}
void ConcavePolygonShape::_update_shape() {
+ Shape::_update_shape();
}
void ConcavePolygonShape::set_faces(const PoolVector<Vector3> &p_faces) {
diff --git a/scene/resources/convex_polygon_shape.cpp b/scene/resources/convex_polygon_shape.cpp
index 5845e4be50..499688a185 100644
--- a/scene/resources/convex_polygon_shape.cpp
+++ b/scene/resources/convex_polygon_shape.cpp
@@ -58,7 +58,7 @@ Vector<Vector3> ConvexPolygonShape::_gen_debug_mesh_lines() {
void ConvexPolygonShape::_update_shape() {
PhysicsServer::get_singleton()->shape_set_data(get_shape(), points);
- emit_changed();
+ Shape::_update_shape();
}
void ConvexPolygonShape::set_points(const PoolVector<Vector3> &p_points) {
diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp
index 464ca60d31..ece8ad4bb0 100644
--- a/scene/resources/curve.cpp
+++ b/scene/resources/curve.cpp
@@ -268,7 +268,7 @@ void Curve::update_auto_tangents(int i) {
}
if (i + 1 < _points.size()) {
- if (p.right_mode == TANGENT_LINEAR && i + 1 < _points.size()) {
+ if (p.right_mode == TANGENT_LINEAR) {
Vector2 v = (_points[i + 1].pos - p.pos).normalized();
p.right_tangent = v.y / v.x;
}
diff --git a/scene/resources/cylinder_shape.cpp b/scene/resources/cylinder_shape.cpp
index 4fd829b349..f60f7ab376 100644
--- a/scene/resources/cylinder_shape.cpp
+++ b/scene/resources/cylinder_shape.cpp
@@ -68,6 +68,7 @@ void CylinderShape::_update_shape() {
d["radius"] = radius;
d["height"] = height;
PhysicsServer::get_singleton()->shape_set_data(get_shape(), d);
+ Shape::_update_shape();
}
void CylinderShape::set_radius(float p_radius) {
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index c7a815d8a4..5ae57fdb52 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -423,6 +423,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_stylebox("completion", "TextEdit", make_stylebox(tree_bg_png, 3, 3, 3, 3, 0, 0, 0, 0));
theme->set_icon("tab", "TextEdit", make_icon(tab_png));
+ theme->set_icon("folded", "TextEdit", make_icon(arrow_right_png));
+ theme->set_icon("fold", "TextEdit", make_icon(arrow_down_png));
theme->set_font("font", "TextEdit", default_font);
@@ -795,7 +797,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_font("bold_italics_font", "RichTextLabel", default_font);
theme->set_font("mono_font", "RichTextLabel", default_font);
- theme->set_color("default_color", "RichTextLabel", control_font_color);
+ theme->set_color("default_color", "RichTextLabel", Color(1, 1, 1));
theme->set_color("font_color_selected", "RichTextLabel", font_color_selection);
theme->set_color("selection_color", "RichTextLabel", Color(0.1, 0.1, 1, 0.8));
diff --git a/scene/resources/multimesh.cpp b/scene/resources/multimesh.cpp
index 1b406551ab..be0b9f9ac3 100644
--- a/scene/resources/multimesh.cpp
+++ b/scene/resources/multimesh.cpp
@@ -162,6 +162,16 @@ int MultiMesh::get_instance_count() const {
return instance_count;
}
+void MultiMesh::set_visible_instance_count(int p_count) {
+ ERR_FAIL_COND(p_count < -1);
+ VisualServer::get_singleton()->multimesh_set_visible_instances(multimesh, p_count);
+ visible_instance_count = p_count;
+}
+int MultiMesh::get_visible_instance_count() const {
+
+ return visible_instance_count;
+}
+
void MultiMesh::set_instance_transform(int p_instance, const Transform &p_transform) {
VisualServer::get_singleton()->multimesh_instance_set_transform(multimesh, p_instance, p_transform);
@@ -255,6 +265,8 @@ void MultiMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_instance_count", "count"), &MultiMesh::set_instance_count);
ClassDB::bind_method(D_METHOD("get_instance_count"), &MultiMesh::get_instance_count);
+ ClassDB::bind_method(D_METHOD("set_visible_instance_count", "count"), &MultiMesh::set_visible_instance_count);
+ ClassDB::bind_method(D_METHOD("get_visible_instance_count"), &MultiMesh::get_visible_instance_count);
ClassDB::bind_method(D_METHOD("set_instance_transform", "instance", "transform"), &MultiMesh::set_instance_transform);
ClassDB::bind_method(D_METHOD("set_instance_transform_2d", "instance", "transform"), &MultiMesh::set_instance_transform_2d);
ClassDB::bind_method(D_METHOD("get_instance_transform", "instance"), &MultiMesh::get_instance_transform);
@@ -276,6 +288,7 @@ void MultiMesh::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "transform_format", PROPERTY_HINT_ENUM, "2D,3D"), "set_transform_format", "get_transform_format");
ADD_PROPERTY(PropertyInfo(Variant::INT, "custom_data_format", PROPERTY_HINT_ENUM, "None,Byte,Float"), "set_custom_data_format", "get_custom_data_format");
ADD_PROPERTY(PropertyInfo(Variant::INT, "instance_count", PROPERTY_HINT_RANGE, "0,16384,1,or_greater"), "set_instance_count", "get_instance_count");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_instance_count", PROPERTY_HINT_RANGE, "-1,16384,1,or_greater"), "set_visible_instance_count", "get_visible_instance_count");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh");
ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR3_ARRAY, "transform_array", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_transform_array", "_get_transform_array");
ADD_PROPERTY(PropertyInfo(Variant::POOL_COLOR_ARRAY, "color_array", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_color_array", "_get_color_array");
@@ -299,6 +312,7 @@ MultiMesh::MultiMesh() {
color_format = COLOR_NONE;
custom_data_format = CUSTOM_DATA_NONE;
transform_format = TRANSFORM_2D;
+ visible_instance_count = -1;
instance_count = 0;
}
diff --git a/scene/resources/multimesh.h b/scene/resources/multimesh.h
index ac2c69e022..24b4beaa89 100644
--- a/scene/resources/multimesh.h
+++ b/scene/resources/multimesh.h
@@ -64,6 +64,7 @@ private:
ColorFormat color_format;
CustomDataFormat custom_data_format;
int instance_count;
+ int visible_instance_count;
protected:
static void _bind_methods();
@@ -93,6 +94,9 @@ public:
void set_instance_count(int p_count);
int get_instance_count() const;
+ void set_visible_instance_count(int p_count);
+ int get_visible_instance_count() const;
+
void set_instance_transform(int p_instance, const Transform &p_transform);
void set_instance_transform_2d(int p_instance, const Transform2D &p_transform);
Transform get_instance_transform(int p_instance) const;
diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp
index 626ed9f5b4..2c6f30f429 100644
--- a/scene/resources/packed_scene.cpp
+++ b/scene/resources/packed_scene.cpp
@@ -535,7 +535,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
float a = value;
float b = original;
- if (Math::abs(a - b) < CMP_EPSILON)
+ if (Math::is_equal_approx(a, b))
continue;
} else if (bool(Variant::evaluate(Variant::OP_EQUAL, value, original))) {
diff --git a/scene/resources/plane_shape.cpp b/scene/resources/plane_shape.cpp
index 419ff8f972..08f6ccd764 100644
--- a/scene/resources/plane_shape.cpp
+++ b/scene/resources/plane_shape.cpp
@@ -64,6 +64,7 @@ Vector<Vector3> PlaneShape::_gen_debug_mesh_lines() {
void PlaneShape::_update_shape() {
PhysicsServer::get_singleton()->shape_set_data(get_shape(), plane);
+ Shape::_update_shape();
}
void PlaneShape::set_plane(Plane p_plane) {
diff --git a/scene/resources/ray_shape.cpp b/scene/resources/ray_shape.cpp
index b7925d8a58..0acfffdc06 100644
--- a/scene/resources/ray_shape.cpp
+++ b/scene/resources/ray_shape.cpp
@@ -47,7 +47,7 @@ void RayShape::_update_shape() {
d["length"] = length;
d["slips_on_slope"] = slips_on_slope;
PhysicsServer::get_singleton()->shape_set_data(get_shape(), d);
- emit_changed();
+ Shape::_update_shape();
}
void RayShape::set_length(float p_length) {
diff --git a/scene/resources/shape.cpp b/scene/resources/shape.cpp
index 825b7f4c8b..6ba46f066c 100644
--- a/scene/resources/shape.cpp
+++ b/scene/resources/shape.cpp
@@ -96,6 +96,11 @@ Ref<ArrayMesh> Shape::get_debug_mesh() {
return debug_mesh_cache;
}
+void Shape::_update_shape() {
+ emit_changed();
+ debug_mesh_cache.unref();
+}
+
void Shape::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_margin", "margin"), &Shape::set_margin);
diff --git a/scene/resources/shape.h b/scene/resources/shape.h
index de99a967a8..ba763eaab1 100644
--- a/scene/resources/shape.h
+++ b/scene/resources/shape.h
@@ -51,6 +51,8 @@ protected:
Shape(RID p_shape);
virtual Vector<Vector3> _gen_debug_mesh_lines() = 0; // { return Vector<Vector3>(); }
+ virtual void _update_shape();
+
public:
virtual RID get_rid() const { return shape; }
diff --git a/scene/resources/sphere_shape.cpp b/scene/resources/sphere_shape.cpp
index 492cf3959d..af89413ced 100644
--- a/scene/resources/sphere_shape.cpp
+++ b/scene/resources/sphere_shape.cpp
@@ -58,6 +58,7 @@ Vector<Vector3> SphereShape::_gen_debug_mesh_lines() {
void SphereShape::_update_shape() {
PhysicsServer::get_singleton()->shape_set_data(get_shape(), radius);
+ Shape::_update_shape();
}
void SphereShape::set_radius(float p_radius) {
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index 4229147ba2..b8f21948c3 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -240,6 +240,9 @@ bool VisualShader::can_connect_nodes(Type p_type, int p_from_node, int p_from_po
if (!g->nodes.has(p_from_node))
return false;
+ if (p_from_node == p_to_node)
+ return false;
+
if (p_from_port < 0 || p_from_port >= g->nodes[p_from_node].node->get_output_port_count())
return false;