diff options
Diffstat (limited to 'scene')
95 files changed, 1527 insertions, 325 deletions
diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp index 932db8f001..25ad6bd5c9 100644 --- a/scene/2d/animated_sprite.cpp +++ b/scene/2d/animated_sprite.cpp @@ -645,6 +645,9 @@ void AnimatedSprite::_reset_timeout() { void AnimatedSprite::set_animation(const StringName &p_animation) { + ERR_EXPLAIN(vformat("There is no animation with name '%s'.", p_animation)); + ERR_FAIL_COND(frames->get_animation_names().find(p_animation) == -1); + if (animation == p_animation) return; diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp index 2a225e5797..b322cfe8f1 100644 --- a/scene/2d/area_2d.cpp +++ b/scene/2d/area_2d.cpp @@ -261,8 +261,9 @@ void Area2D::_area_inout(int p_status, const RID &p_area, int p_instance, int p_ Map<ObjectID, AreaState>::Element *E = area_map.find(objid); - ERR_FAIL_COND(!area_in && !E); - + if (!area_in && !E) { + return; //likely removed from the tree + } locked = true; if (area_in) { diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp index 7b28ad2c12..73f583111b 100644 --- a/scene/2d/audio_stream_player_2d.cpp +++ b/scene/2d/audio_stream_player_2d.cpp @@ -37,7 +37,7 @@ void AudioStreamPlayer2D::_mix_audio() { if (!stream_playback.is_valid() || !active || - (stream_paused && !stream_fade_out)) { + (stream_paused && !stream_paused_fade_out)) { return; } @@ -50,7 +50,7 @@ void AudioStreamPlayer2D::_mix_audio() { AudioFrame *buffer = mix_buffer.ptrw(); int buffer_size = mix_buffer.size(); - if (stream_fade_out) { + if (stream_paused_fade_out) { // Short fadeout ramp buffer_size = MIN(buffer_size, 128); } @@ -84,10 +84,10 @@ void AudioStreamPlayer2D::_mix_audio() { } //mix! - AudioFrame target_volume = stream_fade_out ? AudioFrame(0.f, 0.f) : current.vol; - AudioFrame vol_prev = stream_fade_in ? AudioFrame(0.f, 0.f) : prev_outputs[i].vol; + AudioFrame target_volume = stream_paused_fade_out ? AudioFrame(0.f, 0.f) : current.vol; + AudioFrame vol_prev = stream_paused_fade_in ? AudioFrame(0.f, 0.f) : prev_outputs[i].vol; AudioFrame vol_inc = (target_volume - vol_prev) / float(buffer_size); - AudioFrame vol = stream_fade_in ? AudioFrame(0.f, 0.f) : current.vol; + AudioFrame vol = stream_paused_fade_in ? AudioFrame(0.f, 0.f) : current.vol; int cc = AudioServer::get_singleton()->get_channel_count(); @@ -139,15 +139,9 @@ void AudioStreamPlayer2D::_mix_audio() { active = false; } - if (stream_stop) { - active = false; - set_physics_process_internal(false); - setplay = -1; - } - output_ready = false; - stream_fade_in = false; - stream_fade_out = false; + stream_paused_fade_in = false; + stream_paused_fade_out = false; } void AudioStreamPlayer2D::_notification(int p_what) { @@ -329,7 +323,6 @@ void AudioStreamPlayer2D::play(float p_from_pos) { } if (stream_playback.is_valid()) { - stream_stop = false; active = true; setplay = p_from_pos; output_ready = false; @@ -347,8 +340,9 @@ void AudioStreamPlayer2D::seek(float p_seconds) { void AudioStreamPlayer2D::stop() { if (stream_playback.is_valid()) { - stream_stop = true; - stream_fade_out = true; + active = false; + set_physics_process_internal(false); + setplay = -1; } } @@ -463,8 +457,8 @@ void AudioStreamPlayer2D::set_stream_paused(bool p_pause) { if (p_pause != stream_paused) { stream_paused = p_pause; - stream_fade_in = p_pause ? false : true; - stream_fade_out = p_pause ? true : false; + stream_paused_fade_in = p_pause ? false : true; + stream_paused_fade_out = p_pause ? true : false; } } @@ -473,6 +467,10 @@ bool AudioStreamPlayer2D::get_stream_paused() const { return stream_paused; } +Ref<AudioStreamPlayback> AudioStreamPlayer2D::get_stream_playback() { + return stream_playback; +} + void AudioStreamPlayer2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_stream", "stream"), &AudioStreamPlayer2D::set_stream); @@ -512,6 +510,8 @@ void AudioStreamPlayer2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_stream_paused", "pause"), &AudioStreamPlayer2D::set_stream_paused); ClassDB::bind_method(D_METHOD("get_stream_paused"), &AudioStreamPlayer2D::get_stream_paused); + ClassDB::bind_method(D_METHOD("get_stream_playback"), &AudioStreamPlayer2D::get_stream_playback); + ClassDB::bind_method(D_METHOD("_bus_layout_changed"), &AudioStreamPlayer2D::_bus_layout_changed); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream"); @@ -543,9 +543,8 @@ AudioStreamPlayer2D::AudioStreamPlayer2D() { output_ready = false; area_mask = 1; stream_paused = false; - stream_fade_in = false; - stream_fade_out = false; - stream_stop = false; + stream_paused_fade_in = false; + stream_paused_fade_out = false; AudioServer::get_singleton()->connect("bus_layout_changed", this, "_bus_layout_changed"); } diff --git a/scene/2d/audio_stream_player_2d.h b/scene/2d/audio_stream_player_2d.h index caf5c6ee49..e9cdfa2303 100644 --- a/scene/2d/audio_stream_player_2d.h +++ b/scene/2d/audio_stream_player_2d.h @@ -73,9 +73,8 @@ private: float pitch_scale; bool autoplay; bool stream_paused; - bool stream_fade_in; - bool stream_fade_out; - bool stream_stop; + bool stream_paused_fade_in; + bool stream_paused_fade_out; StringName bus; void _mix_audio(); @@ -130,6 +129,8 @@ public: void set_stream_paused(bool p_pause); bool get_stream_paused() const; + Ref<AudioStreamPlayback> get_stream_playback(); + AudioStreamPlayer2D(); ~AudioStreamPlayer2D(); }; diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp index 2739f71543..78e98deb93 100644 --- a/scene/2d/canvas_item.cpp +++ b/scene/2d/canvas_item.cpp @@ -891,13 +891,13 @@ void CanvasItem::draw_colored_polygon(const Vector<Point2> &p_points, const Colo VisualServer::get_singleton()->canvas_item_add_polygon(canvas_item, p_points, colors, p_uvs, rid, rid_normal, p_antialiased); } -void CanvasItem::draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map) { +void CanvasItem::draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map, const Transform2D &p_transform, const Color &p_modulate) { ERR_FAIL_COND(p_mesh.is_null()); RID texture_rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); RID normal_map_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID(); - VisualServer::get_singleton()->canvas_item_add_mesh(canvas_item, p_mesh->get_rid(), texture_rid, normal_map_rid); + VisualServer::get_singleton()->canvas_item_add_mesh(canvas_item, p_mesh->get_rid(), p_transform, p_modulate, texture_rid, normal_map_rid); } void CanvasItem::draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map) { @@ -1168,7 +1168,7 @@ void CanvasItem::_bind_methods() { ClassDB::bind_method(D_METHOD("draw_colored_polygon", "points", "color", "uvs", "texture", "normal_map", "antialiased"), &CanvasItem::draw_colored_polygon, DEFVAL(PoolVector2Array()), DEFVAL(Variant()), DEFVAL(Variant()), DEFVAL(false)); ClassDB::bind_method(D_METHOD("draw_string", "font", "position", "text", "modulate", "clip_w"), &CanvasItem::draw_string, DEFVAL(Color(1, 1, 1)), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("draw_char", "font", "position", "char", "next", "modulate"), &CanvasItem::draw_char, DEFVAL(Color(1, 1, 1))); - ClassDB::bind_method(D_METHOD("draw_mesh", "mesh", "texture", "normal_map"), &CanvasItem::draw_mesh, DEFVAL(Ref<Texture>())); + ClassDB::bind_method(D_METHOD("draw_mesh", "mesh", "texture", "normal_map", "transform", "modulate"), &CanvasItem::draw_mesh, DEFVAL(Ref<Texture>()), DEFVAL(Transform2D()), DEFVAL(Color(1, 1, 1))); ClassDB::bind_method(D_METHOD("draw_multimesh", "multimesh", "texture", "normal_map"), &CanvasItem::draw_multimesh, DEFVAL(Ref<Texture>())); ClassDB::bind_method(D_METHOD("draw_set_transform", "position", "rotation", "scale"), &CanvasItem::draw_set_transform); diff --git a/scene/2d/canvas_item.h b/scene/2d/canvas_item.h index bf7cfa8e75..a020ab5029 100644 --- a/scene/2d/canvas_item.h +++ b/scene/2d/canvas_item.h @@ -317,7 +317,7 @@ public: void draw_polygon(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), Ref<Texture> p_texture = Ref<Texture>(), const Ref<Texture> &p_normal_map = Ref<Texture>(), bool p_antialiased = false); void draw_colored_polygon(const Vector<Point2> &p_points, const Color &p_color, const Vector<Point2> &p_uvs = Vector<Point2>(), Ref<Texture> p_texture = Ref<Texture>(), const Ref<Texture> &p_normal_map = Ref<Texture>(), bool p_antialiased = false); - void draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map); + void draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map, const Transform2D &p_transform = Transform2D(), const Color &p_modulate = Color(1, 1, 1)); void draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map); void draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, const Color &p_modulate = Color(1, 1, 1), int p_clip_w = -1); diff --git a/scene/2d/light_2d.cpp b/scene/2d/light_2d.cpp index d1fce74085..6b12db9e44 100644 --- a/scene/2d/light_2d.cpp +++ b/scene/2d/light_2d.cpp @@ -438,7 +438,7 @@ void Light2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::REAL, "energy", PROPERTY_HINT_RANGE, "0.01,100,0.01"), "set_energy", "get_energy"); ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Add,Sub,Mix,Mask"), "set_mode", "get_mode"); ADD_GROUP("Range", "range_"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "range_height", PROPERTY_HINT_RANGE, "-100,100,0.1"), "set_height", "get_height"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "range_height", PROPERTY_HINT_RANGE, "-2048,2048,0.1,or_lesser,or_greater"), "set_height", "get_height"); ADD_PROPERTY(PropertyInfo(Variant::INT, "range_z_min", PROPERTY_HINT_RANGE, itos(VS::CANVAS_ITEM_Z_MIN) + "," + itos(VS::CANVAS_ITEM_Z_MAX) + ",1"), "set_z_range_min", "get_z_range_min"); ADD_PROPERTY(PropertyInfo(Variant::INT, "range_z_max", PROPERTY_HINT_RANGE, itos(VS::CANVAS_ITEM_Z_MIN) + "," + itos(VS::CANVAS_ITEM_Z_MAX) + ",1"), "set_z_range_max", "get_z_range_max"); ADD_PROPERTY(PropertyInfo(Variant::INT, "range_layer_min", PROPERTY_HINT_RANGE, "-512,512,1"), "set_layer_range_min", "get_layer_range_min"); diff --git a/scene/2d/light_occluder_2d.cpp b/scene/2d/light_occluder_2d.cpp index 5334caed67..3a3f90ac4b 100644 --- a/scene/2d/light_occluder_2d.cpp +++ b/scene/2d/light_occluder_2d.cpp @@ -32,9 +32,59 @@ #include "core/engine.h" +#define LINE_GRAB_WIDTH 8 +Rect2 OccluderPolygon2D::_edit_get_rect() const { + + if (rect_cache_dirty) { + if (closed) { + PoolVector<Vector2>::Read r = polygon.read(); + item_rect = Rect2(); + for (int i = 0; i < polygon.size(); i++) { + Vector2 pos = r[i]; + if (i == 0) + item_rect.position = pos; + else + item_rect.expand_to(pos); + } + rect_cache_dirty = false; + } else { + if (polygon.size() == 0) { + item_rect = Rect2(); + } else { + Vector2 d = Vector2(LINE_GRAB_WIDTH, LINE_GRAB_WIDTH); + item_rect = Rect2(polygon[0] - d, 2 * d); + for (int i = 1; i < polygon.size(); i++) { + item_rect.expand_to(polygon[i] - d); + item_rect.expand_to(polygon[i] + d); + } + } + } + } + + return item_rect; +} + +bool OccluderPolygon2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + + if (closed) { + return Geometry::is_point_in_polygon(p_point, Variant(polygon)); + } else { + const real_t d = LINE_GRAB_WIDTH / 2 + p_tolerance; + PoolVector<Vector2>::Read points = polygon.read(); + for (int i = 0; i < polygon.size() - 1; i++) { + Vector2 p = Geometry::get_closest_point_to_segment_2d(p_point, &points[i]); + if (p.distance_to(p_point) <= d) + return true; + } + + return false; + } +} + void OccluderPolygon2D::set_polygon(const PoolVector<Vector2> &p_polygon) { polygon = p_polygon; + rect_cache_dirty = true; VS::get_singleton()->canvas_occluder_polygon_set_shape(occ_polygon, p_polygon, closed); emit_changed(); } @@ -100,6 +150,7 @@ OccluderPolygon2D::OccluderPolygon2D() { occ_polygon = VS::get_singleton()->canvas_occluder_polygon_create(); closed = true; cull = CULL_DISABLED; + rect_cache_dirty = true; } OccluderPolygon2D::~OccluderPolygon2D() { @@ -164,6 +215,16 @@ void LightOccluder2D::_notification(int p_what) { } } +Rect2 LightOccluder2D::_edit_get_rect() const { + + return occluder_polygon.is_valid() ? occluder_polygon->_edit_get_rect() : Rect2(); +} + +bool LightOccluder2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + + return occluder_polygon.is_valid() ? occluder_polygon->_edit_is_selected_on_click(p_point, p_tolerance) : false; +} + void LightOccluder2D::set_occluder_polygon(const Ref<OccluderPolygon2D> &p_polygon) { #ifdef DEBUG_ENABLED diff --git a/scene/2d/light_occluder_2d.h b/scene/2d/light_occluder_2d.h index 498d65d764..7931cd0b30 100644 --- a/scene/2d/light_occluder_2d.h +++ b/scene/2d/light_occluder_2d.h @@ -50,10 +50,16 @@ private: bool closed; CullMode cull; + mutable Rect2 item_rect; + mutable bool rect_cache_dirty; + protected: static void _bind_methods(); public: + virtual Rect2 _edit_get_rect() const; + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; + void set_polygon(const PoolVector<Vector2> &p_polygon); PoolVector<Vector2> get_polygon() const; @@ -85,6 +91,9 @@ protected: static void _bind_methods(); public: + virtual Rect2 _edit_get_rect() const; + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; + void set_occluder_polygon(const Ref<OccluderPolygon2D> &p_polygon); Ref<OccluderPolygon2D> get_occluder_polygon() const; diff --git a/scene/2d/line_2d.cpp b/scene/2d/line_2d.cpp index 73692e0535..ba06b3ebff 100644 --- a/scene/2d/line_2d.cpp +++ b/scene/2d/line_2d.cpp @@ -120,8 +120,12 @@ void Line2D::clear_points() { } } -void Line2D::add_point(Vector2 pos) { - _points.append(pos); +void Line2D::add_point(Vector2 pos, int atpos) { + if (atpos < 0 || _points.size() < atpos) { + _points.append(pos); + } else { + _points.insert(atpos, pos); + } update(); } @@ -318,7 +322,7 @@ void Line2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_point_count"), &Line2D::get_point_count); - ClassDB::bind_method(D_METHOD("add_point", "position"), &Line2D::add_point); + ClassDB::bind_method(D_METHOD("add_point", "position", "at_position"), &Line2D::add_point, DEFVAL(-1)); ClassDB::bind_method(D_METHOD("remove_point", "i"), &Line2D::remove_point); ClassDB::bind_method(D_METHOD("clear_points"), &Line2D::clear_points); diff --git a/scene/2d/line_2d.h b/scene/2d/line_2d.h index 32befab2d3..8a6f7b2dc5 100644 --- a/scene/2d/line_2d.h +++ b/scene/2d/line_2d.h @@ -72,7 +72,7 @@ public: void clear_points(); - void add_point(Vector2 pos); + void add_point(Vector2 pos, int atpos = -1); void remove_point(int i); void set_width(float width); diff --git a/scene/2d/navigation_2d.cpp b/scene/2d/navigation_2d.cpp index 57e0a5b118..72b5f2fb12 100644 --- a/scene/2d/navigation_2d.cpp +++ b/scene/2d/navigation_2d.cpp @@ -542,7 +542,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect if (CLOCK_TANGENT(apex_point, portal_left, left) >= 0) { //process - if (portal_left.distance_squared_to(apex_point) < CMP_EPSILON || CLOCK_TANGENT(apex_point, left, portal_right) > 0) { + if (Math::is_zero_approx(portal_left.distance_squared_to(apex_point)) || CLOCK_TANGENT(apex_point, left, portal_right) > 0) { left_poly = p; portal_left = left; } else { @@ -552,7 +552,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect left_poly = p; portal_left = apex_point; portal_right = apex_point; - if (!path.size() || path[path.size() - 1].distance_to(apex_point) > CMP_EPSILON) + if (!path.size() || !Math::is_zero_approx(path[path.size() - 1].distance_to(apex_point))) path.push_back(apex_point); skip = true; } @@ -560,7 +560,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect if (!skip && CLOCK_TANGENT(apex_point, portal_right, right) <= 0) { //process - if (portal_right.distance_squared_to(apex_point) < CMP_EPSILON || CLOCK_TANGENT(apex_point, right, portal_left) < 0) { + if (Math::is_zero_approx(portal_right.distance_squared_to(apex_point)) || CLOCK_TANGENT(apex_point, right, portal_left) < 0) { right_poly = p; portal_right = right; } else { @@ -570,7 +570,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect right_poly = p; portal_right = apex_point; portal_left = apex_point; - if (!path.size() || path[path.size() - 1].distance_to(apex_point) > CMP_EPSILON) + if (!path.size() || !Math::is_zero_approx(path[path.size() - 1].distance_to(apex_point))) path.push_back(apex_point); } } @@ -596,7 +596,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect } } - if (!path.size() || path[path.size() - 1].distance_squared_to(begin_point) > CMP_EPSILON) { + if (!path.size() || !Math::is_zero_approx(path[path.size() - 1].distance_squared_to(begin_point))) { path.push_back(begin_point); // Add the begin point } else { path.write[path.size() - 1] = begin_point; // Replace first midpoint by the exact begin point @@ -604,7 +604,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect path.invert(); - if (path.size() <= 1 || path[path.size() - 1].distance_squared_to(end_point) > CMP_EPSILON) { + if (path.size() <= 1 || !Math::is_zero_approx(path[path.size() - 1].distance_squared_to(end_point))) { path.push_back(end_point); // Add the end point } else { path.write[path.size() - 1] = end_point; // Replace last midpoint by the exact end point diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp index fec861ad2f..4097006b33 100644 --- a/scene/2d/path_2d.cpp +++ b/scene/2d/path_2d.cpp @@ -95,7 +95,7 @@ void Path2D::_notification(int p_what) { return; } -#if TOOLS_ENABLED +#ifdef TOOLS_ENABLED const float line_width = 2 * EDSCALE; #else const float line_width = 2; diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index 690f1d4b4a..578c9aa5f9 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -1275,9 +1275,6 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const if (collided) { found_collision = true; - } - - if (collided) { colliders.push_back(collision); motion = collision.remainder; diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 3562011bc2..b321bcf3ce 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -749,7 +749,10 @@ void TileMap::set_cellv(const Vector2 &p_pos, int p_tile, bool p_flip_x, bool p_ void TileMap::_set_celld(const Vector2 &p_pos, const Dictionary &p_data) { - set_cell(p_pos.x, p_pos.y, p_data["id"], p_data["flip_h"], p_data["flip_y"], p_data["transpose"], p_data["auto_coord"]); + Variant v_pos_x = p_pos.x, v_pos_y = p_pos.y, v_tile = p_data["id"], v_flip_h = p_data["flip_h"], v_flip_v = p_data["flip_y"], v_transpose = p_data["transpose"], v_autotile_coord = p_data["auto_coord"]; + const Variant *args[7] = { &v_pos_x, &v_pos_y, &v_tile, &v_flip_h, &v_flip_v, &v_transpose, &v_autotile_coord }; + Variant::CallError ce; + call("set_cell", args, 7, ce); } void TileMap::set_cell(int p_x, int p_y, int p_tile, bool p_flip_x, bool p_flip_y, bool p_transpose, Vector2 p_autotile_coord) { @@ -1405,7 +1408,8 @@ Vector2 TileMap::_map_to_world(int p_x, int p_y, bool p_ignore_ofs) const { ret += get_cell_transform()[1] * (half_offset == HALF_OFFSET_Y ? 0.5 : -0.5); } } break; - default: {} + default: { + } } } return ret; @@ -1480,7 +1484,8 @@ Vector2 TileMap::world_to_map(const Vector2 &p_pos) const { ret.y += 0.5; } } break; - default: {} + default: { + } } // Account for precision errors on the border (GH-23250). diff --git a/scene/3d/area.cpp b/scene/3d/area.cpp index 13d9181082..3557f0425c 100644 --- a/scene/3d/area.cpp +++ b/scene/3d/area.cpp @@ -356,7 +356,9 @@ void Area::_area_inout(int p_status, const RID &p_area, int p_instance, int p_ar Map<ObjectID, AreaState>::Element *E = area_map.find(objid); - ERR_FAIL_COND(!area_in && !E); + if (!area_in && !E) { + return; //likely removed from the tree + } locked = true; diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp index 4b3934c4ea..ff8c218575 100644 --- a/scene/3d/audio_stream_player_3d.cpp +++ b/scene/3d/audio_stream_player_3d.cpp @@ -38,7 +38,7 @@ void AudioStreamPlayer3D::_mix_audio() { if (!stream_playback.is_valid() || !active || - (stream_paused && !stream_fade_out)) { + (stream_paused && !stream_paused_fade_out)) { return; } @@ -53,7 +53,7 @@ void AudioStreamPlayer3D::_mix_audio() { AudioFrame *buffer = mix_buffer.ptrw(); int buffer_size = mix_buffer.size(); - if (stream_fade_out) { + if (stream_paused_fade_out) { // Short fadeout ramp buffer_size = MIN(buffer_size, 128); } @@ -109,10 +109,10 @@ void AudioStreamPlayer3D::_mix_audio() { int buffers = AudioServer::get_singleton()->get_channel_count(); for (int k = 0; k < buffers; k++) { - AudioFrame target_volume = stream_fade_out ? AudioFrame(0.f, 0.f) : current.vol[k]; - AudioFrame vol_prev = stream_fade_in ? AudioFrame(0.f, 0.f) : prev_outputs[i].vol[k]; + AudioFrame target_volume = stream_paused_fade_out ? AudioFrame(0.f, 0.f) : current.vol[k]; + AudioFrame vol_prev = stream_paused_fade_in ? AudioFrame(0.f, 0.f) : prev_outputs[i].vol[k]; AudioFrame vol_inc = (target_volume - vol_prev) / float(buffer_size); - AudioFrame vol = stream_fade_in ? AudioFrame(0.f, 0.f) : current.vol[k]; + AudioFrame vol = stream_paused_fade_in ? AudioFrame(0.f, 0.f) : current.vol[k]; if (!AudioServer::get_singleton()->thread_has_channel_mix_buffer(current.bus_index, k)) continue; //may have been deleted, will be updated on process @@ -198,15 +198,9 @@ void AudioStreamPlayer3D::_mix_audio() { active = false; } - if (stream_stop) { - active = false; - set_physics_process_internal(false); - setplay = -1; - } - output_ready = false; - stream_fade_in = false; - stream_fade_out = false; + stream_paused_fade_in = false; + stream_paused_fade_out = false; } float AudioStreamPlayer3D::_get_attenuation_db(float p_distance) const { @@ -224,6 +218,7 @@ float AudioStreamPlayer3D::_get_attenuation_db(float p_distance) const { case ATTENUATION_LOGARITHMIC: { att = -20 * Math::log(p_distance / unit_size + CMP_EPSILON); } break; + case ATTENUATION_DISABLED: break; default: { ERR_PRINT("Unknown attenuation type"); break; @@ -662,7 +657,6 @@ float AudioStreamPlayer3D::get_pitch_scale() const { void AudioStreamPlayer3D::play(float p_from_pos) { if (stream_playback.is_valid()) { - stream_stop = false; active = true; setplay = p_from_pos; output_ready = false; @@ -680,8 +674,9 @@ void AudioStreamPlayer3D::seek(float p_seconds) { void AudioStreamPlayer3D::stop() { if (stream_playback.is_valid()) { - stream_stop = true; - stream_fade_out = true; + active = false; + set_physics_process_internal(false); + setplay = -1; } } @@ -831,7 +826,7 @@ float AudioStreamPlayer3D::get_attenuation_filter_db() const { } void AudioStreamPlayer3D::set_attenuation_model(AttenuationModel p_model) { - ERR_FAIL_INDEX(p_model, 3); + ERR_FAIL_INDEX((int)p_model, 4); attenuation_model = p_model; } @@ -877,8 +872,8 @@ void AudioStreamPlayer3D::set_stream_paused(bool p_pause) { if (p_pause != stream_paused) { stream_paused = p_pause; - stream_fade_in = stream_paused ? false : true; - stream_fade_out = stream_paused ? true : false; + stream_paused_fade_in = stream_paused ? false : true; + stream_paused_fade_out = stream_paused ? true : false; } } @@ -887,6 +882,10 @@ bool AudioStreamPlayer3D::get_stream_paused() const { return stream_paused; } +Ref<AudioStreamPlayback> AudioStreamPlayer3D::get_stream_playback() { + return stream_playback; +} + void AudioStreamPlayer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_stream", "stream"), &AudioStreamPlayer3D::set_stream); @@ -953,10 +952,12 @@ void AudioStreamPlayer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_stream_paused", "pause"), &AudioStreamPlayer3D::set_stream_paused); ClassDB::bind_method(D_METHOD("get_stream_paused"), &AudioStreamPlayer3D::get_stream_paused); + ClassDB::bind_method(D_METHOD("get_stream_playback"), &AudioStreamPlayer3D::get_stream_playback); + ClassDB::bind_method(D_METHOD("_bus_layout_changed"), &AudioStreamPlayer3D::_bus_layout_changed); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "attenuation_model", PROPERTY_HINT_ENUM, "Inverse,InverseSquare,Log"), "set_attenuation_model", "get_attenuation_model"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "attenuation_model", PROPERTY_HINT_ENUM, "Inverse,InverseSquare,Log,Disabled"), "set_attenuation_model", "get_attenuation_model"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "unit_db", PROPERTY_HINT_RANGE, "-80,80"), "set_unit_db", "get_unit_db"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "unit_size", PROPERTY_HINT_RANGE, "0.1,100,0.1"), "set_unit_size", "get_unit_size"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_db", PROPERTY_HINT_RANGE, "-24,6"), "set_max_db", "get_max_db"); @@ -981,6 +982,7 @@ void AudioStreamPlayer3D::_bind_methods() { BIND_ENUM_CONSTANT(ATTENUATION_INVERSE_DISTANCE); BIND_ENUM_CONSTANT(ATTENUATION_INVERSE_SQUARE_DISTANCE); BIND_ENUM_CONSTANT(ATTENUATION_LOGARITHMIC); + BIND_ENUM_CONSTANT(ATTENUATION_DISABLED); BIND_ENUM_CONSTANT(OUT_OF_RANGE_MIX); BIND_ENUM_CONSTANT(OUT_OF_RANGE_PAUSE); @@ -1016,9 +1018,8 @@ AudioStreamPlayer3D::AudioStreamPlayer3D() { out_of_range_mode = OUT_OF_RANGE_MIX; doppler_tracking = DOPPLER_TRACKING_DISABLED; stream_paused = false; - stream_fade_in = false; - stream_fade_out = false; - stream_stop = false; + stream_paused_fade_in = false; + stream_paused_fade_out = false; velocity_tracker.instance(); AudioServer::get_singleton()->connect("bus_layout_changed", this, "_bus_layout_changed"); diff --git a/scene/3d/audio_stream_player_3d.h b/scene/3d/audio_stream_player_3d.h index e467c170fb..98bc74b2e4 100644 --- a/scene/3d/audio_stream_player_3d.h +++ b/scene/3d/audio_stream_player_3d.h @@ -46,6 +46,7 @@ public: ATTENUATION_INVERSE_DISTANCE, ATTENUATION_INVERSE_SQUARE_DISTANCE, ATTENUATION_LOGARITHMIC, + ATTENUATION_DISABLED, }; enum OutOfRangeMode { @@ -109,9 +110,8 @@ private: float pitch_scale; bool autoplay; bool stream_paused; - bool stream_fade_in; - bool stream_fade_out; - bool stream_stop; + bool stream_paused_fade_in; + bool stream_paused_fade_out; StringName bus; void _mix_audio(); @@ -206,6 +206,8 @@ public: void set_stream_paused(bool p_pause); bool get_stream_paused() const; + Ref<AudioStreamPlayback> get_stream_playback(); + AudioStreamPlayer3D(); ~AudioStreamPlayer3D(); }; diff --git a/scene/3d/camera.cpp b/scene/3d/camera.cpp index 368cebeeab..54d7681a3a 100644 --- a/scene/3d/camera.cpp +++ b/scene/3d/camera.cpp @@ -55,16 +55,23 @@ void Camera::_update_camera_mode() { case PROJECTION_ORTHOGONAL: { set_orthogonal(size, near, far); } break; + case PROJECTION_FRUSTUM: { + set_frustum(size, frustum_offset, near, far); + } break; } } void Camera::_validate_property(PropertyInfo &p_property) const { if (p_property.name == "fov") { - if (mode == PROJECTION_ORTHOGONAL) { + if (mode != PROJECTION_PERSPECTIVE) { p_property.usage = PROPERTY_USAGE_NOEDITOR; } } else if (p_property.name == "size") { - if (mode == PROJECTION_PERSPECTIVE) { + if (mode != PROJECTION_ORTHOGONAL && mode != PROJECTION_FRUSTUM) { + p_property.usage = PROPERTY_USAGE_NOEDITOR; + } + } else if (p_property.name == "frustum_offset") { + if (mode != PROJECTION_FRUSTUM) { p_property.usage = PROPERTY_USAGE_NOEDITOR; } } @@ -177,8 +184,24 @@ void Camera::set_orthogonal(float p_size, float p_z_near, float p_z_far) { update_gizmo(); } +void Camera::set_frustum(float p_size, Vector2 p_offset, float p_z_near, float p_z_far) { + if (!force_change && size == p_size && frustum_offset == p_offset && p_z_near == near && p_z_far == far && mode == PROJECTION_FRUSTUM) + return; + + size = p_size; + frustum_offset = p_offset; + + near = p_z_near; + far = p_z_far; + mode = PROJECTION_FRUSTUM; + force_change = false; + + VisualServer::get_singleton()->camera_set_frustum(camera, size, frustum_offset, near, far); + update_gizmo(); +} + void Camera::set_projection(Camera::Projection p_mode) { - if (p_mode == PROJECTION_PERSPECTIVE || p_mode == PROJECTION_ORTHOGONAL) { + if (p_mode == PROJECTION_PERSPECTIVE || p_mode == PROJECTION_ORTHOGONAL || p_mode == PROJECTION_FRUSTUM) { mode = p_mode; _update_camera_mode(); _change_notify(); @@ -470,16 +493,19 @@ void Camera::_bind_methods() { ClassDB::bind_method(D_METHOD("project_position", "screen_point"), &Camera::project_position); ClassDB::bind_method(D_METHOD("set_perspective", "fov", "z_near", "z_far"), &Camera::set_perspective); ClassDB::bind_method(D_METHOD("set_orthogonal", "size", "z_near", "z_far"), &Camera::set_orthogonal); + ClassDB::bind_method(D_METHOD("set_frustum", "size", "offset", "z_near", "z_far"), &Camera::set_frustum); ClassDB::bind_method(D_METHOD("make_current"), &Camera::make_current); ClassDB::bind_method(D_METHOD("clear_current", "enable_next"), &Camera::clear_current, DEFVAL(true)); ClassDB::bind_method(D_METHOD("set_current"), &Camera::set_current); ClassDB::bind_method(D_METHOD("is_current"), &Camera::is_current); ClassDB::bind_method(D_METHOD("get_camera_transform"), &Camera::get_camera_transform); ClassDB::bind_method(D_METHOD("get_fov"), &Camera::get_fov); + ClassDB::bind_method(D_METHOD("get_frustum_offset"), &Camera::get_frustum_offset); ClassDB::bind_method(D_METHOD("get_size"), &Camera::get_size); ClassDB::bind_method(D_METHOD("get_zfar"), &Camera::get_zfar); ClassDB::bind_method(D_METHOD("get_znear"), &Camera::get_znear); ClassDB::bind_method(D_METHOD("set_fov"), &Camera::set_fov); + ClassDB::bind_method(D_METHOD("set_frustum_offset"), &Camera::set_frustum_offset); ClassDB::bind_method(D_METHOD("set_size"), &Camera::set_size); ClassDB::bind_method(D_METHOD("set_zfar"), &Camera::set_zfar); ClassDB::bind_method(D_METHOD("set_znear"), &Camera::set_znear); @@ -510,15 +536,17 @@ void Camera::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::REAL, "h_offset"), "set_h_offset", "get_h_offset"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "v_offset"), "set_v_offset", "get_v_offset"); ADD_PROPERTY(PropertyInfo(Variant::INT, "doppler_tracking", PROPERTY_HINT_ENUM, "Disabled,Idle,Physics"), "set_doppler_tracking", "get_doppler_tracking"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "projection", PROPERTY_HINT_ENUM, "Perspective,Orthogonal"), "set_projection", "get_projection"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "projection", PROPERTY_HINT_ENUM, "Perspective,Orthogonal,Frustum"), "set_projection", "get_projection"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "current"), "set_current", "is_current"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "fov", PROPERTY_HINT_RANGE, "1,179,0.1"), "set_fov", "get_fov"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "size", PROPERTY_HINT_RANGE, "0.1,16384,0.01"), "set_size", "get_size"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "frustum_offset"), "set_frustum_offset", "get_frustum_offset"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "near", PROPERTY_HINT_EXP_RANGE, "0.01,8192,0.01,or_greater"), "set_znear", "get_znear"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "far", PROPERTY_HINT_EXP_RANGE, "0.1,8192,0.1,or_greater"), "set_zfar", "get_zfar"); BIND_ENUM_CONSTANT(PROJECTION_PERSPECTIVE); BIND_ENUM_CONSTANT(PROJECTION_ORTHOGONAL); + BIND_ENUM_CONSTANT(PROJECTION_FRUSTUM); BIND_ENUM_CONSTANT(KEEP_WIDTH); BIND_ENUM_CONSTANT(KEEP_HEIGHT); @@ -543,6 +571,10 @@ float Camera::get_znear() const { return near; } +Vector2 Camera::get_frustum_offset() const { + return frustum_offset; +} + float Camera::get_zfar() const { return far; @@ -570,6 +602,11 @@ void Camera::set_znear(float p_znear) { _update_camera_mode(); } +void Camera::set_frustum_offset(Vector2 p_offset) { + frustum_offset = p_offset; + _update_camera_mode(); +} + void Camera::set_zfar(float p_zfar) { far = p_zfar; _update_camera_mode(); @@ -648,6 +685,7 @@ Camera::Camera() { camera = VisualServer::get_singleton()->camera_create(); size = 1; fov = 0; + frustum_offset = Vector2(); near = 0; far = 0; current = false; diff --git a/scene/3d/camera.h b/scene/3d/camera.h index a531324a85..fe8cb84f0d 100644 --- a/scene/3d/camera.h +++ b/scene/3d/camera.h @@ -46,7 +46,8 @@ public: enum Projection { PROJECTION_PERSPECTIVE, - PROJECTION_ORTHOGONAL + PROJECTION_ORTHOGONAL, + PROJECTION_FRUSTUM }; enum KeepAspect { @@ -68,6 +69,7 @@ private: float fov; float size; + Vector2 frustum_offset; float near, far; float v_offset; float h_offset; @@ -110,6 +112,7 @@ public: void set_perspective(float p_fovy_degrees, float p_z_near, float p_z_far); void set_orthogonal(float p_size, float p_z_near, float p_z_far); + void set_frustum(float p_size, Vector2 p_offset, float p_near, float p_far); void set_projection(Camera::Projection p_mode); void make_current(); @@ -123,12 +126,15 @@ public: float get_size() const; float get_zfar() const; float get_znear() const; + Vector2 get_frustum_offset() const; + Projection get_projection() const; void set_fov(float p_fov); void set_size(float p_size); void set_zfar(float p_zfar); void set_znear(float p_znear); + void set_frustum_offset(Vector2 p_offset); virtual Transform get_camera_transform() const; diff --git a/scene/3d/collision_shape.cpp b/scene/3d/collision_shape.cpp index ac33e2b714..6bb2b547c7 100644 --- a/scene/3d/collision_shape.cpp +++ b/scene/3d/collision_shape.cpp @@ -91,7 +91,7 @@ void CollisionShape::_notification(int p_what) { _update_in_shape_owner(); } if (get_tree()->is_debugging_collisions_hint()) { - _create_debug_shape(); + _update_debug_shape(); } } break; case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { @@ -142,17 +142,24 @@ void CollisionShape::_bind_methods() { ClassDB::bind_method(D_METHOD("make_convex_from_brothers"), &CollisionShape::make_convex_from_brothers); ClassDB::set_method_flags("CollisionShape", "make_convex_from_brothers", METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR); + ClassDB::bind_method(D_METHOD("_shape_changed"), &CollisionShape::_shape_changed); + ClassDB::bind_method(D_METHOD("_update_debug_shape"), &CollisionShape::_update_debug_shape); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape"), "set_shape", "get_shape"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled"); } void CollisionShape::set_shape(const Ref<Shape> &p_shape) { - if (!shape.is_null()) + if (!shape.is_null()) { shape->unregister_owner(this); + shape->disconnect("changed", this, "_shape_changed"); + } shape = p_shape; - if (!shape.is_null()) + if (!shape.is_null()) { shape->register_owner(this); + shape->connect("changed", this, "_shape_changed"); + } update_gizmo(); if (parent) { parent->shape_owner_clear_shapes(owner_id); @@ -161,6 +168,8 @@ void CollisionShape::set_shape(const Ref<Shape> &p_shape) { } } + if (is_inside_tree()) + _shape_changed(); update_configuration_warning(); } @@ -199,7 +208,8 @@ CollisionShape::~CollisionShape() { //VisualServer::get_singleton()->free(indicator); } -void CollisionShape::_create_debug_shape() { +void CollisionShape::_update_debug_shape() { + debug_shape_dirty = false; if (debug_shape) { debug_shape->queue_delete(); @@ -207,15 +217,19 @@ void CollisionShape::_create_debug_shape() { } Ref<Shape> s = get_shape(); - if (s.is_null()) return; Ref<Mesh> mesh = s->get_debug_mesh(); - MeshInstance *mi = memnew(MeshInstance); mi->set_mesh(mesh); - add_child(mi); debug_shape = mi; } + +void CollisionShape::_shape_changed() { + if (get_tree()->is_debugging_collisions_hint() && !debug_shape_dirty) { + debug_shape_dirty = true; + call_deferred("_update_debug_shape"); + } +} diff --git a/scene/3d/collision_shape.h b/scene/3d/collision_shape.h index 0c8e383a7f..98427b8590 100644 --- a/scene/3d/collision_shape.h +++ b/scene/3d/collision_shape.h @@ -45,12 +45,14 @@ class CollisionShape : public Spatial { CollisionObject *parent; Node *debug_shape; + bool debug_shape_dirty; void resource_changed(RES res); bool disabled; protected: - void _create_debug_shape(); + void _update_debug_shape(); + void _shape_changed(); void _update_in_shape_owner(bool p_xform_only = false); diff --git a/scene/3d/cpu_particles.cpp b/scene/3d/cpu_particles.cpp index 469a1e87db..d4e242dcb7 100644 --- a/scene/3d/cpu_particles.cpp +++ b/scene/3d/cpu_particles.cpp @@ -1394,6 +1394,7 @@ CPUParticles::CPUParticles() { redraw = false; multimesh = VisualServer::get_singleton()->multimesh_create(); + VisualServer::get_singleton()->multimesh_set_visible_instances(multimesh, 0); set_base(multimesh); set_emitting(true); diff --git a/scene/3d/light.cpp b/scene/3d/light.cpp index cf1af918f7..ed533ee7a4 100644 --- a/scene/3d/light.cpp +++ b/scene/3d/light.cpp @@ -293,7 +293,8 @@ Light::Light(VisualServer::LightType p_type) { case VS::LIGHT_DIRECTIONAL: light = VisualServer::get_singleton()->directional_light_create(); break; case VS::LIGHT_OMNI: light = VisualServer::get_singleton()->omni_light_create(); break; case VS::LIGHT_SPOT: light = VisualServer::get_singleton()->spot_light_create(); break; - default: {}; + default: { + }; } VS::get_singleton()->instance_set_base(get_instance(), light); diff --git a/scene/3d/path.cpp b/scene/3d/path.cpp index 190967d76c..84078911cb 100644 --- a/scene/3d/path.cpp +++ b/scene/3d/path.cpp @@ -173,7 +173,7 @@ void PathFollow::_update_transform() { float dot = t_prev.dot(t_cur); float angle = Math::acos(CLAMP(dot, -1, 1)); - if (likely(Math::abs(angle) > CMP_EPSILON)) { + if (likely(!Math::is_zero_approx(angle))) { if (rotation_mode == ROTATION_Y) { // assuming we're referring to global Y-axis. is this correct? axis.x = 0; @@ -184,7 +184,7 @@ void PathFollow::_update_transform() { // all components are allowed } - if (likely(axis.length() > CMP_EPSILON)) { + if (likely(!Math::is_zero_approx(axis.length()))) { t.rotate_basis(axis.normalized(), angle); } } @@ -193,7 +193,7 @@ void PathFollow::_update_transform() { float tilt_angle = c->interpolate_baked_tilt(o); Vector3 tilt_axis = t_cur; // not sure what tilt is supposed to do, is this correct?? - if (likely(Math::abs(tilt_angle) > CMP_EPSILON)) { + if (likely(!Math::is_zero_approx(Math::abs(tilt_angle)))) { if (rotation_mode == ROTATION_Y) { tilt_axis.x = 0; tilt_axis.z = 0; @@ -203,7 +203,7 @@ void PathFollow::_update_transform() { // all components are allowed } - if (likely(tilt_axis.length() > CMP_EPSILON)) { + if (likely(!Math::is_zero_approx(tilt_axis.length()))) { t.rotate_basis(tilt_axis.normalized(), tilt_angle); } } diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp index 05214ed669..e2dc89aa6e 100644 --- a/scene/3d/physics_body.cpp +++ b/scene/3d/physics_body.cpp @@ -1203,9 +1203,6 @@ Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Ve if (collided) { found_collision = true; - } - - if (collided) { colliders.push_back(collision); motion = collision.remainder; diff --git a/scene/3d/skeleton.cpp b/scene/3d/skeleton.cpp index b7279e4d4f..15c089ec10 100644 --- a/scene/3d/skeleton.cpp +++ b/scene/3d/skeleton.cpp @@ -540,10 +540,11 @@ void Skeleton::clear_bones() { void Skeleton::set_bone_pose(int p_bone, const Transform &p_pose) { ERR_FAIL_INDEX(p_bone, bones.size()); - ERR_FAIL_COND(!is_inside_tree()); bones.write[p_bone].pose = p_pose; - _make_dirty(); + if (is_inside_tree()) { + _make_dirty(); + } } Transform Skeleton::get_bone_pose(int p_bone) const { @@ -772,6 +773,8 @@ void Skeleton::_bind_methods() { ClassDB::bind_method(D_METHOD("get_bone_rest", "bone_idx"), &Skeleton::get_bone_rest); ClassDB::bind_method(D_METHOD("set_bone_rest", "bone_idx", "rest"), &Skeleton::set_bone_rest); + ClassDB::bind_method(D_METHOD("localize_rests"), &Skeleton::localize_rests); + ClassDB::bind_method(D_METHOD("set_bone_disable_rest", "bone_idx", "disable"), &Skeleton::set_bone_disable_rest); ClassDB::bind_method(D_METHOD("is_bone_rest_disabled", "bone_idx"), &Skeleton::is_bone_rest_disabled); diff --git a/scene/3d/spatial.cpp b/scene/3d/spatial.cpp index 83f99a2e3c..395f7b9b35 100644 --- a/scene/3d/spatial.cpp +++ b/scene/3d/spatial.cpp @@ -224,7 +224,8 @@ void Spatial::_notification(int p_what) { #endif } break; - default: {} + default: { + } } } diff --git a/scene/3d/vehicle_body.cpp b/scene/3d/vehicle_body.cpp index c7f7b14a8f..fde135c972 100644 --- a/scene/3d/vehicle_body.cpp +++ b/scene/3d/vehicle_body.cpp @@ -716,7 +716,7 @@ void VehicleBody::_update_friction(PhysicsDirectBodyState *s) { real_t rollingFriction = 0.f; if (wheelInfo.m_raycastInfo.m_isInContact) { - if (engine_force != 0.f) { + if (engine_force != 0.f && wheelInfo.engine_traction != false) { rollingFriction = -engine_force * s->get_step(); } else { real_t defaultRollingFrictionImpulse = 0.f; diff --git a/scene/3d/visual_instance.cpp b/scene/3d/visual_instance.cpp index 1bbf1b7bc7..1aded826c0 100644 --- a/scene/3d/visual_instance.cpp +++ b/scene/3d/visual_instance.cpp @@ -123,6 +123,8 @@ void VisualInstance::_bind_methods() { ClassDB::bind_method(D_METHOD("_get_visual_instance_rid"), &VisualInstance::_get_visual_instance_rid); ClassDB::bind_method(D_METHOD("set_base", "base"), &VisualInstance::set_base); + ClassDB::bind_method(D_METHOD("get_base"), &VisualInstance::get_base); + ClassDB::bind_method(D_METHOD("get_instance"), &VisualInstance::get_instance); ClassDB::bind_method(D_METHOD("set_layer_mask", "mask"), &VisualInstance::set_layer_mask); ClassDB::bind_method(D_METHOD("get_layer_mask"), &VisualInstance::get_layer_mask); ClassDB::bind_method(D_METHOD("set_layer_mask_bit", "layer", "enabled"), &VisualInstance::set_layer_mask_bit); @@ -136,6 +138,12 @@ void VisualInstance::_bind_methods() { void VisualInstance::set_base(const RID &p_base) { VisualServer::get_singleton()->instance_set_base(instance, p_base); + base = p_base; +} + +RID VisualInstance::get_base() const { + + return base; } VisualInstance::VisualInstance() { diff --git a/scene/3d/visual_instance.h b/scene/3d/visual_instance.h index 3b6fccf65f..f5b7479bb1 100644 --- a/scene/3d/visual_instance.h +++ b/scene/3d/visual_instance.h @@ -43,6 +43,7 @@ class VisualInstance : public Spatial { GDCLASS(VisualInstance, Spatial); OBJ_CATEGORY("3D Visual Nodes"); + RID base; RID instance; uint32_t layers; @@ -69,6 +70,7 @@ public: virtual AABB get_transformed_aabb() const; // helper void set_base(const RID &p_base); + RID get_base() const; void set_layer_mask(uint32_t p_mask); uint32_t get_layer_mask() const; diff --git a/scene/3d/voxel_light_baker.cpp b/scene/3d/voxel_light_baker.cpp index 750ed97ae6..75b419ca58 100644 --- a/scene/3d/voxel_light_baker.cpp +++ b/scene/3d/voxel_light_baker.cpp @@ -835,7 +835,7 @@ void VoxelLightBaker::plot_light_directional(const Vector3 &p_direction, const C for (int i = 0; i < 3; i++) { - if (ABS(light_axis[i]) < CMP_EPSILON) + if (Math::is_zero_approx(light_axis[i])) continue; clip[clip_planes].normal[i] = 1.0; @@ -978,7 +978,7 @@ void VoxelLightBaker::plot_light_omni(const Vector3 &p_pos, const Color &p_color for (int c = 0; c < 3; c++) { - if (ABS(light_axis[c]) < CMP_EPSILON) + if (Math::is_zero_approx(light_axis[c])) continue; clip[clip_planes].normal[c] = 1.0; @@ -1113,7 +1113,7 @@ void VoxelLightBaker::plot_light_spot(const Vector3 &p_pos, const Vector3 &p_axi for (int c = 0; c < 3; c++) { - if (ABS(light_axis[c]) < CMP_EPSILON) + if (Math::is_zero_approx(light_axis[c])) continue; clip[clip_planes].normal[c] = 1.0; diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp index 0daa574f72..e9b38ae990 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -363,6 +363,7 @@ AnimationNodeOneShot::AnimationNodeOneShot() { fade_out = 0.1; autorestart = false; autorestart_delay = 1; + autorestart_random_delay = 0; mix = MIX_MODE_BLEND; sync = false; @@ -857,6 +858,7 @@ AnimationNodeTransition::AnimationNodeTransition() { time = "time"; current = "current"; prev_current = "prev_current"; + xfade = 0.0; enabled_inputs = 0; for (int i = 0; i < MAX_INPUTS; i++) { diff --git a/scene/animation/animation_cache.cpp b/scene/animation/animation_cache.cpp index feefe3ddd4..84b3f103c5 100644 --- a/scene/animation/animation_cache.cpp +++ b/scene/animation/animation_cache.cpp @@ -309,7 +309,8 @@ void AnimationCache::set_all(float p_time, float p_delta) { } } break; - default: {} + default: { + } } } } diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp index 68ad71d01c..1e3470cd90 100644 --- a/scene/animation/animation_node_state_machine.cpp +++ b/scene/animation/animation_node_state_machine.cpp @@ -960,7 +960,7 @@ void AnimationNodeStateMachine::_bind_methods() { ClassDB::bind_method(D_METHOD("set_end_node", "name"), &AnimationNodeStateMachine::set_end_node); ClassDB::bind_method(D_METHOD("get_end_node"), &AnimationNodeStateMachine::get_end_node); - ClassDB::bind_method(D_METHOD("set_graph_offset", "name"), &AnimationNodeStateMachine::set_graph_offset); + ClassDB::bind_method(D_METHOD("set_graph_offset", "offset"), &AnimationNodeStateMachine::set_graph_offset); ClassDB::bind_method(D_METHOD("get_graph_offset"), &AnimationNodeStateMachine::get_graph_offset); ClassDB::bind_method(D_METHOD("_tree_changed"), &AnimationNodeStateMachine::_tree_changed); diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index fe1b8247ff..d1d3582c9d 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -891,7 +891,7 @@ void AnimationTree::_process_graph(float p_delta) { t->loc = Vector3(); t->rot = Quat(); t->rot_blend_accum = 0; - t->scale = Vector3(); + t->scale = Vector3(1, 1, 1); } float prev_time = time - delta; @@ -952,11 +952,9 @@ void AnimationTree::_process_graph(float p_delta) { t->loc = loc; t->rot = rot; t->rot_blend_accum = 0; - t->scale = Vector3(); + t->scale = scale; } - scale -= Vector3(1.0, 1.0, 1.0); //helps make it work properly with Add nodes - if (err != OK) continue; @@ -1241,8 +1239,6 @@ void AnimationTree::_process_graph(float p_delta) { Transform xform; xform.origin = t->loc; - t->scale += Vector3(1.0, 1.0, 1.0); //helps make it work properly with Add nodes and root motion - xform.basis.set_quat_scale(t->rot, t->scale); if (t->root_motion) { @@ -1276,7 +1272,8 @@ void AnimationTree::_process_graph(float p_delta) { t->object->set_indexed(t->subpath, t->value); } break; - default: {} //the rest don't matter + default: { + } //the rest don't matter } } } @@ -1301,9 +1298,17 @@ void AnimationTree::_notification(int p_what) { _clear_caches(); if (last_animation_player) { - Object *old_player = ObjectDB::get_instance(last_animation_player); - if (old_player) { - old_player->disconnect("caches_cleared", this, "_clear_caches"); + Object *player = ObjectDB::get_instance(last_animation_player); + if (player) { + player->disconnect("caches_cleared", this, "_clear_caches"); + } + } + } else if (p_what == NOTIFICATION_ENTER_TREE) { + if (last_animation_player) { + + Object *player = ObjectDB::get_instance(last_animation_player); + if (player) { + player->connect("caches_cleared", this, "_clear_caches"); } } } diff --git a/scene/animation/animation_tree_player.cpp b/scene/animation/animation_tree_player.cpp index 3420b48ae3..3cc90c2ad6 100644 --- a/scene/animation/animation_tree_player.cpp +++ b/scene/animation/animation_tree_player.cpp @@ -193,7 +193,8 @@ bool AnimationTreePlayer::_set(const StringName &p_name, const Variant &p_value) } } break; - default: {}; + default: { + }; } } @@ -352,7 +353,8 @@ bool AnimationTreePlayer::_get(const StringName &p_name, Variant &r_ret) const { node["transitions"] = transitions; } break; - default: {}; + default: { + }; } nodes.push_back(node); @@ -772,7 +774,8 @@ float AnimationTreePlayer::_process_node(const StringName &p_node, AnimationNode } } break; - default: {} + default: { + } } return 0; @@ -882,7 +885,8 @@ void AnimationTreePlayer::_process_animation(float p_delta) { tr.track->object->call(method, args[0], args[1], args[2], args[3], args[4]); } } break; - default: {} + default: { + } } } } @@ -969,7 +973,8 @@ void AnimationTreePlayer::add_node(NodeType p_type, const StringName &p_node) { n = memnew(TransitionNode); } break; - default: {} + default: { + } } //n->name+=" "+itos(p_node); diff --git a/scene/animation/skeleton_ik.cpp b/scene/animation/skeleton_ik.cpp index 88fb2d5bfc..4da3e6ee28 100644 --- a/scene/animation/skeleton_ik.cpp +++ b/scene/animation/skeleton_ik.cpp @@ -335,7 +335,7 @@ void SkeletonIK::_validate_property(PropertyInfo &property) const { if (skeleton) { - String names; + String names("--,"); for (int i = 0; i < skeleton->get_bone_count(); i++) { if (i > 0) names += ","; diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp index 2688041d18..144e58d8b9 100644 --- a/scene/audio/audio_stream_player.cpp +++ b/scene/audio/audio_stream_player.cpp @@ -32,33 +32,10 @@ #include "core/engine.h" -void AudioStreamPlayer::_mix_internal(bool p_fadeout) { - - int bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus); - //get data - AudioFrame *buffer = mix_buffer.ptrw(); - int buffer_size = mix_buffer.size(); - - if (p_fadeout) { - // Short fadeout ramp - buffer_size = MIN(buffer_size, 128); - } +void AudioStreamPlayer::_mix_to_bus(const AudioFrame *p_frames,int p_amount) { - stream_playback->mix(buffer, pitch_scale, buffer_size); - - //multiply volume interpolating to avoid clicks if this changes - float target_volume = p_fadeout ? -80.0 : volume_db; - float vol = Math::db2linear(mix_volume_db); - float vol_inc = (Math::db2linear(target_volume) - vol) / float(buffer_size); - - for (int i = 0; i < buffer_size; i++) { - buffer[i] *= vol; - vol += vol_inc; - } - - //set volume for next mix - mix_volume_db = target_volume; + int bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus); AudioFrame *targets[4] = { NULL, NULL, NULL, NULL }; @@ -83,42 +60,84 @@ void AudioStreamPlayer::_mix_internal(bool p_fadeout) { for (int c = 0; c < 4; c++) { if (!targets[c]) break; - for (int i = 0; i < buffer_size; i++) { - targets[c][i] += buffer[i]; + for (int i = 0; i < p_amount; i++) { + targets[c][i] += p_frames[i]; } } } + +void AudioStreamPlayer::_mix_internal(bool p_fadeout) { + + + //get data + AudioFrame *buffer = mix_buffer.ptrw(); + int buffer_size = mix_buffer.size(); + + if (p_fadeout) { + // Short fadeout ramp + buffer_size = MIN(buffer_size, 128); + } + + stream_playback->mix(buffer, pitch_scale, buffer_size); + + //multiply volume interpolating to avoid clicks if this changes + float target_volume = p_fadeout ? -80.0 : volume_db; + float vol = Math::db2linear(mix_volume_db); + float vol_inc = (Math::db2linear(target_volume) - vol) / float(buffer_size); + + for (int i = 0; i < buffer_size; i++) { + buffer[i] *= vol; + vol += vol_inc; + } + + //set volume for next mix + mix_volume_db = target_volume; + + _mix_to_bus(buffer,buffer_size); + +} + void AudioStreamPlayer::_mix_audio() { + if (use_fadeout) { + _mix_to_bus(fadeout_buffer.ptr(),fadeout_buffer.size()); + use_fadeout=false; + } + if (!stream_playback.is_valid() || !active || - (stream_paused && !stream_fade)) { + (stream_paused && !stream_paused_fade)) { return; } - if (stream_fade) { - _mix_internal(true); - stream_fade = false; - - if (stream_stop) { - stream_playback->stop(); - active = false; - set_process_internal(false); + if (stream_paused) { + if (stream_paused_fade) { + _mix_internal(true); + stream_paused_fade = false; } return; } - if (setseek >= 0.0) { + if (setstop) { + _mix_internal(true); + stream_playback->stop(); + setstop=false; + } + + if (setseek >= 0.0 && !stop_has_priority) { if (stream_playback->is_playing()) { //fade out to avoid pops _mix_internal(true); } + stream_playback->start(setseek); setseek = -1.0; //reset seek mix_volume_db = volume_db; //reset ramp } + stop_has_priority = false; + _mix_internal(false); } @@ -135,7 +154,7 @@ void AudioStreamPlayer::_notification(int p_what) { if (p_what == NOTIFICATION_INTERNAL_PROCESS) { if (!active || (setseek < 0 && !stream_playback->is_playing())) { - active = false; + active = false; set_process_internal(false); emit_signal("finished"); } @@ -162,6 +181,28 @@ void AudioStreamPlayer::set_stream(Ref<AudioStream> p_stream) { AudioServer::get_singleton()->lock(); + if (active && stream_playback.is_valid() && !stream_paused) { + //changing streams out of the blue is not a great idea, but at least + //lets try to somehow avoid a click + + AudioFrame *buffer = fadeout_buffer.ptrw(); + int buffer_size = fadeout_buffer.size(); + + stream_playback->mix(buffer, pitch_scale, buffer_size); + + //multiply volume interpolating to avoid clicks if this changes + float target_volume = -80.0; + float vol = Math::db2linear(mix_volume_db); + float vol_inc = (Math::db2linear(target_volume) - vol) / float(buffer_size); + + for (int i = 0; i < buffer_size; i++) { + buffer[i] *= vol; + vol += vol_inc; + } + + use_fadeout=true; + } + mix_buffer.resize(AudioServer::get_singleton()->thread_get_mix_buffer_size()); if (stream_playback.is_valid()) { @@ -169,6 +210,7 @@ void AudioStreamPlayer::set_stream(Ref<AudioStream> p_stream) { stream.unref(); active = false; setseek = -1; + setstop = false; } if (p_stream.is_valid()) { @@ -209,8 +251,8 @@ void AudioStreamPlayer::play(float p_from_pos) { if (stream_playback.is_valid()) { //mix_volume_db = volume_db; do not reset volume ramp here, can cause clicks - stream_stop = false; setseek = p_from_pos; + stop_has_priority=false; active = true; set_process_internal(true); } @@ -225,16 +267,16 @@ void AudioStreamPlayer::seek(float p_seconds) { void AudioStreamPlayer::stop() { - if (stream_playback.is_valid()) { - stream_stop = true; - stream_fade = true; + if (stream_playback.is_valid() && active) { + setstop=true; + stop_has_priority=true; } } bool AudioStreamPlayer::is_playing() const { if (stream_playback.is_valid()) { - return active; //&& stream_playback->is_playing(); + return active && !setstop; //&& stream_playback->is_playing(); } return false; @@ -301,7 +343,7 @@ void AudioStreamPlayer::set_stream_paused(bool p_pause) { if (p_pause != stream_paused) { stream_paused = p_pause; - stream_fade = p_pause ? true : false; + stream_paused_fade = p_pause ? true : false; } } @@ -315,7 +357,7 @@ void AudioStreamPlayer::_validate_property(PropertyInfo &property) const { if (property.name == "bus") { String options; - for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) { + for (int i = 0; i <AudioServer::get_singleton()->get_bus_count(); i++) { if (i > 0) options += ","; String name = AudioServer::get_singleton()->get_bus_name(i); @@ -331,6 +373,10 @@ void AudioStreamPlayer::_bus_layout_changed() { _change_notify(); } +Ref<AudioStreamPlayback> AudioStreamPlayer::get_stream_playback() { + return stream_playback; +} + void AudioStreamPlayer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_stream", "stream"), &AudioStreamPlayer::set_stream); @@ -366,6 +412,8 @@ void AudioStreamPlayer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_stream_paused", "pause"), &AudioStreamPlayer::set_stream_paused); ClassDB::bind_method(D_METHOD("get_stream_paused"), &AudioStreamPlayer::get_stream_paused); + ClassDB::bind_method(D_METHOD("get_stream_playback"), &AudioStreamPlayer::get_stream_playback); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "volume_db", PROPERTY_HINT_RANGE, "-80,24"), "set_volume_db", "get_volume_db"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "pitch_scale", PROPERTY_HINT_RANGE, "0.01,32,0.01"), "set_pitch_scale", "get_pitch_scale"); @@ -391,9 +439,11 @@ AudioStreamPlayer::AudioStreamPlayer() { setseek = -1; active = false; stream_paused = false; - stream_fade = false; - stream_stop = false; + stream_paused_fade = false; mix_target = MIX_TARGET_STEREO; + fadeout_buffer.resize(512); + setstop=false; + use_fadeout=false; AudioServer::get_singleton()->connect("bus_layout_changed", this, "_bus_layout_changed"); } diff --git a/scene/audio/audio_stream_player.h b/scene/audio/audio_stream_player.h index fba8ce7dd3..0b782b67e7 100644 --- a/scene/audio/audio_stream_player.h +++ b/scene/audio/audio_stream_player.h @@ -49,17 +49,20 @@ private: Ref<AudioStreamPlayback> stream_playback; Ref<AudioStream> stream; Vector<AudioFrame> mix_buffer; + Vector<AudioFrame> fadeout_buffer; + bool use_fadeout; volatile float setseek; volatile bool active; + volatile bool setstop; + volatile bool stop_has_priority; float mix_volume_db; float pitch_scale; float volume_db; bool autoplay; bool stream_paused; - bool stream_fade; - bool stream_stop; + bool stream_paused_fade; StringName bus; MixTarget mix_target; @@ -72,6 +75,7 @@ private: bool _is_active() const; void _bus_layout_changed(); + void _mix_to_bus(const AudioFrame *p_frames, int p_amount); protected: void _validate_property(PropertyInfo &property) const; @@ -106,6 +110,9 @@ public: void set_stream_paused(bool p_pause); bool get_stream_paused() const; + Ref<AudioStreamPlayback> get_stream_playback(); + + AudioStreamPlayer(); ~AudioStreamPlayer(); }; diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index 806c8afa5b..f808d6c234 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -59,7 +59,7 @@ void BaseButton::_gui_input(Ref<InputEvent> p_event) { Ref<InputEventMouseButton> b = p_event; if (b.is_valid()) { - if (status.disabled || ((1 << (b->get_button_index() - 1)) & button_mask) == 0) + if (((1 << (b->get_button_index() - 1)) & button_mask) == 0) return; if (status.pressing_button) @@ -282,10 +282,7 @@ void BaseButton::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE) { } - if (p_what == NOTIFICATION_EXIT_TREE) { - } - - if (p_what == NOTIFICATION_VISIBILITY_CHANGED && !is_visible_in_tree()) { + if (p_what == NOTIFICATION_EXIT_TREE || (p_what == NOTIFICATION_VISIBILITY_CHANGED && !is_visible_in_tree())) { if (!toggle_mode) { status.pressed = false; diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index 7aca6acd00..d8b2cfb5b9 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -758,23 +758,33 @@ void ColorPickerButton::_modal_closed() { void ColorPickerButton::pressed() { _update_picker(); - popup->set_position(get_global_position() - picker->get_combined_minimum_size()); + popup->set_position(get_global_position() - picker->get_combined_minimum_size() * get_global_transform().get_scale()); + popup->set_scale(get_global_transform().get_scale()); popup->popup(); picker->set_focus_on_line_edit(); } void ColorPickerButton::_notification(int p_what) { - if (p_what == NOTIFICATION_DRAW) { + switch (p_what) { + case NOTIFICATION_DRAW: { + + Ref<StyleBox> normal = get_stylebox("normal"); + Rect2 r = Rect2(normal->get_offset(), get_size() - normal->get_minimum_size()); + draw_texture_rect(Control::get_icon("bg", "ColorPickerButton"), r, true); + draw_rect(r, color); + } break; + case MainLoop::NOTIFICATION_WM_QUIT_REQUEST: { - Ref<StyleBox> normal = get_stylebox("normal"); - Rect2 r = Rect2(normal->get_offset(), get_size() - normal->get_minimum_size()); - draw_texture_rect(Control::get_icon("bg", "ColorPickerButton"), r, true); - draw_rect(r, color); + if (popup) + popup->hide(); + } break; } - if (p_what == MainLoop::NOTIFICATION_WM_QUIT_REQUEST && popup) { - popup->hide(); + if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { + if (popup && !is_visible_in_tree()) { + popup->hide(); + } } } @@ -825,6 +835,8 @@ void ColorPickerButton::_update_picker() { add_child(popup); picker->connect("color_changed", this, "_color_changed"); popup->connect("modal_closed", this, "_modal_closed"); + popup->connect("about_to_show", this, "set_pressed", varray(true)); + popup->connect("popup_hide", this, "set_pressed", varray(false)); picker->set_pick_color(color); picker->set_edit_alpha(edit_alpha); } @@ -855,4 +867,6 @@ ColorPickerButton::ColorPickerButton() { picker = NULL; popup = NULL; edit_alpha = true; + + set_toggle_mode(true); } diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index 059e59ea21..5671b41de8 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -86,7 +86,9 @@ void FileDialog::_unhandled_input(const Ref<InputEvent> &p_event) { _dir_entered(".."); } break; - default: { handled = false; } + default: { + handled = false; + } } if (handled) diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index 30ad81bb2e..dabff08fea 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -1053,7 +1053,7 @@ void GraphEdit::set_connection_activity(const StringName &p_from, int p_from_por if (E->get().from == p_from && E->get().from_port == p_from_port && E->get().to == p_to && E->get().to_port == p_to_port) { - if (ABS(E->get().activity - p_activity) < CMP_EPSILON) { + if (Math::is_equal_approx(E->get().activity, p_activity)) { //update only if changed top_layer->update(); connections_layer->update(); diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 0c5dc39273..d889c8d8b8 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -56,6 +56,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { if (b->is_pressed() && b->get_button_index() == BUTTON_RIGHT && context_menu_enabled) { menu->set_position(get_global_transform().xform(get_local_mouse_position())); menu->set_size(Vector2(1, 1)); + menu->set_scale(get_global_transform().get_scale()); menu->popup(); grab_focus(); return; @@ -722,6 +723,8 @@ void LineEdit::_notification(int p_what) { } else { x_ofs = MAX(style->get_margin(MARGIN_LEFT), x_ofs - r_icon->get_width() - style->get_margin(MARGIN_RIGHT)); } + + ofs_max -= r_icon->get_width(); } int caret_height = font->get_height() > y_area ? y_area : font->get_height(); @@ -918,6 +921,10 @@ void LineEdit::undo() { TextOperation op = undo_stack_pos->get(); text = op.text; set_cursor_position(op.cursor_pos); + + if (expand_to_text_length) + minimum_size_changed(); + _emit_text_change(); } @@ -932,6 +939,10 @@ void LineEdit::redo() { TextOperation op = undo_stack_pos->get(); text = op.text; set_cursor_position(op.cursor_pos); + + if (expand_to_text_length) + minimum_size_changed(); + _emit_text_change(); } @@ -1159,8 +1170,10 @@ void LineEdit::set_cursor_position(int p_pos) { } else if (cursor_pos > window_pos) { /* Adjust window if cursor goes too much to the right */ int window_width = get_size().width - style->get_minimum_size().width; - if (right_icon.is_valid()) { - window_width -= right_icon->get_width(); + bool display_clear_icon = !text.empty() && is_editable() && clear_button_enabled; + if (right_icon.is_valid() || display_clear_icon) { + Ref<Texture> r_icon = display_clear_icon ? Control::get_icon("clear") : right_icon; + window_width -= r_icon->get_width(); } if (window_width < 0) @@ -1315,7 +1328,27 @@ void LineEdit::select_all() { void LineEdit::set_editable(bool p_editable) { + if (editable == p_editable) + return; + editable = p_editable; + + // Reorganize context menu. + menu->clear(); + if (editable) + menu->add_item(RTR("Cut"), MENU_CUT, KEY_MASK_CMD | KEY_X); + menu->add_item(RTR("Copy"), MENU_COPY, KEY_MASK_CMD | KEY_C); + if (editable) + menu->add_item(RTR("Paste"), MENU_PASTE, KEY_MASK_CMD | KEY_V); + menu->add_separator(); + menu->add_item(RTR("Select All"), MENU_SELECT_ALL, KEY_MASK_CMD | KEY_A); + if (editable) { + menu->add_item(RTR("Clear"), MENU_CLEAR); + menu->add_separator(); + menu->add_item(RTR("Undo"), MENU_UNDO, KEY_MASK_CMD | KEY_Z); + menu->add_item(RTR("Redo"), MENU_REDO, KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Z); + } + update(); } @@ -1611,7 +1644,6 @@ LineEdit::LineEdit() { deselect(); set_focus_mode(FOCUS_ALL); - editable = true; set_default_cursor_shape(CURSOR_IBEAM); set_mouse_filter(MOUSE_FILTER_STOP); @@ -1626,15 +1658,7 @@ LineEdit::LineEdit() { context_menu_enabled = true; menu = memnew(PopupMenu); add_child(menu); - menu->add_item(RTR("Cut"), MENU_CUT, KEY_MASK_CMD | KEY_X); - menu->add_item(RTR("Copy"), MENU_COPY, KEY_MASK_CMD | KEY_C); - menu->add_item(RTR("Paste"), MENU_PASTE, KEY_MASK_CMD | KEY_V); - menu->add_separator(); - menu->add_item(RTR("Select All"), MENU_SELECT_ALL, KEY_MASK_CMD | KEY_A); - menu->add_item(RTR("Clear"), MENU_CLEAR); - menu->add_separator(); - menu->add_item(RTR("Undo"), MENU_UNDO, KEY_MASK_CMD | KEY_Z); - menu->add_item(RTR("Redo"), MENU_REDO, KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Z); + set_editable(true); menu->connect("id_pressed", this, "menu_option"); expand_to_text_length = false; } diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp index b67d8c00d6..e12cd55e6f 100644 --- a/scene/gui/menu_button.cpp +++ b/scene/gui/menu_button.cpp @@ -55,8 +55,9 @@ void MenuButton::pressed() { Size2 size = get_size(); Point2 gp = get_global_position(); - popup->set_global_position(gp + Size2(0, size.height)); + popup->set_global_position(gp + Size2(0, size.height * get_global_transform().get_scale().y)); popup->set_size(Size2(size.width, 0)); + popup->set_scale(get_global_transform().get_scale()); popup->set_parent_rect(Rect2(Point2(gp - popup->get_global_position()), get_size())); popup->popup(); } @@ -91,6 +92,16 @@ bool MenuButton::is_switch_on_hover() { return switch_on_hover; } +void MenuButton::_notification(int p_what) { + + if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { + + if (!is_visible_in_tree()) { + popup->hide(); + } + } +} + void MenuButton::_bind_methods() { ClassDB::bind_method(D_METHOD("get_popup"), &MenuButton::get_popup); @@ -116,15 +127,18 @@ MenuButton::MenuButton() { switch_on_hover = false; set_flat(true); + set_toggle_mode(true); set_disable_shortcuts(false); set_enabled_focus_mode(FOCUS_NONE); + set_process_unhandled_key_input(true); + set_action_mode(ACTION_MODE_BUTTON_PRESS); + popup = memnew(PopupMenu); popup->hide(); add_child(popup); - popup->set_as_toplevel(true); popup->set_pass_on_modal_close_click(false); - set_process_unhandled_key_input(true); - set_action_mode(ACTION_MODE_BUTTON_PRESS); + popup->connect("about_to_show", this, "set_pressed", varray(true)); // For when switching from another MenuButton. + popup->connect("popup_hide", this, "set_pressed", varray(false)); } MenuButton::~MenuButton() { diff --git a/scene/gui/menu_button.h b/scene/gui/menu_button.h index 794840035e..42e909d991 100644 --- a/scene/gui/menu_button.h +++ b/scene/gui/menu_button.h @@ -52,6 +52,7 @@ class MenuButton : public Button { void _gui_input(Ref<InputEvent> p_event); protected: + void _notification(int p_what); static void _bind_methods(); public: diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index b9b270ce0c..7238543a14 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -72,6 +72,11 @@ void OptionButton::_notification(int p_what) { Point2 ofs(size.width - arrow->get_width() - get_constant("arrow_margin"), int(Math::abs((size.height - arrow->get_height()) / 2))); arrow->draw(ci, ofs, clr); + } else if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { + + if (!is_visible_in_tree()) { + popup->hide(); + } } } @@ -104,9 +109,9 @@ void OptionButton::_selected(int p_which) { void OptionButton::pressed() { Size2 size = get_size(); - popup->set_global_position(get_global_position() + Size2(0, size.height)); + popup->set_global_position(get_global_position() + Size2(0, size.height * get_global_transform().get_scale().y)); popup->set_size(Size2(size.width, 0)); - + popup->set_scale(get_global_transform().get_scale()); popup->popup(); } @@ -340,16 +345,18 @@ void OptionButton::_bind_methods() { OptionButton::OptionButton() { current = -1; + set_toggle_mode(true); set_text_align(ALIGN_LEFT); set_action_mode(ACTION_MODE_BUTTON_PRESS); popup = memnew(PopupMenu); popup->hide(); add_child(popup); - popup->set_as_toplevel(true); popup->set_pass_on_modal_close_click(false); + popup->set_notify_transform(true); popup->connect("id_pressed", this, "_selected"); popup->connect("id_focused", this, "_focused"); + popup->connect("popup_hide", this, "set_pressed", varray(false)); } OptionButton::~OptionButton() { diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp index 80ec7049fc..b7601bdd3e 100644 --- a/scene/gui/popup.cpp +++ b/scene/gui/popup.cpp @@ -65,7 +65,7 @@ void Popup::_notification(int p_what) { void Popup::_fix_size() { Point2 pos = get_global_position(); - Size2 size = get_size(); + Size2 size = get_size() * get_scale(); Point2 window_size = get_viewport_rect().size; if (pos.x + size.width > window_size.width) diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index 94c73b2e42..7c6b003dc3 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -159,13 +159,14 @@ void PopupMenu::_activate_submenu(int over) { Rect2 pr(p, get_size()); Ref<StyleBox> style = get_stylebox("panel"); - Point2 pos = p + Point2(get_size().width, items[over]._ofs_cache - style->get_offset().y); + Point2 pos = p + Point2(get_size().width, items[over]._ofs_cache - style->get_offset().y) * get_global_transform().get_scale(); Size2 size = pm->get_size(); // fix pos if (pos.x + size.width > get_viewport_rect().size.width) pos.x = p.x - size.width; pm->set_position(pos); + pm->set_scale(get_global_transform().get_scale()); pm->popup(); PopupMenu *pum = Object::cast_to<PopupMenu>(pm); @@ -196,11 +197,11 @@ void PopupMenu::_scroll(float p_factor, const Point2 &p_over) { int vseparation = get_constant("vseparation"); Ref<Font> font = get_font("font"); - float dy = (vseparation + font->get_height()) * 3 * p_factor; + float dy = (vseparation + font->get_height()) * 3 * p_factor * get_global_transform().get_scale().y; if (dy > 0 && global_y < 0) dy = MIN(dy, -global_y - 1); - else if (dy < 0 && global_y + get_size().y > get_viewport_rect().size.y) - dy = -MIN(-dy, global_y + get_size().y - get_viewport_rect().size.y - 1); + else if (dy < 0 && global_y + get_size().y * get_global_transform().get_scale().y > get_viewport_rect().size.y) + dy = -MIN(-dy, global_y + get_size().y * get_global_transform().get_scale().y - get_viewport_rect().size.y - 1); set_position(get_position() + Vector2(0, dy)); Ref<InputEventMouseMotion> ie; @@ -239,7 +240,7 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) { for (int i = search_from; i >= 0; i--) { - if (i < 0 || i >= items.size()) + if (i >= items.size()) continue; if (!items[i].separator && !items[i].disabled) { @@ -289,7 +290,7 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) { case BUTTON_WHEEL_DOWN: { - if (get_global_position().y + get_size().y > get_viewport_rect().size.y) { + if (get_global_position().y + get_size().y * get_global_transform().get_scale().y > get_viewport_rect().size.y) { _scroll(-b->get_factor(), b->get_position()); } } break; @@ -415,7 +416,6 @@ void PopupMenu::_notification(int p_what) { minimum_size_changed(); update(); - } break; case NOTIFICATION_DRAW: { @@ -528,7 +528,6 @@ void PopupMenu::_notification(int p_what) { ofs.y += h; } - } break; case MainLoop::NOTIFICATION_WM_FOCUS_OUT: { diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 00d6ac3b94..101eb2ac88 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -711,7 +711,8 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & } break; - default: {} + default: { + } } Item *itp = it; @@ -752,6 +753,8 @@ void RichTextLabel::_scroll_changed(double) { else scroll_following = false; + scroll_updated = true; + update(); } @@ -777,7 +780,6 @@ void RichTextLabel::_update_scroll() { main->first_invalid_line = 0; //invalidate ALL _validate_line_caches(main); } - scroll_updated = true; } void RichTextLabel::_notification(int p_what) { @@ -900,13 +902,21 @@ void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item Control::CursorShape RichTextLabel::get_cursor_shape(const Point2 &p_pos) const { - if (selection.click) + if (!underline_meta || selection.click) return CURSOR_ARROW; if (main->first_invalid_line < main->lines.size()) return CURSOR_ARROW; //invalid - return get_default_cursor_shape(); + int line = 0; + Item *item = NULL; + + ((RichTextLabel *)(this))->_find_click(main, p_pos, &item, &line); + + if (item && ((RichTextLabel *)(this))->_find_meta(item, NULL)) + return CURSOR_POINTING_HAND; + + return CURSOR_ARROW; } void RichTextLabel::_gui_input(Ref<InputEvent> p_event) { @@ -1996,7 +2006,7 @@ bool RichTextLabel::search(const String &p_string, bool p_from_selection, bool p Item *it = main; int charidx = 0; - if (p_from_selection && selection.active && selection.enabled) { + if (p_from_selection && selection.active) { it = selection.to; charidx = selection.to_char + 1; } diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp index eb04b85931..028ca41cbf 100644 --- a/scene/gui/slider.cpp +++ b/scene/gui/slider.cpp @@ -34,8 +34,15 @@ Size2 Slider::get_minimum_size() const { Ref<StyleBox> style = get_stylebox("slider"); - Size2i ms = style->get_minimum_size() + style->get_center_size(); - return ms; + Size2i ss = style->get_minimum_size() + style->get_center_size(); + + Ref<Texture> grabber = get_icon("grabber"); + Size2i rs = grabber->get_size(); + + if (orientation == HORIZONTAL) + return Size2i(ss.width, MAX(ss.height, rs.height)); + else + return Size2i(MAX(ss.width, rs.width), ss.height); } void Slider::_gui_input(Ref<InputEvent> p_event) { @@ -134,7 +141,11 @@ void Slider::_gui_input(Ref<InputEvent> p_event) { void Slider::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_THEME_CHANGED: { + minimum_size_changed(); + update(); + } break; case NOTIFICATION_MOUSE_ENTER: { mouse_inside = true; diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index 212efa4976..6997c2809c 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -86,8 +86,8 @@ void TabContainer::_gui_input(const Ref<InputEvent> &p_event) { emit_signal("pre_popup_pressed"); Vector2 popup_pos = get_global_position(); - popup_pos.x += size.width - popup->get_size().width; - popup_pos.y += menu->get_height(); + popup_pos.x += size.width * get_global_transform().get_scale().x - popup->get_size().width * popup->get_global_transform().get_scale().x; + popup_pos.y += menu->get_height() * get_global_transform().get_scale().y; popup->set_global_position(popup_pos); popup->popup(); @@ -127,6 +127,9 @@ void TabContainer::_gui_input(const Ref<InputEvent> &p_event) { // Activate the clicked tab. pos.x -= tabs_ofs_cache; for (int i = first_tab_cache; i <= last_tab_cache; i++) { + if (get_tab_hidden(i)) { + continue; + } int tab_width = _get_tab_width(i); if (pos.x < tab_width) { if (!get_tab_disabled(i)) { @@ -216,6 +219,9 @@ void TabContainer::_notification(int p_what) { // Check if all tabs would fit into the header area. int all_tabs_width = 0; for (int i = 0; i < tabs.size(); i++) { + if (get_tab_hidden(i)) { + continue; + } int tab_width = _get_tab_width(i); all_tabs_width += tab_width; @@ -241,6 +247,9 @@ void TabContainer::_notification(int p_what) { all_tabs_width = 0; Vector<int> tab_widths; for (int i = first_tab_cache; i < tabs.size(); i++) { + if (get_tab_hidden(i)) { + continue; + } int tab_width = _get_tab_width(i); if (all_tabs_width + tab_width > header_width && tab_widths.size() > 0) break; @@ -267,6 +276,9 @@ void TabContainer::_notification(int p_what) { // Draw all visible tabs. int x = 0; for (int i = 0; i < tab_widths.size(); i++) { + if (get_tab_hidden(i)) { + continue; + } Ref<StyleBox> tab_style; Color font_color; if (get_tab_disabled(i + first_tab_cache)) { @@ -338,6 +350,7 @@ void TabContainer::_notification(int p_what) { } } break; case NOTIFICATION_THEME_CHANGED: { + minimum_size_changed(); call_deferred("_on_theme_changed"); //wait until all changed theme } break; @@ -354,7 +367,7 @@ int TabContainer::_get_tab_width(int p_index) const { ERR_FAIL_INDEX_V(p_index, get_tab_count(), 0); Control *control = Object::cast_to<Control>(_get_tabs()[p_index]); - if (!control || control->is_set_as_toplevel()) + if (!control || control->is_set_as_toplevel() || get_tab_hidden(p_index)) return 0; // Get the width of the text displayed on the tab. @@ -765,6 +778,36 @@ bool TabContainer::get_tab_disabled(int p_tab) const { return false; } +void TabContainer::set_tab_hidden(int p_tab, bool p_hidden) { + + Control *child = _get_tab(p_tab); + ERR_FAIL_COND(!child); + child->set_meta("_tab_hidden", p_hidden); + update(); + for (int i = 0; i < get_tab_count(); i++) { + int try_tab = (p_tab + 1 + i) % get_tab_count(); + if (get_tab_disabled(try_tab) || get_tab_hidden(try_tab)) { + continue; + } + + set_current_tab(try_tab); + return; + } + + //assumed no other tab can be switched to, just hide + child->hide(); +} + +bool TabContainer::get_tab_hidden(int p_tab) const { + + Control *child = _get_tab(p_tab); + ERR_FAIL_COND_V(!child, false); + if (child->has_meta("_tab_hidden")) + return child->get_meta("_tab_hidden"); + else + return false; +} + void TabContainer::get_translatable_strings(List<String> *p_strings) const { Vector<Control *> tabs = _get_tabs(); diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h index c110f041d0..f7a9fb64fd 100644 --- a/scene/gui/tab_container.h +++ b/scene/gui/tab_container.h @@ -96,6 +96,9 @@ public: void set_tab_disabled(int p_tab, bool p_disabled); bool get_tab_disabled(int p_tab) const; + void set_tab_hidden(int p_tab, bool p_hidden); + bool get_tab_hidden(int p_tab) const; + int get_tab_count() const; void set_current_tab(int p_current); int get_current_tab() const; diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 9acf8d40c9..e44e340ecf 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -350,6 +350,10 @@ void TextEdit::_update_scrollbars() { total_width += cache.breakpoint_gutter_width; } + if (draw_info_gutter) { + total_width += cache.info_gutter_width; + } + if (draw_fold_gutter) { total_width += cache.fold_gutter_width; } @@ -590,6 +594,12 @@ void TextEdit::_notification(int p_what) { } } break; case NOTIFICATION_DRAW: { + + if (first_draw) { + //size may not be the final one, so attempts to ensure cursor was visible may have failed + adjust_viewport_to_cursor(); + first_draw = false; + } Size2 size = get_size(); if ((!has_focus() && !menu->has_focus()) || !window_has_focus) { draw_caret = false; @@ -602,6 +612,13 @@ void TextEdit::_notification(int p_what) { cache.breakpoint_gutter_width = 0; } + if (draw_info_gutter) { + info_gutter_width = (get_row_height()); + cache.info_gutter_width = info_gutter_width; + } else { + cache.info_gutter_width = 0; + } + if (draw_fold_gutter) { fold_gutter_width = (get_row_height() * 55) / 100; cache.fold_gutter_width = fold_gutter_width; @@ -631,7 +648,7 @@ void TextEdit::_notification(int p_what) { RID ci = get_canvas_item(); VisualServer::get_singleton()->canvas_item_set_clip(get_canvas_item(), true); - int xmargin_beg = cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width; + int xmargin_beg = cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width + cache.info_gutter_width; int xmargin_end = size.width - cache.style_normal->get_margin(MARGIN_RIGHT); //let's do it easy for now: cache.style_normal->draw(ci, Rect2(Point2(), size)); @@ -946,10 +963,53 @@ void TextEdit::_notification(int p_what) { } } + // draw info icons + if (draw_info_gutter && text.has_info_icon(line)) { + int vertical_gap = (get_row_height() * 40) / 100; + int horizontal_gap = (cache.info_gutter_width * 30) / 100; + int gutter_left = cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width; + + Ref<Texture> info_icon = text.get_info_icon(line); + // ensure the icon fits the gutter size + Size2i icon_size = info_icon->get_size(); + if (icon_size.width > cache.info_gutter_width - horizontal_gap) { + icon_size.width = cache.info_gutter_width - horizontal_gap; + } + if (icon_size.height > get_row_height() - horizontal_gap) { + icon_size.height = get_row_height() - horizontal_gap; + } + + Size2i icon_pos; + int xofs = horizontal_gap - (info_icon->get_width() / 4); + int yofs = vertical_gap - (info_icon->get_height() / 4); + icon_pos.x = gutter_left + xofs + ofs_x; + icon_pos.y = ofs_y + yofs; + + draw_texture_rect(info_icon, Rect2(icon_pos, icon_size)); + } + + // draw execution marker + if (executing_line == line) { + if (draw_breakpoint_gutter) { + int icon_extra_size = 4; + int vertical_gap = (get_row_height() * 40) / 100; + int horizontal_gap = (cache.breakpoint_gutter_width * 30) / 100; + int marker_height = get_row_height() - (vertical_gap * 2) + icon_extra_size; + int marker_width = cache.breakpoint_gutter_width - (horizontal_gap * 2) + icon_extra_size; + cache.executing_icon->draw_rect(ci, Rect2(cache.style_normal->get_margin(MARGIN_LEFT) + horizontal_gap - 2 - icon_extra_size / 2, ofs_y + vertical_gap - icon_extra_size / 2, marker_width, marker_height), false, Color(cache.executing_line_color.r, cache.executing_line_color.g, cache.executing_line_color.b)); + } else { +#ifdef TOOLS_ENABLED + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y + get_row_height() - EDSCALE, xmargin_end - xmargin_beg, EDSCALE), cache.executing_line_color); +#else + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, xmargin_end - xmargin_beg, get_row_height()), cache.executing_line_color); +#endif + } + } + // draw fold markers if (draw_fold_gutter) { int horizontal_gap = (cache.fold_gutter_width * 30) / 100; - int gutter_left = cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + cache.line_number_w; + int gutter_left = cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + cache.line_number_w + cache.info_gutter_width; if (is_folded(line)) { int xofs = horizontal_gap - (cache.can_fold_icon->get_width()) / 2; int yofs = (get_row_height() - cache.folded_icon->get_height()) / 2; @@ -969,7 +1029,7 @@ void TextEdit::_notification(int p_what) { fc = line_num_padding + fc; } - cache.font->draw(ci, Point2(cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + ofs_x, yofs + cache.font->get_ascent()), fc, text.is_safe(line) ? cache.safe_line_number_color : cache.line_number_color); + cache.font->draw(ci, Point2(cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + cache.info_gutter_width + ofs_x, yofs + cache.font->get_ascent()), fc, text.is_safe(line) ? cache.safe_line_number_color : cache.line_number_color); } } @@ -1560,6 +1620,10 @@ void TextEdit::backspace_at_cursor() { set_line_as_breakpoint(prev_line, true); } + if (text.has_info_icon(cursor.line)) { + set_line_info_icon(prev_line, text.get_info_icon(cursor.line), text.get_info(cursor.line)); + } + if (auto_brace_completion_enabled && cursor.column > 0 && _is_pair_left_symbol(text[cursor.line][cursor.column - 1])) { @@ -1712,7 +1776,7 @@ void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) co col = text[row].size(); } else { - int colx = p_mouse.x - (cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width); + int colx = p_mouse.x - (cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width + cache.info_gutter_width); colx += cursor.x_ofs; col = get_char_pos_for_line(colx, row, wrap_index); if (is_wrap_enabled() && wrap_index < times_line_wraps(row)) { @@ -1810,18 +1874,28 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { // toggle breakpoint on gutter click if (draw_breakpoint_gutter) { int gutter = cache.style_normal->get_margin(MARGIN_LEFT); - if (mb->get_position().x > gutter && mb->get_position().x <= gutter + cache.breakpoint_gutter_width + 3) { + if (mb->get_position().x > gutter - 6 && mb->get_position().x <= gutter + cache.breakpoint_gutter_width - 3) { set_line_as_breakpoint(row, !is_line_set_as_breakpoint(row)); emit_signal("breakpoint_toggled", row); return; } } + // emit info clicked + if (draw_info_gutter && text.has_info_icon(row)) { + int left_margin = cache.style_normal->get_margin(MARGIN_LEFT); + int gutter_left = left_margin + cache.breakpoint_gutter_width; + if (mb->get_position().x > gutter_left - 6 && mb->get_position().x <= gutter_left + cache.info_gutter_width - 3) { + emit_signal("info_clicked", row, text.get_info(row)); + return; + } + } + // toggle fold on gutter click if can if (draw_fold_gutter) { int left_margin = cache.style_normal->get_margin(MARGIN_LEFT); - int gutter_left = left_margin + cache.breakpoint_gutter_width + cache.line_number_w; + int gutter_left = left_margin + cache.breakpoint_gutter_width + cache.line_number_w + cache.info_gutter_width; if (mb->get_position().x > gutter_left - 6 && mb->get_position().x <= gutter_left + cache.fold_gutter_width - 3) { if (is_folded(row)) { unfold_line(row); @@ -1835,7 +1909,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { // unfold on folded icon click if (is_folded(row)) { int line_width = text.get_line_width(row); - line_width += cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width - cursor.x_ofs; + line_width += cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.info_gutter_width + cache.fold_gutter_width - cursor.x_ofs; if (mb->get_position().x > line_width - 3 && mb->get_position().x <= line_width + cache.folded_eol_icon->get_width() + 3) { unfold_line(row); return; @@ -1953,6 +2027,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { menu->set_position(get_global_transform().xform(get_local_mouse_position())); menu->set_size(Vector2(1, 1)); + menu->set_scale(get_global_transform().get_scale()); menu->popup(); grab_focus(); } @@ -3368,8 +3443,11 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i if (shift_first_line) { text.set_breakpoint(p_line + 1, text.is_breakpoint(p_line)); text.set_hidden(p_line + 1, text.is_hidden(p_line)); + text.set_info_icon(p_line + 1, text.get_info_icon(p_line), text.get_info(p_line)); + text.set_breakpoint(p_line, false); text.set_hidden(p_line, false); + text.set_info_icon(p_line, NULL, ""); } text.set_line_wrap_amount(p_line, -1); @@ -3622,7 +3700,7 @@ int TextEdit::get_total_visible_rows() const { void TextEdit::_update_wrap_at() { - wrap_at = get_size().width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width - wrap_right_offset; + wrap_at = get_size().width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width - cache.info_gutter_width - wrap_right_offset; update_cursor_wrap_offset(); text.clear_wrap_cache(); @@ -3656,7 +3734,7 @@ void TextEdit::adjust_viewport_to_cursor() { set_line_as_last_visible(cur_line, cur_wrap); } - int visible_width = get_size().width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width; + int visible_width = get_size().width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width - cache.info_gutter_width; if (v_scroll->is_visible_in_tree()) visible_width -= v_scroll->get_combined_minimum_size().width; visible_width -= 20; // give it a little more space @@ -3687,7 +3765,7 @@ void TextEdit::center_viewport_to_cursor() { unfold_line(cursor.line); set_line_as_center_visible(cursor.line, get_cursor_wrap_index()); - int visible_width = get_size().width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width; + int visible_width = get_size().width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width - cache.info_gutter_width; if (v_scroll->is_visible_in_tree()) visible_width -= v_scroll->get_combined_minimum_size().width; visible_width -= 20; // give it a little more space @@ -4126,7 +4204,7 @@ Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const { if (highlighted_word != String()) return CURSOR_POINTING_HAND; - int gutter = cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width; + int gutter = cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width + cache.info_gutter_width; if ((completion_active && completion_rect.has_point(p_pos))) { return CURSOR_ARROW; } @@ -4137,18 +4215,27 @@ Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const { int left_margin = cache.style_normal->get_margin(MARGIN_LEFT); // breakpoint icon - if (draw_breakpoint_gutter && p_pos.x > left_margin && p_pos.x <= left_margin + cache.breakpoint_gutter_width + 3) { + if (draw_breakpoint_gutter && p_pos.x > left_margin - 6 && p_pos.x <= left_margin + cache.breakpoint_gutter_width - 3) { return CURSOR_POINTING_HAND; } + // info icons + int gutter_left = left_margin + cache.breakpoint_gutter_width + cache.info_gutter_width; + if (draw_info_gutter && p_pos.x > left_margin + cache.breakpoint_gutter_width - 6 && p_pos.x <= gutter_left - 3) { + if (text.has_info_icon(row)) { + return CURSOR_POINTING_HAND; + } + return CURSOR_ARROW; + } + // fold icon - int gutter_left = left_margin + cache.breakpoint_gutter_width + cache.line_number_w; - if (draw_fold_gutter && p_pos.x > gutter_left - 6 && p_pos.x <= gutter_left + cache.fold_gutter_width - 3) { + if (draw_fold_gutter && p_pos.x > gutter_left + cache.line_number_w - 6 && p_pos.x <= gutter_left + cache.line_number_w + cache.fold_gutter_width - 3) { if (is_folded(row) || can_fold(row)) return CURSOR_POINTING_HAND; else return CURSOR_ARROW; } + return CURSOR_ARROW; } else { int row, col; @@ -4156,7 +4243,7 @@ Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const { // eol fold icon if (is_folded(row)) { int line_width = text.get_line_width(row); - line_width += cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width - cursor.x_ofs; + line_width += cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width + cache.info_gutter_width - cursor.x_ofs; if (p_pos.x > line_width - 3 && p_pos.x <= line_width + cache.folded_eol_icon->get_width() + 3) { return CURSOR_POINTING_HAND; } @@ -4276,7 +4363,27 @@ void TextEdit::clear() { void TextEdit::set_readonly(bool p_readonly) { + if (readonly == p_readonly) + return; + readonly = p_readonly; + + // Reorganize context menu. + menu->clear(); + if (!readonly) + menu->add_item(RTR("Cut"), MENU_CUT, KEY_MASK_CMD | KEY_X); + menu->add_item(RTR("Copy"), MENU_COPY, KEY_MASK_CMD | KEY_C); + if (!readonly) + menu->add_item(RTR("Paste"), MENU_PASTE, KEY_MASK_CMD | KEY_V); + menu->add_separator(); + menu->add_item(RTR("Select All"), MENU_SELECT_ALL, KEY_MASK_CMD | KEY_A); + if (!readonly) { + menu->add_item(RTR("Clear"), MENU_CLEAR); + menu->add_separator(); + menu->add_item(RTR("Undo"), MENU_UNDO, KEY_MASK_CMD | KEY_Z); + menu->add_item(RTR("Redo"), MENU_REDO, KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Z); + } + update(); } @@ -4346,6 +4453,7 @@ void TextEdit::_update_caches() { cache.current_line_color = get_color("current_line_color"); cache.line_length_guideline_color = get_color("line_length_guideline_color"); cache.breakpoint_color = get_color("breakpoint_color"); + cache.executing_line_color = get_color("executing_line_color"); cache.code_folding_color = get_color("code_folding_color"); cache.brace_mismatch_color = get_color("brace_mismatch_color"); cache.word_highlighted_color = get_color("word_highlighted_color"); @@ -4360,9 +4468,10 @@ void TextEdit::_update_caches() { #endif cache.row_height = cache.font->get_height() + cache.line_spacing; cache.tab_icon = get_icon("tab"); - cache.folded_icon = get_icon("GuiTreeArrowRight", "EditorIcons"); - cache.can_fold_icon = get_icon("GuiTreeArrowDown", "EditorIcons"); + cache.folded_icon = get_icon("folded"); + cache.can_fold_icon = get_icon("fold"); cache.folded_eol_icon = get_icon("GuiEllipsis", "EditorIcons"); + cache.executing_icon = get_icon("MainPlay", "EditorIcons"); text.set_font(cache.font); if (syntax_highlighter) { @@ -4928,6 +5037,17 @@ bool TextEdit::is_line_set_as_safe(int p_line) const { return text.is_safe(p_line); } +void TextEdit::set_executing_line(int p_line) { + ERR_FAIL_INDEX(p_line, text.size()); + executing_line = p_line; + update(); +} + +void TextEdit::clear_executing_line() { + executing_line = -1; + update(); +} + bool TextEdit::is_line_set_as_breakpoint(int p_line) const { ERR_FAIL_INDEX_V(p_line, text.size(), false); @@ -4967,6 +5087,19 @@ void TextEdit::remove_breakpoints() { } } +void TextEdit::set_line_info_icon(int p_line, Ref<Texture> p_icon, String p_info) { + ERR_FAIL_INDEX(p_line, text.size()); + text.set_info_icon(p_line, p_icon, p_info); + update(); +} + +void TextEdit::clear_info_icons() { + for (int i = 0; i < text.size(); i++) { + text.set_info_icon(i, NULL, ""); + } + update(); +} + void TextEdit::set_line_as_hidden(int p_line, bool p_hidden) { ERR_FAIL_INDEX(p_line, text.size()); @@ -5183,6 +5316,17 @@ bool TextEdit::is_folded(int p_line) const { return false; } +Vector<int> TextEdit::get_folded_lines() const { + Vector<int> folded_lines; + + for (int i = 0; i < text.size(); i++) { + if (is_folded(i)) { + folded_lines.push_back(i); + } + } + return folded_lines; +} + void TextEdit::fold_line(int p_line) { ERR_FAIL_INDEX(p_line, text.size()); @@ -5323,6 +5467,9 @@ void TextEdit::undo() { TextOperation op = undo_stack_pos->get(); _do_text_op(op, true); + if (op.from_line != op.to_line || op.to_column != op.from_column + 1) + select(op.from_line, op.from_column, op.to_line, op.to_column); + current_op.version = op.prev_version; if (undo_stack_pos->get().chain_backward) { while (true) { @@ -5449,6 +5596,7 @@ int TextEdit::get_indent_size() { void TextEdit::set_draw_tabs(bool p_draw) { draw_tabs = p_draw; + update(); } bool TextEdit::is_drawing_tabs() const { @@ -5993,6 +6141,24 @@ int TextEdit::get_fold_gutter_width() const { return cache.fold_gutter_width; } +void TextEdit::set_draw_info_gutter(bool p_draw) { + draw_info_gutter = p_draw; + update(); +} + +bool TextEdit::is_drawing_info_gutter() const { + return draw_info_gutter; +} + +void TextEdit::set_info_gutter_width(int p_gutter_width) { + info_gutter_width = p_gutter_width; + update(); +} + +int TextEdit::get_info_gutter_width() const { + return info_gutter_width; +} + void TextEdit::set_hiding_enabled(int p_enabled) { if (!p_enabled) unhide_all_lines(); @@ -6149,8 +6315,12 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("set_show_line_numbers", "enable"), &TextEdit::set_show_line_numbers); ClassDB::bind_method(D_METHOD("is_show_line_numbers_enabled"), &TextEdit::is_show_line_numbers_enabled); + ClassDB::bind_method(D_METHOD("set_draw_tabs"), &TextEdit::set_draw_tabs); + ClassDB::bind_method(D_METHOD("is_drawing_tabs"), &TextEdit::is_drawing_tabs); ClassDB::bind_method(D_METHOD("set_breakpoint_gutter_enabled", "enable"), &TextEdit::set_breakpoint_gutter_enabled); ClassDB::bind_method(D_METHOD("is_breakpoint_gutter_enabled"), &TextEdit::is_breakpoint_gutter_enabled); + ClassDB::bind_method(D_METHOD("set_draw_fold_gutter"), &TextEdit::set_draw_fold_gutter); + ClassDB::bind_method(D_METHOD("is_drawing_fold_gutter"), &TextEdit::is_drawing_fold_gutter); ClassDB::bind_method(D_METHOD("set_hiding_enabled", "enable"), &TextEdit::set_hiding_enabled); ClassDB::bind_method(D_METHOD("is_hiding_enabled"), &TextEdit::is_hiding_enabled); @@ -6197,7 +6367,9 @@ void TextEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_current_line"), "set_highlight_current_line", "is_highlight_current_line_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "syntax_highlighting"), "set_syntax_coloring", "is_syntax_coloring_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_line_numbers"), "set_show_line_numbers", "is_show_line_numbers_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_tabs"), "set_draw_tabs", "is_drawing_tabs"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "breakpoint_gutter"), "set_breakpoint_gutter_enabled", "is_breakpoint_gutter_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fold_gutter"), "set_draw_fold_gutter", "is_drawing_fold_gutter"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_all_occurrences"), "set_highlight_all_occurrences", "is_highlight_all_occurrences_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_selected_font_color"), "set_override_selected_font_color", "is_overriding_selected_font_color"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled"); @@ -6218,6 +6390,7 @@ void TextEdit::_bind_methods() { ADD_SIGNAL(MethodInfo("request_completion")); ADD_SIGNAL(MethodInfo("breakpoint_toggled", PropertyInfo(Variant::INT, "row"))); ADD_SIGNAL(MethodInfo("symbol_lookup", PropertyInfo(Variant::STRING, "symbol"), PropertyInfo(Variant::INT, "row"), PropertyInfo(Variant::INT, "column"))); + ADD_SIGNAL(MethodInfo("info_clicked", PropertyInfo(Variant::INT, "row"), PropertyInfo(Variant::STRING, "info"))); BIND_ENUM_CONSTANT(MENU_CUT); BIND_ENUM_CONSTANT(MENU_COPY); @@ -6234,7 +6407,6 @@ void TextEdit::_bind_methods() { TextEdit::TextEdit() { - readonly = false; setting_row = false; draw_tabs = false; override_selected_font_color = false; @@ -6253,6 +6425,8 @@ TextEdit::TextEdit() { breakpoint_gutter_width = 0; cache.fold_gutter_width = 0; fold_gutter_width = 0; + info_gutter_width = 0; + cache.info_gutter_width = 0; set_default_cursor_shape(CURSOR_IBEAM); indent_size = 4; @@ -6325,6 +6499,7 @@ TextEdit::TextEdit() { line_length_guideline_col = 80; draw_breakpoint_gutter = false; draw_fold_gutter = false; + draw_info_gutter = false; hiding_enabled = false; next_operation_is_complex = false; scroll_past_end_of_file_enabled = false; @@ -6346,16 +6521,11 @@ TextEdit::TextEdit() { context_menu_enabled = true; menu = memnew(PopupMenu); add_child(menu); - menu->add_item(RTR("Cut"), MENU_CUT, KEY_MASK_CMD | KEY_X); - menu->add_item(RTR("Copy"), MENU_COPY, KEY_MASK_CMD | KEY_C); - menu->add_item(RTR("Paste"), MENU_PASTE, KEY_MASK_CMD | KEY_V); - menu->add_separator(); - menu->add_item(RTR("Select All"), MENU_SELECT_ALL, KEY_MASK_CMD | KEY_A); - menu->add_item(RTR("Clear"), MENU_CLEAR); - menu->add_separator(); - menu->add_item(RTR("Undo"), MENU_UNDO, KEY_MASK_CMD | KEY_Z); - menu->add_item(RTR("Redo"), MENU_REDO, KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Z); + set_readonly(false); menu->connect("id_pressed", this, "menu_option"); + first_draw = true; + + executing_line = -1; } TextEdit::~TextEdit() { diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 33f0a3f45d..4badd85e07 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -79,6 +79,8 @@ public: bool safe : 1; int wrap_amount_cache : 24; Map<int, ColorRegionInfo> region_info; + Ref<Texture> info_icon; + String info; String data; }; @@ -109,6 +111,13 @@ public: bool is_hidden(int p_line) const { return text[p_line].hidden; } void set_safe(int p_line, bool p_safe) { text.write[p_line].safe = p_safe; } bool is_safe(int p_line) const { return text[p_line].safe; } + void set_info_icon(int p_line, Ref<Texture> p_icon, String p_info) { + text.write[p_line].info_icon = p_icon; + text.write[p_line].info = p_info; + } + bool has_info_icon(int p_line) const { return text[p_line].info_icon.is_valid(); } + const Ref<Texture> &get_info_icon(int p_line) const { return text[p_line].info_icon; } + const String &get_info(int p_line) const { return text[p_line].info; } void insert(int p_at, const String &p_text); void remove(int p_at); int size() const { return text.size(); } @@ -157,6 +166,7 @@ private: Ref<Texture> can_fold_icon; Ref<Texture> folded_icon; Ref<Texture> folded_eol_icon; + Ref<Texture> executing_icon; Ref<StyleBox> style_normal; Ref<StyleBox> style_focus; Ref<StyleBox> style_readonly; @@ -178,6 +188,7 @@ private: Color selection_color; Color mark_color; Color breakpoint_color; + Color executing_line_color; Color code_folding_color; Color current_line_color; Color line_length_guideline_color; @@ -193,6 +204,7 @@ private: int line_number_w; int breakpoint_gutter_width; int fold_gutter_width; + int info_gutter_width; } cache; Map<int, int> color_region_cache; @@ -275,6 +287,7 @@ private: int wrap_at; int wrap_right_offset; + bool first_draw; bool setting_row; bool draw_tabs; bool override_selected_font_color; @@ -290,6 +303,8 @@ private: bool draw_fold_gutter; int fold_gutter_width; bool hiding_enabled; + bool draw_info_gutter; + int info_gutter_width; bool highlight_all_occurrences; bool scroll_past_end_of_file_enabled; @@ -332,6 +347,8 @@ private: bool context_menu_enabled; + int executing_line; + int get_visible_rows() const; int get_total_visible_rows() const; @@ -473,12 +490,17 @@ public: void set_line_as_marked(int p_line, bool p_marked); void set_line_as_breakpoint(int p_line, bool p_breakpoint); bool is_line_set_as_breakpoint(int p_line) const; + void set_executing_line(int p_line); + void clear_executing_line(); void set_line_as_safe(int p_line, bool p_safe); bool is_line_set_as_safe(int p_line) const; void get_breakpoints(List<int> *p_breakpoints) const; Array get_breakpoints_array() const; void remove_breakpoints(); + void set_line_info_icon(int p_line, Ref<Texture> p_icon, String p_info = ""); + void clear_info_icons(); + void set_line_as_hidden(int p_line, bool p_hidden); bool is_line_hidden(int p_line) const; void fold_all_lines(); @@ -489,6 +511,7 @@ public: bool can_fold(int p_line) const; bool is_folded(int p_line) const; + Vector<int> get_folded_lines() const; void fold_line(int p_line); void unfold_line(int p_line); void toggle_fold_line(int p_line); @@ -649,6 +672,12 @@ public: void set_fold_gutter_width(int p_gutter_width); int get_fold_gutter_width() const; + void set_draw_info_gutter(bool p_draw); + bool is_drawing_info_gutter() const; + + void set_info_gutter_width(int p_gutter_width); + int get_info_gutter_width() const; + void set_hiding_enabled(int p_enabled); int is_hiding_enabled() const; diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 679b752fa3..f22fe5b6a5 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -962,7 +962,8 @@ int Tree::compute_item_height(TreeItem *p_item) const { } } break; - default: {} + default: { + } } } int item_min_height = p_item->get_custom_minimum_height(); @@ -2020,7 +2021,9 @@ void Tree::text_editor_enter(String p_text) { //popup_edited_item->edited_signal.call( popup_edited_item_col ); } break; - default: { ERR_FAIL(); } + default: { + ERR_FAIL(); + } } item_edited(popup_edited_item_col, popup_edited_item); @@ -2847,7 +2850,7 @@ void Tree::_notification(int p_what) { if (p_what == NOTIFICATION_DRAG_BEGIN) { single_select_defer = NULL; - if (cache.scroll_speed > 0 && get_rect().has_point(get_viewport()->get_mouse_position() - get_global_position())) { + if (cache.scroll_speed > 0) { scrolling = true; set_physics_process_internal(true); } @@ -2894,22 +2897,22 @@ void Tree::_notification(int p_what) { } } - if (scrolling) { - Point2 point = get_viewport()->get_mouse_position() - get_global_position(); - if (point.x < cache.scroll_border) { - point.x -= cache.scroll_border; - } else if (point.x > get_size().width - cache.scroll_border) { - point.x -= get_size().width - cache.scroll_border; - } else { - point.x = 0; + Point2 mouse_position = get_viewport()->get_mouse_position() - get_global_position(); + if (scrolling && get_rect().grow(cache.scroll_border).has_point(mouse_position)) { + Point2 point; + + if ((ABS(mouse_position.x) < ABS(mouse_position.x - get_size().width)) && (ABS(mouse_position.x) < cache.scroll_border)) { + point.x = mouse_position.x - cache.scroll_border; + } else if (ABS(mouse_position.x - get_size().width) < cache.scroll_border) { + point.x = mouse_position.x - (get_size().width - cache.scroll_border); } - if (point.y < cache.scroll_border) { - point.y -= cache.scroll_border; - } else if (point.y > get_size().height - cache.scroll_border) { - point.y -= get_size().height - cache.scroll_border; - } else { - point.y = 0; + + if ((ABS(mouse_position.y) < ABS(mouse_position.y - get_size().height)) && (ABS(mouse_position.y) < cache.scroll_border)) { + point.y = mouse_position.y - cache.scroll_border; + } else if (ABS(mouse_position.y - get_size().height) < cache.scroll_border) { + point.y = mouse_position.y - (get_size().height - cache.scroll_border); } + point *= cache.scroll_speed * get_physics_process_delta_time(); point += get_scroll(); h_scroll->set_value(point.x); @@ -3385,10 +3388,13 @@ void Tree::ensure_cursor_is_visible() { int h = compute_item_height(selected) + cache.vseparation; int screenh = get_size().height - h_scroll->get_combined_minimum_size().height; - if (ofs + h > v_scroll->get_value() + screenh) + if (h > screenh) { //screen size is too small, maybe it was not resized yet. + v_scroll->set_value(ofs); + } else if (ofs + h > v_scroll->get_value() + screenh) { v_scroll->call_deferred("set_value", ofs - screenh + h); - else if (ofs < v_scroll->get_value()) + } else if (ofs < v_scroll->get_value()) { v_scroll->set_value(ofs); + } } int Tree::get_pressed_button() const { @@ -3932,7 +3938,6 @@ Tree::Tree() { cache.click_item = NULL; cache.click_column = 0; cache.hover_cell = -1; - cache.hover_index = -1; last_keypress = 0; focus_in_id = 0; diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp index 8b68b3215c..e65314644e 100644 --- a/scene/main/http_request.cpp +++ b/scene/main/http_request.cpp @@ -286,7 +286,7 @@ bool HTTPRequest::_update_connection() { call_deferred("_request_done", RESULT_SUCCESS, response_code, response_headers, PoolByteArray()); return true; } - if (got_response && body_len < 0) { + if (body_len < 0) { // Chunked transfer is done call_deferred("_request_done", RESULT_SUCCESS, response_code, response_headers, body); return true; diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 128168ca72..0465ffe442 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -41,6 +41,8 @@ VARIANT_ENUM_CAST(Node::PauseMode); +int Node::orphan_node_count = 0; + void Node::_notification(int p_notification) { switch (p_notification) { @@ -84,11 +86,14 @@ void Node::_notification(int p_notification) { add_to_group("_vp_unhandled_key_input" + itos(get_viewport()->get_instance_id())); get_tree()->node_count++; + orphan_node_count--; } break; case NOTIFICATION_EXIT_TREE: { get_tree()->node_count--; + orphan_node_count++; + if (data.input) remove_from_group("_vp_input" + itos(get_viewport()->get_instance_id())); if (data.unhandled_input) @@ -2421,7 +2426,7 @@ void Node::_replace_connections_target(Node *p_new_target) { if (c.flags & CONNECT_PERSIST) { c.source->disconnect(c.signal, this, c.method); - bool valid = p_new_target->has_method(c.method) || p_new_target->get_script().is_null() || Ref<Script>(p_new_target->get_script())->has_method(c.method); + bool valid = p_new_target->has_method(c.method) || Ref<Script>(p_new_target->get_script()).is_null() || Ref<Script>(p_new_target->get_script())->has_method(c.method); ERR_EXPLAIN("Attempt to connect signal \'" + c.source->get_class() + "." + c.signal + "\' to nonexistent method \'" + c.target->get_class() + "." + c.method + "\'"); ERR_CONTINUE(!valid); c.source->connect(c.signal, p_new_target, c.method, c.binds, c.flags); @@ -2759,6 +2764,7 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("can_process"), &Node::can_process); ClassDB::bind_method(D_METHOD("print_stray_nodes"), &Node::_print_stray_nodes); ClassDB::bind_method(D_METHOD("get_position_in_parent"), &Node::get_position_in_parent); + ClassDB::bind_method(D_METHOD("set_display_folded", "fold"), &Node::set_display_folded); ClassDB::bind_method(D_METHOD("is_displayed_folded"), &Node::is_displayed_folded); @@ -2871,7 +2877,12 @@ void Node::_bind_methods() { //ADD_PROPERTY( PropertyInfo( Variant::BOOL, "process/unhandled_input" ), "set_process_unhandled_input","is_processing_unhandled_input" ) ; ADD_GROUP("Pause", "pause_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "pause_mode", PROPERTY_HINT_ENUM, "Inherit,Stop,Process"), "set_pause_mode", "get_pause_mode"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor/display_folded", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_display_folded", "is_displayed_folded"); + +#ifdef ENABLE_DEPRECATED + //no longer exists, but remains for compatibility (keep previous scenes folded + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor/display_folded", PROPERTY_HINT_NONE, "", 0), "set_display_folded", "is_displayed_folded"); +#endif + ADD_PROPERTY(PropertyInfo(Variant::STRING, "name", PROPERTY_HINT_NONE, "", 0), "set_name", "get_name"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "filename", PROPERTY_HINT_NONE, "", 0), "set_filename", "get_filename"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "owner", PROPERTY_HINT_RESOURCE_TYPE, "Node", 0), "set_owner", "get_owner"); @@ -2932,6 +2943,8 @@ Node::Node() { data.use_placeholder = false; data.display_folded = false; data.ready_first = true; + + orphan_node_count++; } Node::~Node() { @@ -2942,6 +2955,8 @@ Node::~Node() { ERR_FAIL_COND(data.parent); ERR_FAIL_COND(data.children.size()); + + orphan_node_count--; } //////////////////////////////// diff --git a/scene/main/node.h b/scene/main/node.h index b490db37c5..9b9ca06455 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -75,6 +75,8 @@ public: bool operator()(const Node *p_a, const Node *p_b) const { return p_b->data.process_priority == p_a->data.process_priority ? p_b->is_greater_than(p_a) : p_b->data.process_priority > p_a->data.process_priority; } }; + static int orphan_node_count; + private: struct GroupData { diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 81c38cec89..b81364e2f0 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -369,8 +369,7 @@ void SceneTree::set_group_flags(uint32_t p_call_flags, const StringName &p_group } void SceneTree::call_group(const StringName &p_group, const StringName &p_function, VARIANT_ARG_DECLARE) { - - call_group_flags(0, p_group, VARIANT_ARG_PASS); + call_group_flags(0, p_group, p_function, VARIANT_ARG_PASS); } void SceneTree::notify_group(const StringName &p_group, int p_notification) { @@ -1160,7 +1159,7 @@ void SceneTree::_update_root_rect() { WARN_PRINT("Font oversampling only works with the resize modes 'Keep Width', 'Keep Height', and 'Expand'."); } - if (stretch_aspect == STRETCH_ASPECT_IGNORE || ABS(viewport_aspect - video_mode_aspect) < CMP_EPSILON) { + if (stretch_aspect == STRETCH_ASPECT_IGNORE || Math::is_equal_approx(viewport_aspect, video_mode_aspect)) { //same aspect or ignore aspect viewport_size = desired_res; screen_size = video_mode; diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 2524af9cfb..ae2c571201 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -391,9 +391,11 @@ void Viewport::_notification(int p_what) { if (physics_object_picking && (to_screen_rect == Rect2() || Input::get_singleton()->get_mouse_mode() != Input::MOUSE_MODE_CAPTURED)) { +#ifndef _3D_DISABLED Vector2 last_pos(1e20, 1e20); CollisionObject *last_object = NULL; ObjectID last_id = 0; +#endif PhysicsDirectSpaceState::RayResult result; Physics2DDirectSpaceState *ss2d = Physics2DServer::get_singleton()->space_get_direct_state(find_world_2d()->get_space()); @@ -2480,11 +2482,7 @@ void Viewport::_gui_hid_control(Control *p_control) { if (gui.mouse_over == p_control) gui.mouse_over = NULL; if (gui.tooltip == p_control) - gui.tooltip = NULL; - if (gui.tooltip == p_control) { - gui.tooltip = NULL; _gui_cancel_tooltip(); - } } void Viewport::_gui_remove_control(Control *p_control) { @@ -3222,7 +3220,7 @@ Viewport::Viewport() { gui.tooltip_timer = -1; //gui.tooltip_timer->force_parent_owned(); - gui.tooltip_delay = GLOBAL_DEF("gui/timers/tooltip_delay_sec", 0.7); + gui.tooltip_delay = GLOBAL_DEF("gui/timers/tooltip_delay_sec", 0.5); ProjectSettings::get_singleton()->set_custom_property_info("gui/timers/tooltip_delay_sec", PropertyInfo(Variant::REAL, "gui/timers/tooltip_delay_sec", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater")); // No negative numbers gui.tooltip = NULL; diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 87a15bbeda..47f5b152f0 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -521,6 +521,8 @@ void register_scene_types() { ClassDB::register_class<VisualShaderNodeTransformUniform>(); ClassDB::register_class<VisualShaderNodeTextureUniform>(); ClassDB::register_class<VisualShaderNodeCubeMapUniform>(); + ClassDB::register_class<VisualShaderNodeIf>(); + ClassDB::register_class<VisualShaderNodeSwitch>(); ClassDB::register_class<ShaderMaterial>(); ClassDB::register_virtual_class<CanvasItem>(); @@ -633,6 +635,7 @@ void register_scene_types() { ClassDB::register_class<StreamTexture>(); ClassDB::register_class<ImageTexture>(); ClassDB::register_class<AtlasTexture>(); + ClassDB::register_class<MeshTexture>(); ClassDB::register_class<LargeTexture>(); ClassDB::register_class<CurveTexture>(); ClassDB::register_class<GradientTexture>(); @@ -779,7 +782,11 @@ void unregister_scene_types() { ResourceLoader::remove_resource_format_loader(resource_loader_bmfont); resource_loader_bmfont.unref(); + //SpatialMaterial is not initialised when 3D is disabled, so it shouldn't be cleaned up either +#ifndef _3D_DISABLED SpatialMaterial::finish_shaders(); +#endif // _3D_DISABLED + ParticlesMaterial::finish_shaders(); CanvasItemMaterial::finish_shaders(); SceneStringNames::free(); diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index f73914b186..9c79b2ba3b 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -1466,7 +1466,7 @@ int Animation::_find(const Vector<K> &p_keys, float p_time) const { int high = len - 1; int middle = 0; -#if DEBUG_ENABLED +#ifdef DEBUG_ENABLED if (low > high) ERR_PRINT("low > high, this may be a bug"); #endif @@ -1477,7 +1477,7 @@ int Animation::_find(const Vector<K> &p_keys, float p_time) const { middle = (low + high) / 2; - if (Math::abs(p_time - keys[middle].time) < CMP_EPSILON) { //match + if (Math::is_equal_approx(p_time, keys[middle].time)) { //match return middle; } else if (p_time < keys[middle].time) high = middle - 1; //search low end of array @@ -1680,10 +1680,10 @@ T Animation::_interpolate(const Vector<TKey<T> > &p_keys, float p_time, Interpol float delta = p_keys[next].time - p_keys[idx].time; float from = p_time - p_keys[idx].time; - if (Math::absf(delta) > CMP_EPSILON) - c = from / delta; - else + if (Math::is_zero_approx(delta)) c = 0; + else + c = from / delta; } else { @@ -1691,10 +1691,10 @@ T Animation::_interpolate(const Vector<TKey<T> > &p_keys, float p_time, Interpol float delta = (length - p_keys[idx].time) + p_keys[next].time; float from = p_time - p_keys[idx].time; - if (Math::absf(delta) > CMP_EPSILON) - c = from / delta; - else + if (Math::is_zero_approx(delta)) c = 0; + else + c = from / delta; } } else { @@ -1707,10 +1707,10 @@ T Animation::_interpolate(const Vector<TKey<T> > &p_keys, float p_time, Interpol float delta = endtime + p_keys[next].time; float from = endtime + p_time; - if (Math::absf(delta) > CMP_EPSILON) - c = from / delta; - else + if (Math::is_zero_approx(delta)) c = 0; + else + c = from / delta; } } else { // no loop @@ -1723,10 +1723,10 @@ T Animation::_interpolate(const Vector<TKey<T> > &p_keys, float p_time, Interpol float delta = p_keys[next].time - p_keys[idx].time; float from = p_time - p_keys[idx].time; - if (Math::absf(delta) > CMP_EPSILON) - c = from / delta; - else + if (Math::is_zero_approx(delta)) c = 0; + else + c = from / delta; } else { @@ -2774,9 +2774,9 @@ bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, cons const Vector3 &v1 = t1.value.loc; const Vector3 &v2 = t2.value.loc; - if (v0.distance_to(v2) < CMP_EPSILON) { + if (Math::is_zero_approx(v0.distance_to(v2))) { //0 and 2 are close, let's see if 1 is close - if (v0.distance_to(v1) > CMP_EPSILON) { + if (!Math::is_zero_approx(v0.distance_to(v1))) { //not close, not optimizable return false; } @@ -2813,9 +2813,9 @@ bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, cons //localize both to rotation from q0 - if ((q0 - q2).length() < CMP_EPSILON) { + if (Math::is_zero_approx((q0 - q2).length())) { - if ((q0 - q1).length() > CMP_EPSILON) + if (!Math::is_zero_approx((q0 - q1).length())) return false; } else { @@ -2863,9 +2863,9 @@ bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, cons const Vector3 &v1 = t1.value.scale; const Vector3 &v2 = t2.value.scale; - if (v0.distance_to(v2) < CMP_EPSILON) { + if (Math::is_zero_approx(v0.distance_to(v2))) { //0 and 2 are close, let's see if 1 is close - if (v0.distance_to(v1) > CMP_EPSILON) { + if (!Math::is_zero_approx(v0.distance_to(v1))) { //not close, not optimizable return false; } diff --git a/scene/resources/bit_map.cpp b/scene/resources/bit_map.cpp index 55264bcdf9..a9d85be0dc 100644 --- a/scene/resources/bit_map.cpp +++ b/scene/resources/bit_map.cpp @@ -595,6 +595,64 @@ Array BitMap::_opaque_to_polygons_bind(const Rect2 &p_rect, float p_epsilon) con return result_array; } +void BitMap::resize(const Size2& p_new_size) { + + Ref<BitMap> new_bitmap; + new_bitmap.instance(); + new_bitmap->create(p_new_size); + int lw = MIN(width,p_new_size.width); + int lh = MIN(height,p_new_size.height); + for(int x=0;x<lw;x++) { + for(int y=0;y<lh;y++) { + new_bitmap->set_bit(Vector2(x,y),get_bit(Vector2(x,y))); + } + } + + width = new_bitmap->width; + height = new_bitmap->height; + bitmask = new_bitmap->bitmask; +} + +Ref<Image> BitMap::convert_to_image() const { + + Ref<Image> image; + image.instance(); + image->create(width,height,false,Image::FORMAT_L8); + image->lock(); + for(int i=0;i<width;i++) { + for(int j=0;j<height;j++) { + image->set_pixel( i,j,get_bit(Point2(i,j)) ? Color(1,1,1) : Color(0,0,0)); + } + } + + image->unlock(); + + return image; +} +void BitMap::blit(const Vector2& p_pos,const Ref<BitMap>& p_bitmap) { + + int x = p_pos.x; + int y = p_pos.y; + int w = p_bitmap->get_size().width; + int h = p_bitmap->get_size().height; + + for(int i=0;i<w;i++) { + for (int j=0;j<h;j++) { + int px = x+i; + int py = y+j; + if (px<0 || px>=width) + continue; + if (py<0 || py>=height) + continue; + if (p_bitmap->get_bit(Vector2(i,j))) { + set_bit(Vector2(x,y),true); + } + } + } + +} + + void BitMap::_bind_methods() { ClassDB::bind_method(D_METHOD("create", "size"), &BitMap::create); diff --git a/scene/resources/bit_map.h b/scene/resources/bit_map.h index b3c86afd38..6e1171b8a9 100644 --- a/scene/resources/bit_map.h +++ b/scene/resources/bit_map.h @@ -64,9 +64,13 @@ public: int get_true_bit_count() const; Size2 get_size() const; + void resize(const Size2& p_new_size); void grow_mask(int p_pixels, const Rect2 &p_rect); + void blit(const Vector2& p_pos,const Ref<BitMap>& p_bitmap); + Ref<Image> convert_to_image() const; + Vector<Vector<Vector2> > clip_opaque_to_polygons(const Rect2 &p_rect, float p_epsilon = 2.0) const; BitMap(); diff --git a/scene/resources/box_shape.cpp b/scene/resources/box_shape.cpp index d93754076c..d819e9f776 100644 --- a/scene/resources/box_shape.cpp +++ b/scene/resources/box_shape.cpp @@ -51,6 +51,7 @@ Vector<Vector3> BoxShape::_gen_debug_mesh_lines() { void BoxShape::_update_shape() { PhysicsServer::get_singleton()->shape_set_data(get_shape(), extents); + Shape::_update_shape(); } void BoxShape::set_extents(const Vector3 &p_extents) { diff --git a/scene/resources/capsule_shape.cpp b/scene/resources/capsule_shape.cpp index 3f7bf1e0ec..669b261bfe 100644 --- a/scene/resources/capsule_shape.cpp +++ b/scene/resources/capsule_shape.cpp @@ -75,6 +75,7 @@ void CapsuleShape::_update_shape() { d["radius"] = radius; d["height"] = height; PhysicsServer::get_singleton()->shape_set_data(get_shape(), d); + Shape::_update_shape(); } void CapsuleShape::set_radius(float p_radius) { diff --git a/scene/resources/concave_polygon_shape.cpp b/scene/resources/concave_polygon_shape.cpp index b192d088d8..b4cc38c8c0 100644 --- a/scene/resources/concave_polygon_shape.cpp +++ b/scene/resources/concave_polygon_shape.cpp @@ -65,6 +65,7 @@ Vector<Vector3> ConcavePolygonShape::_gen_debug_mesh_lines() { } void ConcavePolygonShape::_update_shape() { + Shape::_update_shape(); } void ConcavePolygonShape::set_faces(const PoolVector<Vector3> &p_faces) { diff --git a/scene/resources/convex_polygon_shape.cpp b/scene/resources/convex_polygon_shape.cpp index 5845e4be50..499688a185 100644 --- a/scene/resources/convex_polygon_shape.cpp +++ b/scene/resources/convex_polygon_shape.cpp @@ -58,7 +58,7 @@ Vector<Vector3> ConvexPolygonShape::_gen_debug_mesh_lines() { void ConvexPolygonShape::_update_shape() { PhysicsServer::get_singleton()->shape_set_data(get_shape(), points); - emit_changed(); + Shape::_update_shape(); } void ConvexPolygonShape::set_points(const PoolVector<Vector3> &p_points) { diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp index 464ca60d31..ece8ad4bb0 100644 --- a/scene/resources/curve.cpp +++ b/scene/resources/curve.cpp @@ -268,7 +268,7 @@ void Curve::update_auto_tangents(int i) { } if (i + 1 < _points.size()) { - if (p.right_mode == TANGENT_LINEAR && i + 1 < _points.size()) { + if (p.right_mode == TANGENT_LINEAR) { Vector2 v = (_points[i + 1].pos - p.pos).normalized(); p.right_tangent = v.y / v.x; } diff --git a/scene/resources/cylinder_shape.cpp b/scene/resources/cylinder_shape.cpp index 4fd829b349..f60f7ab376 100644 --- a/scene/resources/cylinder_shape.cpp +++ b/scene/resources/cylinder_shape.cpp @@ -68,6 +68,7 @@ void CylinderShape::_update_shape() { d["radius"] = radius; d["height"] = height; PhysicsServer::get_singleton()->shape_set_data(get_shape(), d); + Shape::_update_shape(); } void CylinderShape::set_radius(float p_radius) { diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index c7a815d8a4..79d93113b3 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -423,6 +423,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_stylebox("completion", "TextEdit", make_stylebox(tree_bg_png, 3, 3, 3, 3, 0, 0, 0, 0)); theme->set_icon("tab", "TextEdit", make_icon(tab_png)); + theme->set_icon("folded", "TextEdit", make_icon(arrow_right_png)); + theme->set_icon("fold", "TextEdit", make_icon(arrow_down_png)); theme->set_font("font", "TextEdit", default_font); @@ -437,6 +439,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("selection_color", "TextEdit", font_color_selection); theme->set_color("mark_color", "TextEdit", Color(1.0, 0.4, 0.4, 0.4)); theme->set_color("breakpoint_color", "TextEdit", Color(0.8, 0.8, 0.4, 0.2)); + theme->set_color("executing_line_color", "TextEdit", Color(0.2, 0.8, 0.2, 0.4)); theme->set_color("code_folding_color", "TextEdit", Color(0.8, 0.8, 0.8, 0.8)); theme->set_color("current_line_color", "TextEdit", Color(0.25, 0.25, 0.26, 0.8)); theme->set_color("caret_color", "TextEdit", control_font_color); @@ -795,7 +798,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_font("bold_italics_font", "RichTextLabel", default_font); theme->set_font("mono_font", "RichTextLabel", default_font); - theme->set_color("default_color", "RichTextLabel", control_font_color); + theme->set_color("default_color", "RichTextLabel", Color(1, 1, 1)); theme->set_color("font_color_selected", "RichTextLabel", font_color_selection); theme->set_color("selection_color", "RichTextLabel", Color(0.1, 0.1, 1, 0.8)); diff --git a/scene/resources/gradient.h b/scene/resources/gradient.h index a51a0ca0d0..7e7fc99a5d 100644 --- a/scene/resources/gradient.h +++ b/scene/resources/gradient.h @@ -91,7 +91,7 @@ public: int high = points.size() - 1; int middle = 0; -#if DEBUG_ENABLED +#ifdef DEBUG_ENABLED if (low > high) ERR_PRINT("low > high, this may be a bug"); #endif diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index 85018f38d7..67639858ce 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -37,6 +37,8 @@ #include <stdlib.h> +Mesh::ConvexDecompositionFunc Mesh::convex_composition_function = NULL; + Ref<TriangleMesh> Mesh::generate_triangle_mesh() const { if (triangle_mesh.is_valid()) @@ -543,6 +545,49 @@ void Mesh::clear_cache() const { debug_lines.clear(); } +Vector<Ref<Shape> > Mesh::convex_decompose() const { + + ERR_FAIL_COND_V(!convex_composition_function, Vector<Ref<Shape> >()); + + PoolVector<Face3> faces = get_faces(); + Vector<Face3> f3; + f3.resize(faces.size()); + PoolVector<Face3>::Read f = faces.read(); + for (int i = 0; i < f3.size(); i++) { + f3.write[i] = f[i]; + } + + Vector<Vector<Face3> > decomposed = convex_composition_function(f3); + + Vector<Ref<Shape> > ret; + + for (int i = 0; i < decomposed.size(); i++) { + Set<Vector3> points; + for (int j = 0; j < decomposed[i].size(); j++) { + points.insert(decomposed[i][j].vertex[0]); + points.insert(decomposed[i][j].vertex[1]); + points.insert(decomposed[i][j].vertex[2]); + } + + PoolVector<Vector3> convex_points; + convex_points.resize(points.size()); + { + PoolVector<Vector3>::Write w = convex_points.write(); + int idx = 0; + for (Set<Vector3>::Element *E = points.front(); E; E = E->next()) { + w[idx++] = E->get(); + } + } + + Ref<ConvexPolygonShape> shape; + shape.instance(); + shape->set_points(convex_points); + ret.push_back(shape); + } + + return ret; +} + Mesh::Mesh() { } diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h index dabfc6ea60..1457d283bd 100644 --- a/scene/resources/mesh.h +++ b/scene/resources/mesh.h @@ -31,6 +31,7 @@ #ifndef MESH_H #define MESH_H +#include "core/math/face3.h" #include "core/math/triangle_mesh.h" #include "core/resource.h" #include "scene/resources/material.h" @@ -147,6 +148,12 @@ public: Size2 get_lightmap_size_hint() const; void clear_cache() const; + typedef Vector<Vector<Face3> > (*ConvexDecompositionFunc)(const Vector<Face3> &); + + static ConvexDecompositionFunc convex_composition_function; + + Vector<Ref<Shape> > convex_decompose() const; + Mesh(); }; diff --git a/scene/resources/multimesh.cpp b/scene/resources/multimesh.cpp index 1b406551ab..be0b9f9ac3 100644 --- a/scene/resources/multimesh.cpp +++ b/scene/resources/multimesh.cpp @@ -162,6 +162,16 @@ int MultiMesh::get_instance_count() const { return instance_count; } +void MultiMesh::set_visible_instance_count(int p_count) { + ERR_FAIL_COND(p_count < -1); + VisualServer::get_singleton()->multimesh_set_visible_instances(multimesh, p_count); + visible_instance_count = p_count; +} +int MultiMesh::get_visible_instance_count() const { + + return visible_instance_count; +} + void MultiMesh::set_instance_transform(int p_instance, const Transform &p_transform) { VisualServer::get_singleton()->multimesh_instance_set_transform(multimesh, p_instance, p_transform); @@ -255,6 +265,8 @@ void MultiMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_instance_count", "count"), &MultiMesh::set_instance_count); ClassDB::bind_method(D_METHOD("get_instance_count"), &MultiMesh::get_instance_count); + ClassDB::bind_method(D_METHOD("set_visible_instance_count", "count"), &MultiMesh::set_visible_instance_count); + ClassDB::bind_method(D_METHOD("get_visible_instance_count"), &MultiMesh::get_visible_instance_count); ClassDB::bind_method(D_METHOD("set_instance_transform", "instance", "transform"), &MultiMesh::set_instance_transform); ClassDB::bind_method(D_METHOD("set_instance_transform_2d", "instance", "transform"), &MultiMesh::set_instance_transform_2d); ClassDB::bind_method(D_METHOD("get_instance_transform", "instance"), &MultiMesh::get_instance_transform); @@ -276,6 +288,7 @@ void MultiMesh::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "transform_format", PROPERTY_HINT_ENUM, "2D,3D"), "set_transform_format", "get_transform_format"); ADD_PROPERTY(PropertyInfo(Variant::INT, "custom_data_format", PROPERTY_HINT_ENUM, "None,Byte,Float"), "set_custom_data_format", "get_custom_data_format"); ADD_PROPERTY(PropertyInfo(Variant::INT, "instance_count", PROPERTY_HINT_RANGE, "0,16384,1,or_greater"), "set_instance_count", "get_instance_count"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_instance_count", PROPERTY_HINT_RANGE, "-1,16384,1,or_greater"), "set_visible_instance_count", "get_visible_instance_count"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh"); ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR3_ARRAY, "transform_array", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_transform_array", "_get_transform_array"); ADD_PROPERTY(PropertyInfo(Variant::POOL_COLOR_ARRAY, "color_array", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_color_array", "_get_color_array"); @@ -299,6 +312,7 @@ MultiMesh::MultiMesh() { color_format = COLOR_NONE; custom_data_format = CUSTOM_DATA_NONE; transform_format = TRANSFORM_2D; + visible_instance_count = -1; instance_count = 0; } diff --git a/scene/resources/multimesh.h b/scene/resources/multimesh.h index ac2c69e022..24b4beaa89 100644 --- a/scene/resources/multimesh.h +++ b/scene/resources/multimesh.h @@ -64,6 +64,7 @@ private: ColorFormat color_format; CustomDataFormat custom_data_format; int instance_count; + int visible_instance_count; protected: static void _bind_methods(); @@ -93,6 +94,9 @@ public: void set_instance_count(int p_count); int get_instance_count() const; + void set_visible_instance_count(int p_count); + int get_visible_instance_count() const; + void set_instance_transform(int p_instance, const Transform &p_transform); void set_instance_transform_2d(int p_instance, const Transform2D &p_transform); Transform get_instance_transform(int p_instance) const; diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index 626ed9f5b4..2c6f30f429 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -535,7 +535,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map float a = value; float b = original; - if (Math::abs(a - b) < CMP_EPSILON) + if (Math::is_equal_approx(a, b)) continue; } else if (bool(Variant::evaluate(Variant::OP_EQUAL, value, original))) { diff --git a/scene/resources/plane_shape.cpp b/scene/resources/plane_shape.cpp index 419ff8f972..08f6ccd764 100644 --- a/scene/resources/plane_shape.cpp +++ b/scene/resources/plane_shape.cpp @@ -64,6 +64,7 @@ Vector<Vector3> PlaneShape::_gen_debug_mesh_lines() { void PlaneShape::_update_shape() { PhysicsServer::get_singleton()->shape_set_data(get_shape(), plane); + Shape::_update_shape(); } void PlaneShape::set_plane(Plane p_plane) { diff --git a/scene/resources/ray_shape.cpp b/scene/resources/ray_shape.cpp index b7925d8a58..0acfffdc06 100644 --- a/scene/resources/ray_shape.cpp +++ b/scene/resources/ray_shape.cpp @@ -47,7 +47,7 @@ void RayShape::_update_shape() { d["length"] = length; d["slips_on_slope"] = slips_on_slope; PhysicsServer::get_singleton()->shape_set_data(get_shape(), d); - emit_changed(); + Shape::_update_shape(); } void RayShape::set_length(float p_length) { diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index b6efca9acc..e9f90fc85f 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -445,6 +445,11 @@ Error ResourceInteractiveLoaderText::poll() { } else { resource_cache.push_back(res); +#ifdef TOOLS_ENABLED + //remember ID for saving + res->set_id_for_path(local_path,index); +#endif + } ExtResource er; @@ -1355,7 +1360,7 @@ String ResourceFormatSaverTextInstance::_write_resource(const RES &res) { if (external_resources.has(res)) { - return "ExtResource( " + itos(external_resources[res] + 1) + " )"; + return "ExtResource( " + itos(external_resources[res]) + " )"; } else { if (internal_resources.has(res)) { @@ -1459,7 +1464,8 @@ void ResourceFormatSaverTextInstance::_find_resources(const Variant &p_variant, _find_resources(v); } } break; - default: {} + default: { + } } } @@ -1538,18 +1544,65 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r f->store_line("]\n"); //one empty line } - Vector<RES> sorted_er; - sorted_er.resize(external_resources.size()); + { + + + } + +#ifdef TOOLS_ENABLED + //keep order from cached ids + Set<int> cached_ids_found; + for (Map<RES, int>::Element *E = external_resources.front(); E; E = E->next()) { + int cached_id = E->key()->get_id_for_path(local_path); + if (cached_id < 0 || cached_ids_found.has(cached_id)) { + E->get() = -1; //reset + } else { + E->get() = cached_id; + cached_ids_found.insert(cached_id); + } + } + //create IDs for non cached resources for (Map<RES, int>::Element *E = external_resources.front(); E; E = E->next()) { + if (cached_ids_found.has(E->get())) { //already cached, go on + continue; + } + + int attempt = 1; //start from one, more readable format + while(cached_ids_found.has(attempt)) { + attempt++; + } - sorted_er.write[E->get()] = E->key(); + cached_ids_found.insert(attempt); + E->get() = attempt; + //update also in resource + Ref<Resource> res = E->key(); + res->set_id_for_path(local_path,attempt); } +#else + //make sure to start from one, as it makes format more readable + for (Map<RES, int>::Element *E = external_resources.front(); E; E = E->next()) { + E->get() = E->get() + 1; + } +#endif + + Vector<ResourceSort> sorted_er; + + for (Map<RES, int>::Element *E = external_resources.front(); E; E = E->next()) { + + ResourceSort rs; + rs.resource = E->key(); + rs.index = E->get(); + sorted_er.push_back(rs); + } + + sorted_er.sort(); + for (int i = 0; i < sorted_er.size(); i++) { - String p = sorted_er[i]->get_path(); + String p = sorted_er[i].resource->get_path(); - f->store_string("[ext_resource path=\"" + p + "\" type=\"" + sorted_er[i]->get_save_class() + "\" id=" + itos(i + 1) + "]\n"); //bundled + f->store_string("[ext_resource path=\"" + p + "\" type=\"" + sorted_er[i].resource->get_save_class() + "\" id=" + itos(sorted_er[i].index) + "]\n"); //bundled } if (external_resources.size()) diff --git a/scene/resources/resource_format_text.h b/scene/resources/resource_format_text.h index 8d78ab33b0..ab6f94986c 100644 --- a/scene/resources/resource_format_text.h +++ b/scene/resources/resource_format_text.h @@ -169,6 +169,15 @@ class ResourceFormatSaverTextInstance { Map<RES, int> external_resources; Map<RES, int> internal_resources; + struct ResourceSort { + RES resource; + int index; + bool operator<(const ResourceSort& p_right) const { + return index < p_right.index; + } + + }; + void _find_resources(const Variant &p_variant, bool p_main = false); static String _write_resources(void *ud, const RES &p_resource); diff --git a/scene/resources/shape.cpp b/scene/resources/shape.cpp index 825b7f4c8b..6ba46f066c 100644 --- a/scene/resources/shape.cpp +++ b/scene/resources/shape.cpp @@ -96,6 +96,11 @@ Ref<ArrayMesh> Shape::get_debug_mesh() { return debug_mesh_cache; } +void Shape::_update_shape() { + emit_changed(); + debug_mesh_cache.unref(); +} + void Shape::_bind_methods() { ClassDB::bind_method(D_METHOD("set_margin", "margin"), &Shape::set_margin); diff --git a/scene/resources/shape.h b/scene/resources/shape.h index de99a967a8..ba763eaab1 100644 --- a/scene/resources/shape.h +++ b/scene/resources/shape.h @@ -51,6 +51,8 @@ protected: Shape(RID p_shape); virtual Vector<Vector3> _gen_debug_mesh_lines() = 0; // { return Vector<Vector3>(); } + virtual void _update_shape(); + public: virtual RID get_rid() const { return shape; } diff --git a/scene/resources/sky.cpp b/scene/resources/sky.cpp index f9f8ff19e4..48945d4e63 100644 --- a/scene/resources/sky.cpp +++ b/scene/resources/sky.cpp @@ -553,10 +553,10 @@ ProceduralSky::ProceduralSky(bool p_desaturate) { ground_energy = 1; if (p_desaturate) { - sky_top_color.set_hsv(sky_top_color.get_h(),0,sky_top_color.get_v()); - sky_horizon_color.set_hsv(sky_horizon_color.get_h(),0,sky_horizon_color.get_v()); - ground_bottom_color.set_hsv(ground_bottom_color.get_h(),0,ground_bottom_color.get_v()); - ground_horizon_color.set_hsv(ground_horizon_color.get_h(),0,ground_horizon_color.get_v()); + sky_top_color.set_hsv(sky_top_color.get_h(), 0, sky_top_color.get_v()); + sky_horizon_color.set_hsv(sky_horizon_color.get_h(), 0, sky_horizon_color.get_v()); + ground_bottom_color.set_hsv(ground_bottom_color.get_h(), 0, ground_bottom_color.get_v()); + ground_horizon_color.set_hsv(ground_horizon_color.get_h(), 0, ground_horizon_color.get_v()); } sun_color = Color(1, 1, 1); sun_latitude = 35; diff --git a/scene/resources/sky.h b/scene/resources/sky.h index 7327b2a627..3a6f44e11b 100644 --- a/scene/resources/sky.h +++ b/scene/resources/sky.h @@ -191,7 +191,7 @@ public: virtual RID get_rid() const; - ProceduralSky(bool p_desaturate=false); + ProceduralSky(bool p_desaturate = false); ~ProceduralSky(); }; diff --git a/scene/resources/sphere_shape.cpp b/scene/resources/sphere_shape.cpp index 492cf3959d..af89413ced 100644 --- a/scene/resources/sphere_shape.cpp +++ b/scene/resources/sphere_shape.cpp @@ -58,6 +58,7 @@ Vector<Vector3> SphereShape::_gen_debug_mesh_lines() { void SphereShape::_update_shape() { PhysicsServer::get_singleton()->shape_set_data(get_shape(), radius); + Shape::_update_shape(); } void SphereShape::set_radius(float p_radius) { diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp index 83f29503fa..3ba43006a3 100644 --- a/scene/resources/surface_tool.cpp +++ b/scene/resources/surface_tool.cpp @@ -108,8 +108,55 @@ void SurfaceTool::add_vertex(const Vector3 &p_vertex) { vtx.bones = last_bones; vtx.tangent = last_tangent.normal; vtx.binormal = last_normal.cross(last_tangent.normal).normalized() * last_tangent.d; + + const int expected_vertices = 4; + + if ((format & Mesh::ARRAY_FORMAT_WEIGHTS || format & Mesh::ARRAY_FORMAT_BONES) && (vtx.weights.size() != expected_vertices || vtx.bones.size() != expected_vertices)) { + //ensure vertices are the expected amount + ERR_FAIL_COND(vtx.weights.size() != vtx.bones.size()); + if (vtx.weights.size() < expected_vertices) { + //less than requred, fill + for (int i = vtx.weights.size(); i < expected_vertices; i++) { + vtx.weights.push_back(0); + vtx.bones.push_back(0); + } + } else if (vtx.weights.size() > expected_vertices) { + //more than required, sort, cap and normalize. + Vector<WeightSort> weights; + for (int i = 0; i < vtx.weights.size(); i++) { + WeightSort ws; + ws.index = vtx.bones[i]; + ws.weight = vtx.weights[i]; + weights.push_back(ws); + } + + //sort + weights.sort(); + //cap + weights.resize(expected_vertices); + //renormalize + float total = 0; + for (int i = 0; i < expected_vertices; i++) { + total += weights[i].weight; + } + + vtx.weights.resize(expected_vertices); + vtx.bones.resize(expected_vertices); + + for (int i = 0; i < expected_vertices; i++) { + if (total > 0) { + vtx.weights.write[i] = weights[i].weight / total; + } else { + vtx.weights.write[i] = 0; + } + vtx.bones.write[i] = weights[i].index; + } + } + } + vertex_array.push_back(vtx); first = false; + format |= Mesh::ARRAY_FORMAT_VERTEX; } void SurfaceTool::add_color(Color p_color) { @@ -161,7 +208,6 @@ void SurfaceTool::add_uv2(const Vector2 &p_uv2) { void SurfaceTool::add_bones(const Vector<int> &p_bones) { ERR_FAIL_COND(!begun); - ERR_FAIL_COND(p_bones.size() != 4); ERR_FAIL_COND(!first && !(format & Mesh::ARRAY_FORMAT_BONES)); format |= Mesh::ARRAY_FORMAT_BONES; @@ -171,8 +217,6 @@ void SurfaceTool::add_bones(const Vector<int> &p_bones) { void SurfaceTool::add_weights(const Vector<float> &p_weights) { ERR_FAIL_COND(!begun); - - ERR_FAIL_COND(p_weights.size() != 4); ERR_FAIL_COND(!first && !(format & Mesh::ARRAY_FORMAT_WEIGHTS)); format |= Mesh::ARRAY_FORMAT_WEIGHTS; @@ -397,7 +441,8 @@ Array SurfaceTool::commit_to_arrays() { a[i] = array; } break; - default: {} + default: { + } } } diff --git a/scene/resources/surface_tool.h b/scene/resources/surface_tool.h index a3b110f0d8..c55cade813 100644 --- a/scene/resources/surface_tool.h +++ b/scene/resources/surface_tool.h @@ -62,6 +62,14 @@ private: static _FORCE_INLINE_ uint32_t hash(const Vertex &p_vtx); }; + struct WeightSort { + int index; + float weight; + bool operator<(const WeightSort &p_right) const { + return weight < p_right.weight; + } + }; + bool begun; bool first; Mesh::PrimitiveType primitive; diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index 3ca2b56d08..a5eb950c36 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -34,6 +34,7 @@ #include "core/io/image_loader.h" #include "core/method_bind_ext.gen.inc" #include "core/os/os.h" +#include "mesh.h" #include "scene/resources/bit_map.h" Size2 Texture::get_size() const { @@ -1133,6 +1134,138 @@ AtlasTexture::AtlasTexture() { filter_clip = false; } +///////////////////////////////////////// + +int MeshTexture::get_width() const { + return size.width; +} +int MeshTexture::get_height() const { + return size.height; +} +RID MeshTexture::get_rid() const { + return RID(); +} + +bool MeshTexture::has_alpha() const { + return false; +} + +void MeshTexture::set_flags(uint32_t p_flags) { +} + +uint32_t MeshTexture::get_flags() const { + return 0; +} + +void MeshTexture::set_mesh(const Ref<Mesh> &p_mesh) { + mesh = p_mesh; +} +Ref<Mesh> MeshTexture::get_mesh() const { + return mesh; +} + +void MeshTexture::set_image_size(const Size2 &p_size) { + size = p_size; +} + +Size2 MeshTexture::get_image_size() const { + + return size; +} + +void MeshTexture::set_base_texture(const Ref<Texture> &p_texture) { + base_texture = p_texture; +} + +Ref<Texture> MeshTexture::get_base_texture() const { + return base_texture; +} + +void MeshTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose, const Ref<Texture> &p_normal_map) const { + + if (mesh.is_null() || base_texture.is_null()) { + return; + } + Transform2D xform; + xform.set_origin(p_pos); + if (p_transpose) { + SWAP(xform.elements[0][1], xform.elements[1][0]); + SWAP(xform.elements[0][0], xform.elements[1][1]); + } + RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID(); + VisualServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid(), normal_rid); +} +void MeshTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose, const Ref<Texture> &p_normal_map) const { + if (mesh.is_null() || base_texture.is_null()) { + return; + } + Transform2D xform; + Vector2 origin = p_rect.position; + if (p_rect.size.x < 0) { + origin.x += size.x; + } + if (p_rect.size.y < 0) { + origin.y += size.y; + } + xform.set_origin(origin); + xform.set_scale(p_rect.size / size); + + if (p_transpose) { + SWAP(xform.elements[0][1], xform.elements[1][0]); + SWAP(xform.elements[0][0], xform.elements[1][1]); + } + RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID(); + VisualServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid(), normal_rid); +} +void MeshTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, const Ref<Texture> &p_normal_map, bool p_clip_uv) const { + + if (mesh.is_null() || base_texture.is_null()) { + return; + } + Transform2D xform; + Vector2 origin = p_rect.position; + if (p_rect.size.x < 0) { + origin.x += size.x; + } + if (p_rect.size.y < 0) { + origin.y += size.y; + } + xform.set_origin(origin); + xform.set_scale(p_rect.size / size); + + if (p_transpose) { + SWAP(xform.elements[0][1], xform.elements[1][0]); + SWAP(xform.elements[0][0], xform.elements[1][1]); + } + RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID(); + VisualServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid(), normal_rid); +} +bool MeshTexture::get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, Rect2 &r_rect, Rect2 &r_src_rect) const { + r_rect = p_rect; + r_src_rect = p_src_rect; + return true; +} + +bool MeshTexture::is_pixel_opaque(int p_x, int p_y) const { + return true; +} + +void MeshTexture::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_mesh", "mesh"), &MeshTexture::set_mesh); + ClassDB::bind_method(D_METHOD("get_mesh"), &MeshTexture::get_mesh); + ClassDB::bind_method(D_METHOD("set_image_size", "size"), &MeshTexture::set_image_size); + ClassDB::bind_method(D_METHOD("get_image_size"), &MeshTexture::get_image_size); + ClassDB::bind_method(D_METHOD("set_base_texture", "texture"), &MeshTexture::set_base_texture); + ClassDB::bind_method(D_METHOD("get_base_texture"), &MeshTexture::get_base_texture); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "base_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_base_texture", "get_base_texture"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "image_size", PROPERTY_HINT_RANGE, "0,16384,1"), "set_image_size", "get_image_size"); +} + +MeshTexture::MeshTexture() { +} + ////////////////////////////////////////// int LargeTexture::get_width() const { diff --git a/scene/resources/texture.h b/scene/resources/texture.h index 281a33929c..58287b7593 100644 --- a/scene/resources/texture.h +++ b/scene/resources/texture.h @@ -293,6 +293,49 @@ public: AtlasTexture(); }; +class Mesh; + +class MeshTexture : public Texture { + + GDCLASS(MeshTexture, Texture); + RES_BASE_EXTENSION("meshtex"); + + Ref<Texture> base_texture; + Ref<Mesh> mesh; + Size2i size; + +protected: + static void _bind_methods(); + +public: + virtual int get_width() const; + virtual int get_height() const; + virtual RID get_rid() const; + + virtual bool has_alpha() const; + + virtual void set_flags(uint32_t p_flags); + virtual uint32_t get_flags() const; + + void set_mesh(const Ref<Mesh> &p_mesh); + Ref<Mesh> get_mesh() const; + + void set_image_size(const Size2 &p_size); + Size2 get_image_size() const; + + void set_base_texture(const Ref<Texture> &p_texture); + Ref<Texture> get_base_texture() const; + + virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture> &p_normal_map = Ref<Texture>()) const; + virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture> &p_normal_map = Ref<Texture>()) const; + virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture> &p_normal_map = Ref<Texture>(), bool p_clip_uv = true) const; + virtual bool get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, Rect2 &r_rect, Rect2 &r_src_rect) const; + + bool is_pixel_opaque(int p_x, int p_y) const; + + MeshTexture(); +}; + class LargeTexture : public Texture { GDCLASS(LargeTexture, Texture); diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index bac10a1cd5..b8f21948c3 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -240,6 +240,9 @@ bool VisualShader::can_connect_nodes(Type p_type, int p_from_node, int p_from_po if (!g->nodes.has(p_from_node)) return false; + if (p_from_node == p_to_node) + return false; + if (p_from_port < 0 || p_from_port >= g->nodes[p_from_node].node->get_output_port_count()) return false; @@ -782,7 +785,7 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui } else if (in_type == VisualShaderNode::PORT_TYPE_VECTOR && out_type == VisualShaderNode::PORT_TYPE_SCALAR) { inputs[i] = "vec3(" + src_var + ")"; } else if (in_type == VisualShaderNode::PORT_TYPE_BOOLEAN && out_type == VisualShaderNode::PORT_TYPE_VECTOR) { - inputs[i] = "all(" + src_var + ")"; + inputs[i] = "all(bvec3(" + src_var + "))"; } else if (in_type == VisualShaderNode::PORT_TYPE_BOOLEAN && out_type == VisualShaderNode::PORT_TYPE_SCALAR) { inputs[i] = src_var + ">0.0?true:false"; } else if (in_type == VisualShaderNode::PORT_TYPE_SCALAR && out_type == VisualShaderNode::PORT_TYPE_BOOLEAN) { @@ -839,7 +842,8 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui case VisualShaderNode::PORT_TYPE_VECTOR: code += String() + "\tvec3 " + outputs[i] + ";\n"; break; case VisualShaderNode::PORT_TYPE_BOOLEAN: code += String() + "\tbool " + outputs[i] + ";\n"; break; case VisualShaderNode::PORT_TYPE_TRANSFORM: code += String() + "\tmat4 " + outputs[i] + ";\n"; break; - default: {} + default: { + } } } diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp index dac999bf1d..d02902572c 100644 --- a/scene/resources/visual_shader_nodes.cpp +++ b/scene/resources/visual_shader_nodes.cpp @@ -2948,3 +2948,140 @@ String VisualShaderNodeCubeMapUniform::generate_code(Shader::Mode p_mode, Visual VisualShaderNodeCubeMapUniform::VisualShaderNodeCubeMapUniform() { } + +////////////// If + +String VisualShaderNodeIf::get_caption() const { + return "If"; +} + +int VisualShaderNodeIf::get_input_port_count() const { + return 6; +} + +VisualShaderNodeIf::PortType VisualShaderNodeIf::get_input_port_type(int p_port) const { + if (p_port == 0 || p_port == 1 || p_port == 2) { + return PORT_TYPE_SCALAR; + } + return PORT_TYPE_VECTOR; +} + +String VisualShaderNodeIf::get_input_port_name(int p_port) const { + switch (p_port) { + case 0: + return "a"; + case 1: + return "b"; + case 2: + return "tolerance"; + case 3: + return "a == b"; + case 4: + return "a > b"; + case 5: + return "a < b"; + default: + return ""; + } +} + +int VisualShaderNodeIf::get_output_port_count() const { + return 1; +} + +VisualShaderNodeIf::PortType VisualShaderNodeIf::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} + +String VisualShaderNodeIf::get_output_port_name(int p_port) const { + return "result"; +} + +String VisualShaderNodeIf::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { + + String code; + code += "\tif(abs(" + p_input_vars[0] + "-" + p_input_vars[1] + ")<" + p_input_vars[2] + ")\n"; // abs(a - b) < tolerance eg. a == b + code += "\t{\n"; + code += "\t\t" + p_output_vars[0] + "=" + p_input_vars[3] + ";\n"; + code += "\t}\n"; + code += "\telse if(" + p_input_vars[0] + "<" + p_input_vars[1] + ")\n"; // a < b + code += "\t{\n"; + code += "\t\t" + p_output_vars[0] + "=" + p_input_vars[5] + ";\n"; + code += "\t}\n"; + code += "\telse\n"; // a > b (or a >= b if abs(a - b) < tolerance is false) + code += "\t{\n"; + code += "\t\t" + p_output_vars[0] + "=" + p_input_vars[4] + ";\n"; + code += "\t}\n"; + return code; +} + +VisualShaderNodeIf::VisualShaderNodeIf() { + set_input_port_default_value(0, 0.0); + set_input_port_default_value(1, 0.0); + set_input_port_default_value(2, CMP_EPSILON); + set_input_port_default_value(3, Vector3(0.0, 0.0, 0.0)); + set_input_port_default_value(4, Vector3(0.0, 0.0, 0.0)); + set_input_port_default_value(5, Vector3(0.0, 0.0, 0.0)); +} + +////////////// Switch + +String VisualShaderNodeSwitch::get_caption() const { + return "Switch"; +} + +int VisualShaderNodeSwitch::get_input_port_count() const { + return 3; +} + +VisualShaderNodeSwitch::PortType VisualShaderNodeSwitch::get_input_port_type(int p_port) const { + if (p_port == 0) { + return PORT_TYPE_BOOLEAN; + } + return PORT_TYPE_VECTOR; +} + +String VisualShaderNodeSwitch::get_input_port_name(int p_port) const { + switch (p_port) { + case 0: + return "value"; + case 1: + return "true"; + case 2: + return "false"; + default: + return ""; + } +} + +int VisualShaderNodeSwitch::get_output_port_count() const { + return 1; +} + +VisualShaderNodeSwitch::PortType VisualShaderNodeSwitch::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} + +String VisualShaderNodeSwitch::get_output_port_name(int p_port) const { + return "result"; +} + +String VisualShaderNodeSwitch::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { + + String code; + code += "\tif(" + p_input_vars[0] + ")\n"; + code += "\t{\n"; + code += "\t\t" + p_output_vars[0] + "=" + p_input_vars[1] + ";\n"; + code += "\t}\n"; + code += "\telse\n"; + code += "\t{\n"; + code += "\t\t" + p_output_vars[0] + "=" + p_input_vars[2] + ";\n"; + code += "\t}\n"; + return code; +} + +VisualShaderNodeSwitch::VisualShaderNodeSwitch() { + set_input_port_default_value(0, false); + set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0)); + set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0)); +} diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h index 28ead64fe2..90c479bd48 100644 --- a/scene/resources/visual_shader_nodes.h +++ b/scene/resources/visual_shader_nodes.h @@ -1440,4 +1440,48 @@ public: VisualShaderNodeCubeMapUniform(); }; +/////////////////////////////////////// +/// IF +/////////////////////////////////////// + +class VisualShaderNodeIf : public VisualShaderNode { + GDCLASS(VisualShaderNodeIf, VisualShaderNode) +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; + + VisualShaderNodeIf(); +}; + +/////////////////////////////////////// +/// SWITCH +/////////////////////////////////////// + +class VisualShaderNodeSwitch : public VisualShaderNode { + GDCLASS(VisualShaderNodeSwitch, VisualShaderNode) +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; + + VisualShaderNodeSwitch(); +}; + #endif // VISUAL_SHADER_NODES_H |