summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/animated_sprite.cpp8
-rw-r--r--scene/2d/canvas_item.cpp31
-rw-r--r--scene/2d/canvas_item.h3
-rw-r--r--scene/2d/cpu_particles_2d.cpp151
-rw-r--r--scene/2d/cpu_particles_2d.h1
-rw-r--r--scene/2d/joints_2d.cpp6
-rw-r--r--scene/2d/navigation_2d.cpp12
-rw-r--r--scene/2d/sprite.cpp4
-rw-r--r--scene/3d/arvr_nodes.h2
-rw-r--r--scene/3d/camera.cpp2
-rw-r--r--scene/3d/camera.h2
-rw-r--r--scene/3d/cpu_particles.cpp178
-rw-r--r--scene/3d/cpu_particles.h1
-rw-r--r--scene/3d/navigation_mesh.h2
-rw-r--r--scene/3d/particles.cpp7
-rw-r--r--scene/3d/physics_body.cpp4
-rw-r--r--scene/3d/soft_body.cpp11
-rw-r--r--scene/3d/sprite_3d.cpp4
-rw-r--r--scene/SCsub1
-rw-r--r--scene/animation/animation_blend_space_1d.cpp1
-rw-r--r--scene/animation/animation_blend_space_2d.cpp1
-rw-r--r--scene/animation/tween.cpp44
-rw-r--r--scene/animation/tween.h3
-rw-r--r--scene/debugger/SCsub5
-rw-r--r--scene/debugger/script_debugger_remote.cpp1280
-rw-r--r--scene/debugger/script_debugger_remote.h196
-rw-r--r--scene/gui/button.cpp8
-rw-r--r--scene/gui/control.cpp6
-rw-r--r--scene/gui/dialogs.cpp12
-rw-r--r--scene/gui/dialogs.h5
-rw-r--r--scene/gui/file_dialog.cpp28
-rw-r--r--scene/gui/file_dialog.h3
-rw-r--r--scene/gui/item_list.cpp1
-rw-r--r--scene/gui/label.cpp18
-rw-r--r--scene/gui/range.cpp1
-rw-r--r--scene/gui/rich_text_label.cpp115
-rw-r--r--scene/gui/rich_text_label.h8
-rw-r--r--scene/gui/slider.cpp8
-rw-r--r--scene/gui/spin_box.cpp6
-rw-r--r--scene/gui/text_edit.cpp169
-rw-r--r--scene/gui/text_edit.h16
-rw-r--r--scene/gui/tree.cpp9
-rw-r--r--scene/gui/video_player.cpp23
-rw-r--r--scene/gui/video_player.h7
-rw-r--r--scene/main/node.cpp2
-rw-r--r--scene/main/scene_tree.cpp89
-rw-r--r--scene/main/scene_tree.h25
-rw-r--r--scene/main/viewport.cpp132
-rw-r--r--scene/main/viewport.h36
-rw-r--r--scene/resources/animation.cpp120
-rw-r--r--scene/resources/default_theme/default_theme.cpp9
-rw-r--r--scene/resources/material.cpp53
-rw-r--r--scene/resources/particles_material.cpp2
-rw-r--r--scene/resources/ray_shape.cpp10
-rw-r--r--scene/resources/texture.cpp9
-rw-r--r--scene/resources/theme.cpp136
-rw-r--r--scene/resources/theme.h72
-rw-r--r--scene/resources/tile_set.cpp45
-rw-r--r--scene/resources/video_stream.h2
-rw-r--r--scene/resources/visual_shader.cpp38
-rw-r--r--scene/resources/visual_shader.h1
-rw-r--r--scene/resources/visual_shader_nodes.cpp17
62 files changed, 2665 insertions, 536 deletions
diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp
index 20ec06f033..9d02408641 100644
--- a/scene/2d/animated_sprite.cpp
+++ b/scene/2d/animated_sprite.cpp
@@ -604,10 +604,14 @@ bool AnimatedSprite::_is_playing() const {
void AnimatedSprite::play(const StringName &p_animation, const bool p_backwards) {
- if (p_animation)
+ backwards = p_backwards;
+
+ if (p_animation) {
set_animation(p_animation);
+ if (backwards && get_frame() == 0)
+ set_frame(frames->get_frame_count(p_animation) - 1);
+ }
- backwards = p_backwards;
_set_playing(true);
}
diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp
index b38fbfe981..fa23e3fd39 100644
--- a/scene/2d/canvas_item.cpp
+++ b/scene/2d/canvas_item.cpp
@@ -360,6 +360,10 @@ bool CanvasItem::_edit_is_selected_on_click(const Point2 &p_point, double p_tole
}
}
+Transform2D CanvasItem::_edit_get_transform() const {
+ return Transform2D(_edit_get_rotation(), _edit_get_position() + _edit_get_pivot());
+}
+
bool CanvasItem::is_visible_in_tree() const {
if (!is_inside_tree())
@@ -735,6 +739,19 @@ void CanvasItem::draw_polyline_colors(const Vector<Point2> &p_points, const Vect
VisualServer::get_singleton()->canvas_item_add_polyline(canvas_item, p_points, p_colors, p_width, p_antialiased);
}
+void CanvasItem::draw_arc(const Vector2 &p_center, float p_radius, float p_start_angle, float p_end_angle, int p_point_count, const Color &p_color, float p_width, bool p_antialiased) {
+
+ Vector<Point2> points;
+ points.resize(p_point_count);
+ const float delta_angle = p_end_angle - p_start_angle;
+ for (int i = 0; i < p_point_count; i++) {
+ float theta = (i / (p_point_count - 1.0f)) * delta_angle + p_start_angle;
+ points.set(i, p_center + Vector2(Math::cos(theta), Math::sin(theta)) * p_radius);
+ }
+
+ draw_polyline(points, p_color, p_width, p_antialiased);
+}
+
void CanvasItem::draw_multiline(const Vector<Point2> &p_points, const Color &p_color, float p_width, bool p_antialiased) {
ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
@@ -777,29 +794,29 @@ void CanvasItem::draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_fil
VisualServer::get_singleton()->canvas_item_add_line(
canvas_item,
- p_rect.position + Point2(-offset, 0),
+ p_rect.position + Size2(-offset, 0),
p_rect.position + Size2(p_rect.size.width + offset, 0),
p_color,
p_width,
p_antialiased);
VisualServer::get_singleton()->canvas_item_add_line(
canvas_item,
- p_rect.position + Point2(0, offset),
- p_rect.position + Size2(0, p_rect.size.height - offset),
+ p_rect.position + Size2(p_rect.size.width, offset),
+ p_rect.position + Size2(p_rect.size.width, p_rect.size.height - offset),
p_color,
p_width,
p_antialiased);
VisualServer::get_singleton()->canvas_item_add_line(
canvas_item,
- p_rect.position + Point2(-offset, p_rect.size.height),
p_rect.position + Size2(p_rect.size.width + offset, p_rect.size.height),
+ p_rect.position + Size2(-offset, p_rect.size.height),
p_color,
p_width,
p_antialiased);
VisualServer::get_singleton()->canvas_item_add_line(
canvas_item,
- p_rect.position + Point2(p_rect.size.width, offset),
- p_rect.position + Size2(p_rect.size.width, p_rect.size.height - offset),
+ p_rect.position + Size2(0, p_rect.size.height - offset),
+ p_rect.position + Size2(0, offset),
p_color,
p_width,
p_antialiased);
@@ -1120,6 +1137,7 @@ void CanvasItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("_edit_set_pivot", "pivot"), &CanvasItem::_edit_set_pivot);
ClassDB::bind_method(D_METHOD("_edit_get_pivot"), &CanvasItem::_edit_get_pivot);
ClassDB::bind_method(D_METHOD("_edit_use_pivot"), &CanvasItem::_edit_use_pivot);
+ ClassDB::bind_method(D_METHOD("_edit_get_transform"), &CanvasItem::_edit_get_transform);
ClassDB::bind_method(D_METHOD("get_canvas_item"), &CanvasItem::get_canvas_item);
@@ -1152,6 +1170,7 @@ void CanvasItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("draw_line", "from", "to", "color", "width", "antialiased"), &CanvasItem::draw_line, DEFVAL(1.0), DEFVAL(false));
ClassDB::bind_method(D_METHOD("draw_polyline", "points", "color", "width", "antialiased"), &CanvasItem::draw_polyline, DEFVAL(1.0), DEFVAL(false));
ClassDB::bind_method(D_METHOD("draw_polyline_colors", "points", "colors", "width", "antialiased"), &CanvasItem::draw_polyline_colors, DEFVAL(1.0), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("draw_arc", "center", "radius", "start_angle", "end_angle", "point_count", "color", "width", "antialiased"), &CanvasItem::draw_arc, DEFVAL(1.0), DEFVAL(false));
ClassDB::bind_method(D_METHOD("draw_multiline", "points", "color", "width", "antialiased"), &CanvasItem::draw_multiline, DEFVAL(1.0), DEFVAL(false));
ClassDB::bind_method(D_METHOD("draw_multiline_colors", "points", "colors", "width", "antialiased"), &CanvasItem::draw_multiline_colors, DEFVAL(1.0), DEFVAL(false));
ClassDB::bind_method(D_METHOD("draw_rect", "rect", "color", "filled", "width", "antialiased"), &CanvasItem::draw_rect, DEFVAL(true), DEFVAL(1.0), DEFVAL(false));
diff --git a/scene/2d/canvas_item.h b/scene/2d/canvas_item.h
index 9c6799a441..e51ee601e2 100644
--- a/scene/2d/canvas_item.h
+++ b/scene/2d/canvas_item.h
@@ -281,6 +281,8 @@ public:
virtual void _edit_set_pivot(const Point2 &p_pivot){};
virtual Point2 _edit_get_pivot() const { return Point2(); };
+ virtual Transform2D _edit_get_transform() const;
+
/* VISIBILITY */
void set_visible(bool p_visible);
@@ -305,6 +307,7 @@ public:
void draw_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, float p_width = 1.0, bool p_antialiased = false);
void draw_polyline(const Vector<Point2> &p_points, const Color &p_color, float p_width = 1.0, bool p_antialiased = false);
void draw_polyline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width = 1.0, bool p_antialiased = false);
+ void draw_arc(const Vector2 &p_center, float p_radius, float p_start_angle, float p_end_angle, int p_point_count, const Color &p_color, float p_width = 1.0, bool p_antialiased = false);
void draw_multiline(const Vector<Point2> &p_points, const Color &p_color, float p_width = 1.0, bool p_antialiased = false);
void draw_multiline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width = 1.0, bool p_antialiased = false);
void draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled = true, float p_width = 1.0, bool p_antialiased = false);
diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp
index 85c423964b..372d8f614b 100644
--- a/scene/2d/cpu_particles_2d.cpp
+++ b/scene/2d/cpu_particles_2d.cpp
@@ -37,6 +37,9 @@
void CPUParticles2D::set_emitting(bool p_emitting) {
+ if (emitting == p_emitting)
+ return;
+
emitting = p_emitting;
if (emitting)
set_process_internal(true);
@@ -257,8 +260,7 @@ void CPUParticles2D::restart() {
inactive_time = 0;
frame_remainder = 0;
cycle = 0;
-
- set_emitting(true);
+ emitting = false;
{
int pc = particles.size();
@@ -268,6 +270,8 @@ void CPUParticles2D::restart() {
w[i].active = false;
}
}
+
+ set_emitting(true);
}
void CPUParticles2D::set_direction(Vector2 p_direction) {
@@ -535,6 +539,74 @@ static float rand_from_seed(uint32_t &seed) {
return float(seed % uint32_t(65536)) / 65535.0;
}
+void CPUParticles2D::_update_internal() {
+
+ if (particles.size() == 0 || !is_visible_in_tree()) {
+ _set_redraw(false);
+ return;
+ }
+
+ float delta = get_process_delta_time();
+ if (emitting) {
+ inactive_time = 0;
+ } else {
+ inactive_time += delta;
+ if (inactive_time > lifetime * 1.2) {
+ set_process_internal(false);
+ _set_redraw(false);
+
+ //reset variables
+ time = 0;
+ inactive_time = 0;
+ frame_remainder = 0;
+ cycle = 0;
+ return;
+ }
+ }
+ _set_redraw(true);
+
+ if (time == 0 && pre_process_time > 0.0) {
+
+ float frame_time;
+ if (fixed_fps > 0)
+ frame_time = 1.0 / fixed_fps;
+ else
+ frame_time = 1.0 / 30.0;
+
+ float todo = pre_process_time;
+
+ while (todo >= 0) {
+ _particles_process(frame_time);
+ todo -= frame_time;
+ }
+ }
+
+ if (fixed_fps > 0) {
+ float frame_time = 1.0 / fixed_fps;
+ float decr = frame_time;
+
+ float ldelta = delta;
+ if (ldelta > 0.1) { //avoid recursive stalls if fps goes below 10
+ ldelta = 0.1;
+ } else if (ldelta <= 0.0) { //unlikely but..
+ ldelta = 0.001;
+ }
+ float todo = frame_remainder + ldelta;
+
+ while (todo >= frame_time) {
+ _particles_process(frame_time);
+ todo -= decr;
+ }
+
+ frame_remainder = todo;
+
+ } else {
+ _particles_process(delta);
+ }
+
+ _update_particle_data_buffer();
+}
+
void CPUParticles2D::_particles_process(float p_delta) {
p_delta *= speed_scale;
@@ -965,7 +1037,9 @@ void CPUParticles2D::_set_redraw(bool p_redraw) {
VS::get_singleton()->multimesh_set_visible_instances(multimesh, -1);
} else {
- VS::get_singleton()->disconnect("frame_pre_draw", this, "_update_render_thread");
+ if (VS::get_singleton()->is_connected("frame_pre_draw", this, "_update_render_thread")) {
+ VS::get_singleton()->disconnect("frame_pre_draw", this, "_update_render_thread");
+ }
VS::get_singleton()->canvas_item_set_update_when_visible(get_canvas_item(), false);
VS::get_singleton()->multimesh_set_visible_instances(multimesh, 0);
@@ -1000,6 +1074,10 @@ void CPUParticles2D::_notification(int p_what) {
}
if (p_what == NOTIFICATION_DRAW) {
+ // first update before rendering to avoid one frame delay after emitting starts
+ if (emitting && (time == 0))
+ _update_internal();
+
if (!redraw)
return; // don't add to render list
@@ -1017,71 +1095,7 @@ void CPUParticles2D::_notification(int p_what) {
}
if (p_what == NOTIFICATION_INTERNAL_PROCESS) {
-
- if (particles.size() == 0 || !is_visible_in_tree()) {
- _set_redraw(false);
- return;
- }
-
- float delta = get_process_delta_time();
- if (emitting) {
- inactive_time = 0;
- } else {
- inactive_time += delta;
- if (inactive_time > lifetime * 1.2) {
- set_process_internal(false);
- _set_redraw(false);
-
- //reset variables
- time = 0;
- inactive_time = 0;
- frame_remainder = 0;
- cycle = 0;
- return;
- }
- }
- _set_redraw(true);
-
- if (time == 0 && pre_process_time > 0.0) {
-
- float frame_time;
- if (fixed_fps > 0)
- frame_time = 1.0 / fixed_fps;
- else
- frame_time = 1.0 / 30.0;
-
- float todo = pre_process_time;
-
- while (todo >= 0) {
- _particles_process(frame_time);
- todo -= frame_time;
- }
- }
-
- if (fixed_fps > 0) {
- float frame_time = 1.0 / fixed_fps;
- float decr = frame_time;
-
- float ldelta = delta;
- if (ldelta > 0.1) { //avoid recursive stalls if fps goes below 10
- ldelta = 0.1;
- } else if (ldelta <= 0.0) { //unlikely but..
- ldelta = 0.001;
- }
- float todo = frame_remainder + ldelta;
-
- while (todo >= frame_time) {
- _particles_process(frame_time);
- todo -= decr;
- }
-
- frame_remainder = todo;
-
- } else {
- _particles_process(delta);
- }
-
- _update_particle_data_buffer();
+ _update_internal();
}
if (p_what == NOTIFICATION_TRANSFORM_CHANGED) {
@@ -1411,6 +1425,7 @@ CPUParticles2D::CPUParticles2D() {
frame_remainder = 0;
cycle = 0;
redraw = false;
+ emitting = false;
mesh = VisualServer::get_singleton()->mesh_create();
multimesh = VisualServer::get_singleton()->multimesh_create();
diff --git a/scene/2d/cpu_particles_2d.h b/scene/2d/cpu_particles_2d.h
index da668664b9..47b4568dd4 100644
--- a/scene/2d/cpu_particles_2d.h
+++ b/scene/2d/cpu_particles_2d.h
@@ -174,6 +174,7 @@ private:
Vector2 gravity;
+ void _update_internal();
void _particles_process(float p_delta);
void _update_particle_data_buffer();
diff --git a/scene/2d/joints_2d.cpp b/scene/2d/joints_2d.cpp
index 12fb18cedc..847d08b025 100644
--- a/scene/2d/joints_2d.cpp
+++ b/scene/2d/joints_2d.cpp
@@ -37,8 +37,8 @@
void Joint2D::_update_joint(bool p_only_free) {
if (joint.is_valid()) {
- if (ba.is_valid() && bb.is_valid())
- Physics2DServer::get_singleton()->body_remove_collision_exception(ba, bb);
+ if (ba.is_valid() && bb.is_valid() && exclude_from_collision)
+ Physics2DServer::get_singleton()->joint_disable_collisions_between_bodies(joint, false);
Physics2DServer::get_singleton()->free(joint);
joint = RID();
@@ -131,6 +131,8 @@ void Joint2D::set_exclude_nodes_from_collision(bool p_enable) {
if (exclude_from_collision == p_enable)
return;
+
+ _update_joint(true);
exclude_from_collision = p_enable;
_update_joint();
}
diff --git a/scene/2d/navigation_2d.cpp b/scene/2d/navigation_2d.cpp
index 5cf28d6c89..c7d2dabbae 100644
--- a/scene/2d/navigation_2d.cpp
+++ b/scene/2d/navigation_2d.cpp
@@ -541,7 +541,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect
if (CLOCK_TANGENT(apex_point, portal_left, left) >= 0) {
//process
- if (Math::is_zero_approx(portal_left.distance_squared_to(apex_point)) || CLOCK_TANGENT(apex_point, left, portal_right) > 0) {
+ if (portal_left.is_equal_approx(apex_point) || CLOCK_TANGENT(apex_point, left, portal_right) > 0) {
left_poly = p;
portal_left = left;
} else {
@@ -551,7 +551,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] != apex_point)
+ if (!path.size() || !path[path.size() - 1].is_equal_approx(apex_point))
path.push_back(apex_point);
skip = true;
}
@@ -559,7 +559,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 (Math::is_zero_approx(portal_right.distance_squared_to(apex_point)) || CLOCK_TANGENT(apex_point, right, portal_left) < 0) {
+ if (portal_right.is_equal_approx(apex_point) || CLOCK_TANGENT(apex_point, right, portal_left) < 0) {
right_poly = p;
portal_right = right;
} else {
@@ -569,7 +569,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] != apex_point)
+ if (!path.size() || !path[path.size() - 1].is_equal_approx(apex_point))
path.push_back(apex_point);
}
}
@@ -595,7 +595,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect
}
}
- if (!path.size() || !Math::is_zero_approx(path[path.size() - 1].distance_squared_to(begin_point))) {
+ if (!path.size() || !path[path.size() - 1].is_equal_approx(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
@@ -603,7 +603,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect
path.invert();
- if (path.size() <= 1 || !Math::is_zero_approx(path[path.size() - 1].distance_squared_to(end_point))) {
+ if (path.size() <= 1 || !path[path.size() - 1].is_equal_approx(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/sprite.cpp b/scene/2d/sprite.cpp
index d2e1e494e3..8cdfceea52 100644
--- a/scene/2d/sprite.cpp
+++ b/scene/2d/sprite.cpp
@@ -387,6 +387,10 @@ void Sprite::_validate_property(PropertyInfo &property) const {
property.hint_string = "0," + itos(vframes * hframes - 1) + ",1";
property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS;
}
+
+ if (property.name == "frame_coords") {
+ property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS;
+ }
}
void Sprite::_texture_changed() {
diff --git a/scene/3d/arvr_nodes.h b/scene/3d/arvr_nodes.h
index 8e735f7110..b647df70aa 100644
--- a/scene/3d/arvr_nodes.h
+++ b/scene/3d/arvr_nodes.h
@@ -55,7 +55,7 @@ public:
virtual Vector3 project_local_ray_normal(const Point2 &p_pos) const;
virtual Point2 unproject_position(const Vector3 &p_pos) const;
- virtual Vector3 project_position(const Point2 &p_point, float p_z_depth = 0) const;
+ virtual Vector3 project_position(const Point2 &p_point, float p_z_depth) const;
virtual Vector<Plane> get_frustum() const;
ARVRCamera();
diff --git a/scene/3d/camera.cpp b/scene/3d/camera.cpp
index 9797b5f3ab..4d9bb69778 100644
--- a/scene/3d/camera.cpp
+++ b/scene/3d/camera.cpp
@@ -486,7 +486,7 @@ void Camera::_bind_methods() {
ClassDB::bind_method(D_METHOD("project_ray_origin", "screen_point"), &Camera::project_ray_origin);
ClassDB::bind_method(D_METHOD("unproject_position", "world_point"), &Camera::unproject_position);
ClassDB::bind_method(D_METHOD("is_position_behind", "world_point"), &Camera::is_position_behind);
- ClassDB::bind_method(D_METHOD("project_position", "screen_point", "z_depth"), &Camera::project_position, DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("project_position", "screen_point", "z_depth"), &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);
diff --git a/scene/3d/camera.h b/scene/3d/camera.h
index 22223880c1..d81e097fc5 100644
--- a/scene/3d/camera.h
+++ b/scene/3d/camera.h
@@ -142,7 +142,7 @@ public:
virtual Vector3 project_local_ray_normal(const Point2 &p_pos) const;
virtual Point2 unproject_position(const Vector3 &p_pos) const;
bool is_position_behind(const Vector3 &p_pos) const;
- virtual Vector3 project_position(const Point2 &p_point, float p_z_depth = 0) const;
+ virtual Vector3 project_position(const Point2 &p_point, float p_z_depth) const;
Vector<Vector3> get_near_plane_points() const;
diff --git a/scene/3d/cpu_particles.cpp b/scene/3d/cpu_particles.cpp
index 93ff60bc4e..86daabefd2 100644
--- a/scene/3d/cpu_particles.cpp
+++ b/scene/3d/cpu_particles.cpp
@@ -46,9 +46,17 @@ PoolVector<Face3> CPUParticles::get_faces(uint32_t p_usage_flags) const {
void CPUParticles::set_emitting(bool p_emitting) {
+ if (emitting == p_emitting)
+ return;
+
emitting = p_emitting;
- if (emitting)
+ if (emitting) {
set_process_internal(true);
+
+ // first update before rendering to avoid one frame delay after emitting starts
+ if (time == 0)
+ _update_internal();
+ }
}
void CPUParticles::set_amount(int p_amount) {
@@ -232,8 +240,7 @@ void CPUParticles::restart() {
inactive_time = 0;
frame_remainder = 0;
cycle = 0;
-
- set_emitting(true);
+ emitting = false;
{
int pc = particles.size();
@@ -243,6 +250,8 @@ void CPUParticles::restart() {
w[i].active = false;
}
}
+
+ set_emitting(true);
}
void CPUParticles::set_direction(Vector3 p_direction) {
@@ -508,6 +517,81 @@ static float rand_from_seed(uint32_t &seed) {
return float(seed % uint32_t(65536)) / 65535.0;
}
+void CPUParticles::_update_internal() {
+
+ if (particles.size() == 0 || !is_visible_in_tree()) {
+ _set_redraw(false);
+ return;
+ }
+
+ float delta = get_process_delta_time();
+ if (emitting) {
+ inactive_time = 0;
+ } else {
+ inactive_time += delta;
+ if (inactive_time > lifetime * 1.2) {
+ set_process_internal(false);
+ _set_redraw(false);
+
+ //reset variables
+ time = 0;
+ inactive_time = 0;
+ frame_remainder = 0;
+ cycle = 0;
+ return;
+ }
+ }
+ _set_redraw(true);
+
+ bool processed = false;
+
+ if (time == 0 && pre_process_time > 0.0) {
+
+ float frame_time;
+ if (fixed_fps > 0)
+ frame_time = 1.0 / fixed_fps;
+ else
+ frame_time = 1.0 / 30.0;
+
+ float todo = pre_process_time;
+
+ while (todo >= 0) {
+ _particles_process(frame_time);
+ processed = true;
+ todo -= frame_time;
+ }
+ }
+
+ if (fixed_fps > 0) {
+ float frame_time = 1.0 / fixed_fps;
+ float decr = frame_time;
+
+ float ldelta = delta;
+ if (ldelta > 0.1) { //avoid recursive stalls if fps goes below 10
+ ldelta = 0.1;
+ } else if (ldelta <= 0.0) { //unlikely but..
+ ldelta = 0.001;
+ }
+ float todo = frame_remainder + ldelta;
+
+ while (todo >= frame_time) {
+ _particles_process(frame_time);
+ processed = true;
+ todo -= decr;
+ }
+
+ frame_remainder = todo;
+
+ } else {
+ _particles_process(delta);
+ processed = true;
+ }
+
+ if (processed) {
+ _update_particle_data_buffer();
+ }
+}
+
void CPUParticles::_particles_process(float p_delta) {
p_delta *= speed_scale;
@@ -1040,7 +1124,9 @@ void CPUParticles::_set_redraw(bool p_redraw) {
VS::get_singleton()->instance_geometry_set_flag(get_instance(), VS::INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE, true);
VS::get_singleton()->multimesh_set_visible_instances(multimesh, -1);
} else {
- VS::get_singleton()->disconnect("frame_pre_draw", this, "_update_render_thread");
+ if (VS::get_singleton()->is_connected("frame_pre_draw", this, "_update_render_thread")) {
+ VS::get_singleton()->disconnect("frame_pre_draw", this, "_update_render_thread");
+ }
VS::get_singleton()->instance_geometry_set_flag(get_instance(), VS::INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE, false);
VS::get_singleton()->multimesh_set_visible_instances(multimesh, 0);
}
@@ -1068,85 +1154,24 @@ void CPUParticles::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
set_process_internal(emitting);
+
+ // first update before rendering to avoid one frame delay after emitting starts
+ if (emitting && (time == 0))
+ _update_internal();
}
if (p_what == NOTIFICATION_EXIT_TREE) {
_set_redraw(false);
}
- if (p_what == NOTIFICATION_INTERNAL_PROCESS) {
-
- if (particles.size() == 0 || !is_visible_in_tree()) {
- _set_redraw(false);
- return;
- }
-
- float delta = get_process_delta_time();
- if (emitting) {
- inactive_time = 0;
- } else {
- inactive_time += delta;
- if (inactive_time > lifetime * 1.2) {
- set_process_internal(false);
- _set_redraw(false);
-
- //reset variables
- time = 0;
- inactive_time = 0;
- frame_remainder = 0;
- cycle = 0;
- return;
- }
- }
- _set_redraw(true);
-
- bool processed = false;
-
- if (time == 0 && pre_process_time > 0.0) {
-
- float frame_time;
- if (fixed_fps > 0)
- frame_time = 1.0 / fixed_fps;
- else
- frame_time = 1.0 / 30.0;
-
- float todo = pre_process_time;
-
- while (todo >= 0) {
- _particles_process(frame_time);
- processed = true;
- todo -= frame_time;
- }
- }
-
- if (fixed_fps > 0) {
- float frame_time = 1.0 / fixed_fps;
- float decr = frame_time;
-
- float ldelta = delta;
- if (ldelta > 0.1) { //avoid recursive stalls if fps goes below 10
- ldelta = 0.1;
- } else if (ldelta <= 0.0) { //unlikely but..
- ldelta = 0.001;
- }
- float todo = frame_remainder + ldelta;
-
- while (todo >= frame_time) {
- _particles_process(frame_time);
- processed = true;
- todo -= decr;
- }
-
- frame_remainder = todo;
-
- } else {
- _particles_process(delta);
- processed = true;
- }
+ if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
+ // first update before rendering to avoid one frame delay after emitting starts
+ if (emitting && (time == 0))
+ _update_internal();
+ }
- if (processed) {
- _update_particle_data_buffer();
- }
+ if (p_what == NOTIFICATION_INTERNAL_PROCESS) {
+ _update_internal();
}
if (p_what == NOTIFICATION_TRANSFORM_CHANGED) {
@@ -1472,6 +1497,7 @@ CPUParticles::CPUParticles() {
frame_remainder = 0;
cycle = 0;
redraw = false;
+ emitting = false;
set_notify_transform(true);
diff --git a/scene/3d/cpu_particles.h b/scene/3d/cpu_particles.h
index 66b37f359a..635265be7f 100644
--- a/scene/3d/cpu_particles.h
+++ b/scene/3d/cpu_particles.h
@@ -173,6 +173,7 @@ private:
Vector3 gravity;
+ void _update_internal();
void _particles_process(float p_delta);
void _update_particle_data_buffer();
diff --git a/scene/3d/navigation_mesh.h b/scene/3d/navigation_mesh.h
index 8467f80f0e..d5de653e40 100644
--- a/scene/3d/navigation_mesh.h
+++ b/scene/3d/navigation_mesh.h
@@ -124,7 +124,7 @@ public:
void set_collision_mask_bit(int p_bit, bool p_value);
bool get_collision_mask_bit(int p_bit) const;
- void set_source_geometry_mode(int p_source_mode);
+ void set_source_geometry_mode(int p_geometry_mode);
int get_source_geometry_mode() const;
void set_source_group_name(StringName p_group_name);
diff --git a/scene/3d/particles.cpp b/scene/3d/particles.cpp
index 241eb7d1ca..06b5613eb8 100644
--- a/scene/3d/particles.cpp
+++ b/scene/3d/particles.cpp
@@ -331,6 +331,13 @@ void Particles::_notification(int p_what) {
set_process_internal(false);
}
}
+
+ if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
+ // make sure particles are updated before rendering occurs if they were active before
+ if (is_visible_in_tree() && !VS::get_singleton()->particles_is_inactive(particles)) {
+ VS::get_singleton()->particles_request_process(particles);
+ }
+ }
}
void Particles::_bind_methods() {
diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp
index a02cc4bee6..a107c3bf7a 100644
--- a/scene/3d/physics_body.cpp
+++ b/scene/3d/physics_body.cpp
@@ -1201,7 +1201,7 @@ Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Ve
if (p_stop_on_slope) {
if ((lv_n + p_floor_direction).length() < 0.01 && collision.travel.length() < 1) {
Transform gt = get_global_transform();
- gt.origin -= collision.travel;
+ gt.origin -= collision.travel.slide(p_floor_direction);
set_global_transform(gt);
return Vector3();
}
@@ -1265,7 +1265,7 @@ Vector3 KinematicBody::move_and_slide_with_snap(const Vector3 &p_linear_velocity
if (p_stop_on_slope) {
// move and collide may stray the object a bit because of pre un-stucking,
// so only ensure that motion happens on floor direction in this case.
- col.travel = p_floor_direction * p_floor_direction.dot(col.travel);
+ col.travel = col.travel.project(p_floor_direction);
}
} else {
apply = false; //snapped with floor direction, but did not snap to a floor, do not snap.
diff --git a/scene/3d/soft_body.cpp b/scene/3d/soft_body.cpp
index 6c3949a0a8..6883da7f6d 100644
--- a/scene/3d/soft_body.cpp
+++ b/scene/3d/soft_body.cpp
@@ -115,7 +115,7 @@ SoftBody::PinnedPoint SoftBody::PinnedPoint::operator=(const PinnedPoint &obj) {
void SoftBody::_update_pickable() {
if (!is_inside_tree())
return;
- bool pickable = ray_pickable && is_inside_tree() && is_visible_in_tree();
+ bool pickable = ray_pickable && is_visible_in_tree();
PhysicsServer::get_singleton()->soft_body_set_ray_pickable(physics_rid, pickable);
}
@@ -395,6 +395,8 @@ void SoftBody::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "damping_coefficient", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_damping_coefficient", "get_damping_coefficient");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "drag_coefficient", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_coefficient", "get_drag_coefficient");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "pose_matching_coefficient", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_pose_matching_coefficient", "get_pose_matching_coefficient");
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ray_pickable"), "set_ray_pickable", "is_ray_pickable");
}
String SoftBody::get_configuration_warning() const {
@@ -460,7 +462,9 @@ void SoftBody::update_physics_server() {
} else {
PhysicsServer::get_singleton()->soft_body_set_mesh(physics_rid, NULL);
- VS::get_singleton()->disconnect("frame_pre_draw", this, "_draw_soft_mesh");
+ if (VS::get_singleton()->is_connected("frame_pre_draw", this, "_draw_soft_mesh")) {
+ VS::get_singleton()->disconnect("frame_pre_draw", this, "_draw_soft_mesh");
+ }
}
}
@@ -698,7 +702,8 @@ SoftBody::SoftBody() :
collision_mask(1),
collision_layer(1),
simulation_started(false),
- pinned_points_cache_dirty(true) {
+ pinned_points_cache_dirty(true),
+ ray_pickable(true) {
PhysicsServer::get_singleton()->body_attach_object_instance_id(physics_rid, get_instance_id());
//set_notify_transform(true);
diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp
index a8d2f4d415..adcd80b0ab 100644
--- a/scene/3d/sprite_3d.cpp
+++ b/scene/3d/sprite_3d.cpp
@@ -662,6 +662,10 @@ void Sprite3D::_validate_property(PropertyInfo &property) const {
property.hint_string = "0," + itos(vframes * hframes - 1) + ",1";
property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS;
}
+
+ if (property.name == "frame_coords") {
+ property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS;
+ }
}
void Sprite3D::_bind_methods() {
diff --git a/scene/SCsub b/scene/SCsub
index d8839ce3a8..1c5b87b87a 100644
--- a/scene/SCsub
+++ b/scene/SCsub
@@ -30,6 +30,7 @@ SConscript('2d/SCsub')
SConscript('animation/SCsub')
SConscript('audio/SCsub')
SConscript('resources/SCsub')
+SConscript('debugger/SCsub')
# Build it all as a library
diff --git a/scene/animation/animation_blend_space_1d.cpp b/scene/animation/animation_blend_space_1d.cpp
index 416a291da1..7fe544eaab 100644
--- a/scene/animation/animation_blend_space_1d.cpp
+++ b/scene/animation/animation_blend_space_1d.cpp
@@ -157,6 +157,7 @@ Ref<AnimationRootNode> AnimationNodeBlendSpace1D::get_blend_point_node(int p_poi
void AnimationNodeBlendSpace1D::remove_blend_point(int p_point) {
ERR_FAIL_INDEX(p_point, blend_points_used);
+ ERR_FAIL_COND(blend_points[p_point].node.is_null());
blend_points[p_point].node->disconnect("tree_changed", this, "_tree_changed");
for (int i = p_point; i < blend_points_used - 1; i++) {
diff --git a/scene/animation/animation_blend_space_2d.cpp b/scene/animation/animation_blend_space_2d.cpp
index 75031f0149..b04eefbe31 100644
--- a/scene/animation/animation_blend_space_2d.cpp
+++ b/scene/animation/animation_blend_space_2d.cpp
@@ -113,6 +113,7 @@ Ref<AnimationRootNode> AnimationNodeBlendSpace2D::get_blend_point_node(int p_poi
void AnimationNodeBlendSpace2D::remove_blend_point(int p_point) {
ERR_FAIL_INDEX(p_point, blend_points_used);
+ ERR_FAIL_COND(blend_points[p_point].node.is_null());
blend_points[p_point].node->disconnect("tree_changed", this, "_tree_changed");
for (int i = 0; i < triangles.size(); i++) {
diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp
index 1f9793190d..a7d936fcd3 100644
--- a/scene/animation/tween.cpp
+++ b/scene/animation/tween.cpp
@@ -281,7 +281,7 @@ void Tween::_bind_methods() {
BIND_ENUM_CONSTANT(EASE_OUT_IN);
}
-Variant &Tween::_get_initial_val(InterpolateData &p_data) {
+Variant Tween::_get_initial_val(const InterpolateData &p_data) const {
// What type of data are we interpolating?
switch (p_data.type) {
@@ -299,7 +299,7 @@ Variant &Tween::_get_initial_val(InterpolateData &p_data) {
ERR_FAIL_COND_V(object == NULL, p_data.initial_val);
// Are we targeting a property or a method?
- static Variant initial_val;
+ Variant initial_val;
if (p_data.type == TARGETING_PROPERTY) {
// Get the property from the target object
bool valid = false;
@@ -322,6 +322,41 @@ Variant &Tween::_get_initial_val(InterpolateData &p_data) {
return p_data.delta_val;
}
+Variant Tween::_get_final_val(const InterpolateData &p_data) const {
+ switch (p_data.type) {
+ case FOLLOW_PROPERTY:
+ case FOLLOW_METHOD: {
+ // Get the object that is being followed
+ Object *target = ObjectDB::get_instance(p_data.target_id);
+ ERR_FAIL_COND_V(target == NULL, p_data.initial_val);
+
+ // We want to figure out the final value
+ Variant final_val;
+ if (p_data.type == FOLLOW_PROPERTY) {
+ // Read the property as-is
+ bool valid = false;
+ final_val = target->get_indexed(p_data.target_key, &valid);
+ ERR_FAIL_COND_V(!valid, p_data.initial_val);
+ } else {
+ // We're looking at a method. Call the method on the target object
+ Variant::CallError error;
+ final_val = target->call(p_data.target_key[0], NULL, 0, error);
+ ERR_FAIL_COND_V(error.error != Variant::CallError::CALL_OK, p_data.initial_val);
+ }
+
+ // If we're looking at an INT value, instead convert it to a REAL
+ // This is better for interpolation
+ if (final_val.get_type() == Variant::INT) final_val = final_val.operator real_t();
+
+ return final_val;
+ }
+ default: {
+ // If we're not following a final value/method, use the final value from the data
+ return p_data.final_val;
+ }
+ }
+}
+
Variant &Tween::_get_delta_val(InterpolateData &p_data) {
// What kind of data are we interpolating?
@@ -384,7 +419,7 @@ Variant &Tween::_get_delta_val(InterpolateData &p_data) {
Variant Tween::_run_equation(InterpolateData &p_data) {
// Get the initial and delta values from the data
- Variant &initial_val = _get_initial_val(p_data);
+ Variant initial_val = _get_initial_val(p_data);
Variant &delta_val = _get_delta_val(p_data);
Variant result;
@@ -718,7 +753,8 @@ void Tween::_tween_process(float p_delta) {
// Is the tween now finished?
if (data.finish) {
// Set it to the final value directly
- _apply_tween_value(data, data.final_val);
+ Variant final_val = _get_final_val(data);
+ _apply_tween_value(data, final_val);
// Mark the tween as completed and emit the signal
data.elapsed = 0;
diff --git a/scene/animation/tween.h b/scene/animation/tween.h
index 64ce099ecd..574238f5c9 100644
--- a/scene/animation/tween.h
+++ b/scene/animation/tween.h
@@ -127,7 +127,8 @@ private:
real_t _run_equation(TransitionType p_trans_type, EaseType p_ease_type, real_t t, real_t b, real_t c, real_t d);
Variant &_get_delta_val(InterpolateData &p_data);
- Variant &_get_initial_val(InterpolateData &p_data);
+ Variant _get_initial_val(const InterpolateData &p_data) const;
+ Variant _get_final_val(const InterpolateData &p_data) const;
Variant _run_equation(InterpolateData &p_data);
bool _calc_delta_val(const Variant &p_initial_val, const Variant &p_final_val, Variant &p_delta_val);
bool _apply_tween_value(InterpolateData &p_data, Variant &value);
diff --git a/scene/debugger/SCsub b/scene/debugger/SCsub
new file mode 100644
index 0000000000..b01e2fd54d
--- /dev/null
+++ b/scene/debugger/SCsub
@@ -0,0 +1,5 @@
+#!/usr/bin/env python
+
+Import('env')
+
+env.add_source_files(env.scene_sources, "*.cpp")
diff --git a/scene/debugger/script_debugger_remote.cpp b/scene/debugger/script_debugger_remote.cpp
new file mode 100644
index 0000000000..c3c6a088cb
--- /dev/null
+++ b/scene/debugger/script_debugger_remote.cpp
@@ -0,0 +1,1280 @@
+/*************************************************************************/
+/* script_debugger_remote.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 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 "script_debugger_remote.h"
+
+#include "core/engine.h"
+#include "core/io/ip.h"
+#include "core/io/marshalls.h"
+#include "core/os/input.h"
+#include "core/os/os.h"
+#include "core/project_settings.h"
+#include "scene/main/node.h"
+#include "scene/main/scene_tree.h"
+#include "scene/main/viewport.h"
+#include "scene/resources/packed_scene.h"
+#include "servers/visual_server.h"
+
+void ScriptDebuggerRemote::_send_video_memory() {
+
+ List<ResourceUsage> usage;
+ if (resource_usage_func)
+ resource_usage_func(&usage);
+
+ usage.sort();
+
+ packet_peer_stream->put_var("message:video_mem");
+ packet_peer_stream->put_var(usage.size() * 4);
+
+ for (List<ResourceUsage>::Element *E = usage.front(); E; E = E->next()) {
+
+ packet_peer_stream->put_var(E->get().path);
+ packet_peer_stream->put_var(E->get().type);
+ packet_peer_stream->put_var(E->get().format);
+ packet_peer_stream->put_var(E->get().vram);
+ }
+}
+
+Error ScriptDebuggerRemote::connect_to_host(const String &p_host, uint16_t p_port) {
+
+ IP_Address ip;
+ if (p_host.is_valid_ip_address())
+ ip = p_host;
+ else
+ ip = IP::get_singleton()->resolve_hostname(p_host);
+
+ int port = p_port;
+
+ const int tries = 6;
+ int waits[tries] = { 1, 10, 100, 1000, 1000, 1000 };
+
+ tcp_client->connect_to_host(ip, port);
+
+ for (int i = 0; i < tries; i++) {
+
+ if (tcp_client->get_status() == StreamPeerTCP::STATUS_CONNECTED) {
+ print_verbose("Remote Debugger: Connected!");
+ break;
+ } else {
+
+ const int ms = waits[i];
+ OS::get_singleton()->delay_usec(ms * 1000);
+ print_verbose("Remote Debugger: Connection failed with status: '" + String::num(tcp_client->get_status()) + "', retrying in " + String::num(ms) + " msec.");
+ };
+ };
+
+ if (tcp_client->get_status() != StreamPeerTCP::STATUS_CONNECTED) {
+
+ ERR_PRINTS("Remote Debugger: Unable to connect. Status: " + String::num(tcp_client->get_status()) + ".");
+ return FAILED;
+ };
+
+ packet_peer_stream->set_stream_peer(tcp_client);
+
+ return OK;
+}
+
+void ScriptDebuggerRemote::_put_variable(const String &p_name, const Variant &p_variable) {
+
+ packet_peer_stream->put_var(p_name);
+
+ Variant var = p_variable;
+ if (p_variable.get_type() == Variant::OBJECT && !ObjectDB::instance_validate(p_variable)) {
+ var = Variant();
+ }
+
+ int len = 0;
+ Error err = encode_variant(var, NULL, len, true);
+ if (err != OK)
+ ERR_PRINT("Failed to encode variant.");
+
+ if (len > packet_peer_stream->get_output_buffer_max_size()) { //limit to max size
+ packet_peer_stream->put_var(Variant());
+ } else {
+ packet_peer_stream->put_var(var);
+ }
+}
+
+void ScriptDebuggerRemote::_save_node(ObjectID id, const String &p_path) {
+
+ Node *node = Object::cast_to<Node>(ObjectDB::get_instance(id));
+ ERR_FAIL_COND(!node);
+
+ Ref<PackedScene> ps = memnew(PackedScene);
+ ps->pack(node);
+ ResourceSaver::save(p_path, ps);
+}
+
+void ScriptDebuggerRemote::debug(ScriptLanguage *p_script, bool p_can_continue, bool p_is_error_breakpoint) {
+
+ //this function is called when there is a debugger break (bug on script)
+ //or when execution is paused from editor
+
+ if (skip_breakpoints && !p_is_error_breakpoint)
+ return;
+
+ ERR_FAIL_COND_MSG(!tcp_client->is_connected_to_host(), "Script Debugger failed to connect, but being used anyway.");
+
+ packet_peer_stream->put_var("debug_enter");
+ packet_peer_stream->put_var(2);
+ packet_peer_stream->put_var(p_can_continue);
+ packet_peer_stream->put_var(p_script->debug_get_error());
+
+ skip_profile_frame = true; // to avoid super long frame time for the frame
+
+ Input::MouseMode mouse_mode = Input::get_singleton()->get_mouse_mode();
+ if (mouse_mode != Input::MOUSE_MODE_VISIBLE)
+ Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
+
+ uint64_t loop_begin_usec = 0;
+ uint64_t loop_time_sec = 0;
+ while (true) {
+ loop_begin_usec = OS::get_singleton()->get_ticks_usec();
+
+ _get_output();
+
+ if (packet_peer_stream->get_available_packet_count() > 0) {
+
+ Variant var;
+ Error err = packet_peer_stream->get_var(var);
+
+ ERR_CONTINUE(err != OK);
+ ERR_CONTINUE(var.get_type() != Variant::ARRAY);
+
+ Array cmd = var;
+
+ ERR_CONTINUE(cmd.size() == 0);
+ ERR_CONTINUE(cmd[0].get_type() != Variant::STRING);
+
+ String command = cmd[0];
+
+ if (command == "get_stack_dump") {
+
+ packet_peer_stream->put_var("stack_dump");
+ int slc = p_script->debug_get_stack_level_count();
+ packet_peer_stream->put_var(slc);
+
+ for (int i = 0; i < slc; i++) {
+
+ Dictionary d;
+ d["file"] = p_script->debug_get_stack_level_source(i);
+ d["line"] = p_script->debug_get_stack_level_line(i);
+ d["function"] = p_script->debug_get_stack_level_function(i);
+ //d["id"]=p_script->debug_get_stack_level_
+ d["id"] = 0;
+
+ packet_peer_stream->put_var(d);
+ }
+
+ } else if (command == "get_stack_frame_vars") {
+
+ cmd.remove(0);
+ ERR_CONTINUE(cmd.size() != 1);
+ int lv = cmd[0];
+
+ List<String> members;
+ List<Variant> member_vals;
+ if (ScriptInstance *inst = p_script->debug_get_stack_level_instance(lv)) {
+ members.push_back("self");
+ member_vals.push_back(inst->get_owner());
+ }
+ p_script->debug_get_stack_level_members(lv, &members, &member_vals);
+ ERR_CONTINUE(members.size() != member_vals.size());
+
+ List<String> locals;
+ List<Variant> local_vals;
+ p_script->debug_get_stack_level_locals(lv, &locals, &local_vals);
+ ERR_CONTINUE(locals.size() != local_vals.size());
+
+ List<String> globals;
+ List<Variant> globals_vals;
+ p_script->debug_get_globals(&globals, &globals_vals);
+ ERR_CONTINUE(globals.size() != globals_vals.size());
+
+ packet_peer_stream->put_var("stack_frame_vars");
+ packet_peer_stream->put_var(3 + (locals.size() + members.size() + globals.size()) * 2);
+
+ { //locals
+ packet_peer_stream->put_var(locals.size());
+
+ List<String>::Element *E = locals.front();
+ List<Variant>::Element *F = local_vals.front();
+
+ while (E) {
+ _put_variable(E->get(), F->get());
+
+ E = E->next();
+ F = F->next();
+ }
+ }
+
+ { //members
+ packet_peer_stream->put_var(members.size());
+
+ List<String>::Element *E = members.front();
+ List<Variant>::Element *F = member_vals.front();
+
+ while (E) {
+
+ _put_variable(E->get(), F->get());
+
+ E = E->next();
+ F = F->next();
+ }
+ }
+
+ { //globals
+ packet_peer_stream->put_var(globals.size());
+
+ List<String>::Element *E = globals.front();
+ List<Variant>::Element *F = globals_vals.front();
+
+ while (E) {
+ _put_variable(E->get(), F->get());
+
+ E = E->next();
+ F = F->next();
+ }
+ }
+
+ } else if (command == "step") {
+
+ set_depth(-1);
+ set_lines_left(1);
+ break;
+ } else if (command == "next") {
+
+ set_depth(0);
+ set_lines_left(1);
+ break;
+
+ } else if (command == "continue") {
+ set_depth(-1);
+ set_lines_left(-1);
+ OS::get_singleton()->move_window_to_foreground();
+ break;
+ } else if (command == "break") {
+ ERR_PRINT("Got break when already broke!");
+ break;
+ } else if (command == "request_scene_tree") {
+
+#ifdef DEBUG_ENABLED
+ if (scene_tree)
+ scene_tree->_debugger_request_tree();
+#endif
+ } else if (command == "request_video_mem") {
+
+ _send_video_memory();
+ } else if (command == "inspect_object") {
+
+ ObjectID id = cmd[1];
+ _send_object_id(id);
+ } else if (command == "set_object_property") {
+
+ _set_object_property(cmd[1], cmd[2], cmd[3]);
+
+ } else if (command == "override_camera_2D:set") {
+ bool enforce = cmd[1];
+
+ if (scene_tree) {
+ scene_tree->get_root()->enable_canvas_transform_override(enforce);
+ }
+ } else if (command == "override_camera_2D:transform") {
+ Transform2D transform = cmd[1];
+
+ if (scene_tree) {
+ scene_tree->get_root()->set_canvas_transform_override(transform);
+ }
+ } else if (command == "override_camera_3D:set") {
+ bool enable = cmd[1];
+
+ if (scene_tree) {
+ scene_tree->get_root()->enable_camera_override(enable);
+ }
+ } else if (command == "override_camera_3D:transform") {
+ Transform transform = cmd[1];
+ bool is_perspective = cmd[2];
+ float size_or_fov = cmd[3];
+ float near = cmd[4];
+ float far = cmd[5];
+
+ if (scene_tree) {
+ if (is_perspective) {
+ scene_tree->get_root()->set_camera_override_perspective(size_or_fov, near, far);
+ } else {
+ scene_tree->get_root()->set_camera_override_orthogonal(size_or_fov, near, far);
+ }
+ scene_tree->get_root()->set_camera_override_transform(transform);
+ }
+
+ } else if (command == "reload_scripts") {
+ reload_all_scripts = true;
+ } else if (command == "breakpoint") {
+
+ bool set = cmd[3];
+ if (set)
+ insert_breakpoint(cmd[2], cmd[1]);
+ else
+ remove_breakpoint(cmd[2], cmd[1]);
+
+ } else if (command == "save_node") {
+ _save_node(cmd[1], cmd[2]);
+ } else if (command == "set_skip_breakpoints") {
+ skip_breakpoints = cmd[1];
+ } else {
+ _parse_live_edit(cmd);
+ }
+
+ } else {
+ OS::get_singleton()->delay_usec(10000);
+ OS::get_singleton()->process_and_drop_events();
+ }
+
+ // This is for the camera override to stay live even when the game is paused from the editor
+ loop_time_sec = (OS::get_singleton()->get_ticks_usec() - loop_begin_usec) / 1000000.0f;
+ VisualServer::get_singleton()->sync();
+ if (VisualServer::get_singleton()->has_changed()) {
+ VisualServer::get_singleton()->draw(true, loop_time_sec * Engine::get_singleton()->get_time_scale());
+ }
+ }
+
+ packet_peer_stream->put_var("debug_exit");
+ packet_peer_stream->put_var(0);
+
+ if (mouse_mode != Input::MOUSE_MODE_VISIBLE)
+ Input::get_singleton()->set_mouse_mode(mouse_mode);
+}
+
+void ScriptDebuggerRemote::_get_output() {
+
+ mutex->lock();
+ if (output_strings.size()) {
+
+ locking = true;
+ packet_peer_stream->put_var("output");
+ packet_peer_stream->put_var(output_strings.size());
+
+ while (output_strings.size()) {
+
+ packet_peer_stream->put_var(output_strings.front()->get());
+ output_strings.pop_front();
+ }
+ locking = false;
+ }
+
+ if (n_messages_dropped > 0) {
+ Message msg;
+ msg.message = "Too many messages! " + String::num_int64(n_messages_dropped) + " messages were dropped.";
+ messages.push_back(msg);
+ n_messages_dropped = 0;
+ }
+
+ while (messages.size()) {
+ locking = true;
+ packet_peer_stream->put_var("message:" + messages.front()->get().message);
+ packet_peer_stream->put_var(messages.front()->get().data.size());
+ for (int i = 0; i < messages.front()->get().data.size(); i++) {
+ packet_peer_stream->put_var(messages.front()->get().data[i]);
+ }
+ messages.pop_front();
+ locking = false;
+ }
+
+ if (n_errors_dropped == 1) {
+ // Only print one message about dropping per second
+ OutputError oe;
+ oe.error = "TOO_MANY_ERRORS";
+ oe.error_descr = "Too many errors! Ignoring errors for up to 1 second.";
+ oe.warning = false;
+ uint64_t time = OS::get_singleton()->get_ticks_msec();
+ oe.hr = time / 3600000;
+ oe.min = (time / 60000) % 60;
+ oe.sec = (time / 1000) % 60;
+ oe.msec = time % 1000;
+ errors.push_back(oe);
+ }
+
+ if (n_warnings_dropped == 1) {
+ // Only print one message about dropping per second
+ OutputError oe;
+ oe.error = "TOO_MANY_WARNINGS";
+ oe.error_descr = "Too many warnings! Ignoring warnings for up to 1 second.";
+ oe.warning = true;
+ uint64_t time = OS::get_singleton()->get_ticks_msec();
+ oe.hr = time / 3600000;
+ oe.min = (time / 60000) % 60;
+ oe.sec = (time / 1000) % 60;
+ oe.msec = time % 1000;
+ errors.push_back(oe);
+ }
+
+ while (errors.size()) {
+ locking = true;
+ packet_peer_stream->put_var("error");
+ OutputError oe = errors.front()->get();
+
+ packet_peer_stream->put_var(oe.callstack.size() + 2);
+
+ Array error_data;
+
+ error_data.push_back(oe.hr);
+ error_data.push_back(oe.min);
+ error_data.push_back(oe.sec);
+ error_data.push_back(oe.msec);
+ error_data.push_back(oe.source_func);
+ error_data.push_back(oe.source_file);
+ error_data.push_back(oe.source_line);
+ error_data.push_back(oe.error);
+ error_data.push_back(oe.error_descr);
+ error_data.push_back(oe.warning);
+ packet_peer_stream->put_var(error_data);
+ packet_peer_stream->put_var(oe.callstack.size());
+ for (int i = 0; i < oe.callstack.size(); i++) {
+ packet_peer_stream->put_var(oe.callstack[i]);
+ }
+
+ errors.pop_front();
+ locking = false;
+ }
+ mutex->unlock();
+}
+
+void ScriptDebuggerRemote::line_poll() {
+
+ //the purpose of this is just processing events every now and then when the script might get too busy
+ //otherwise bugs like infinite loops can't be caught
+ if (poll_every % 2048 == 0)
+ _poll_events();
+ poll_every++;
+}
+
+void ScriptDebuggerRemote::_err_handler(void *ud, const char *p_func, const char *p_file, int p_line, const char *p_err, const char *p_descr, ErrorHandlerType p_type) {
+
+ if (p_type == ERR_HANDLER_SCRIPT)
+ return; //ignore script errors, those go through debugger
+
+ Vector<ScriptLanguage::StackInfo> si;
+
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ si = ScriptServer::get_language(i)->debug_get_current_stack_info();
+ if (si.size())
+ break;
+ }
+
+ ScriptDebuggerRemote *sdr = (ScriptDebuggerRemote *)ud;
+ sdr->send_error(p_func, p_file, p_line, p_err, p_descr, p_type, si);
+}
+
+bool ScriptDebuggerRemote::_parse_live_edit(const Array &p_command) {
+
+#ifdef DEBUG_ENABLED
+
+ String cmdstr = p_command[0];
+ if (!scene_tree || !cmdstr.begins_with("live_"))
+ return false;
+
+ if (cmdstr == "live_set_root") {
+
+ scene_tree->_live_edit_root_func(p_command[1], p_command[2]);
+
+ } else if (cmdstr == "live_node_path") {
+
+ scene_tree->_live_edit_node_path_func(p_command[1], p_command[2]);
+
+ } else if (cmdstr == "live_res_path") {
+
+ scene_tree->_live_edit_res_path_func(p_command[1], p_command[2]);
+
+ } else if (cmdstr == "live_node_prop_res") {
+
+ scene_tree->_live_edit_node_set_res_func(p_command[1], p_command[2], p_command[3]);
+
+ } else if (cmdstr == "live_node_prop") {
+
+ scene_tree->_live_edit_node_set_func(p_command[1], p_command[2], p_command[3]);
+
+ } else if (cmdstr == "live_res_prop_res") {
+
+ scene_tree->_live_edit_res_set_res_func(p_command[1], p_command[2], p_command[3]);
+
+ } else if (cmdstr == "live_res_prop") {
+
+ scene_tree->_live_edit_res_set_func(p_command[1], p_command[2], p_command[3]);
+
+ } else if (cmdstr == "live_node_call") {
+
+ scene_tree->_live_edit_node_call_func(p_command[1], p_command[2], p_command[3], p_command[4], p_command[5], p_command[6], p_command[7]);
+
+ } else if (cmdstr == "live_res_call") {
+
+ scene_tree->_live_edit_res_call_func(p_command[1], p_command[2], p_command[3], p_command[4], p_command[5], p_command[6], p_command[7]);
+
+ } else if (cmdstr == "live_create_node") {
+
+ scene_tree->_live_edit_create_node_func(p_command[1], p_command[2], p_command[3]);
+
+ } else if (cmdstr == "live_instance_node") {
+
+ scene_tree->_live_edit_instance_node_func(p_command[1], p_command[2], p_command[3]);
+
+ } else if (cmdstr == "live_remove_node") {
+
+ scene_tree->_live_edit_remove_node_func(p_command[1]);
+
+ } else if (cmdstr == "live_remove_and_keep_node") {
+
+ scene_tree->_live_edit_remove_and_keep_node_func(p_command[1], p_command[2]);
+
+ } else if (cmdstr == "live_restore_node") {
+
+ scene_tree->_live_edit_restore_node_func(p_command[1], p_command[2], p_command[3]);
+
+ } else if (cmdstr == "live_duplicate_node") {
+
+ scene_tree->_live_edit_duplicate_node_func(p_command[1], p_command[2]);
+
+ } else if (cmdstr == "live_reparent_node") {
+
+ scene_tree->_live_edit_reparent_node_func(p_command[1], p_command[2], p_command[3], p_command[4]);
+
+ } else {
+
+ return false;
+ }
+
+ return true;
+#else
+
+ return false;
+#endif
+}
+
+void ScriptDebuggerRemote::_send_object_id(ObjectID p_id) {
+
+ Object *obj = ObjectDB::get_instance(p_id);
+ if (!obj)
+ return;
+
+ typedef Pair<PropertyInfo, Variant> PropertyDesc;
+ List<PropertyDesc> properties;
+
+ if (ScriptInstance *si = obj->get_script_instance()) {
+ if (!si->get_script().is_null()) {
+
+ typedef Map<const Script *, Set<StringName> > ScriptMemberMap;
+ typedef Map<const Script *, Map<StringName, Variant> > ScriptConstantsMap;
+
+ ScriptMemberMap members;
+ members[si->get_script().ptr()] = Set<StringName>();
+ si->get_script()->get_members(&(members[si->get_script().ptr()]));
+
+ ScriptConstantsMap constants;
+ constants[si->get_script().ptr()] = Map<StringName, Variant>();
+ si->get_script()->get_constants(&(constants[si->get_script().ptr()]));
+
+ Ref<Script> base = si->get_script()->get_base_script();
+ while (base.is_valid()) {
+
+ members[base.ptr()] = Set<StringName>();
+ base->get_members(&(members[base.ptr()]));
+
+ constants[base.ptr()] = Map<StringName, Variant>();
+ base->get_constants(&(constants[base.ptr()]));
+
+ base = base->get_base_script();
+ }
+
+ for (ScriptMemberMap::Element *sm = members.front(); sm; sm = sm->next()) {
+ for (Set<StringName>::Element *E = sm->get().front(); E; E = E->next()) {
+ Variant m;
+ if (si->get(E->get(), m)) {
+ String script_path = sm->key() == si->get_script().ptr() ? "" : sm->key()->get_path().get_file() + "/";
+ PropertyInfo pi(m.get_type(), "Members/" + script_path + E->get());
+ properties.push_back(PropertyDesc(pi, m));
+ }
+ }
+ }
+
+ for (ScriptConstantsMap::Element *sc = constants.front(); sc; sc = sc->next()) {
+ for (Map<StringName, Variant>::Element *E = sc->get().front(); E; E = E->next()) {
+ String script_path = sc->key() == si->get_script().ptr() ? "" : sc->key()->get_path().get_file() + "/";
+ if (E->value().get_type() == Variant::OBJECT) {
+ Variant id = ((Object *)E->value())->get_instance_id();
+ PropertyInfo pi(id.get_type(), "Constants/" + E->key(), PROPERTY_HINT_OBJECT_ID, "Object");
+ properties.push_back(PropertyDesc(pi, id));
+ } else {
+ PropertyInfo pi(E->value().get_type(), "Constants/" + script_path + E->key());
+ properties.push_back(PropertyDesc(pi, E->value()));
+ }
+ }
+ }
+ }
+ }
+
+ if (Node *node = Object::cast_to<Node>(obj)) {
+ // in some cases node will not be in tree here
+ // for instance where it created as variable and not yet added to tree
+ // in such cases we can't ask for it's path
+ if (node->is_inside_tree()) {
+ PropertyInfo pi(Variant::NODE_PATH, String("Node/path"));
+ properties.push_front(PropertyDesc(pi, node->get_path()));
+ } else {
+ PropertyInfo pi(Variant::STRING, String("Node/path"));
+ properties.push_front(PropertyDesc(pi, "[Orphan]"));
+ }
+
+ } else if (Resource *res = Object::cast_to<Resource>(obj)) {
+ if (Script *s = Object::cast_to<Script>(res)) {
+ Map<StringName, Variant> constants;
+ s->get_constants(&constants);
+ for (Map<StringName, Variant>::Element *E = constants.front(); E; E = E->next()) {
+ if (E->value().get_type() == Variant::OBJECT) {
+ Variant id = ((Object *)E->value())->get_instance_id();
+ PropertyInfo pi(id.get_type(), "Constants/" + E->key(), PROPERTY_HINT_OBJECT_ID, "Object");
+ properties.push_front(PropertyDesc(pi, E->value()));
+ } else {
+ PropertyInfo pi(E->value().get_type(), String("Constants/") + E->key());
+ properties.push_front(PropertyDesc(pi, E->value()));
+ }
+ }
+ }
+ }
+
+ List<PropertyInfo> pinfo;
+ obj->get_property_list(&pinfo, true);
+ for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
+ if (E->get().usage & (PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CATEGORY)) {
+ properties.push_back(PropertyDesc(E->get(), obj->get(E->get().name)));
+ }
+ }
+
+ Array send_props;
+ for (int i = 0; i < properties.size(); i++) {
+ const PropertyInfo &pi = properties[i].first;
+ Variant &var = properties[i].second;
+
+ WeakRef *ref = Object::cast_to<WeakRef>(var);
+ if (ref) {
+ var = ref->get_ref();
+ }
+
+ RES res = var;
+
+ Array prop;
+ prop.push_back(pi.name);
+ prop.push_back(pi.type);
+
+ //only send information that can be sent..
+ int len = 0; //test how big is this to encode
+ encode_variant(var, NULL, len);
+ if (len > packet_peer_stream->get_output_buffer_max_size()) { //limit to max size
+ prop.push_back(PROPERTY_HINT_OBJECT_TOO_BIG);
+ prop.push_back("");
+ prop.push_back(pi.usage);
+ prop.push_back(Variant());
+ } else {
+ prop.push_back(pi.hint);
+ prop.push_back(pi.hint_string);
+ prop.push_back(pi.usage);
+
+ if (!res.is_null()) {
+ var = res->get_path();
+ }
+
+ prop.push_back(var);
+ }
+ send_props.push_back(prop);
+ }
+
+ packet_peer_stream->put_var("message:inspect_object");
+ packet_peer_stream->put_var(3);
+ packet_peer_stream->put_var(p_id);
+ packet_peer_stream->put_var(obj->get_class());
+ packet_peer_stream->put_var(send_props);
+}
+
+void ScriptDebuggerRemote::_set_object_property(ObjectID p_id, const String &p_property, const Variant &p_value) {
+
+ Object *obj = ObjectDB::get_instance(p_id);
+ if (!obj)
+ return;
+
+ String prop_name = p_property;
+ if (p_property.begins_with("Members/")) {
+ Vector<String> ss = p_property.split("/");
+ prop_name = ss[ss.size() - 1];
+ }
+
+ obj->set(prop_name, p_value);
+}
+
+void ScriptDebuggerRemote::_poll_events() {
+
+ //this si called from ::idle_poll, happens only when running the game,
+ //does not get called while on debug break
+
+ while (packet_peer_stream->get_available_packet_count() > 0) {
+
+ _get_output();
+
+ //send over output_strings
+
+ Variant var;
+ Error err = packet_peer_stream->get_var(var);
+
+ ERR_CONTINUE(err != OK);
+ ERR_CONTINUE(var.get_type() != Variant::ARRAY);
+
+ Array cmd = var;
+
+ ERR_CONTINUE(cmd.size() == 0);
+ ERR_CONTINUE(cmd[0].get_type() != Variant::STRING);
+
+ String command = cmd[0];
+ //cmd.remove(0);
+
+ if (command == "break") {
+
+ if (get_break_language())
+ debug(get_break_language());
+ } else if (command == "request_scene_tree") {
+
+#ifdef DEBUG_ENABLED
+ if (scene_tree)
+ scene_tree->_debugger_request_tree();
+#endif
+ } else if (command == "request_video_mem") {
+
+ _send_video_memory();
+ } else if (command == "inspect_object") {
+
+ ObjectID id = cmd[1];
+ _send_object_id(id);
+ } else if (command == "set_object_property") {
+
+ _set_object_property(cmd[1], cmd[2], cmd[3]);
+
+ } else if (command == "start_profiling") {
+
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ ScriptServer::get_language(i)->profiling_start();
+ }
+
+ max_frame_functions = cmd[1];
+ profiler_function_signature_map.clear();
+ profiling = true;
+ frame_time = 0;
+ idle_time = 0;
+ physics_time = 0;
+ physics_frame_time = 0;
+
+ print_line("PROFILING ALRIGHT!");
+
+ } else if (command == "stop_profiling") {
+
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ ScriptServer::get_language(i)->profiling_stop();
+ }
+ profiling = false;
+ _send_profiling_data(false);
+ print_line("PROFILING END!");
+ } else if (command == "start_network_profiling") {
+
+ multiplayer->profiling_start();
+ profiling_network = true;
+ } else if (command == "stop_network_profiling") {
+
+ multiplayer->profiling_end();
+ profiling_network = false;
+ } else if (command == "override_camera_2D:set") {
+ bool enforce = cmd[1];
+
+ if (scene_tree) {
+ scene_tree->get_root()->enable_canvas_transform_override(enforce);
+ }
+ } else if (command == "override_camera_2D:transform") {
+ Transform2D transform = cmd[1];
+
+ if (scene_tree) {
+ scene_tree->get_root()->set_canvas_transform_override(transform);
+ }
+ } else if (command == "override_camera_3D:set") {
+ bool enable = cmd[1];
+
+ if (scene_tree) {
+ scene_tree->get_root()->enable_camera_override(enable);
+ }
+ } else if (command == "override_camera_3D:transform") {
+ Transform transform = cmd[1];
+ bool is_perspective = cmd[2];
+ float size_or_fov = cmd[3];
+ float near = cmd[4];
+ float far = cmd[5];
+
+ if (scene_tree) {
+ if (is_perspective) {
+ scene_tree->get_root()->set_camera_override_perspective(size_or_fov, near, far);
+ } else {
+ scene_tree->get_root()->set_camera_override_orthogonal(size_or_fov, near, far);
+ }
+ scene_tree->get_root()->set_camera_override_transform(transform);
+ }
+
+ } else if (command == "reload_scripts") {
+ reload_all_scripts = true;
+ } else if (command == "breakpoint") {
+
+ bool set = cmd[3];
+ if (set)
+ insert_breakpoint(cmd[2], cmd[1]);
+ else
+ remove_breakpoint(cmd[2], cmd[1]);
+ } else if (command == "set_skip_breakpoints") {
+ skip_breakpoints = cmd[1];
+ } else {
+ _parse_live_edit(cmd);
+ }
+ }
+}
+
+void ScriptDebuggerRemote::_send_profiling_data(bool p_for_frame) {
+
+ int ofs = 0;
+
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ if (p_for_frame)
+ ofs += ScriptServer::get_language(i)->profiling_get_frame_data(&profile_info.write[ofs], profile_info.size() - ofs);
+ else
+ ofs += ScriptServer::get_language(i)->profiling_get_accumulated_data(&profile_info.write[ofs], profile_info.size() - ofs);
+ }
+
+ for (int i = 0; i < ofs; i++) {
+ profile_info_ptrs.write[i] = &profile_info.write[i];
+ }
+
+ SortArray<ScriptLanguage::ProfilingInfo *, ProfileInfoSort> sa;
+ sa.sort(profile_info_ptrs.ptrw(), ofs);
+
+ int to_send = MIN(ofs, max_frame_functions);
+
+ //check signatures first
+ uint64_t total_script_time = 0;
+
+ for (int i = 0; i < to_send; i++) {
+
+ if (!profiler_function_signature_map.has(profile_info_ptrs[i]->signature)) {
+
+ int idx = profiler_function_signature_map.size();
+ packet_peer_stream->put_var("profile_sig");
+ packet_peer_stream->put_var(2);
+ packet_peer_stream->put_var(profile_info_ptrs[i]->signature);
+ packet_peer_stream->put_var(idx);
+
+ profiler_function_signature_map[profile_info_ptrs[i]->signature] = idx;
+ }
+
+ total_script_time += profile_info_ptrs[i]->self_time;
+ }
+
+ //send frames then
+
+ if (p_for_frame) {
+ packet_peer_stream->put_var("profile_frame");
+ packet_peer_stream->put_var(8 + profile_frame_data.size() * 2 + to_send * 4);
+ } else {
+ packet_peer_stream->put_var("profile_total");
+ packet_peer_stream->put_var(8 + to_send * 4);
+ }
+
+ packet_peer_stream->put_var(Engine::get_singleton()->get_frames_drawn()); //total frame time
+ packet_peer_stream->put_var(frame_time); //total frame time
+ packet_peer_stream->put_var(idle_time); //idle frame time
+ packet_peer_stream->put_var(physics_time); //fixed frame time
+ packet_peer_stream->put_var(physics_frame_time); //fixed frame time
+
+ packet_peer_stream->put_var(USEC_TO_SEC(total_script_time)); //total script execution time
+
+ if (p_for_frame) {
+
+ packet_peer_stream->put_var(profile_frame_data.size()); //how many profile framedatas to send
+ packet_peer_stream->put_var(to_send); //how many script functions to send
+ for (int i = 0; i < profile_frame_data.size(); i++) {
+
+ packet_peer_stream->put_var(profile_frame_data[i].name);
+ packet_peer_stream->put_var(profile_frame_data[i].data);
+ }
+ } else {
+ packet_peer_stream->put_var(0); //how many script functions to send
+ packet_peer_stream->put_var(to_send); //how many script functions to send
+ }
+
+ for (int i = 0; i < to_send; i++) {
+
+ int sig_id = -1;
+
+ if (profiler_function_signature_map.has(profile_info_ptrs[i]->signature)) {
+ sig_id = profiler_function_signature_map[profile_info_ptrs[i]->signature];
+ }
+
+ packet_peer_stream->put_var(sig_id);
+ packet_peer_stream->put_var(profile_info_ptrs[i]->call_count);
+ packet_peer_stream->put_var(profile_info_ptrs[i]->total_time / 1000000.0);
+ packet_peer_stream->put_var(profile_info_ptrs[i]->self_time / 1000000.0);
+ }
+
+ if (p_for_frame) {
+ profile_frame_data.clear();
+ }
+}
+
+void ScriptDebuggerRemote::idle_poll() {
+
+ // this function is called every frame, except when there is a debugger break (::debug() in this class)
+ // execution stops and remains in the ::debug function
+
+ _get_output();
+
+ if (requested_quit) {
+
+ packet_peer_stream->put_var("kill_me");
+ packet_peer_stream->put_var(0);
+ requested_quit = false;
+ }
+
+ if (performance) {
+
+ uint64_t pt = OS::get_singleton()->get_ticks_msec();
+ if (pt - last_perf_time > 1000) {
+
+ last_perf_time = pt;
+ int max = performance->get("MONITOR_MAX");
+ Array arr;
+ arr.resize(max);
+ for (int i = 0; i < max; i++) {
+ arr[i] = performance->call("get_monitor", i);
+ }
+ packet_peer_stream->put_var("performance");
+ packet_peer_stream->put_var(1);
+ packet_peer_stream->put_var(arr);
+ }
+ }
+
+ if (profiling) {
+
+ if (skip_profile_frame) {
+ skip_profile_frame = false;
+ } else {
+ //send profiling info normally
+ _send_profiling_data(true);
+ }
+ }
+
+ if (profiling_network) {
+ uint64_t pt = OS::get_singleton()->get_ticks_msec();
+ if (pt - last_net_bandwidth_time > 200) {
+ last_net_bandwidth_time = pt;
+ _send_network_bandwidth_usage();
+ }
+ if (pt - last_net_prof_time > 100) {
+ last_net_prof_time = pt;
+ _send_network_profiling_data();
+ }
+ }
+
+ if (reload_all_scripts) {
+
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ ScriptServer::get_language(i)->reload_all_scripts();
+ }
+ reload_all_scripts = false;
+ }
+
+ _poll_events();
+}
+
+void ScriptDebuggerRemote::_send_network_profiling_data() {
+ ERR_FAIL_COND(multiplayer.is_null());
+
+ int n_nodes = multiplayer->get_profiling_frame(&network_profile_info.write[0]);
+
+ packet_peer_stream->put_var("network_profile");
+ packet_peer_stream->put_var(n_nodes * 6);
+ for (int i = 0; i < n_nodes; ++i) {
+ packet_peer_stream->put_var(network_profile_info[i].node);
+ packet_peer_stream->put_var(network_profile_info[i].node_path);
+ packet_peer_stream->put_var(network_profile_info[i].incoming_rpc);
+ packet_peer_stream->put_var(network_profile_info[i].incoming_rset);
+ packet_peer_stream->put_var(network_profile_info[i].outgoing_rpc);
+ packet_peer_stream->put_var(network_profile_info[i].outgoing_rset);
+ }
+}
+
+void ScriptDebuggerRemote::_send_network_bandwidth_usage() {
+ ERR_FAIL_COND(multiplayer.is_null());
+
+ int incoming_bandwidth = multiplayer->get_incoming_bandwidth_usage();
+ int outgoing_bandwidth = multiplayer->get_outgoing_bandwidth_usage();
+
+ packet_peer_stream->put_var("network_bandwidth");
+ packet_peer_stream->put_var(2);
+ packet_peer_stream->put_var(incoming_bandwidth);
+ packet_peer_stream->put_var(outgoing_bandwidth);
+}
+
+void ScriptDebuggerRemote::send_message(const String &p_message, const Array &p_args) {
+
+ mutex->lock();
+ if (!locking && tcp_client->is_connected_to_host()) {
+
+ if (messages.size() >= max_messages_per_frame) {
+ n_messages_dropped++;
+ } else {
+ Message msg;
+ msg.message = p_message;
+ msg.data = p_args;
+ messages.push_back(msg);
+ }
+ }
+ mutex->unlock();
+}
+
+void ScriptDebuggerRemote::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type, const Vector<ScriptLanguage::StackInfo> &p_stack_info) {
+
+ OutputError oe;
+ oe.error = p_err;
+ oe.error_descr = p_descr;
+ oe.source_file = p_file;
+ oe.source_line = p_line;
+ oe.source_func = p_func;
+ oe.warning = p_type == ERR_HANDLER_WARNING;
+ uint64_t time = OS::get_singleton()->get_ticks_msec();
+ oe.hr = time / 3600000;
+ oe.min = (time / 60000) % 60;
+ oe.sec = (time / 1000) % 60;
+ oe.msec = time % 1000;
+ Array cstack;
+
+ uint64_t ticks = OS::get_singleton()->get_ticks_usec() / 1000;
+ msec_count += ticks - last_msec;
+ last_msec = ticks;
+
+ if (msec_count > 1000) {
+ msec_count = 0;
+
+ err_count = 0;
+ n_errors_dropped = 0;
+ warn_count = 0;
+ n_warnings_dropped = 0;
+ }
+
+ cstack.resize(p_stack_info.size() * 3);
+ for (int i = 0; i < p_stack_info.size(); i++) {
+ cstack[i * 3 + 0] = p_stack_info[i].file;
+ cstack[i * 3 + 1] = p_stack_info[i].func;
+ cstack[i * 3 + 2] = p_stack_info[i].line;
+ }
+
+ oe.callstack = cstack;
+ if (oe.warning) {
+ warn_count++;
+ } else {
+ err_count++;
+ }
+
+ mutex->lock();
+
+ if (!locking && tcp_client->is_connected_to_host()) {
+
+ if (oe.warning) {
+ if (warn_count > max_warnings_per_second) {
+ n_warnings_dropped++;
+ } else {
+ errors.push_back(oe);
+ }
+ } else {
+ if (err_count > max_errors_per_second) {
+ n_errors_dropped++;
+ } else {
+ errors.push_back(oe);
+ }
+ }
+ }
+
+ mutex->unlock();
+}
+
+void ScriptDebuggerRemote::_print_handler(void *p_this, const String &p_string, bool p_error) {
+
+ ScriptDebuggerRemote *sdr = (ScriptDebuggerRemote *)p_this;
+
+ uint64_t ticks = OS::get_singleton()->get_ticks_usec() / 1000;
+ sdr->msec_count += ticks - sdr->last_msec;
+ sdr->last_msec = ticks;
+
+ if (sdr->msec_count > 1000) {
+ sdr->char_count = 0;
+ sdr->msec_count = 0;
+ }
+
+ String s = p_string;
+ int allowed_chars = MIN(MAX(sdr->max_cps - sdr->char_count, 0), s.length());
+
+ if (allowed_chars == 0)
+ return;
+
+ if (allowed_chars < s.length()) {
+ s = s.substr(0, allowed_chars);
+ }
+
+ sdr->char_count += allowed_chars;
+ bool overflowed = sdr->char_count >= sdr->max_cps;
+
+ sdr->mutex->lock();
+ if (!sdr->locking && sdr->tcp_client->is_connected_to_host()) {
+
+ if (overflowed)
+ s += "[...]";
+
+ sdr->output_strings.push_back(s);
+
+ if (overflowed) {
+ sdr->output_strings.push_back("[output overflow, print less text!]");
+ }
+ }
+ sdr->mutex->unlock();
+}
+
+void ScriptDebuggerRemote::request_quit() {
+
+ requested_quit = true;
+}
+
+void ScriptDebuggerRemote::set_multiplayer(Ref<MultiplayerAPI> p_multiplayer) {
+ multiplayer = p_multiplayer;
+}
+
+bool ScriptDebuggerRemote::is_profiling() const {
+
+ return profiling;
+}
+void ScriptDebuggerRemote::add_profiling_frame_data(const StringName &p_name, const Array &p_data) {
+
+ int idx = -1;
+ for (int i = 0; i < profile_frame_data.size(); i++) {
+ if (profile_frame_data[i].name == p_name) {
+ idx = i;
+ break;
+ }
+ }
+
+ FrameData fd;
+ fd.name = p_name;
+ fd.data = p_data;
+
+ if (idx == -1) {
+ profile_frame_data.push_back(fd);
+ } else {
+ profile_frame_data.write[idx] = fd;
+ }
+}
+
+void ScriptDebuggerRemote::profiling_start() {
+ //ignores this, uses it via connection
+}
+
+void ScriptDebuggerRemote::profiling_end() {
+ //ignores this, uses it via connection
+}
+
+void ScriptDebuggerRemote::profiling_set_frame_times(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) {
+
+ frame_time = p_frame_time;
+ idle_time = p_idle_time;
+ physics_time = p_physics_time;
+ physics_frame_time = p_physics_frame_time;
+}
+
+void ScriptDebuggerRemote::set_skip_breakpoints(bool p_skip_breakpoints) {
+ skip_breakpoints = p_skip_breakpoints;
+}
+
+ScriptDebuggerRemote::ResourceUsageFunc ScriptDebuggerRemote::resource_usage_func = NULL;
+
+ScriptDebuggerRemote::ScriptDebuggerRemote() :
+ profiling(false),
+ profiling_network(false),
+ max_frame_functions(16),
+ skip_profile_frame(false),
+ reload_all_scripts(false),
+ tcp_client(Ref<StreamPeerTCP>(memnew(StreamPeerTCP))),
+ packet_peer_stream(Ref<PacketPeerStream>(memnew(PacketPeerStream))),
+ last_perf_time(0),
+ last_net_prof_time(0),
+ last_net_bandwidth_time(0),
+ performance(Engine::get_singleton()->get_singleton_object("Performance")),
+ requested_quit(false),
+ mutex(Mutex::create()),
+ max_messages_per_frame(GLOBAL_GET("network/limits/debugger_stdout/max_messages_per_frame")),
+ n_messages_dropped(0),
+ max_errors_per_second(GLOBAL_GET("network/limits/debugger_stdout/max_errors_per_second")),
+ max_warnings_per_second(GLOBAL_GET("network/limits/debugger_stdout/max_warnings_per_second")),
+ n_errors_dropped(0),
+ max_cps(GLOBAL_GET("network/limits/debugger_stdout/max_chars_per_second")),
+ char_count(0),
+ err_count(0),
+ warn_count(0),
+ last_msec(0),
+ msec_count(0),
+ locking(false),
+ poll_every(0),
+ scene_tree(NULL) {
+
+ packet_peer_stream->set_stream_peer(tcp_client);
+ packet_peer_stream->set_output_buffer_max_size(1024 * 1024 * 8); //8mb should be way more than enough
+
+ phl.printfunc = _print_handler;
+ phl.userdata = this;
+ add_print_handler(&phl);
+
+ eh.errfunc = _err_handler;
+ eh.userdata = this;
+ add_error_handler(&eh);
+
+ profile_info.resize(GLOBAL_GET("debug/settings/profiler/max_functions"));
+ network_profile_info.resize(GLOBAL_GET("debug/settings/profiler/max_functions"));
+ profile_info_ptrs.resize(profile_info.size());
+}
+
+ScriptDebuggerRemote::~ScriptDebuggerRemote() {
+
+ remove_print_handler(&phl);
+ remove_error_handler(&eh);
+ memdelete(mutex);
+}
diff --git a/scene/debugger/script_debugger_remote.h b/scene/debugger/script_debugger_remote.h
new file mode 100644
index 0000000000..13ad7ddbe3
--- /dev/null
+++ b/scene/debugger/script_debugger_remote.h
@@ -0,0 +1,196 @@
+/*************************************************************************/
+/* script_debugger_remote.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 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 SCRIPT_DEBUGGER_REMOTE_H
+#define SCRIPT_DEBUGGER_REMOTE_H
+
+#include "core/io/packet_peer.h"
+#include "core/io/stream_peer_tcp.h"
+#include "core/list.h"
+#include "core/os/os.h"
+#include "core/script_language.h"
+
+class SceneTree;
+
+class ScriptDebuggerRemote : public ScriptDebugger {
+
+ struct Message {
+
+ String message;
+ Array data;
+ };
+
+ struct ProfileInfoSort {
+
+ bool operator()(ScriptLanguage::ProfilingInfo *A, ScriptLanguage::ProfilingInfo *B) const {
+ return A->total_time < B->total_time;
+ }
+ };
+
+ Vector<ScriptLanguage::ProfilingInfo> profile_info;
+ Vector<ScriptLanguage::ProfilingInfo *> profile_info_ptrs;
+ Vector<MultiplayerAPI::ProfilingInfo> network_profile_info;
+
+ Map<StringName, int> profiler_function_signature_map;
+ float frame_time, idle_time, physics_time, physics_frame_time;
+
+ bool profiling;
+ bool profiling_network;
+ int max_frame_functions;
+ bool skip_profile_frame;
+ bool reload_all_scripts;
+
+ Ref<StreamPeerTCP> tcp_client;
+ Ref<PacketPeerStream> packet_peer_stream;
+
+ uint64_t last_perf_time;
+ uint64_t last_net_prof_time;
+ uint64_t last_net_bandwidth_time;
+ Object *performance;
+ bool requested_quit;
+ Mutex *mutex;
+
+ struct OutputError {
+
+ int hr;
+ int min;
+ int sec;
+ int msec;
+ String source_file;
+ String source_func;
+ int source_line;
+ String error;
+ String error_descr;
+ bool warning;
+ Array callstack;
+ };
+
+ List<String> output_strings;
+ List<Message> messages;
+ int max_messages_per_frame;
+ int n_messages_dropped;
+ List<OutputError> errors;
+ int max_errors_per_second;
+ int max_warnings_per_second;
+ int n_errors_dropped;
+ int n_warnings_dropped;
+
+ int max_cps;
+ int char_count;
+ int err_count;
+ int warn_count;
+ uint64_t last_msec;
+ uint64_t msec_count;
+
+ bool locking; //hack to avoid a deadloop
+ static void _print_handler(void *p_this, const String &p_string, bool p_error);
+
+ PrintHandlerList phl;
+
+ void _get_output();
+ void _poll_events();
+ uint32_t poll_every;
+
+ SceneTree *scene_tree;
+
+ bool _parse_live_edit(const Array &p_command);
+
+ void _set_object_property(ObjectID p_id, const String &p_property, const Variant &p_value);
+
+ void _send_object_id(ObjectID p_id);
+ void _send_video_memory();
+
+ Ref<MultiplayerAPI> multiplayer;
+
+ ErrorHandlerList eh;
+ static void _err_handler(void *, const char *, const char *, int p_line, const char *, const char *, ErrorHandlerType p_type);
+
+ void _send_profiling_data(bool p_for_frame);
+ void _send_network_profiling_data();
+ void _send_network_bandwidth_usage();
+
+ struct FrameData {
+
+ StringName name;
+ Array data;
+ };
+
+ Vector<FrameData> profile_frame_data;
+
+ void _put_variable(const String &p_name, const Variant &p_variable);
+
+ void _save_node(ObjectID id, const String &p_path);
+
+ bool skip_breakpoints;
+
+public:
+ struct ResourceUsage {
+
+ String path;
+ String format;
+ String type;
+ RID id;
+ int vram;
+ bool operator<(const ResourceUsage &p_img) const { return vram == p_img.vram ? id < p_img.id : vram > p_img.vram; }
+ };
+
+ typedef void (*ResourceUsageFunc)(List<ResourceUsage> *);
+
+ static ResourceUsageFunc resource_usage_func;
+
+ Error connect_to_host(const String &p_host, uint16_t p_port);
+ virtual void debug(ScriptLanguage *p_script, bool p_can_continue = true, bool p_is_error_breakpoint = false);
+ virtual void idle_poll();
+ virtual void line_poll();
+
+ virtual bool is_remote() const { return true; }
+ virtual void request_quit();
+
+ virtual void send_message(const String &p_message, const Array &p_args);
+ virtual void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type, const Vector<ScriptLanguage::StackInfo> &p_stack_info);
+
+ virtual void set_multiplayer(Ref<MultiplayerAPI> p_multiplayer);
+
+ virtual bool is_profiling() const;
+ virtual void add_profiling_frame_data(const StringName &p_name, const Array &p_data);
+
+ virtual void profiling_start();
+ virtual void profiling_end();
+ virtual void profiling_set_frame_times(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time);
+
+ virtual void set_skip_breakpoints(bool p_skip_breakpoints);
+
+ void set_scene_tree(SceneTree *p_scene_tree) { scene_tree = p_scene_tree; };
+
+ ScriptDebuggerRemote();
+ ~ScriptDebuggerRemote();
+};
+
+#endif // SCRIPT_DEBUGGER_REMOTE_H
diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp
index 6b3e89af6c..ca4c255855 100644
--- a/scene/gui/button.cpp
+++ b/scene/gui/button.cpp
@@ -174,17 +174,17 @@ void Button::_notification(int p_what) {
_size.width -= get_constant("hseparation") + icon_ofs_region;
if (!clip_text)
_size.width -= get_font("font")->get_string_size(xl_text).width;
- float icon_width = icon->get_width() * _size.height / icon->get_height();
+ float icon_width = _icon->get_width() * _size.height / _icon->get_height();
float icon_height = _size.height;
if (icon_width > _size.width) {
icon_width = _size.width;
- icon_height = icon->get_height() * icon_width / icon->get_width();
+ icon_height = _icon->get_height() * icon_width / _icon->get_width();
}
icon_region = Rect2(style->get_offset() + Point2(icon_ofs_region, (_size.height - icon_height) / 2), Size2(icon_width, icon_height));
} else {
- icon_region = Rect2(style->get_offset() + Point2(icon_ofs_region, Math::floor((valign - _icon->get_height()) / 2.0)), icon->get_size());
+ icon_region = Rect2(style->get_offset() + Point2(icon_ofs_region, Math::floor((valign - _icon->get_height()) / 2.0)), _icon->get_size());
}
}
@@ -221,7 +221,7 @@ void Button::_notification(int p_what) {
font->draw(ci, text_ofs.floor(), xl_text, color, clip_text ? text_clip : -1);
if (!_icon.is_null() && icon_region.size.width > 0) {
- draw_texture_rect_region(_icon, icon_region, Rect2(Point2(), icon->get_size()), color_icon);
+ draw_texture_rect_region(_icon, icon_region, Rect2(Point2(), _icon->get_size()), color_icon);
}
} break;
}
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index fafbcf0c55..8b4d5d4980 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -2221,9 +2221,11 @@ void Control::_modal_stack_remove() {
if (!data.MI)
return;
- get_viewport()->_gui_remove_from_modal_stack(data.MI, data.modal_prev_focus_owner);
-
+ List<Control *>::Element *element = data.MI;
data.MI = NULL;
+
+ get_viewport()->_gui_remove_from_modal_stack(element, data.modal_prev_focus_owner);
+
data.modal_prev_focus_owner = 0;
}
diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp
index 31551d6257..a1b584bad6 100644
--- a/scene/gui/dialogs.cpp
+++ b/scene/gui/dialogs.cpp
@@ -239,12 +239,14 @@ void WindowDialog::_notification(int p_what) {
#ifdef TOOLS_ENABLED
case NOTIFICATION_POST_POPUP: {
- if (get_tree() && Engine::get_singleton()->is_editor_hint() && EditorNode::get_singleton())
+ if (get_tree() && Engine::get_singleton()->is_editor_hint() && EditorNode::get_singleton()) {
+ was_editor_dimmed = EditorNode::get_singleton()->is_editor_dimmed();
EditorNode::get_singleton()->dim_editor(true);
+ }
} break;
case NOTIFICATION_POPUP_HIDE: {
- if (get_tree() && Engine::get_singleton()->is_editor_hint() && EditorNode::get_singleton() && !get_viewport()->gui_has_modal_stack())
+ if (get_tree() && Engine::get_singleton()->is_editor_hint() && EditorNode::get_singleton() && !was_editor_dimmed)
EditorNode::get_singleton()->dim_editor(false);
} break;
#endif
@@ -345,6 +347,10 @@ WindowDialog::WindowDialog() {
close_button = memnew(TextureButton);
add_child(close_button);
close_button->connect("pressed", this, "_closed");
+
+#ifdef TOOLS_ENABLED
+ was_editor_dimmed = false;
+#endif
}
WindowDialog::~WindowDialog() {
@@ -356,7 +362,7 @@ void PopupDialog::_notification(int p_what) {
if (p_what == NOTIFICATION_DRAW) {
RID ci = get_canvas_item();
- get_stylebox("panel", "PopupMenu")->draw(ci, Rect2(Point2(), get_size()));
+ get_stylebox("panel")->draw(ci, Rect2(Point2(), get_size()));
}
}
diff --git a/scene/gui/dialogs.h b/scene/gui/dialogs.h
index afd1173f28..2eb0978e9b 100644
--- a/scene/gui/dialogs.h
+++ b/scene/gui/dialogs.h
@@ -59,6 +59,10 @@ class WindowDialog : public Popup {
Point2 drag_offset_far;
bool resizable;
+#ifdef TOOLS_ENABLED
+ bool was_editor_dimmed;
+#endif
+
void _gui_input(const Ref<InputEvent> &p_event);
void _closed();
int _drag_hit_test(const Point2 &pos) const;
@@ -106,7 +110,6 @@ class AcceptDialog : public WindowDialog {
HBoxContainer *hbc;
Label *label;
Button *ok;
- //Button *cancel; no more cancel (there is X on that titlebar)
bool hide_on_ok;
void _custom_action(const String &p_action);
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index 5cb4bcc64f..6400061309 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -178,8 +178,12 @@ void FileDialog::_post_popup() {
set_process_unhandled_input(true);
// For open dir mode, deselect all items on file dialog open.
- if (mode == MODE_OPEN_DIR)
+ if (mode == MODE_OPEN_DIR) {
deselect_items();
+ file_box->set_visible(false);
+ } else {
+ file_box->set_visible(true);
+ }
}
void FileDialog::_action_pressed() {
@@ -413,6 +417,10 @@ void FileDialog::update_file_name() {
void FileDialog::update_file_list() {
tree->clear();
+
+ // Scroll back to the top after opening a directory
+ tree->get_vscroll_bar()->set_value(0);
+
dir_access->list_dir_begin();
TreeItem *root = tree->create_item();
@@ -894,6 +902,10 @@ FileDialog::FileDialog() {
hbc->add_child(dir_up);
dir_up->connect("pressed", this, "_go_up");
+ drives = memnew(OptionButton);
+ hbc->add_child(drives);
+ drives->connect("item_selected", this, "_select_drive");
+
hbc->add_child(memnew(Label(RTR("Path:"))));
dir = memnew(LineEdit);
hbc->add_child(dir);
@@ -911,10 +923,6 @@ FileDialog::FileDialog() {
show_hidden->connect("toggled", this, "set_show_hidden_files");
hbc->add_child(show_hidden);
- drives = memnew(OptionButton);
- hbc->add_child(drives);
- drives->connect("item_selected", this, "_select_drive");
-
makedir = memnew(Button);
makedir->set_text(RTR("Create Folder"));
makedir->connect("pressed", this, "_make_dir");
@@ -925,18 +933,18 @@ FileDialog::FileDialog() {
tree->set_hide_root(true);
vbc->add_margin_child(RTR("Directories & Files:"), tree, true);
- hbc = memnew(HBoxContainer);
- hbc->add_child(memnew(Label(RTR("File:"))));
+ file_box = memnew(HBoxContainer);
+ file_box->add_child(memnew(Label(RTR("File:"))));
file = memnew(LineEdit);
file->set_stretch_ratio(4);
file->set_h_size_flags(SIZE_EXPAND_FILL);
- hbc->add_child(file);
+ file_box->add_child(file);
filter = memnew(OptionButton);
filter->set_stretch_ratio(3);
filter->set_h_size_flags(SIZE_EXPAND_FILL);
filter->set_clip_text(true); // too many extensions overflows it
- hbc->add_child(filter);
- vbc->add_child(hbc);
+ file_box->add_child(filter);
+ vbc->add_child(file_box);
dir_access = DirAccess::create(DirAccess::ACCESS_RESOURCES);
access = ACCESS_RESOURCES;
diff --git a/scene/gui/file_dialog.h b/scene/gui/file_dialog.h
index 4fd6d0d13c..687ebc8036 100644
--- a/scene/gui/file_dialog.h
+++ b/scene/gui/file_dialog.h
@@ -78,10 +78,11 @@ private:
LineEdit *dir;
OptionButton *drives;
Tree *tree;
+ HBoxContainer *file_box;
LineEdit *file;
+ OptionButton *filter;
AcceptDialog *mkdirerr;
AcceptDialog *exterr;
- OptionButton *filter;
DirAccess *dir_access;
ConfirmationDialog *confirm_save;
diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp
index 1a0539effa..1406586361 100644
--- a/scene/gui/item_list.cpp
+++ b/scene/gui/item_list.cpp
@@ -411,6 +411,7 @@ void ItemList::set_max_columns(int p_amount) {
ERR_FAIL_COND(p_amount < 0);
max_columns = p_amount;
update();
+ shape_changed = true;
}
int ItemList::get_max_columns() const {
diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp
index 510f1b18ad..9e2cd9e941 100644
--- a/scene/gui/label.cpp
+++ b/scene/gui/label.cpp
@@ -296,8 +296,9 @@ Size2 Label::get_minimum_size() const {
Size2 min_style = get_stylebox("normal")->get_minimum_size();
// don't want to mutable everything
- if (word_cache_dirty)
+ if (word_cache_dirty) {
const_cast<Label *>(this)->regenerate_word_cache();
+ }
if (autowrap)
return Size2(1, clip ? 1 : minsize.height) + min_style;
@@ -377,8 +378,14 @@ void Label::regenerate_word_cache() {
memdelete(current);
}
- Ref<StyleBox> style = get_stylebox("normal");
- int width = autowrap ? (get_size().width - style->get_minimum_size().width) : get_longest_line_width();
+ int width;
+ if (autowrap) {
+ Ref<StyleBox> style = get_stylebox("normal");
+ width = MAX(get_size().width, get_custom_minimum_size().width) - style->get_minimum_size().width;
+ } else {
+ width = get_longest_line_width();
+ }
+
Ref<Font> font = get_font("font");
int current_word_size = 0;
@@ -452,6 +459,11 @@ void Label::regenerate_word_cache() {
current_word_size += char_width;
line_width += char_width;
total_char_cache++;
+
+ // allow autowrap to cut words when they exceed line width
+ if (autowrap && (current_word_size > width)) {
+ separatable = true;
+ }
}
if ((autowrap && (line_width >= width) && ((last && last->char_pos >= 0) || separatable)) || insert_newline) {
diff --git a/scene/gui/range.cpp b/scene/gui/range.cpp
index ed5dd77f53..9c016b5a50 100644
--- a/scene/gui/range.cpp
+++ b/scene/gui/range.cpp
@@ -213,6 +213,7 @@ void Range::unshare() {
nshared->val = shared->val;
nshared->step = shared->step;
nshared->page = shared->page;
+ nshared->exp_ratio = shared->exp_ratio;
nshared->allow_greater = shared->allow_greater;
nshared->allow_lesser = shared->allow_lesser;
_unref_shared();
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 42cb89b2d6..0331046492 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -555,12 +555,12 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
if (p_font_color_shadow.a > 0) {
float x_ofs_shadow = align_ofs + pofs;
float y_ofs_shadow = y + lh - line_descent;
- font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + shadow_ofs, fx_char, c[i + 1], p_font_color_shadow);
+ font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + shadow_ofs + fx_offset, fx_char, c[i + 1], p_font_color_shadow);
if (p_shadow_as_outline) {
- font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, shadow_ofs.y), fx_char, c[i + 1], p_font_color_shadow);
- font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(shadow_ofs.x, -shadow_ofs.y), fx_char, c[i + 1], p_font_color_shadow);
- font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, -shadow_ofs.y), fx_char, c[i + 1], p_font_color_shadow);
+ font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, shadow_ofs.y) + fx_offset, fx_char, c[i + 1], p_font_color_shadow);
+ font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(shadow_ofs.x, -shadow_ofs.y) + fx_offset, fx_char, c[i + 1], p_font_color_shadow);
+ font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, -shadow_ofs.y) + fx_offset, fx_char, c[i + 1], p_font_color_shadow);
}
}
@@ -624,19 +624,19 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
if (p_mode == PROCESS_POINTER && r_click_char)
*r_click_char = 0;
- ENSURE_WIDTH(img->image->get_width());
+ ENSURE_WIDTH(img->size.width);
- bool visible = visible_characters < 0 || (p_char_count < visible_characters && YRANGE_VISIBLE(y + lh - font->get_descent() - img->image->get_height(), img->image->get_height()));
+ bool visible = visible_characters < 0 || (p_char_count < visible_characters && YRANGE_VISIBLE(y + lh - font->get_descent() - img->size.height, img->size.height));
if (visible)
line_is_blank = false;
if (p_mode == PROCESS_DRAW && visible) {
- img->image->draw(ci, p_ofs + Point2(align_ofs + wofs, y + lh - font->get_descent() - img->image->get_height()));
+ img->image->draw_rect(ci, Rect2(p_ofs + Point2(align_ofs + wofs, y + lh - font->get_descent() - img->size.height), img->size));
}
p_char_count++;
- ADVANCE(img->image->get_width());
- CHECK_HEIGHT((img->image->get_height() + font->get_descent()));
+ ADVANCE(img->size.width);
+ CHECK_HEIGHT((img->size.height + font->get_descent()));
} break;
case ITEM_NEWLINE: {
@@ -859,7 +859,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
void RichTextLabel::_scroll_changed(double) {
- if (updating_scroll)
+ if (updating_scroll || !scroll_active)
return;
if (scroll_follow && vscroll->get_value() >= (vscroll->get_max() - vscroll->get_page()))
@@ -1634,7 +1634,7 @@ void RichTextLabel::_remove_item(Item *p_item, const int p_line, const int p_sub
}
}
-void RichTextLabel::add_image(const Ref<Texture> &p_image) {
+void RichTextLabel::add_image(const Ref<Texture> &p_image, const int p_width, const int p_height) {
if (current->type == ITEM_TABLE)
return;
@@ -1643,6 +1643,30 @@ void RichTextLabel::add_image(const Ref<Texture> &p_image) {
ItemImage *item = memnew(ItemImage);
item->image = p_image;
+
+ if (p_width > 0) {
+ // custom width
+ item->size.width = p_width;
+ if (p_height > 0) {
+ // custom height
+ item->size.height = p_height;
+ } else {
+ // calculate height to keep aspect ratio
+ item->size.height = p_image->get_height() * p_width / p_image->get_width();
+ }
+ } else {
+ if (p_height > 0) {
+ // custom height
+ item->size.height = p_height;
+ // calculate width to keep aspect ratio
+ item->size.width = p_image->get_width() * p_height / p_image->get_height();
+ } else {
+ // keep original width and height
+ item->size.height = p_image->get_height();
+ item->size.width = p_image->get_width();
+ }
+ }
+
_add_item(item, false);
}
@@ -1697,6 +1721,41 @@ void RichTextLabel::push_font(const Ref<Font> &p_font) {
_add_item(item, true);
}
+void RichTextLabel::push_normal() {
+ Ref<Font> normal_font = get_font("normal_font");
+ ERR_FAIL_COND(normal_font.is_null());
+
+ push_font(normal_font);
+}
+
+void RichTextLabel::push_bold() {
+ Ref<Font> bold_font = get_font("bold_font");
+ ERR_FAIL_COND(bold_font.is_null());
+
+ push_font(bold_font);
+}
+
+void RichTextLabel::push_bold_italics() {
+ Ref<Font> bold_italics_font = get_font("bold_italics_font");
+ ERR_FAIL_COND(bold_italics_font.is_null());
+
+ push_font(bold_italics_font);
+}
+
+void RichTextLabel::push_italics() {
+ Ref<Font> italics_font = get_font("italics_font");
+ ERR_FAIL_COND(italics_font.is_null());
+
+ push_font(italics_font);
+}
+
+void RichTextLabel::push_mono() {
+ Ref<Font> mono_font = get_font("mono_font");
+ ERR_FAIL_COND(mono_font.is_null());
+
+ push_font(mono_font);
+}
+
void RichTextLabel::push_color(const Color &p_color) {
ERR_FAIL_COND(current->type == ITEM_TABLE);
@@ -2125,6 +2184,7 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) {
int end = p_bbcode.find("[", brk_end);
if (end == -1)
end = p_bbcode.length();
+
String image = p_bbcode.substr(brk_end + 1, end - brk_end - 1);
Ref<Texture> texture = ResourceLoader::load(image, "Texture");
@@ -2133,6 +2193,32 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) {
pos = end;
tag_stack.push_front(tag);
+ } else if (tag.begins_with("img=")) {
+
+ int width = 0;
+ int height = 0;
+
+ String params = tag.substr(4, tag.length());
+ int sep = params.find("x");
+ if (sep == -1) {
+ width = params.to_int();
+ } else {
+ width = params.substr(0, sep).to_int();
+ height = params.substr(sep + 1, params.length()).to_int();
+ }
+
+ int end = p_bbcode.find("[", brk_end);
+ if (end == -1)
+ end = p_bbcode.length();
+
+ String image = p_bbcode.substr(brk_end + 1, end - brk_end - 1);
+
+ Ref<Texture> texture = ResourceLoader::load(image, "Texture");
+ if (texture.is_valid())
+ add_image(texture, width, height);
+
+ pos = end;
+ tag_stack.push_front("img");
} else if (tag.begins_with("color=")) {
String col = tag.substr(6, tag.length());
@@ -2581,10 +2667,15 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_text"), &RichTextLabel::get_text);
ClassDB::bind_method(D_METHOD("add_text", "text"), &RichTextLabel::add_text);
ClassDB::bind_method(D_METHOD("set_text", "text"), &RichTextLabel::set_text);
- ClassDB::bind_method(D_METHOD("add_image", "image"), &RichTextLabel::add_image);
+ ClassDB::bind_method(D_METHOD("add_image", "image", "width", "height"), &RichTextLabel::add_image, DEFVAL(0), DEFVAL(0));
ClassDB::bind_method(D_METHOD("newline"), &RichTextLabel::add_newline);
ClassDB::bind_method(D_METHOD("remove_line", "line"), &RichTextLabel::remove_line);
ClassDB::bind_method(D_METHOD("push_font", "font"), &RichTextLabel::push_font);
+ ClassDB::bind_method(D_METHOD("push_normal"), &RichTextLabel::push_normal);
+ ClassDB::bind_method(D_METHOD("push_bold"), &RichTextLabel::push_bold);
+ ClassDB::bind_method(D_METHOD("push_bold_italics"), &RichTextLabel::push_bold_italics);
+ ClassDB::bind_method(D_METHOD("push_italics"), &RichTextLabel::push_italics);
+ ClassDB::bind_method(D_METHOD("push_mono"), &RichTextLabel::push_mono);
ClassDB::bind_method(D_METHOD("push_color", "color"), &RichTextLabel::push_color);
ClassDB::bind_method(D_METHOD("push_align", "align"), &RichTextLabel::push_align);
ClassDB::bind_method(D_METHOD("push_indent", "level"), &RichTextLabel::push_indent);
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index 1c90d974e4..b9837fdfcc 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -148,6 +148,7 @@ private:
struct ItemImage : public Item {
Ref<Texture> image;
+ Size2 size;
ItemImage() { type = ITEM_IMAGE; }
};
@@ -406,10 +407,15 @@ protected:
public:
String get_text();
void add_text(const String &p_text);
- void add_image(const Ref<Texture> &p_image);
+ void add_image(const Ref<Texture> &p_image, const int p_width = 0, const int p_height = 0);
void add_newline();
bool remove_line(const int p_line);
void push_font(const Ref<Font> &p_font);
+ void push_normal();
+ void push_bold();
+ void push_bold_italics();
+ void push_italics();
+ void push_mono();
void push_color(const Color &p_color);
void push_underline();
void push_strikethrough();
diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp
index 9f853cf0c8..ba57be1686 100644
--- a/scene/gui/slider.cpp
+++ b/scene/gui/slider.cpp
@@ -101,26 +101,26 @@ void Slider::_gui_input(Ref<InputEvent> p_event) {
if (!mm.is_valid() && !mb.is_valid()) {
- if (p_event->is_action("ui_left") && p_event->is_pressed()) {
+ if (p_event->is_action_pressed("ui_left", true)) {
if (orientation != HORIZONTAL)
return;
set_value(get_value() - (custom_step >= 0 ? custom_step : get_step()));
accept_event();
- } else if (p_event->is_action("ui_right") && p_event->is_pressed()) {
+ } else if (p_event->is_action_pressed("ui_right", true)) {
if (orientation != HORIZONTAL)
return;
set_value(get_value() + (custom_step >= 0 ? custom_step : get_step()));
accept_event();
- } else if (p_event->is_action("ui_up") && p_event->is_pressed()) {
+ } else if (p_event->is_action_pressed("ui_up", true)) {
if (orientation != VERTICAL)
return;
set_value(get_value() + (custom_step >= 0 ? custom_step : get_step()));
accept_event();
- } else if (p_event->is_action("ui_down") && p_event->is_pressed()) {
+ } else if (p_event->is_action_pressed("ui_down", true)) {
if (orientation != VERTICAL)
return;
diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp
index 172c366c41..bf067898e6 100644
--- a/scene/gui/spin_box.cpp
+++ b/scene/gui/spin_box.cpp
@@ -108,21 +108,21 @@ void SpinBox::_gui_input(const Ref<InputEvent> &p_event) {
case BUTTON_LEFT: {
+ line_edit->grab_focus();
+
set_value(get_value() + (up ? get_step() : -get_step()));
range_click_timer->set_wait_time(0.6);
range_click_timer->set_one_shot(true);
range_click_timer->start();
- line_edit->grab_focus();
-
drag.allowed = true;
drag.capture_pos = mb->get_position();
} break;
case BUTTON_RIGHT: {
- set_value((up ? get_max() : get_min()));
line_edit->grab_focus();
+ set_value((up ? get_max() : get_min()));
} break;
case BUTTON_WHEEL_UP: {
if (line_edit->has_focus()) {
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 69d076c41b..8ddc31745e 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -161,57 +161,58 @@ void TextEdit::Text::_update_line_cache(int p_line) const {
/* BEGIN */
int lr = cr.begin_key.length();
- if (lr == 0 || lr > left)
- continue;
+ const CharType *kc;
+ bool match;
- const CharType *kc = cr.begin_key.c_str();
+ if (lr != 0 && lr <= left) {
+ kc = cr.begin_key.c_str();
- bool match = true;
+ match = true;
- for (int k = 0; k < lr; k++) {
- if (kc[k] != str[i + k]) {
- match = false;
- break;
+ for (int k = 0; k < lr; k++) {
+ if (kc[k] != str[i + k]) {
+ match = false;
+ break;
+ }
}
- }
- if (match) {
+ if (match) {
- ColorRegionInfo cri;
- cri.end = false;
- cri.region = j;
- text.write[p_line].region_info[i] = cri;
- i += lr - 1;
+ ColorRegionInfo cri;
+ cri.end = false;
+ cri.region = j;
+ text.write[p_line].region_info[i] = cri;
+ i += lr - 1;
- break;
+ break;
+ }
}
/* END */
lr = cr.end_key.length();
- if (lr == 0 || lr > left)
- continue;
-
- kc = cr.end_key.c_str();
+ if (lr != 0 && lr <= left) {
+ kc = cr.end_key.c_str();
- match = true;
+ match = true;
- for (int k = 0; k < lr; k++) {
- if (kc[k] != str[i + k]) {
- match = false;
- break;
+ for (int k = 0; k < lr; k++) {
+ if (kc[k] != str[i + k]) {
+ match = false;
+ break;
+ }
}
- }
- if (match) {
+ if (match) {
- ColorRegionInfo cri;
- cri.end = true;
- cri.region = j;
- text.write[p_line].region_info[i] = cri;
- i += lr - 1;
+ ColorRegionInfo cri;
+ cri.end = true;
+ cri.region = j;
+ text.write[p_line].region_info[i] = cri;
+ i += lr - 1;
- break;
+ break;
+ }
}
}
}
@@ -268,6 +269,12 @@ void TextEdit::Text::clear_wrap_cache() {
}
}
+void TextEdit::Text::clear_info_icons() {
+ for (int i = 0; i < text.size(); i++) {
+ text.write[i].has_info = false;
+ }
+}
+
void TextEdit::Text::clear() {
text.clear();
@@ -302,6 +309,7 @@ void TextEdit::Text::insert(int p_at, const String &p_text) {
line.breakpoint = false;
line.bookmark = false;
line.hidden = false;
+ line.has_info = false;
line.width_cache = -1;
line.wrap_amount_cache = -1;
line.data = p_text;
@@ -957,6 +965,10 @@ void TextEdit::_notification(int p_what) {
}
}
+ if (minimap_line < 0 || minimap_line >= (int)text.size()) {
+ break;
+ }
+
Map<int, HighlighterInfo> color_map;
if (syntax_coloring) {
color_map = _get_line_syntax_highlighting(minimap_line);
@@ -1723,7 +1735,9 @@ void TextEdit::_notification(int p_what) {
end = font->get_string_size(l.substr(0, l.rfind(String::chr(0xFFFF)))).x;
}
- draw_string(font, hint_ofs + sb->get_offset() + Vector2(0, font->get_ascent() + font->get_height() * i + spacing), l.replace(String::chr(0xFFFF), ""), font_color);
+ Point2 round_ofs = hint_ofs + sb->get_offset() + Vector2(0, font->get_ascent() + font->get_height() * i + spacing);
+ round_ofs = round_ofs.round();
+ draw_string(font, round_ofs, l.replace(String::chr(0xFFFF), ""), font_color);
if (end > 0) {
Vector2 b = hint_ofs + sb->get_offset() + Vector2(begin, font->get_height() + font->get_height() * i + spacing - 1);
draw_line(b, b + Vector2(end - begin, 0), font_color);
@@ -2843,19 +2857,51 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
// No need to indent if we are going upwards.
if (auto_indent && !(k->get_command() && k->get_shift())) {
- // Indent once again if previous line will end with ':' or '{' and the line is not a comment
+ // Indent once again if previous line will end with ':','{','[','(' and the line is not a comment
// (i.e. colon/brace precedes current cursor position).
- if (cursor.column > 0 && (text[cursor.line][cursor.column - 1] == ':' || text[cursor.line][cursor.column - 1] == '{') && !is_line_comment(cursor.line)) {
- if (indent_using_spaces) {
- ins += space_indent;
- } else {
- ins += "\t";
+ if (cursor.column > 0) {
+ const Map<int, Text::ColorRegionInfo> &cri_map = text.get_color_region_info(cursor.line);
+ bool indent_char_found = false;
+ bool should_indent = false;
+ char indent_char = ':';
+ char c = text[cursor.line][cursor.column];
+
+ for (int i = 0; i < cursor.column; i++) {
+ c = text[cursor.line][i];
+ switch (c) {
+ case ':':
+ case '{':
+ case '[':
+ case '(':
+ indent_char_found = true;
+ should_indent = true;
+ indent_char = c;
+ continue;
+ }
+
+ if (indent_char_found && cri_map.has(i) && (color_regions[cri_map[i].region].begin_key == "#" || color_regions[cri_map[i].region].begin_key == "//")) {
+
+ should_indent = true;
+ break;
+ } else if (indent_char_found && !_is_whitespace(c)) {
+ should_indent = false;
+ indent_char_found = false;
+ }
}
- // No need to move the brace below if we are not taking the text with us.
- if (text[cursor.line][cursor.column] == '}' && !k->get_command()) {
- brace_indent = true;
- ins += "\n" + ins.substr(1, ins.length() - 2);
+ if (!is_line_comment(cursor.line) && should_indent) {
+ if (indent_using_spaces) {
+ ins += space_indent;
+ } else {
+ ins += "\t";
+ }
+
+ // No need to move the brace below if we are not taking the text with us.
+ char closing_char = _get_right_pair_symbol(indent_char);
+ if ((closing_char != 0) && (closing_char == text[cursor.line][cursor.column]) && !k->get_command()) {
+ brace_indent = true;
+ ins += "\n" + ins.substr(1, ins.length() - 2);
+ }
}
}
}
@@ -3882,7 +3928,9 @@ 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));
+ if (text.has_info_icon(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);
@@ -4537,7 +4585,7 @@ void TextEdit::_scroll_moved(double p_to_val) {
int v_scroll_i = floor(get_v_scroll());
int sc = 0;
int n_line;
- for (n_line = 0; n_line < text.size() - 1; n_line++) {
+ for (n_line = 0; n_line < text.size(); n_line++) {
if (!is_line_hidden(n_line)) {
sc++;
sc += times_line_wraps(n_line);
@@ -4751,6 +4799,9 @@ void TextEdit::set_text(String p_text) {
selection.active = false;
}
+ cursor_set_line(0);
+ cursor_set_column(0);
+
update();
setting_text = false;
};
@@ -5052,15 +5103,18 @@ Map<int, TextEdit::Text::ColorRegionInfo> TextEdit::_get_line_color_region_info(
void TextEdit::clear_colors() {
keywords.clear();
+ member_keywords.clear();
color_regions.clear();
color_region_cache.clear();
syntax_highlighting_cache.clear();
text.clear_width_cache();
+ update();
}
void TextEdit::add_keyword_color(const String &p_keyword, const Color &p_color) {
keywords[p_keyword] = p_color;
+ syntax_highlighting_cache.clear();
update();
}
@@ -5077,12 +5131,14 @@ Color TextEdit::get_keyword_color(String p_keyword) const {
void TextEdit::add_color_region(const String &p_begin_key, const String &p_end_key, const Color &p_color, bool p_line_only) {
color_regions.push_back(ColorRegion(p_begin_key, p_end_key, p_color, p_line_only));
+ syntax_highlighting_cache.clear();
text.clear_width_cache();
update();
}
void TextEdit::add_member_keyword(const String &p_keyword, const Color &p_color) {
member_keywords[p_keyword] = p_color;
+ syntax_highlighting_cache.clear();
update();
}
@@ -5096,6 +5152,7 @@ Color TextEdit::get_member_color(String p_member) const {
void TextEdit::clear_member_keywords() {
member_keywords.clear();
+ syntax_highlighting_cache.clear();
update();
}
@@ -5388,11 +5445,11 @@ int TextEdit::_get_column_pos_of_word(const String &p_key, const String &p_searc
PoolVector<int> TextEdit::_search_bind(const String &p_key, uint32_t p_search_flags, int p_from_line, int p_from_column) const {
int col, line;
- if (search(p_key, p_search_flags, p_from_line, p_from_column, col, line)) {
+ if (search(p_key, p_search_flags, p_from_line, p_from_column, line, col)) {
PoolVector<int> result;
result.resize(2);
- result.set(0, line);
- result.set(1, col);
+ result.set(SEARCH_RESULT_COLUMN, col);
+ result.set(SEARCH_RESULT_LINE, line);
return result;
} else {
@@ -5636,9 +5693,7 @@ void TextEdit::set_line_info_icon(int p_line, Ref<Texture> p_icon, String p_info
}
void TextEdit::clear_info_icons() {
- for (int i = 0; i < text.size(); i++) {
- text.set_info_icon(i, NULL, "");
- }
+ text.clear_info_icons();
update();
}
@@ -6020,6 +6075,7 @@ void TextEdit::undo() {
}
}
+ _update_scrollbars();
if (undo_stack_pos->get().type == TextOperation::TYPE_REMOVE) {
cursor_set_line(undo_stack_pos->get().to_line);
cursor_set_column(undo_stack_pos->get().to_column);
@@ -6055,6 +6111,8 @@ void TextEdit::redo() {
break;
}
}
+
+ _update_scrollbars();
cursor_set_line(undo_stack_pos->get().to_line);
cursor_set_column(undo_stack_pos->get().to_column);
undo_stack_pos = undo_stack_pos->next();
@@ -6330,8 +6388,9 @@ void TextEdit::_confirm_completion() {
String line = text[cursor.line];
CharType next_char = line[cursor.column];
CharType last_completion_char = completion_current.insert_text[completion_current.insert_text.length() - 1];
+ CharType last_completion_char_display = completion_current.display[completion_current.display.length() - 1];
- if ((last_completion_char == '"' || last_completion_char == '\'') && last_completion_char == next_char) {
+ if ((last_completion_char == '"' || last_completion_char == '\'') && (last_completion_char == next_char || last_completion_char_display == next_char)) {
_remove_text(cursor.line, cursor.column, cursor.line, cursor.column + 1);
}
@@ -6478,6 +6537,7 @@ void TextEdit::_update_completion_candidates() {
if (inquote && restore_quotes == 1 && !option.display.is_quoted()) {
String quote = single_quote ? "'" : "\"";
option.display = option.display.quote(quote);
+ option.insert_text = option.insert_text.quote(quote);
}
if (option.display.begins_with(s)) {
@@ -6903,6 +6963,9 @@ void TextEdit::_bind_methods() {
BIND_ENUM_CONSTANT(SEARCH_WHOLE_WORDS);
BIND_ENUM_CONSTANT(SEARCH_BACKWARDS);
+ BIND_ENUM_CONSTANT(SEARCH_RESULT_COLUMN);
+ BIND_ENUM_CONSTANT(SEARCH_RESULT_LINE);
+
/*
ClassDB::bind_method(D_METHOD("delete_char"),&TextEdit::delete_char);
ClassDB::bind_method(D_METHOD("delete_line"),&TextEdit::delete_line);
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index e5d9b006fe..594366de7d 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -78,6 +78,7 @@ public:
bool bookmark : 1;
bool hidden : 1;
bool safe : 1;
+ bool has_info : 1;
int wrap_amount_cache : 24;
Map<int, ColorRegionInfo> region_info;
Ref<Texture> info_icon;
@@ -115,10 +116,15 @@ public:
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) {
+ if (p_icon.is_null()) {
+ text.write[p_line].has_info = false;
+ return;
+ }
text.write[p_line].info_icon = p_icon;
text.write[p_line].info = p_info;
+ text.write[p_line].has_info = true;
}
- bool has_info_icon(int p_line) const { return text[p_line].info_icon.is_valid(); }
+ bool has_info_icon(int p_line) const { return text[p_line].has_info; }
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);
@@ -127,6 +133,7 @@ public:
void clear();
void clear_width_cache();
void clear_wrap_cache();
+ void clear_info_icons();
_FORCE_INLINE_ const String &operator[](int p_line) const { return text[p_line].data; }
Text() { indent_size = 4; }
};
@@ -497,12 +504,16 @@ public:
};
enum SearchFlags {
-
SEARCH_MATCH_CASE = 1,
SEARCH_WHOLE_WORDS = 2,
SEARCH_BACKWARDS = 4
};
+ enum SearchResult {
+ SEARCH_RESULT_COLUMN,
+ SEARCH_RESULT_LINE,
+ };
+
virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const;
void _get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) const;
@@ -761,6 +772,7 @@ public:
VARIANT_ENUM_CAST(TextEdit::MenuItems);
VARIANT_ENUM_CAST(TextEdit::SearchFlags);
+VARIANT_ENUM_CAST(TextEdit::SearchResult);
class SyntaxHighlighter {
protected:
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index c9d1295557..d479a1636a 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -1234,7 +1234,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
}
}
- if (select_mode != SELECT_ROW && (p_item->cells[i].selected || selected_item == p_item)) {
+ if ((select_mode == SELECT_ROW && selected_item == p_item) || p_item->cells[i].selected) {
Rect2i r(cell_rect.position, cell_rect.size);
if (p_item->cells[i].text.size() > 0) {
@@ -2224,8 +2224,9 @@ void Tree::_go_down() {
TreeItem *next = NULL;
if (!selected_item) {
- next = hide_root ? root->get_next_visible() : root;
- selected_item = 0;
+ if (root) {
+ next = hide_root ? root->get_next_visible() : root;
+ }
} else {
next = selected_item->get_next_visible();
@@ -2765,7 +2766,6 @@ bool Tree::edit_selected() {
return false;
Rect2 rect = s->get_meta("__focus_rect");
-
popup_edited_item = s;
popup_edited_item_col = col;
@@ -4050,6 +4050,7 @@ Tree::Tree() {
drop_mode_section = 0;
single_select_defer = NULL;
+ scrolling = false;
allow_rmb_select = false;
force_edit_checkbox_only_on_checkbox = false;
diff --git a/scene/gui/video_player.cpp b/scene/gui/video_player.cpp
index 5768f58977..fd2d4a1a11 100644
--- a/scene/gui/video_player.cpp
+++ b/scene/gui/video_player.cpp
@@ -36,6 +36,10 @@
int VideoPlayer::sp_get_channel_count() const {
+ if (playback.is_null()) {
+ return 0;
+ }
+
return playback->get_channels();
}
@@ -56,6 +60,9 @@ bool VideoPlayer::mix(AudioFrame *p_buffer, int p_frames) {
// Called from main thread (eg VideoStreamPlaybackWebm::update)
int VideoPlayer::_audio_mix_callback(void *p_udata, const float *p_data, int p_frames) {
+ ERR_FAIL_NULL_V(p_udata, 0);
+ ERR_FAIL_NULL_V(p_data, 0);
+
VideoPlayer *vp = (VideoPlayer *)p_udata;
int todo = MIN(vp->resampler.get_writer_space(), p_frames);
@@ -71,6 +78,12 @@ int VideoPlayer::_audio_mix_callback(void *p_udata, const float *p_data, int p_f
return todo;
}
+void VideoPlayer::_mix_audios(void *p_self) {
+
+ ERR_FAIL_NULL(p_self);
+ reinterpret_cast<VideoPlayer *>(p_self)->_mix_audio();
+}
+
// Called from audio thread
void VideoPlayer::_mix_audio() {
@@ -143,7 +156,7 @@ void VideoPlayer::_notification(int p_notification) {
bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus);
- if (stream.is_null() || paused || !playback->is_playing())
+ if (stream.is_null() || paused || playback.is_null() || !playback->is_playing())
return;
double audio_time = USEC_TO_SEC(OS::get_singleton()->get_ticks_usec());
@@ -358,7 +371,7 @@ void VideoPlayer::set_stream_position(float p_position) {
playback->seek(p_position);
}
-Ref<Texture> VideoPlayer::get_video_texture() {
+Ref<Texture> VideoPlayer::get_video_texture() const {
if (playback.is_valid())
return playback->get_texture();
@@ -394,9 +407,9 @@ StringName VideoPlayer::get_bus() const {
return "Master";
}
-void VideoPlayer::_validate_property(PropertyInfo &property) const {
+void VideoPlayer::_validate_property(PropertyInfo &p_property) const {
- if (property.name == "bus") {
+ if (p_property.name == "bus") {
String options;
for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) {
@@ -406,7 +419,7 @@ void VideoPlayer::_validate_property(PropertyInfo &property) const {
options += name;
}
- property.hint_string = options;
+ p_property.hint_string = options;
}
}
diff --git a/scene/gui/video_player.h b/scene/gui/video_player.h
index 62fb7838b6..7d2821427e 100644
--- a/scene/gui/video_player.h
+++ b/scene/gui/video_player.h
@@ -55,7 +55,6 @@ class VideoPlayer : public Control {
RID stream_rid;
Ref<ImageTexture> texture;
- Ref<Image> last_frame;
AudioRBResampler resampler;
Vector<AudioFrame> mix_buffer;
@@ -75,19 +74,19 @@ class VideoPlayer : public Control {
void _mix_audio();
static int _audio_mix_callback(void *p_udata, const float *p_data, int p_frames);
- static void _mix_audios(void *self) { reinterpret_cast<VideoPlayer *>(self)->_mix_audio(); }
+ static void _mix_audios(void *p_self);
protected:
static void _bind_methods();
void _notification(int p_notification);
- void _validate_property(PropertyInfo &property) const;
+ void _validate_property(PropertyInfo &p_property) const;
public:
Size2 get_minimum_size() const;
void set_expand(bool p_expand);
bool has_expand() const;
- Ref<Texture> get_video_texture();
+ Ref<Texture> get_video_texture() const;
void set_stream(const Ref<VideoStream> &p_stream);
Ref<VideoStream> get_stream() const;
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index 7b6c90766f..217dacfbfe 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -2832,6 +2832,8 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("rset_unreliable", "property", "value"), &Node::rset_unreliable);
ClassDB::bind_method(D_METHOD("rset_unreliable_id", "peer_id", "property", "value"), &Node::rset_unreliable_id);
+ ClassDB::bind_method(D_METHOD("update_configuration_warning"), &Node::update_configuration_warning);
+
BIND_CONSTANT(NOTIFICATION_ENTER_TREE);
BIND_CONSTANT(NOTIFICATION_EXIT_TREE);
BIND_CONSTANT(NOTIFICATION_MOVED_IN_PARENT);
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index 830d314245..78ceccfca2 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -40,6 +40,7 @@
#include "core/project_settings.h"
#include "main/input_default.h"
#include "node.h"
+#include "scene/debugger/script_debugger_remote.h"
#include "scene/resources/dynamic_font.h"
#include "scene/resources/material.h"
#include "scene/resources/mesh.h"
@@ -78,6 +79,17 @@ bool SceneTreeTimer::is_pause_mode_process() {
return process_pause;
}
+void SceneTreeTimer::release_connections() {
+
+ List<Connection> connections;
+ get_all_signal_connections(&connections);
+
+ for (List<Connection>::Element *E = connections.front(); E; E = E->next()) {
+ Connection const &connection = E->get();
+ disconnect(connection.signal, connection.target, connection.method);
+ }
+}
+
SceneTreeTimer::SceneTreeTimer() {
time_left = 0;
process_pause = true;
@@ -611,6 +623,12 @@ void SceneTree::finish() {
memdelete(root); //delete root
root = NULL;
}
+
+ // cleanup timers
+ for (List<Ref<SceneTreeTimer> >::Element *E = timers.front(); E; E = E->next()) {
+ E->get()->release_connections();
+ }
+ timers.clear();
}
void SceneTree::quit() {
@@ -1077,27 +1095,6 @@ void SceneTree::get_nodes_in_group(const StringName &p_group, List<Node *> *p_li
}
}
-static void _fill_array(Node *p_node, Array &array, int p_level) {
-
- array.push_back(p_node->get_child_count());
- array.push_back(p_node->get_name());
- array.push_back(p_node->get_class());
- array.push_back(p_node->get_instance_id());
- for (int i = 0; i < p_node->get_child_count(); i++) {
-
- _fill_array(p_node->get_child(i), array, p_level + 1);
- }
-}
-
-void SceneTree::_debugger_request_tree(void *self) {
-
- SceneTree *sml = (SceneTree *)self;
-
- Array arr;
- _fill_array(sml->root, arr, 0);
- ScriptDebugger::get_singleton()->send_message("scene_tree", arr);
-}
-
void SceneTree::_flush_delete_queue() {
_THREAD_SAFE_METHOD_
@@ -1320,6 +1317,25 @@ void SceneTree::add_current_scene(Node *p_current) {
}
#ifdef DEBUG_ENABLED
+static void _fill_array(Node *p_node, Array &array, int p_level) {
+
+ array.push_back(p_node->get_child_count());
+ array.push_back(p_node->get_name());
+ array.push_back(p_node->get_class());
+ array.push_back(p_node->get_instance_id());
+ for (int i = 0; i < p_node->get_child_count(); i++) {
+
+ _fill_array(p_node->get_child(i), array, p_level + 1);
+ }
+}
+
+void SceneTree::_debugger_request_tree() {
+
+ Array arr;
+ _fill_array(root, arr, 0);
+ ScriptDebugger::get_singleton()->send_message("scene_tree", arr);
+}
+
void SceneTree::_live_edit_node_path_func(const NodePath &p_path, int p_id) {
live_edit_node_path_cache[p_id] = p_path;
@@ -2050,7 +2066,7 @@ SceneTree::SceneTree() {
int ref_atlas_subdiv = GLOBAL_DEF("rendering/quality/reflections/atlas_subdiv", 8);
ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/reflections/atlas_subdiv", PropertyInfo(Variant::INT, "rendering/quality/reflections/atlas_subdiv", PROPERTY_HINT_RANGE, "0,32,or_greater")); //next_power_of_2 will return a 0 as min value
int msaa_mode = GLOBAL_DEF("rendering/quality/filters/msaa", 0);
- ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/filters/msaa", PropertyInfo(Variant::INT, "rendering/quality/filters/msaa", PROPERTY_HINT_ENUM, "Disabled,2x,4x,8x,16x"));
+ ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/filters/msaa", PropertyInfo(Variant::INT, "rendering/quality/filters/msaa", PROPERTY_HINT_ENUM, "Disabled,2x,4x,8x,16x,External 2x,External 4x"));
root->set_msaa(Viewport::MSAA(msaa_mode));
GLOBAL_DEF("rendering/quality/depth/hdr", true);
@@ -2100,7 +2116,11 @@ SceneTree::SceneTree() {
_update_root_rect();
if (ScriptDebugger::get_singleton()) {
- ScriptDebugger::get_singleton()->set_request_scene_tree_message_func(_debugger_request_tree, this);
+ if (ScriptDebugger::get_singleton()->is_remote()) {
+ ScriptDebuggerRemote *remote_debugger = static_cast<ScriptDebuggerRemote *>(ScriptDebugger::get_singleton());
+
+ remote_debugger->set_scene_tree(this);
+ }
ScriptDebugger::get_singleton()->set_multiplayer(multiplayer);
}
@@ -2112,29 +2132,6 @@ SceneTree::SceneTree() {
#ifdef DEBUG_ENABLED
- live_edit_funcs.udata = this;
- live_edit_funcs.node_path_func = _live_edit_node_path_funcs;
- live_edit_funcs.res_path_func = _live_edit_res_path_funcs;
- live_edit_funcs.node_set_func = _live_edit_node_set_funcs;
- live_edit_funcs.node_set_res_func = _live_edit_node_set_res_funcs;
- live_edit_funcs.node_call_func = _live_edit_node_call_funcs;
- live_edit_funcs.res_set_func = _live_edit_res_set_funcs;
- live_edit_funcs.res_set_res_func = _live_edit_res_set_res_funcs;
- live_edit_funcs.res_call_func = _live_edit_res_call_funcs;
- live_edit_funcs.root_func = _live_edit_root_funcs;
-
- live_edit_funcs.tree_create_node_func = _live_edit_create_node_funcs;
- live_edit_funcs.tree_instance_node_func = _live_edit_instance_node_funcs;
- live_edit_funcs.tree_remove_node_func = _live_edit_remove_node_funcs;
- live_edit_funcs.tree_remove_and_keep_node_func = _live_edit_remove_and_keep_node_funcs;
- live_edit_funcs.tree_restore_node_func = _live_edit_restore_node_funcs;
- live_edit_funcs.tree_duplicate_node_func = _live_edit_duplicate_node_funcs;
- live_edit_funcs.tree_reparent_node_func = _live_edit_reparent_node_funcs;
-
- if (ScriptDebugger::get_singleton()) {
- ScriptDebugger::get_singleton()->set_live_edit_funcs(&live_edit_funcs);
- }
-
live_edit_root = NodePath("/root");
#endif
diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h
index d387886d61..2cf6a117e7 100644
--- a/scene/main/scene_tree.h
+++ b/scene/main/scene_tree.h
@@ -61,6 +61,8 @@ public:
void set_pause_mode_process(bool p_pause_mode_process);
bool is_pause_mode_process();
+ void release_connections();
+
SceneTreeTimer();
};
@@ -209,7 +211,6 @@ private:
Variant _call_group_flags(const Variant **p_args, int p_argcount, Variant::CallError &r_error);
Variant _call_group(const Variant **p_args, int p_argcount, Variant::CallError &r_error);
- static void _debugger_request_tree(void *self);
void _flush_delete_queue();
//optimization
friend class CanvasItem;
@@ -218,6 +219,7 @@ private:
SelfList<Node>::List xform_change_list;
+ friend class ScriptDebuggerRemote;
#ifdef DEBUG_ENABLED
Map<int, NodePath> live_edit_node_path_cache;
@@ -229,7 +231,7 @@ private:
Map<String, Set<Node *> > live_scene_edit_cache;
Map<Node *, Map<ObjectID, Node *> > live_edit_remove_list;
- ScriptDebugger::LiveEditFuncs live_edit_funcs;
+ void _debugger_request_tree();
void _live_edit_node_path_func(const NodePath &p_path, int p_id);
void _live_edit_res_path_func(const String &p_path, int p_id);
@@ -250,25 +252,6 @@ private:
void _live_edit_duplicate_node_func(const NodePath &p_at, const String &p_new_name);
void _live_edit_reparent_node_func(const NodePath &p_at, const NodePath &p_new_place, const String &p_new_name, int p_at_pos);
- static void _live_edit_node_path_funcs(void *self, const NodePath &p_path, int p_id) { reinterpret_cast<SceneTree *>(self)->_live_edit_node_path_func(p_path, p_id); }
- static void _live_edit_res_path_funcs(void *self, const String &p_path, int p_id) { reinterpret_cast<SceneTree *>(self)->_live_edit_res_path_func(p_path, p_id); }
-
- static void _live_edit_node_set_funcs(void *self, int p_id, const StringName &p_prop, const Variant &p_value) { reinterpret_cast<SceneTree *>(self)->_live_edit_node_set_func(p_id, p_prop, p_value); }
- static void _live_edit_node_set_res_funcs(void *self, int p_id, const StringName &p_prop, const String &p_value) { reinterpret_cast<SceneTree *>(self)->_live_edit_node_set_res_func(p_id, p_prop, p_value); }
- static void _live_edit_node_call_funcs(void *self, int p_id, const StringName &p_method, VARIANT_ARG_DECLARE) { reinterpret_cast<SceneTree *>(self)->_live_edit_node_call_func(p_id, p_method, VARIANT_ARG_PASS); }
- static void _live_edit_res_set_funcs(void *self, int p_id, const StringName &p_prop, const Variant &p_value) { reinterpret_cast<SceneTree *>(self)->_live_edit_res_set_func(p_id, p_prop, p_value); }
- static void _live_edit_res_set_res_funcs(void *self, int p_id, const StringName &p_prop, const String &p_value) { reinterpret_cast<SceneTree *>(self)->_live_edit_res_set_res_func(p_id, p_prop, p_value); }
- static void _live_edit_res_call_funcs(void *self, int p_id, const StringName &p_method, VARIANT_ARG_DECLARE) { reinterpret_cast<SceneTree *>(self)->_live_edit_res_call_func(p_id, p_method, VARIANT_ARG_PASS); }
- static void _live_edit_root_funcs(void *self, const NodePath &p_scene_path, const String &p_scene_from) { reinterpret_cast<SceneTree *>(self)->_live_edit_root_func(p_scene_path, p_scene_from); }
-
- static void _live_edit_create_node_funcs(void *self, const NodePath &p_parent, const String &p_type, const String &p_name) { reinterpret_cast<SceneTree *>(self)->_live_edit_create_node_func(p_parent, p_type, p_name); }
- static void _live_edit_instance_node_funcs(void *self, const NodePath &p_parent, const String &p_path, const String &p_name) { reinterpret_cast<SceneTree *>(self)->_live_edit_instance_node_func(p_parent, p_path, p_name); }
- static void _live_edit_remove_node_funcs(void *self, const NodePath &p_at) { reinterpret_cast<SceneTree *>(self)->_live_edit_remove_node_func(p_at); }
- static void _live_edit_remove_and_keep_node_funcs(void *self, const NodePath &p_at, ObjectID p_keep_id) { reinterpret_cast<SceneTree *>(self)->_live_edit_remove_and_keep_node_func(p_at, p_keep_id); }
- static void _live_edit_restore_node_funcs(void *self, ObjectID p_id, const NodePath &p_at, int p_at_pos) { reinterpret_cast<SceneTree *>(self)->_live_edit_restore_node_func(p_id, p_at, p_at_pos); }
- static void _live_edit_duplicate_node_funcs(void *self, const NodePath &p_at, const String &p_new_name) { reinterpret_cast<SceneTree *>(self)->_live_edit_duplicate_node_func(p_at, p_new_name); }
- static void _live_edit_reparent_node_funcs(void *self, const NodePath &p_at, const NodePath &p_new_place, const String &p_new_name, int p_at_pos) { reinterpret_cast<SceneTree *>(self)->_live_edit_reparent_node_func(p_at, p_new_place, p_new_name, p_at_pos); }
-
#endif
enum {
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 52ef225364..563fcb9961 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -779,10 +779,45 @@ bool Viewport::is_audio_listener_2d() const {
return audio_listener_2d;
}
+void Viewport::enable_canvas_transform_override(bool p_enable) {
+ if (override_canvas_transform == p_enable) {
+ return;
+ }
+
+ override_canvas_transform = p_enable;
+ if (p_enable) {
+ VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, find_world_2d()->get_canvas(), canvas_transform_override);
+ } else {
+ VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, find_world_2d()->get_canvas(), canvas_transform);
+ }
+}
+
+bool Viewport::is_canvas_transform_override_enbled() const {
+ return override_canvas_transform;
+}
+
+void Viewport::set_canvas_transform_override(const Transform2D &p_transform) {
+ if (canvas_transform_override == p_transform) {
+ return;
+ }
+
+ canvas_transform_override = p_transform;
+ if (override_canvas_transform) {
+ VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, find_world_2d()->get_canvas(), canvas_transform_override);
+ }
+}
+
+Transform2D Viewport::get_canvas_transform_override() const {
+ return canvas_transform_override;
+}
+
void Viewport::set_canvas_transform(const Transform2D &p_transform) {
canvas_transform = p_transform;
- VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, find_world_2d()->get_canvas(), canvas_transform);
+
+ if (!override_canvas_transform) {
+ VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, find_world_2d()->get_canvas(), canvas_transform);
+ }
}
Transform2D Viewport::get_canvas_transform() const {
@@ -890,10 +925,12 @@ void Viewport::_camera_set(Camera *p_camera) {
camera->notification(Camera::NOTIFICATION_LOST_CURRENT);
}
camera = p_camera;
- if (camera)
- VisualServer::get_singleton()->viewport_attach_camera(viewport, camera->get_camera());
- else
- VisualServer::get_singleton()->viewport_attach_camera(viewport, RID());
+ if (!camera_override) {
+ if (camera)
+ VisualServer::get_singleton()->viewport_attach_camera(viewport, camera->get_camera());
+ else
+ VisualServer::get_singleton()->viewport_attach_camera(viewport, RID());
+ }
if (camera) {
camera->notification(Camera::NOTIFICATION_BECAME_CURRENT);
@@ -1108,10 +1145,82 @@ Listener *Viewport::get_listener() const {
}
Camera *Viewport::get_camera() const {
-
return camera;
}
+void Viewport::enable_camera_override(bool p_enable) {
+
+#ifndef _3D_DISABLED
+ if (p_enable == camera_override) {
+ return;
+ }
+
+ if (p_enable) {
+ camera_override.rid = VisualServer::get_singleton()->camera_create();
+ } else {
+ VisualServer::get_singleton()->free(camera_override.rid);
+ camera_override.rid = RID();
+ }
+
+ if (p_enable) {
+ VisualServer::get_singleton()->viewport_attach_camera(viewport, camera_override.rid);
+ } else if (camera) {
+ VisualServer::get_singleton()->viewport_attach_camera(viewport, camera->get_camera());
+ } else {
+ VisualServer::get_singleton()->viewport_attach_camera(viewport, RID());
+ }
+#endif
+}
+
+bool Viewport::is_camera_override_enabled() const {
+ return camera_override;
+}
+
+void Viewport::set_camera_override_transform(const Transform &p_transform) {
+ if (camera_override) {
+ camera_override.transform = p_transform;
+ VisualServer::get_singleton()->camera_set_transform(camera_override.rid, p_transform);
+ }
+}
+
+Transform Viewport::get_camera_override_transform() const {
+ if (camera_override) {
+ return camera_override.transform;
+ }
+
+ return Transform();
+}
+
+void Viewport::set_camera_override_perspective(float p_fovy_degrees, float p_z_near, float p_z_far) {
+ if (camera_override) {
+ if (camera_override.fov == p_fovy_degrees && camera_override.z_near == p_z_near &&
+ camera_override.z_far == p_z_far && camera_override.projection == CameraOverrideData::PROJECTION_PERSPECTIVE)
+ return;
+
+ camera_override.fov = p_fovy_degrees;
+ camera_override.z_near = p_z_near;
+ camera_override.z_far = p_z_far;
+ camera_override.projection = CameraOverrideData::PROJECTION_PERSPECTIVE;
+
+ VisualServer::get_singleton()->camera_set_perspective(camera_override.rid, camera_override.fov, camera_override.z_near, camera_override.z_far);
+ }
+}
+
+void Viewport::set_camera_override_orthogonal(float p_size, float p_z_near, float p_z_far) {
+ if (camera_override) {
+ if (camera_override.size == p_size && camera_override.z_near == p_z_near &&
+ camera_override.z_far == p_z_far && camera_override.projection == CameraOverrideData::PROJECTION_ORTHOGONAL)
+ return;
+
+ camera_override.size = p_size;
+ camera_override.z_near = p_z_near;
+ camera_override.z_far = p_z_far;
+ camera_override.projection = CameraOverrideData::PROJECTION_ORTHOGONAL;
+
+ VisualServer::get_singleton()->camera_set_orthogonal(camera_override.rid, camera_override.size, camera_override.z_near, camera_override.z_far);
+ }
+}
+
Transform2D Viewport::get_final_transform() const {
return stretch_transform * global_canvas_transform;
@@ -2298,12 +2407,12 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
Input *input = Input::get_singleton();
- if (p_event->is_action_pressed("ui_focus_next") && input->is_action_just_pressed("ui_focus_next")) {
+ if (!mods && p_event->is_action_pressed("ui_focus_next") && input->is_action_just_pressed("ui_focus_next")) {
next = from->find_next_valid_focus();
}
- if (p_event->is_action_pressed("ui_focus_prev") && input->is_action_just_pressed("ui_focus_prev")) {
+ if (!mods && p_event->is_action_pressed("ui_focus_prev") && input->is_action_just_pressed("ui_focus_prev")) {
next = from->find_prev_valid_focus();
}
@@ -2374,7 +2483,6 @@ void Viewport::_gui_remove_from_modal_stack(List<Control *>::Element *MI, Object
List<Control *>::Element *next = MI->next();
gui.modal_stack.erase(MI);
- MI = NULL;
if (p_prev_focus_owner) {
@@ -2847,7 +2955,7 @@ int Viewport::gui_get_canvas_sort_index() {
void Viewport::set_msaa(MSAA p_msaa) {
- ERR_FAIL_INDEX(p_msaa, 5);
+ ERR_FAIL_INDEX(p_msaa, 7);
if (msaa == p_msaa)
return;
msaa = p_msaa;
@@ -2914,6 +3022,7 @@ bool Viewport::gui_is_dragging() const {
}
void Viewport::set_input_as_handled() {
+ _drop_physics_mouseover();
if (handle_input_locally) {
local_input_handled = true;
} else {
@@ -3078,7 +3187,7 @@ void Viewport::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "transparent_bg"), "set_transparent_background", "has_transparent_background");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "handle_input_locally"), "set_handle_input_locally", "is_handling_input_locally");
ADD_GROUP("Rendering", "");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "msaa", PROPERTY_HINT_ENUM, "Disabled,2x,4x,8x,16x"), "set_msaa", "get_msaa");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "msaa", PROPERTY_HINT_ENUM, "Disabled,2x,4x,8x,16x,External 2x,External 4x"), "set_msaa", "get_msaa");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hdr"), "set_hdr", "get_hdr");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disable_3d"), "set_disable_3d", "is_3d_disabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_3d_linear"), "set_keep_3d_linear", "get_keep_3d_linear");
@@ -3181,6 +3290,7 @@ Viewport::Viewport() {
parent = NULL;
listener = NULL;
camera = NULL;
+ override_canvas_transform = false;
canvas_layers.insert(NULL); // This eases picking code (interpreted as the canvas of the Viewport)
arvr = false;
size_override = false;
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index 6393785b22..3c3b436ca1 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -160,6 +160,24 @@ private:
bool arvr;
+ struct CameraOverrideData {
+ Transform transform;
+ enum Projection {
+ PROJECTION_PERSPECTIVE,
+ PROJECTION_ORTHOGONAL
+ };
+ Projection projection;
+ float fov;
+ float size;
+ float z_near;
+ float z_far;
+ RID rid;
+
+ operator bool() const {
+ return rid != RID();
+ }
+ } camera_override;
+
Camera *camera;
Set<Camera *> cameras;
Set<CanvasLayer *> canvas_layers;
@@ -173,6 +191,9 @@ private:
bool audio_listener_2d;
RID internal_listener_2d;
+ bool override_canvas_transform;
+
+ Transform2D canvas_transform_override;
Transform2D canvas_transform;
Transform2D global_canvas_transform;
Transform2D stretch_transform;
@@ -394,6 +415,15 @@ public:
Listener *get_listener() const;
Camera *get_camera() const;
+ void enable_camera_override(bool p_enable);
+ bool is_camera_override_enabled() const;
+
+ void set_camera_override_transform(const Transform &p_transform);
+ Transform get_camera_override_transform() const;
+
+ void set_camera_override_perspective(float p_fovy_degrees, float p_z_near, float p_z_far);
+ void set_camera_override_orthogonal(float p_size, float p_z_near, float p_z_far);
+
void set_use_arvr(bool p_use_arvr);
bool use_arvr();
@@ -418,6 +448,12 @@ public:
Ref<World2D> get_world_2d() const;
Ref<World2D> find_world_2d() const;
+ void enable_canvas_transform_override(bool p_enable);
+ bool is_canvas_transform_override_enbled() const;
+
+ void set_canvas_transform_override(const Transform2D &p_transform);
+ Transform2D get_canvas_transform_override() const;
+
void set_canvas_transform(const Transform2D &p_transform);
Transform2D get_canvas_transform() const;
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index 985b38f913..f4ac277d00 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -2741,77 +2741,77 @@ void Animation::copy_track(int p_track, Ref<Animation> p_to_animation) {
void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_track", "type", "at_position"), &Animation::add_track, DEFVAL(-1));
- ClassDB::bind_method(D_METHOD("remove_track", "idx"), &Animation::remove_track);
+ ClassDB::bind_method(D_METHOD("remove_track", "track_idx"), &Animation::remove_track);
ClassDB::bind_method(D_METHOD("get_track_count"), &Animation::get_track_count);
- ClassDB::bind_method(D_METHOD("track_get_type", "idx"), &Animation::track_get_type);
- ClassDB::bind_method(D_METHOD("track_get_path", "idx"), &Animation::track_get_path);
- ClassDB::bind_method(D_METHOD("track_set_path", "idx", "path"), &Animation::track_set_path);
+ ClassDB::bind_method(D_METHOD("track_get_type", "track_idx"), &Animation::track_get_type);
+ ClassDB::bind_method(D_METHOD("track_get_path", "track_idx"), &Animation::track_get_path);
+ ClassDB::bind_method(D_METHOD("track_set_path", "track_idx", "path"), &Animation::track_set_path);
ClassDB::bind_method(D_METHOD("find_track", "path"), &Animation::find_track);
- ClassDB::bind_method(D_METHOD("track_move_up", "idx"), &Animation::track_move_up);
- ClassDB::bind_method(D_METHOD("track_move_down", "idx"), &Animation::track_move_down);
- ClassDB::bind_method(D_METHOD("track_move_to", "idx", "to_idx"), &Animation::track_move_to);
- ClassDB::bind_method(D_METHOD("track_swap", "idx", "with_idx"), &Animation::track_swap);
+ ClassDB::bind_method(D_METHOD("track_move_up", "track_idx"), &Animation::track_move_up);
+ ClassDB::bind_method(D_METHOD("track_move_down", "track_idx"), &Animation::track_move_down);
+ ClassDB::bind_method(D_METHOD("track_move_to", "track_idx", "to_idx"), &Animation::track_move_to);
+ ClassDB::bind_method(D_METHOD("track_swap", "track_idx", "with_idx"), &Animation::track_swap);
- ClassDB::bind_method(D_METHOD("track_set_imported", "idx", "imported"), &Animation::track_set_imported);
- ClassDB::bind_method(D_METHOD("track_is_imported", "idx"), &Animation::track_is_imported);
+ ClassDB::bind_method(D_METHOD("track_set_imported", "track_idx", "imported"), &Animation::track_set_imported);
+ ClassDB::bind_method(D_METHOD("track_is_imported", "track_idx"), &Animation::track_is_imported);
- ClassDB::bind_method(D_METHOD("track_set_enabled", "idx", "enabled"), &Animation::track_set_enabled);
- ClassDB::bind_method(D_METHOD("track_is_enabled", "idx"), &Animation::track_is_enabled);
+ ClassDB::bind_method(D_METHOD("track_set_enabled", "track_idx", "enabled"), &Animation::track_set_enabled);
+ ClassDB::bind_method(D_METHOD("track_is_enabled", "track_idx"), &Animation::track_is_enabled);
- ClassDB::bind_method(D_METHOD("transform_track_insert_key", "idx", "time", "location", "rotation", "scale"), &Animation::transform_track_insert_key);
- ClassDB::bind_method(D_METHOD("track_insert_key", "idx", "time", "key", "transition"), &Animation::track_insert_key, DEFVAL(1));
- ClassDB::bind_method(D_METHOD("track_remove_key", "idx", "key_idx"), &Animation::track_remove_key);
- ClassDB::bind_method(D_METHOD("track_remove_key_at_position", "idx", "position"), &Animation::track_remove_key_at_position);
- ClassDB::bind_method(D_METHOD("track_set_key_value", "idx", "key", "value"), &Animation::track_set_key_value);
- ClassDB::bind_method(D_METHOD("track_set_key_transition", "idx", "key_idx", "transition"), &Animation::track_set_key_transition);
- ClassDB::bind_method(D_METHOD("track_set_key_time", "idx", "key_idx", "time"), &Animation::track_set_key_time);
- ClassDB::bind_method(D_METHOD("track_get_key_transition", "idx", "key_idx"), &Animation::track_get_key_transition);
+ ClassDB::bind_method(D_METHOD("transform_track_insert_key", "track_idx", "time", "location", "rotation", "scale"), &Animation::transform_track_insert_key);
+ ClassDB::bind_method(D_METHOD("track_insert_key", "track_idx", "time", "key", "transition"), &Animation::track_insert_key, DEFVAL(1));
+ ClassDB::bind_method(D_METHOD("track_remove_key", "track_idx", "key_idx"), &Animation::track_remove_key);
+ ClassDB::bind_method(D_METHOD("track_remove_key_at_position", "track_idx", "position"), &Animation::track_remove_key_at_position);
+ ClassDB::bind_method(D_METHOD("track_set_key_value", "track_idx", "key", "value"), &Animation::track_set_key_value);
+ ClassDB::bind_method(D_METHOD("track_set_key_transition", "track_idx", "key_idx", "transition"), &Animation::track_set_key_transition);
+ ClassDB::bind_method(D_METHOD("track_set_key_time", "track_idx", "key_idx", "time"), &Animation::track_set_key_time);
+ ClassDB::bind_method(D_METHOD("track_get_key_transition", "track_idx", "key_idx"), &Animation::track_get_key_transition);
- ClassDB::bind_method(D_METHOD("track_get_key_count", "idx"), &Animation::track_get_key_count);
- ClassDB::bind_method(D_METHOD("track_get_key_value", "idx", "key_idx"), &Animation::track_get_key_value);
- ClassDB::bind_method(D_METHOD("track_get_key_time", "idx", "key_idx"), &Animation::track_get_key_time);
- ClassDB::bind_method(D_METHOD("track_find_key", "idx", "time", "exact"), &Animation::track_find_key, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("track_get_key_count", "track_idx"), &Animation::track_get_key_count);
+ ClassDB::bind_method(D_METHOD("track_get_key_value", "track_idx", "key_idx"), &Animation::track_get_key_value);
+ ClassDB::bind_method(D_METHOD("track_get_key_time", "track_idx", "key_idx"), &Animation::track_get_key_time);
+ ClassDB::bind_method(D_METHOD("track_find_key", "track_idx", "time", "exact"), &Animation::track_find_key, DEFVAL(false));
- ClassDB::bind_method(D_METHOD("track_set_interpolation_type", "idx", "interpolation"), &Animation::track_set_interpolation_type);
- ClassDB::bind_method(D_METHOD("track_get_interpolation_type", "idx"), &Animation::track_get_interpolation_type);
+ ClassDB::bind_method(D_METHOD("track_set_interpolation_type", "track_idx", "interpolation"), &Animation::track_set_interpolation_type);
+ ClassDB::bind_method(D_METHOD("track_get_interpolation_type", "track_idx"), &Animation::track_get_interpolation_type);
- ClassDB::bind_method(D_METHOD("track_set_interpolation_loop_wrap", "idx", "interpolation"), &Animation::track_set_interpolation_loop_wrap);
- ClassDB::bind_method(D_METHOD("track_get_interpolation_loop_wrap", "idx"), &Animation::track_get_interpolation_loop_wrap);
+ ClassDB::bind_method(D_METHOD("track_set_interpolation_loop_wrap", "track_idx", "interpolation"), &Animation::track_set_interpolation_loop_wrap);
+ ClassDB::bind_method(D_METHOD("track_get_interpolation_loop_wrap", "track_idx"), &Animation::track_get_interpolation_loop_wrap);
- ClassDB::bind_method(D_METHOD("transform_track_interpolate", "idx", "time_sec"), &Animation::_transform_track_interpolate);
- ClassDB::bind_method(D_METHOD("value_track_set_update_mode", "idx", "mode"), &Animation::value_track_set_update_mode);
- ClassDB::bind_method(D_METHOD("value_track_get_update_mode", "idx"), &Animation::value_track_get_update_mode);
+ ClassDB::bind_method(D_METHOD("transform_track_interpolate", "track_idx", "time_sec"), &Animation::_transform_track_interpolate);
+ ClassDB::bind_method(D_METHOD("value_track_set_update_mode", "track_idx", "mode"), &Animation::value_track_set_update_mode);
+ ClassDB::bind_method(D_METHOD("value_track_get_update_mode", "track_idx"), &Animation::value_track_get_update_mode);
- ClassDB::bind_method(D_METHOD("value_track_get_key_indices", "idx", "time_sec", "delta"), &Animation::_value_track_get_key_indices);
+ ClassDB::bind_method(D_METHOD("value_track_get_key_indices", "track_idx", "time_sec", "delta"), &Animation::_value_track_get_key_indices);
- ClassDB::bind_method(D_METHOD("method_track_get_key_indices", "idx", "time_sec", "delta"), &Animation::_method_track_get_key_indices);
- ClassDB::bind_method(D_METHOD("method_track_get_name", "idx", "key_idx"), &Animation::method_track_get_name);
- ClassDB::bind_method(D_METHOD("method_track_get_params", "idx", "key_idx"), &Animation::method_track_get_params);
+ ClassDB::bind_method(D_METHOD("method_track_get_key_indices", "track_idx", "time_sec", "delta"), &Animation::_method_track_get_key_indices);
+ ClassDB::bind_method(D_METHOD("method_track_get_name", "track_idx", "key_idx"), &Animation::method_track_get_name);
+ ClassDB::bind_method(D_METHOD("method_track_get_params", "track_idx", "key_idx"), &Animation::method_track_get_params);
- ClassDB::bind_method(D_METHOD("bezier_track_insert_key", "track", "time", "value", "in_handle", "out_handle"), &Animation::bezier_track_insert_key, DEFVAL(Vector2()), DEFVAL(Vector2()));
+ ClassDB::bind_method(D_METHOD("bezier_track_insert_key", "track_idx", "time", "value", "in_handle", "out_handle"), &Animation::bezier_track_insert_key, DEFVAL(Vector2()), DEFVAL(Vector2()));
- ClassDB::bind_method(D_METHOD("bezier_track_set_key_value", "idx", "key_idx", "value"), &Animation::bezier_track_set_key_value);
- ClassDB::bind_method(D_METHOD("bezier_track_set_key_in_handle", "idx", "key_idx", "in_handle"), &Animation::bezier_track_set_key_in_handle);
- ClassDB::bind_method(D_METHOD("bezier_track_set_key_out_handle", "idx", "key_idx", "out_handle"), &Animation::bezier_track_set_key_out_handle);
+ ClassDB::bind_method(D_METHOD("bezier_track_set_key_value", "track_idx", "key_idx", "value"), &Animation::bezier_track_set_key_value);
+ ClassDB::bind_method(D_METHOD("bezier_track_set_key_in_handle", "track_idx", "key_idx", "in_handle"), &Animation::bezier_track_set_key_in_handle);
+ ClassDB::bind_method(D_METHOD("bezier_track_set_key_out_handle", "track_idx", "key_idx", "out_handle"), &Animation::bezier_track_set_key_out_handle);
- ClassDB::bind_method(D_METHOD("bezier_track_get_key_value", "idx", "key_idx"), &Animation::bezier_track_get_key_value);
- ClassDB::bind_method(D_METHOD("bezier_track_get_key_in_handle", "idx", "key_idx"), &Animation::bezier_track_get_key_in_handle);
- ClassDB::bind_method(D_METHOD("bezier_track_get_key_out_handle", "idx", "key_idx"), &Animation::bezier_track_get_key_out_handle);
+ ClassDB::bind_method(D_METHOD("bezier_track_get_key_value", "track_idx", "key_idx"), &Animation::bezier_track_get_key_value);
+ ClassDB::bind_method(D_METHOD("bezier_track_get_key_in_handle", "track_idx", "key_idx"), &Animation::bezier_track_get_key_in_handle);
+ ClassDB::bind_method(D_METHOD("bezier_track_get_key_out_handle", "track_idx", "key_idx"), &Animation::bezier_track_get_key_out_handle);
- ClassDB::bind_method(D_METHOD("bezier_track_interpolate", "track", "time"), &Animation::bezier_track_interpolate);
+ ClassDB::bind_method(D_METHOD("bezier_track_interpolate", "track_idx", "time"), &Animation::bezier_track_interpolate);
- ClassDB::bind_method(D_METHOD("audio_track_insert_key", "track", "time", "stream", "start_offset", "end_offset"), &Animation::audio_track_insert_key, DEFVAL(0), DEFVAL(0));
- ClassDB::bind_method(D_METHOD("audio_track_set_key_stream", "idx", "key_idx", "stream"), &Animation::audio_track_set_key_stream);
- ClassDB::bind_method(D_METHOD("audio_track_set_key_start_offset", "idx", "key_idx", "offset"), &Animation::audio_track_set_key_start_offset);
- ClassDB::bind_method(D_METHOD("audio_track_set_key_end_offset", "idx", "key_idx", "offset"), &Animation::audio_track_set_key_end_offset);
- ClassDB::bind_method(D_METHOD("audio_track_get_key_stream", "idx", "key_idx"), &Animation::audio_track_get_key_stream);
- ClassDB::bind_method(D_METHOD("audio_track_get_key_start_offset", "idx", "key_idx"), &Animation::audio_track_get_key_start_offset);
- ClassDB::bind_method(D_METHOD("audio_track_get_key_end_offset", "idx", "key_idx"), &Animation::audio_track_get_key_end_offset);
+ ClassDB::bind_method(D_METHOD("audio_track_insert_key", "track_idx", "time", "stream", "start_offset", "end_offset"), &Animation::audio_track_insert_key, DEFVAL(0), DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("audio_track_set_key_stream", "track_idx", "key_idx", "stream"), &Animation::audio_track_set_key_stream);
+ ClassDB::bind_method(D_METHOD("audio_track_set_key_start_offset", "track_idx", "key_idx", "offset"), &Animation::audio_track_set_key_start_offset);
+ ClassDB::bind_method(D_METHOD("audio_track_set_key_end_offset", "track_idx", "key_idx", "offset"), &Animation::audio_track_set_key_end_offset);
+ ClassDB::bind_method(D_METHOD("audio_track_get_key_stream", "track_idx", "key_idx"), &Animation::audio_track_get_key_stream);
+ ClassDB::bind_method(D_METHOD("audio_track_get_key_start_offset", "track_idx", "key_idx"), &Animation::audio_track_get_key_start_offset);
+ ClassDB::bind_method(D_METHOD("audio_track_get_key_end_offset", "track_idx", "key_idx"), &Animation::audio_track_get_key_end_offset);
- ClassDB::bind_method(D_METHOD("animation_track_insert_key", "track", "time", "animation"), &Animation::animation_track_insert_key);
- ClassDB::bind_method(D_METHOD("animation_track_set_key_animation", "idx", "key_idx", "animation"), &Animation::animation_track_set_key_animation);
- ClassDB::bind_method(D_METHOD("animation_track_get_key_animation", "idx", "key_idx"), &Animation::animation_track_get_key_animation);
+ ClassDB::bind_method(D_METHOD("animation_track_insert_key", "track_idx", "time", "animation"), &Animation::animation_track_insert_key);
+ ClassDB::bind_method(D_METHOD("animation_track_set_key_animation", "track_idx", "key_idx", "animation"), &Animation::animation_track_set_key_animation);
+ ClassDB::bind_method(D_METHOD("animation_track_get_key_animation", "track_idx", "key_idx"), &Animation::animation_track_get_key_animation);
ClassDB::bind_method(D_METHOD("set_length", "time_sec"), &Animation::set_length);
ClassDB::bind_method(D_METHOD("get_length"), &Animation::get_length);
@@ -2823,7 +2823,7 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_step"), &Animation::get_step);
ClassDB::bind_method(D_METHOD("clear"), &Animation::clear);
- ClassDB::bind_method(D_METHOD("copy_track", "track", "to_animation"), &Animation::copy_track);
+ ClassDB::bind_method(D_METHOD("copy_track", "track_idx", "to_animation"), &Animation::copy_track);
ADD_PROPERTY(PropertyInfo(Variant::REAL, "length", PROPERTY_HINT_RANGE, "0.001,99999,0.001"), "set_length", "get_length");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop");
@@ -2870,9 +2870,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 == v2) {
+ if (v0.is_equal_approx(v2)) {
//0 and 2 are close, let's see if 1 is close
- if (v0 != v1) {
+ if (!v0.is_equal_approx(v1)) {
//not close, not optimizable
return false;
}
@@ -2909,9 +2909,9 @@ bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, cons
//localize both to rotation from q0
- if (Math::is_zero_approx((q0 - q2).length())) {
+ if (q0.is_equal_approx(q2)) {
- if (!Math::is_zero_approx((q0 - q1).length()))
+ if (!q0.is_equal_approx(q1))
return false;
} else {
@@ -2959,9 +2959,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 == v2) {
+ if (v0.is_equal_approx(v2)) {
//0 and 2 are close, let's see if 1 is close
- if (v0 != v1) {
+ if (!v0.is_equal_approx(v1)) {
//not close, not optimizable
return false;
}
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index 0dcc184a1d..e82819f270 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -432,7 +432,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_font("font", "TextEdit", default_font);
- theme->set_color("background_color", "TextEdit", Color(0, 0, 0));
+ theme->set_color("background_color", "TextEdit", Color(0, 0, 0, 0));
theme->set_color("completion_background_color", "TextEdit", Color(0.17, 0.16, 0.2));
theme->set_color("completion_selected_color", "TextEdit", Color(0.26, 0.26, 0.27));
theme->set_color("completion_existing_color", "TextEdit", Color(0.87, 0.87, 0.87, 0.13));
@@ -558,9 +558,14 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_stylebox("panel", "PopupPanel", style_pp);
+ // PopupDialog
+
+ Ref<StyleBoxTexture> style_pd = make_stylebox(popup_bg_png, 4, 4, 4, 4, 10, 10, 10, 10);
+ theme->set_stylebox("panel", "PopupDialog", style_pd);
+
// PopupMenu
- theme->set_stylebox("panel", "PopupMenu", make_stylebox(popup_bg_png, 4, 4, 4, 4, 10, 10, 10, 10));
+ theme->set_stylebox("panel", "PopupMenu", style_pd);
theme->set_stylebox("panel_disabled", "PopupMenu", make_stylebox(popup_bg_disabled_png, 4, 4, 4, 4));
theme->set_stylebox("hover", "PopupMenu", selected);
theme->set_stylebox("separator", "PopupMenu", make_stylebox(vseparator_png, 3, 3, 3, 3));
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index 0de462d616..fab0aace14 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -496,10 +496,16 @@ void SpatialMaterial::_update_shader() {
}
code += "uniform float roughness : hint_range(0,1);\n";
code += "uniform float point_size : hint_range(0,128);\n";
- code += "uniform sampler2D texture_metallic : hint_white;\n";
- code += "uniform vec4 metallic_texture_channel;\n";
- code += "uniform sampler2D texture_roughness : hint_white;\n";
- code += "uniform vec4 roughness_texture_channel;\n";
+
+ if (textures[TEXTURE_METALLIC] != NULL) {
+ code += "uniform sampler2D texture_metallic : hint_white;\n";
+ code += "uniform vec4 metallic_texture_channel;\n";
+ }
+
+ if (textures[TEXTURE_ROUGHNESS] != NULL) {
+ code += "uniform sampler2D texture_roughness : hint_white;\n";
+ code += "uniform vec4 roughness_texture_channel;\n";
+ }
if (billboard_mode == BILLBOARD_PARTICLES) {
code += "uniform int particles_anim_h_frames;\n";
code += "uniform int particles_anim_v_frames;\n";
@@ -790,20 +796,30 @@ void SpatialMaterial::_update_shader() {
if (flags[FLAG_ALBEDO_FROM_VERTEX_COLOR]) {
code += "\talbedo_tex *= COLOR;\n";
}
-
code += "\tALBEDO = albedo.rgb * albedo_tex.rgb;\n";
- if (flags[FLAG_UV1_USE_TRIPLANAR]) {
- code += "\tfloat metallic_tex = dot(triplanar_texture(texture_metallic,uv1_power_normal,uv1_triplanar_pos),metallic_texture_channel);\n";
+
+ if (textures[TEXTURE_METALLIC] != NULL) {
+ if (flags[FLAG_UV1_USE_TRIPLANAR]) {
+ code += "\tfloat metallic_tex = dot(triplanar_texture(texture_metallic,uv1_power_normal,uv1_triplanar_pos),metallic_texture_channel);\n";
+ } else {
+ code += "\tfloat metallic_tex = dot(texture(texture_metallic,base_uv),metallic_texture_channel);\n";
+ }
+ code += "\tMETALLIC = metallic_tex * metallic;\n";
} else {
- code += "\tfloat metallic_tex = dot(texture(texture_metallic,base_uv),metallic_texture_channel);\n";
+ code += "\tMETALLIC = metallic;\n";
}
- code += "\tMETALLIC = metallic_tex * metallic;\n";
- if (flags[FLAG_UV1_USE_TRIPLANAR]) {
- code += "\tfloat roughness_tex = dot(triplanar_texture(texture_roughness,uv1_power_normal,uv1_triplanar_pos),roughness_texture_channel);\n";
+
+ if (textures[TEXTURE_ROUGHNESS] != NULL) {
+ if (flags[FLAG_UV1_USE_TRIPLANAR]) {
+ code += "\tfloat roughness_tex = dot(triplanar_texture(texture_roughness,uv1_power_normal,uv1_triplanar_pos),roughness_texture_channel);\n";
+ } else {
+ code += "\tfloat roughness_tex = dot(texture(texture_roughness,base_uv),roughness_texture_channel);\n";
+ }
+ code += "\tROUGHNESS = roughness_tex * roughness;\n";
} else {
- code += "\tfloat roughness_tex = dot(texture(texture_roughness,base_uv),roughness_texture_channel);\n";
+ code += "\tROUGHNESS = roughness;\n";
}
- code += "\tROUGHNESS = roughness_tex * roughness;\n";
+
code += "\tSPECULAR = specular;\n";
if (features[FEATURE_NORMAL_MAPPING]) {
@@ -1389,6 +1405,8 @@ void SpatialMaterial::set_texture(TextureParam p_param, const Ref<Texture> &p_te
textures[p_param] = p_texture;
RID rid = p_texture.is_valid() ? p_texture->get_rid() : RID();
VS::get_singleton()->material_set_param(_get_material(), shader_names->texture_names[p_param], rid);
+ _queue_shader_change();
+ _change_notify();
}
Ref<Texture> SpatialMaterial::get_texture(TextureParam p_param) const {
@@ -1754,6 +1772,7 @@ SpatialMaterial::TextureChannel SpatialMaterial::get_roughness_texture_channel()
void SpatialMaterial::set_ao_texture_channel(TextureChannel p_channel) {
+ ERR_FAIL_INDEX(p_channel, 5);
ao_texture_channel = p_channel;
VS::get_singleton()->material_set_param(_get_material(), shader_names->ao_texture_channel, _get_texture_mask(p_channel));
}
@@ -1764,6 +1783,7 @@ SpatialMaterial::TextureChannel SpatialMaterial::get_ao_texture_channel() const
void SpatialMaterial::set_refraction_texture_channel(TextureChannel p_channel) {
+ ERR_FAIL_INDEX(p_channel, 5);
refraction_texture_channel = p_channel;
VS::get_singleton()->material_set_param(_get_material(), shader_names->refraction_texture_channel, _get_texture_mask(p_channel));
}
@@ -1804,10 +1824,9 @@ RID SpatialMaterial::get_material_rid_for_2d(bool p_shaded, bool p_transparent,
material->set_flag(FLAG_SRGB_VERTEX_COLOR, true);
material->set_flag(FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
material->set_flag(FLAG_USE_ALPHA_SCISSOR, p_cut_alpha);
- if (p_billboard) {
- material->set_billboard_mode(BILLBOARD_ENABLED);
- } else if (p_billboard_y) {
- material->set_billboard_mode(BILLBOARD_FIXED_Y);
+ if (p_billboard || p_billboard_y) {
+ material->set_flag(FLAG_BILLBOARD_KEEP_SCALE, true);
+ material->set_billboard_mode(p_billboard_y ? BILLBOARD_FIXED_Y : BILLBOARD_ENABLED);
}
materials_for_2d[version] = material;
diff --git a/scene/resources/particles_material.cpp b/scene/resources/particles_material.cpp
index 969743f78c..c5956d9bc2 100644
--- a/scene/resources/particles_material.cpp
+++ b/scene/resources/particles_material.cpp
@@ -332,7 +332,7 @@ void ParticlesMaterial::_update_shader() {
code += " float base_angle = (initial_angle + tex_angle) * mix(1.0, angle_rand, initial_angle_random);\n";
code += " CUSTOM.x = base_angle * degree_to_rad;\n"; // angle
code += " CUSTOM.y = 0.0;\n"; // phase
- code += " CUSTOM.w = LIFETIME * (1.0 - lifetime_randomness * rand_from_seed(alt_seed));\n";
+ code += " CUSTOM.w = (1.0 - lifetime_randomness * rand_from_seed(alt_seed));\n";
code += " CUSTOM.z = (anim_offset + tex_anim_offset) * mix(1.0, anim_offset_rand, anim_offset_random);\n"; // animation offset (0-1)
switch (emission_shape) {
diff --git a/scene/resources/ray_shape.cpp b/scene/resources/ray_shape.cpp
index 5a696aee23..f185263a36 100644
--- a/scene/resources/ray_shape.cpp
+++ b/scene/resources/ray_shape.cpp
@@ -90,6 +90,12 @@ void RayShape::_bind_methods() {
RayShape::RayShape() :
Shape(PhysicsServer::get_singleton()->shape_create(PhysicsServer::SHAPE_RAY)) {
- set_length(1.0);
- set_slips_on_slope(false);
+ length = 1.0;
+ slips_on_slope = false;
+
+ /* Code copied from setters to prevent the use of uninitialized variables */
+ _update_shape();
+ notify_change_to_owners();
+ _change_notify("length");
+ _change_notify("slips_on_slope");
}
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index c2e2f85723..593c399f62 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -2369,16 +2369,20 @@ RES ResourceFormatLoaderTextureLayered::load(const String &p_path, const String
if (header[0] == 'G' && header[1] == 'D' && header[2] == '3' && header[3] == 'T') {
if (tex3d.is_null()) {
+ f->close();
memdelete(f);
ERR_FAIL_COND_V(tex3d.is_null(), RES())
}
} else if (header[0] == 'G' && header[1] == 'D' && header[2] == 'A' && header[3] == 'T') {
if (texarr.is_null()) {
+ f->close();
memdelete(f);
ERR_FAIL_COND_V(texarr.is_null(), RES())
}
} else {
+ f->close();
+ memdelete(f);
ERR_FAIL_V_MSG(RES(), "Unrecognized layered texture file format '" + String((const char *)header) + "'.");
}
@@ -2418,6 +2422,7 @@ RES ResourceFormatLoaderTextureLayered::load(const String &p_path, const String
if (r_error) {
*r_error = ERR_FILE_CORRUPT;
}
+ f->close();
memdelete(f);
ERR_FAIL_V(RES());
}
@@ -2453,6 +2458,7 @@ RES ResourceFormatLoaderTextureLayered::load(const String &p_path, const String
if (r_error) {
*r_error = ERR_FILE_CORRUPT;
}
+ f->close();
memdelete(f);
ERR_FAIL_V(RES());
}
@@ -2473,8 +2479,9 @@ RES ResourceFormatLoaderTextureLayered::load(const String &p_path, const String
if (bytes != total_size) {
if (r_error) {
*r_error = ERR_FILE_CORRUPT;
- memdelete(f);
}
+ f->close();
+ memdelete(f);
ERR_FAIL_V(RES());
}
}
diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp
index ae18be1695..c897365b21 100644
--- a/scene/resources/theme.cpp
+++ b/scene/resources/theme.cpp
@@ -37,6 +37,118 @@ void Theme::_emit_theme_changed() {
emit_changed();
}
+PoolVector<String> Theme::_get_icon_list(const String &p_type) const {
+
+ PoolVector<String> ilret;
+ List<StringName> il;
+
+ get_icon_list(p_type, &il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ PoolVector<String>::Write w = ilret.write();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+PoolVector<String> Theme::_get_stylebox_list(const String &p_type) const {
+
+ PoolVector<String> ilret;
+ List<StringName> il;
+
+ get_stylebox_list(p_type, &il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ PoolVector<String>::Write w = ilret.write();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+PoolVector<String> Theme::_get_stylebox_types(void) const {
+
+ PoolVector<String> ilret;
+ List<StringName> il;
+
+ get_stylebox_types(&il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ PoolVector<String>::Write w = ilret.write();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+PoolVector<String> Theme::_get_font_list(const String &p_type) const {
+
+ PoolVector<String> ilret;
+ List<StringName> il;
+
+ get_font_list(p_type, &il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ PoolVector<String>::Write w = ilret.write();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+PoolVector<String> Theme::_get_color_list(const String &p_type) const {
+
+ PoolVector<String> ilret;
+ List<StringName> il;
+
+ get_color_list(p_type, &il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ PoolVector<String>::Write w = ilret.write();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+PoolVector<String> Theme::_get_constant_list(const String &p_type) const {
+
+ PoolVector<String> ilret;
+ List<StringName> il;
+
+ get_constant_list(p_type, &il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ PoolVector<String>::Write w = ilret.write();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+PoolVector<String> Theme::_get_type_list(const String &p_type) const {
+
+ PoolVector<String> ilret;
+ List<StringName> il;
+
+ get_type_list(&il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ PoolVector<String>::Write w = ilret.write();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
bool Theme::_set(const StringName &p_name, const Variant &p_value) {
String sname = p_name;
@@ -300,6 +412,8 @@ void Theme::clear_icon(const StringName &p_name, const StringName &p_type) {
void Theme::get_icon_list(StringName p_type, List<StringName> *p_list) const {
+ ERR_FAIL_NULL(p_list);
+
if (!icon_map.has(p_type))
return;
@@ -344,6 +458,9 @@ void Theme::clear_shader(const StringName &p_name, const StringName &p_type) {
}
void Theme::get_shader_list(const StringName &p_type, List<StringName> *p_list) const {
+
+ ERR_FAIL_NULL(p_list);
+
if (!shader_map.has(p_type))
return;
@@ -408,6 +525,8 @@ void Theme::clear_stylebox(const StringName &p_name, const StringName &p_type) {
void Theme::get_stylebox_list(StringName p_type, List<StringName> *p_list) const {
+ ERR_FAIL_NULL(p_list);
+
if (!style_map.has(p_type))
return;
@@ -420,6 +539,8 @@ void Theme::get_stylebox_list(StringName p_type, List<StringName> *p_list) const
}
void Theme::get_stylebox_types(List<StringName> *p_list) const {
+ ERR_FAIL_NULL(p_list);
+
const StringName *key = NULL;
while ((key = style_map.next(key))) {
p_list->push_back(*key);
@@ -478,6 +599,8 @@ void Theme::clear_font(const StringName &p_name, const StringName &p_type) {
void Theme::get_font_list(StringName p_type, List<StringName> *p_list) const {
+ ERR_FAIL_NULL(p_list);
+
if (!font_map.has(p_type))
return;
@@ -526,6 +649,8 @@ void Theme::clear_color(const StringName &p_name, const StringName &p_type) {
void Theme::get_color_list(StringName p_type, List<StringName> *p_list) const {
+ ERR_FAIL_NULL(p_list);
+
if (!color_map.has(p_type))
return;
@@ -574,6 +699,8 @@ void Theme::clear_constant(const StringName &p_name, const StringName &p_type) {
void Theme::get_constant_list(StringName p_type, List<StringName> *p_list) const {
+ ERR_FAIL_NULL(p_list);
+
if (!constant_map.has(p_type))
return;
@@ -637,6 +764,12 @@ void Theme::copy_default_theme() {
void Theme::copy_theme(const Ref<Theme> &p_other) {
+ if (p_other.is_null()) {
+ clear();
+
+ return;
+ }
+
//these need reconnecting, so add normally
{
const StringName *K = NULL;
@@ -680,8 +813,9 @@ void Theme::copy_theme(const Ref<Theme> &p_other) {
void Theme::get_type_list(List<StringName> *p_list) const {
- Set<StringName> types;
+ ERR_FAIL_NULL(p_list);
+ Set<StringName> types;
const StringName *key = NULL;
while ((key = icon_map.next(key))) {
diff --git a/scene/resources/theme.h b/scene/resources/theme.h
index 187694de65..471e5b1a51 100644
--- a/scene/resources/theme.h
+++ b/scene/resources/theme.h
@@ -52,6 +52,14 @@ class Theme : public Resource {
HashMap<StringName, HashMap<StringName, Color> > color_map;
HashMap<StringName, HashMap<StringName, int> > constant_map;
+ PoolVector<String> _get_icon_list(const String &p_type) const;
+ PoolVector<String> _get_stylebox_list(const String &p_type) const;
+ PoolVector<String> _get_stylebox_types(void) const;
+ PoolVector<String> _get_font_list(const String &p_type) const;
+ PoolVector<String> _get_color_list(const String &p_type) const;
+ PoolVector<String> _get_constant_list(const String &p_type) const;
+ PoolVector<String> _get_type_list(const String &p_type) const;
+
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
@@ -65,70 +73,6 @@ protected:
Ref<Font> default_theme_font;
- PoolVector<String> _get_icon_list(const String &p_type) const {
- PoolVector<String> ilret;
- List<StringName> il;
- get_icon_list(p_type, &il);
- for (List<StringName>::Element *E = il.front(); E; E = E->next()) {
- ilret.push_back(E->get());
- }
- return ilret;
- }
- PoolVector<String> _get_stylebox_list(const String &p_type) const {
- PoolVector<String> ilret;
- List<StringName> il;
- get_stylebox_list(p_type, &il);
- for (List<StringName>::Element *E = il.front(); E; E = E->next()) {
- ilret.push_back(E->get());
- }
- return ilret;
- }
- PoolVector<String> _get_stylebox_types(void) const {
- PoolVector<String> ilret;
- List<StringName> il;
- get_stylebox_types(&il);
- for (List<StringName>::Element *E = il.front(); E; E = E->next()) {
- ilret.push_back(E->get());
- }
- return ilret;
- }
- PoolVector<String> _get_font_list(const String &p_type) const {
- PoolVector<String> ilret;
- List<StringName> il;
- get_font_list(p_type, &il);
- for (List<StringName>::Element *E = il.front(); E; E = E->next()) {
- ilret.push_back(E->get());
- }
- return ilret;
- }
- PoolVector<String> _get_color_list(const String &p_type) const {
- PoolVector<String> ilret;
- List<StringName> il;
- get_color_list(p_type, &il);
- for (List<StringName>::Element *E = il.front(); E; E = E->next()) {
- ilret.push_back(E->get());
- }
- return ilret;
- }
- PoolVector<String> _get_constant_list(const String &p_type) const {
- PoolVector<String> ilret;
- List<StringName> il;
- get_constant_list(p_type, &il);
- for (List<StringName>::Element *E = il.front(); E; E = E->next()) {
- ilret.push_back(E->get());
- }
- return ilret;
- }
- PoolVector<String> _get_type_list(const String &p_type) const {
- PoolVector<String> ilret;
- List<StringName> il;
- get_type_list(&il);
- for (List<StringName>::Element *E = il.front(); E; E = E->next()) {
- ilret.push_back(E->get());
- }
- return ilret;
- }
-
static void _bind_methods();
public:
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index 24122a8d99..16a95e65a8 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -148,20 +148,45 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) {
}
}
} else if (what == "shape")
- for (int i = 0; i < tile_get_shape_count(id); i++)
- tile_set_shape(id, i, p_value);
+ if (tile_get_shape_count(id) > 0) {
+ for (int i = 0; i < tile_get_shape_count(id); i++) {
+ tile_set_shape(id, i, p_value);
+ }
+ } else {
+ tile_set_shape(id, 0, p_value);
+ }
else if (what == "shape_offset")
- for (int i = 0; i < tile_get_shape_count(id); i++)
- tile_set_shape_offset(id, i, p_value);
+ if (tile_get_shape_count(id) > 0) {
+ for (int i = 0; i < tile_get_shape_count(id); i++) {
+ tile_set_shape_offset(id, i, p_value);
+ }
+ } else {
+ tile_set_shape_offset(id, 0, p_value);
+ }
else if (what == "shape_transform")
- for (int i = 0; i < tile_get_shape_count(id); i++)
- tile_set_shape_transform(id, i, p_value);
+ if (tile_get_shape_count(id) > 0) {
+ for (int i = 0; i < tile_get_shape_count(id); i++) {
+ tile_set_shape_transform(id, i, p_value);
+ }
+ } else {
+ tile_set_shape_transform(id, 0, p_value);
+ }
else if (what == "shape_one_way")
- for (int i = 0; i < tile_get_shape_count(id); i++)
- tile_set_shape_one_way(id, i, p_value);
+ if (tile_get_shape_count(id) > 0) {
+ for (int i = 0; i < tile_get_shape_count(id); i++) {
+ tile_set_shape_one_way(id, i, p_value);
+ }
+ } else {
+ tile_set_shape_one_way(id, 0, p_value);
+ }
else if (what == "shape_one_way_margin")
- for (int i = 0; i < tile_get_shape_count(id); i++)
- tile_set_shape_one_way_margin(id, i, p_value);
+ if (tile_get_shape_count(id) > 0) {
+ for (int i = 0; i < tile_get_shape_count(id); i++) {
+ tile_set_shape_one_way_margin(id, i, p_value);
+ }
+ } else {
+ tile_set_shape_one_way_margin(id, 0, p_value);
+ }
else if (what == "shapes")
_tile_set_shapes(id, p_value);
else if (what == "occluder")
diff --git a/scene/resources/video_stream.h b/scene/resources/video_stream.h
index eb3bf6770f..81c7b062cc 100644
--- a/scene/resources/video_stream.h
+++ b/scene/resources/video_stream.h
@@ -63,7 +63,7 @@ public:
//virtual int mix(int16_t* p_buffer,int p_frames)=0;
- virtual Ref<Texture> get_texture() = 0;
+ virtual Ref<Texture> get_texture() const = 0;
virtual void update(float p_delta) = 0;
virtual void set_mix_callback(AudioMixCallback p_callback, void *p_userdata) = 0;
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index bd6835f816..9f99732714 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -1064,10 +1064,11 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
String src_var = "n_out" + itos(from_node) + "p" + itos(from_port);
if (in_type == VisualShaderNode::PORT_TYPE_SAMPLER && out_type == VisualShaderNode::PORT_TYPE_SAMPLER) {
-
- VisualShaderNodeUniform *uniform = (VisualShaderNodeUniform *)graph[type].nodes[from_node].node.ptr();
- if (uniform) {
- inputs[i] = uniform->get_uniform_name();
+ VisualShaderNode *ptr = const_cast<VisualShaderNode *>(graph[type].nodes[from_node].node.ptr());
+ if (ptr->has_method("get_input_real_name")) {
+ inputs[i] = ptr->call("get_input_real_name");
+ } else if (ptr->has_method("get_uniform_name")) {
+ inputs[i] = ptr->call("get_uniform_name");
} else {
inputs[i] = "";
}
@@ -1442,6 +1443,8 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(VIEWPORT_SIZE, 0.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "output_is_srgb", "OUTPUT_IS_SRGB" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "front_facing", "FRONT_FACING" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "screen_texture", "SCREEN_TEXTURE" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "depth_texture", "DEPTH_TEXTURE" },
// Spatial, Light
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.xyz" },
@@ -1488,6 +1491,9 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "point_coord", "vec3(POINT_COORD,0.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "light_pass", "float(AT_LIGHT_PASS ? 1.0 : 0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "texture", "TEXTURE" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "normal_texture", "NORMAL_TEXTURE" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "screen_texture", "SCREEN_TEXTURE" },
// Canvas Item, Light
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.xyz" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" },
@@ -1504,6 +1510,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "texture_pixel_size", "vec3(TEXTURE_PIXEL_SIZE, 1.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "point_coord", "vec3(POINT_COORD,0.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SAMPLER, "texture", "TEXTURE" },
// Particles, Vertex
{ Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
@@ -1599,11 +1606,15 @@ String VisualShaderNodeInput::get_output_port_name(int p_port) const {
}
String VisualShaderNodeInput::get_caption() const {
- return TTR("Input");
+ return "Input";
}
String VisualShaderNodeInput::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 {
+ if (get_output_port_type(0) == PORT_TYPE_SAMPLER) {
+ return "";
+ }
+
if (p_for_preview) {
int idx = 0;
@@ -1672,6 +1683,20 @@ String VisualShaderNodeInput::get_input_name() const {
return input_name;
}
+String VisualShaderNodeInput::get_input_real_name() const {
+
+ int idx = 0;
+
+ while (ports[idx].mode != Shader::MODE_MAX) {
+ if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type && ports[idx].name == input_name) {
+ return String(ports[idx].string);
+ }
+ idx++;
+ }
+
+ return "";
+}
+
VisualShaderNodeInput::PortType VisualShaderNodeInput::get_input_type_by_name(String p_name) const {
int idx = 0;
@@ -1768,6 +1793,7 @@ void VisualShaderNodeInput::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_input_name", "name"), &VisualShaderNodeInput::set_input_name);
ClassDB::bind_method(D_METHOD("get_input_name"), &VisualShaderNodeInput::get_input_name);
+ ClassDB::bind_method(D_METHOD("get_input_real_name"), &VisualShaderNodeInput::get_input_real_name);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "input_name", PROPERTY_HINT_ENUM, ""), "set_input_name", "get_input_name");
ADD_SIGNAL(MethodInfo("input_type_changed"));
@@ -1921,7 +1947,7 @@ bool VisualShaderNodeOutput::is_port_separator(int p_index) const {
}
String VisualShaderNodeOutput::get_caption() const {
- return TTR("Output");
+ return "Output";
}
String VisualShaderNodeOutput::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 {
diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h
index 4f73316404..09222c8d81 100644
--- a/scene/resources/visual_shader.h
+++ b/scene/resources/visual_shader.h
@@ -299,6 +299,7 @@ public:
void set_input_name(String p_name);
String get_input_name() const;
+ String get_input_real_name() const;
int get_input_index_count() const;
PortType get_input_index_type(int p_index) const;
diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp
index a7df736c78..a94fdd9d7b 100644
--- a/scene/resources/visual_shader_nodes.cpp
+++ b/scene/resources/visual_shader_nodes.cpp
@@ -475,27 +475,29 @@ String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader:
String id = p_input_vars[2];
String code;
+ code += "\t{\n";
if (id == String()) {
- code += "\tvec4 " + id + "_tex_read = vec4(0.0);\n";
+ code += "\t\tvec4 " + id + "_tex_read = vec4(0.0);\n";
} else {
if (p_input_vars[0] == String()) { // Use UV by default.
if (p_input_vars[1] == String()) {
- code += "\tvec4 " + id + "_tex_read = texture( " + id + " , UV.xy );\n";
+ code += "\t\tvec4 " + id + "_tex_read = texture( " + id + " , UV.xy );\n";
} else {
- code += "\tvec4 " + id + "_tex_read = textureLod( " + id + " , UV.xy , " + p_input_vars[1] + " );\n";
+ code += "\t\tvec4 " + id + "_tex_read = textureLod( " + id + " , UV.xy , " + p_input_vars[1] + " );\n";
}
} else if (p_input_vars[1] == String()) {
//no lod
- code += "\tvec4 " + id + "_tex_read = texture( " + id + " , " + p_input_vars[0] + ".xy );\n";
+ code += "\t\tvec4 " + id + "_tex_read = texture( " + id + " , " + p_input_vars[0] + ".xy );\n";
} else {
- code += "\tvec4 " + id + "_tex_read = textureLod( " + id + " , " + p_input_vars[0] + ".xy , " + p_input_vars[1] + " );\n";
+ code += "\t\tvec4 " + id + "_tex_read = textureLod( " + id + " , " + p_input_vars[0] + ".xy , " + p_input_vars[1] + " );\n";
}
- code += "\t" + p_output_vars[0] + " = " + id + "_tex_read.rgb;\n";
- code += "\t" + p_output_vars[1] + " = " + id + "_tex_read.a;\n";
+ code += "\t\t" + p_output_vars[0] + " = " + id + "_tex_read.rgb;\n";
+ code += "\t\t" + p_output_vars[1] + " = " + id + "_tex_read.a;\n";
}
+ code += "\t}\n";
return code;
}
@@ -905,6 +907,7 @@ void VisualShaderNodeCubeMap::_bind_methods() {
VisualShaderNodeCubeMap::VisualShaderNodeCubeMap() {
texture_type = TYPE_DATA;
+ source = SOURCE_TEXTURE;
}
////////////// Scalar Op