diff options
Diffstat (limited to 'scene')
450 files changed, 24899 insertions, 15931 deletions
diff --git a/scene/2d/animated_sprite_2d.cpp b/scene/2d/animated_sprite_2d.cpp index d56c7b8811..7ee9861d3f 100644 --- a/scene/2d/animated_sprite_2d.cpp +++ b/scene/2d/animated_sprite_2d.cpp @@ -63,9 +63,13 @@ Rect2 AnimatedSprite2D::_edit_get_rect() const { } bool AnimatedSprite2D::_edit_use_rect() const { - if (!frames.is_valid() || !frames->has_animation(animation) || frame < 0 || frame >= frames->get_frame_count(animation)) { + if (frames.is_null() || !frames->has_animation(animation)) { return false; } + if (frame < 0 || frame >= frames->get_frame_count(animation)) { + return false; + } + Ref<Texture2D> t; if (animation) { t = frames->get_frame(animation, frame); @@ -79,7 +83,10 @@ Rect2 AnimatedSprite2D::get_anchorable_rect() const { } Rect2 AnimatedSprite2D::_get_rect() const { - if (!frames.is_valid() || !frames->has_animation(animation) || frame < 0 || frame >= frames->get_frame_count(animation)) { + if (frames.is_null() || !frames->has_animation(animation)) { + return Rect2(); + } + if (frame < 0 || frame >= frames->get_frame_count(animation)) { return Rect2(); } @@ -104,105 +111,113 @@ Rect2 AnimatedSprite2D::_get_rect() const { return Rect2(ofs, s); } -void AnimatedSprite2D::_validate_property(PropertyInfo &property) const { +void AnimatedSprite2D::_validate_property(PropertyInfo &p_property) const { if (!frames.is_valid()) { return; } - if (property.name == "animation") { - property.hint = PROPERTY_HINT_ENUM; + + if (p_property.name == "animation") { + p_property.hint = PROPERTY_HINT_ENUM; List<StringName> names; frames->get_animation_list(&names); names.sort_custom<StringName::AlphCompare>(); bool current_found = false; + bool is_first_element = true; - for (List<StringName>::Element *E = names.front(); E; E = E->next()) { - if (E->prev()) { - property.hint_string += ","; + for (const StringName &E : names) { + if (!is_first_element) { + p_property.hint_string += ","; + } else { + is_first_element = false; } - property.hint_string += String(E->get()); - if (animation == E->get()) { + p_property.hint_string += String(E); + if (animation == E) { current_found = true; } } if (!current_found) { - if (property.hint_string.is_empty()) { - property.hint_string = String(animation); + if (p_property.hint_string.is_empty()) { + p_property.hint_string = String(animation); } else { - property.hint_string = String(animation) + "," + property.hint_string; + p_property.hint_string = String(animation) + "," + p_property.hint_string; } } + return; } - if (property.name == "frame") { - property.hint = PROPERTY_HINT_RANGE; + if (p_property.name == "frame") { + if (playing) { + p_property.usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY; + return; + } + + p_property.hint = PROPERTY_HINT_RANGE; if (frames->has_animation(animation) && frames->get_frame_count(animation) > 0) { - property.hint_string = "0," + itos(frames->get_frame_count(animation) - 1) + ",1"; + p_property.hint_string = "0," + itos(frames->get_frame_count(animation) - 1) + ",1"; } else { // Avoid an error, `hint_string` is required for `PROPERTY_HINT_RANGE`. - property.hint_string = "0,0,1"; + p_property.hint_string = "0,0,1"; } - property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS; + p_property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS; } } void AnimatedSprite2D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_INTERNAL_PROCESS: { - if (frames.is_null()) { - return; - } - if (!frames->has_animation(animation)) { + if (frames.is_null() || !frames->has_animation(animation)) { return; } - if (frame < 0) { - return; + + double speed = frames->get_animation_speed(animation) * Math::abs(speed_scale); + if (speed == 0) { + return; // Do nothing. } + int last_frame = frames->get_frame_count(animation) - 1; double remaining = get_process_delta_time(); - while (remaining) { - double speed = frames->get_animation_speed(animation) * speed_scale; - if (speed == 0) { - return; // Do nothing. - } - if (timeout <= 0) { timeout = _get_frame_duration(); - int fc = frames->get_frame_count(animation); - if ((!backwards && frame >= fc - 1) || (backwards && frame <= 0)) { - if (frames->get_animation_loop(animation)) { - if (backwards) { - frame = fc - 1; - } else { - frame = 0; - } - - emit_signal(SceneStringNames::get_singleton()->animation_finished); - } else { - if (backwards) { + if (!playing_backwards) { + // Forward. + if (frame >= last_frame) { + if (frames->get_animation_loop(animation)) { frame = 0; - } else { - frame = fc - 1; - } - - if (!is_over) { - is_over = true; emit_signal(SceneStringNames::get_singleton()->animation_finished); + } else { + frame = last_frame; + if (!is_over) { + is_over = true; + emit_signal(SceneStringNames::get_singleton()->animation_finished); + } } + } else { + frame++; } } else { - if (backwards) { - frame--; + // Reversed. + if (frame <= 0) { + if (frames->get_animation_loop(animation)) { + frame = last_frame; + emit_signal(SceneStringNames::get_singleton()->animation_finished); + } else { + frame = 0; + if (!is_over) { + is_over = true; + emit_signal(SceneStringNames::get_singleton()->animation_finished); + } + } } else { - frame++; + frame--; } } - update(); + queue_redraw(); emit_signal(SceneStringNames::get_singleton()->frame_changed); } @@ -214,13 +229,7 @@ void AnimatedSprite2D::_notification(int p_what) { } break; case NOTIFICATION_DRAW: { - if (frames.is_null()) { - return; - } - if (frame < 0) { - return; - } - if (!frames->has_animation(animation)) { + if (frames.is_null() || !frames->has_animation(animation)) { return; } @@ -256,14 +265,15 @@ void AnimatedSprite2D::_notification(int p_what) { void AnimatedSprite2D::set_sprite_frames(const Ref<SpriteFrames> &p_frames) { if (frames.is_valid()) { - frames->disconnect("changed", callable_mp(this, &AnimatedSprite2D::_res_changed)); + frames->disconnect(SceneStringNames::get_singleton()->changed, callable_mp(this, &AnimatedSprite2D::_res_changed)); } + frames = p_frames; if (frames.is_valid()) { - frames->connect("changed", callable_mp(this, &AnimatedSprite2D::_res_changed)); + frames->connect(SceneStringNames::get_singleton()->changed, callable_mp(this, &AnimatedSprite2D::_res_changed)); } - if (!frames.is_valid()) { + if (frames.is_null()) { frame = 0; } else { set_frame(frame); @@ -271,7 +281,7 @@ void AnimatedSprite2D::set_sprite_frames(const Ref<SpriteFrames> &p_frames) { notify_property_list_changed(); _reset_timeout(); - update(); + queue_redraw(); update_configuration_warnings(); } @@ -280,7 +290,7 @@ Ref<SpriteFrames> AnimatedSprite2D::get_sprite_frames() const { } void AnimatedSprite2D::set_frame(int p_frame) { - if (!frames.is_valid()) { + if (frames.is_null()) { return; } @@ -301,7 +311,7 @@ void AnimatedSprite2D::set_frame(int p_frame) { frame = p_frame; _reset_timeout(); - update(); + queue_redraw(); emit_signal(SceneStringNames::get_singleton()->frame_changed); } @@ -311,11 +321,16 @@ int AnimatedSprite2D::get_frame() const { } void AnimatedSprite2D::set_speed_scale(double p_speed_scale) { + if (speed_scale == p_speed_scale) { + return; + } + double elapsed = _get_frame_duration() - timeout; - speed_scale = MAX(p_speed_scale, 0.0f); + speed_scale = p_speed_scale; + playing_backwards = signbit(speed_scale) != backwards; - // We adapt the timeout so that the animation speed adapts as soon as the speed scale is changed + // We adapt the timeout so that the animation speed adapts as soon as the speed scale is changed. _reset_timeout(); timeout -= elapsed; } @@ -326,7 +341,7 @@ double AnimatedSprite2D::get_speed_scale() const { void AnimatedSprite2D::set_centered(bool p_center) { centered = p_center; - update(); + queue_redraw(); item_rect_changed(); } @@ -336,7 +351,7 @@ bool AnimatedSprite2D::is_centered() const { void AnimatedSprite2D::set_offset(const Point2 &p_offset) { offset = p_offset; - update(); + queue_redraw(); item_rect_changed(); } @@ -346,7 +361,7 @@ Point2 AnimatedSprite2D::get_offset() const { void AnimatedSprite2D::set_flip_h(bool p_flip) { hflip = p_flip; - update(); + queue_redraw(); } bool AnimatedSprite2D::is_flipped_h() const { @@ -355,7 +370,7 @@ bool AnimatedSprite2D::is_flipped_h() const { void AnimatedSprite2D::set_flip_v(bool p_flip) { vflip = p_flip; - update(); + queue_redraw(); } bool AnimatedSprite2D::is_flipped_v() const { @@ -365,7 +380,7 @@ bool AnimatedSprite2D::is_flipped_v() const { void AnimatedSprite2D::_res_changed() { set_frame(frame); - update(); + queue_redraw(); } void AnimatedSprite2D::set_playing(bool p_playing) { @@ -375,18 +390,20 @@ void AnimatedSprite2D::set_playing(bool p_playing) { playing = p_playing; _reset_timeout(); set_process_internal(playing); + notify_property_list_changed(); } bool AnimatedSprite2D::is_playing() const { return playing; } -void AnimatedSprite2D::play(const StringName &p_animation, const bool p_backwards) { +void AnimatedSprite2D::play(const StringName &p_animation, bool p_backwards) { backwards = p_backwards; + playing_backwards = signbit(speed_scale) != backwards; if (p_animation) { set_animation(p_animation); - if (frames.is_valid() && backwards && get_frame() == 0) { + if (frames.is_valid() && playing_backwards && get_frame() == 0) { set_frame(frames->get_frame_count(p_animation) - 1); } } @@ -401,7 +418,7 @@ void AnimatedSprite2D::stop() { double AnimatedSprite2D::_get_frame_duration() { if (frames.is_valid() && frames->has_animation(animation)) { - double speed = frames->get_animation_speed(animation) * speed_scale; + double speed = frames->get_animation_speed(animation) * Math::abs(speed_scale); if (speed > 0) { return 1.0 / speed; } @@ -430,18 +447,18 @@ void AnimatedSprite2D::set_animation(const StringName &p_animation) { _reset_timeout(); set_frame(0); notify_property_list_changed(); - update(); + queue_redraw(); } StringName AnimatedSprite2D::get_animation() const { return animation; } -TypedArray<String> AnimatedSprite2D::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray AnimatedSprite2D::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (frames.is_null()) { - warnings.push_back(RTR("A SpriteFrames resource must be created or set in the \"Frames\" property in order for AnimatedSprite to display frames.")); + warnings.push_back(RTR("A SpriteFrames resource must be created or set in the \"Frames\" property in order for AnimatedSprite2D to display frames.")); } return warnings; diff --git a/scene/2d/animated_sprite_2d.h b/scene/2d/animated_sprite_2d.h index 3a41f810dc..11c4adb816 100644 --- a/scene/2d/animated_sprite_2d.h +++ b/scene/2d/animated_sprite_2d.h @@ -39,6 +39,7 @@ class AnimatedSprite2D : public Node2D { Ref<SpriteFrames> frames; bool playing = false; + bool playing_backwards = false; bool backwards = false; StringName animation = "default"; int frame = 0; @@ -62,7 +63,7 @@ class AnimatedSprite2D : public Node2D { protected: static void _bind_methods(); void _notification(int p_what); - virtual void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; public: #ifdef TOOLS_ENABLED @@ -81,7 +82,7 @@ public: void set_sprite_frames(const Ref<SpriteFrames> &p_frames); Ref<SpriteFrames> get_sprite_frames() const; - void play(const StringName &p_animation = StringName(), const bool p_backwards = false); + void play(const StringName &p_animation = StringName(), bool p_backwards = false); void stop(); void set_playing(bool p_playing); @@ -108,10 +109,10 @@ public: void set_flip_v(bool p_flip); bool is_flipped_v() const; - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; AnimatedSprite2D(); }; -#endif // ANIMATED_SPRITE_H +#endif // ANIMATED_SPRITE_2D_H diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp index dfc1016c84..b3f80b5e43 100644 --- a/scene/2d/area_2d.cpp +++ b/scene/2d/area_2d.cpp @@ -184,8 +184,8 @@ void Area2D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, i E->value.rc = 0; E->value.in_tree = node && node->is_inside_tree(); if (node) { - node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area2D::_body_enter_tree), make_binds(objid)); - node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area2D::_body_exit_tree), make_binds(objid)); + node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area2D::_body_enter_tree).bind(objid)); + node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area2D::_body_exit_tree).bind(objid)); if (E->value.in_tree) { emit_signal(SceneStringNames::get_singleton()->body_entered, node); } @@ -277,8 +277,8 @@ void Area2D::_area_inout(int p_status, const RID &p_area, ObjectID p_instance, i E->value.rc = 0; E->value.in_tree = node && node->is_inside_tree(); if (node) { - node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area2D::_area_enter_tree), make_binds(objid)); - node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area2D::_area_exit_tree), make_binds(objid)); + node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area2D::_area_enter_tree).bind(objid)); + node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area2D::_area_exit_tree).bind(objid)); if (E->value.in_tree) { emit_signal(SceneStringNames::get_singleton()->area_entered, node); } @@ -426,39 +426,49 @@ bool Area2D::is_monitorable() const { } TypedArray<Node2D> Area2D::get_overlapping_bodies() const { - ERR_FAIL_COND_V_MSG(!monitoring, Array(), "Can't find overlapping bodies when monitoring is off."); TypedArray<Node2D> ret; + ERR_FAIL_COND_V_MSG(!monitoring, ret, "Can't find overlapping bodies when monitoring is off."); ret.resize(body_map.size()); int idx = 0; for (const KeyValue<ObjectID, BodyState> &E : body_map) { Object *obj = ObjectDB::get_instance(E.key); - if (!obj) { - ret.resize(ret.size() - 1); //ops - } else { - ret[idx++] = obj; + if (obj) { + ret[idx] = obj; + idx++; } } + ret.resize(idx); return ret; } TypedArray<Area2D> Area2D::get_overlapping_areas() const { - ERR_FAIL_COND_V_MSG(!monitoring, Array(), "Can't find overlapping bodies when monitoring is off."); TypedArray<Area2D> ret; + ERR_FAIL_COND_V_MSG(!monitoring, ret, "Can't find overlapping areas when monitoring is off."); ret.resize(area_map.size()); int idx = 0; for (const KeyValue<ObjectID, AreaState> &E : area_map) { Object *obj = ObjectDB::get_instance(E.key); - if (!obj) { - ret.resize(ret.size() - 1); //ops - } else { - ret[idx++] = obj; + if (obj) { + ret[idx] = obj; + idx++; } } + ret.resize(idx); return ret; } +bool Area2D::has_overlapping_bodies() const { + ERR_FAIL_COND_V_MSG(!monitoring, false, "Can't find overlapping bodies when monitoring is off."); + return !body_map.is_empty(); +} + +bool Area2D::has_overlapping_areas() const { + ERR_FAIL_COND_V_MSG(!monitoring, false, "Can't find overlapping areas when monitoring is off."); + return !area_map.is_empty(); +} + bool Area2D::overlaps_area(Node *p_area) const { ERR_FAIL_NULL_V(p_area, false); HashMap<ObjectID, AreaState>::ConstIterator E = area_map.find(p_area->get_instance_id()); @@ -498,8 +508,8 @@ StringName Area2D::get_audio_bus_name() const { return "Master"; } -void Area2D::_validate_property(PropertyInfo &property) const { - if (property.name == "audio_bus_name") { +void Area2D::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "audio_bus_name") { String options; for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) { if (i > 0) { @@ -509,28 +519,28 @@ void Area2D::_validate_property(PropertyInfo &property) const { options += name; } - property.hint_string = options; - } else if (property.name.begins_with("gravity") && property.name != "gravity_space_override") { + p_property.hint_string = options; + } else if (p_property.name.begins_with("gravity") && p_property.name != "gravity_space_override") { if (gravity_space_override == SPACE_OVERRIDE_DISABLED) { - property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } else { if (gravity_is_point) { - if (property.name == "gravity_direction") { - property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + if (p_property.name == "gravity_direction") { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } } else { - if (property.name.begins_with("gravity_point_")) { - property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + if (p_property.name.begins_with("gravity_point_")) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } } } - } else if (property.name.begins_with("linear_damp") && property.name != "linear_damp_space_override") { + } else if (p_property.name.begins_with("linear_damp") && p_property.name != "linear_damp_space_override") { if (linear_damp_space_override == SPACE_OVERRIDE_DISABLED) { - property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } - } else if (property.name.begins_with("angular_damp") && property.name != "angular_damp_space_override") { + } else if (p_property.name.begins_with("angular_damp") && p_property.name != "angular_damp_space_override") { if (angular_damp_space_override == SPACE_OVERRIDE_DISABLED) { - property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } } } @@ -578,6 +588,9 @@ void Area2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_overlapping_bodies"), &Area2D::get_overlapping_bodies); ClassDB::bind_method(D_METHOD("get_overlapping_areas"), &Area2D::get_overlapping_areas); + ClassDB::bind_method(D_METHOD("has_overlapping_bodies"), &Area2D::has_overlapping_bodies); + ClassDB::bind_method(D_METHOD("has_overlapping_areas"), &Area2D::has_overlapping_areas); + ClassDB::bind_method(D_METHOD("overlaps_body", "body"), &Area2D::overlaps_body); ClassDB::bind_method(D_METHOD("overlaps_area", "area"), &Area2D::overlaps_area); @@ -607,7 +620,7 @@ void Area2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_point_distance_scale", PROPERTY_HINT_RANGE, "0,1024,0.001,or_greater,exp"), "set_gravity_point_distance_scale", "get_gravity_point_distance_scale"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "gravity_point_center", PROPERTY_HINT_NONE, "suffix:px"), "set_gravity_point_center", "get_gravity_point_center"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "gravity_direction"), "set_gravity_direction", "get_gravity_direction"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity", PROPERTY_HINT_RANGE, U"-4096,4096,0.001,or_lesser,or_greater,suffix:px/s\u00B2"), "set_gravity", "get_gravity"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity", PROPERTY_HINT_RANGE, U"-4096,4096,0.001,or_less,or_greater,suffix:px/s\u00B2"), "set_gravity", "get_gravity"); ADD_GROUP("Linear Damp", "linear_damp_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "linear_damp_space_override", PROPERTY_HINT_ENUM, "Disabled,Combine,Combine-Replace,Replace,Replace-Combine", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_linear_damp_space_override_mode", "get_linear_damp_space_override_mode"); diff --git a/scene/2d/area_2d.h b/scene/2d/area_2d.h index a584420ced..f70f1dfc3d 100644 --- a/scene/2d/area_2d.h +++ b/scene/2d/area_2d.h @@ -135,7 +135,7 @@ private: protected: void _notification(int p_what); static void _bind_methods(); - void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; public: void set_gravity_space_override_mode(SpaceOverride p_mode); @@ -180,6 +180,9 @@ public: TypedArray<Node2D> get_overlapping_bodies() const; //function for script TypedArray<Area2D> get_overlapping_areas() const; //function for script + bool has_overlapping_bodies() const; + bool has_overlapping_areas() const; + bool overlaps_area(Node *p_area) const; bool overlaps_body(Node *p_body) const; diff --git a/scene/2d/audio_listener_2d.h b/scene/2d/audio_listener_2d.h index 172d388efc..5cd1bfb251 100644 --- a/scene/2d/audio_listener_2d.h +++ b/scene/2d/audio_listener_2d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef LISTENER_2D_H -#define LISTENER_2D_H +#ifndef AUDIO_LISTENER_2D_H +#define AUDIO_LISTENER_2D_H #include "scene/2d/node_2d.h" #include "scene/main/window.h" @@ -56,4 +56,4 @@ public: bool is_current() const; }; -#endif +#endif // AUDIO_LISTENER_2D_H diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp index eaab58c4ae..85ec745aee 100644 --- a/scene/2d/audio_stream_player_2d.cpp +++ b/scene/2d/audio_stream_player_2d.cpp @@ -43,13 +43,18 @@ void AudioStreamPlayer2D::_notification(int p_what) { if (autoplay && !Engine::get_singleton()->is_editor_hint()) { play(); } + set_stream_paused(false); } break; case NOTIFICATION_EXIT_TREE: { - stop(); + set_stream_paused(true); AudioServer::get_singleton()->remove_listener_changed_callback(_listener_changed_cb, this); } break; + case NOTIFICATION_PREDELETE: { + stop(); + } break; + case NOTIFICATION_PAUSED: { if (!can_process()) { // Node can't process so we start fading out to silence. @@ -69,7 +74,7 @@ void AudioStreamPlayer2D::_notification(int p_what) { if (setplay.get() >= 0 && stream.is_valid()) { active.set(); - Ref<AudioStreamPlayback> new_playback = stream->instance_playback(); + Ref<AudioStreamPlayback> new_playback = stream->instantiate_playback(); ERR_FAIL_COND_MSG(new_playback.is_null(), "Failed to instantiate playback."); AudioServer::get_singleton()->start_playback_stream(new_playback, _get_actual_bus(), volume_vector, setplay.get(), pitch_scale); stream_playbacks.push_back(new_playback); @@ -185,7 +190,7 @@ void AudioStreamPlayer2D::_update_panning() { } float multiplier = Math::pow(1.0f - dist / max_distance, attenuation); - multiplier *= Math::db2linear(volume_db); //also apply player volume! + multiplier *= Math::db_to_linear(volume_db); //also apply player volume! float pan = relative_to_listener.x / screen_size.x; // Don't let the panning effect extend (too far) beyond the screen. @@ -323,8 +328,8 @@ bool AudioStreamPlayer2D::_is_active() const { return active.is_set(); } -void AudioStreamPlayer2D::_validate_property(PropertyInfo &property) const { - if (property.name == "bus") { +void AudioStreamPlayer2D::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "bus") { String options; for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) { if (i > 0) { @@ -334,7 +339,7 @@ void AudioStreamPlayer2D::_validate_property(PropertyInfo &property) const { options += name; } - property.hint_string = options; + p_property.hint_string = options; } } diff --git a/scene/2d/audio_stream_player_2d.h b/scene/2d/audio_stream_player_2d.h index a22782fe44..616d7fdb60 100644 --- a/scene/2d/audio_stream_player_2d.h +++ b/scene/2d/audio_stream_player_2d.h @@ -85,7 +85,7 @@ private: float cached_global_panning_strength = 1.0f; protected: - void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; void _notification(int p_what); static void _bind_methods(); @@ -135,4 +135,4 @@ public: ~AudioStreamPlayer2D(); }; -#endif +#endif // AUDIO_STREAM_PLAYER_2D_H diff --git a/scene/2d/back_buffer_copy.h b/scene/2d/back_buffer_copy.h index 4e7cac1f3e..1f2d5810b0 100644 --- a/scene/2d/back_buffer_copy.h +++ b/scene/2d/back_buffer_copy.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef BACKBUFFERCOPY_H -#define BACKBUFFERCOPY_H +#ifndef BACK_BUFFER_COPY_H +#define BACK_BUFFER_COPY_H #include "scene/2d/node_2d.h" @@ -71,4 +71,4 @@ public: VARIANT_ENUM_CAST(BackBufferCopy::CopyMode); -#endif // BACKBUFFERCOPY_H +#endif // BACK_BUFFER_COPY_H diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp index 2616d1f09e..e120aa871b 100644 --- a/scene/2d/camera_2d.cpp +++ b/scene/2d/camera_2d.cpp @@ -39,7 +39,7 @@ void Camera2D::_update_scroll() { } if (Engine::get_singleton()->is_editor_hint()) { - update(); //will just be drawn + queue_redraw(); //will just be drawn return; } @@ -172,7 +172,7 @@ Transform2D Camera2D::get_camera_transform() { Point2 screen_offset = (anchor_mode == ANCHOR_MODE_DRAG_CENTER ? (screen_size * 0.5 * zoom_scale) : Point2()); real_t angle = get_global_rotation(); - if (rotating) { + if (!ignore_rotation) { screen_offset = screen_offset.rotated(angle); } @@ -204,7 +204,7 @@ Transform2D Camera2D::get_camera_transform() { Transform2D xform; xform.scale_basis(zoom_scale); - if (rotating) { + if (!ignore_rotation) { xform.set_rotation(angle); } xform.set_origin(screen_rect.position); @@ -247,8 +247,8 @@ void Camera2D::_notification(int p_what) { add_to_group(canvas_group_name); _update_process_callback(); - _update_scroll(); first = true; + _update_scroll(); } break; case NOTIFICATION_EXIT_TREE: { @@ -363,15 +363,15 @@ Camera2D::AnchorMode Camera2D::get_anchor_mode() const { return anchor_mode; } -void Camera2D::set_rotating(bool p_rotating) { - rotating = p_rotating; +void Camera2D::set_ignore_rotation(bool p_ignore) { + ignore_rotation = p_ignore; Point2 old_smoothed_camera_pos = smoothed_camera_pos; _update_scroll(); smoothed_camera_pos = old_smoothed_camera_pos; } -bool Camera2D::is_rotating() const { - return rotating; +bool Camera2D::is_ignoring_rotation() const { + return ignore_rotation; } void Camera2D::set_process_callback(Camera2DProcessCallback p_mode) { @@ -392,7 +392,7 @@ void Camera2D::_make_current(Object *p_which) { current = true; if (is_inside_tree()) { get_viewport()->_camera_2d_set(this); - update(); + queue_redraw(); } } else { current = false; @@ -400,7 +400,7 @@ void Camera2D::_make_current(Object *p_which) { if (get_viewport()->get_camera_2d() == this) { get_viewport()->_camera_2d_set(nullptr); } - update(); + queue_redraw(); } } } @@ -439,7 +439,9 @@ void Camera2D::clear_current() { void Camera2D::set_limit(Side p_side, int p_limit) { ERR_FAIL_INDEX((int)p_side, 4); limit[p_side] = p_limit; - update(); + Point2 old_smoothed_camera_pos = smoothed_camera_pos; + _update_scroll(); + smoothed_camera_pos = old_smoothed_camera_pos; } int Camera2D::get_limit(Side p_side) const { @@ -459,7 +461,7 @@ bool Camera2D::is_limit_smoothing_enabled() const { void Camera2D::set_drag_margin(Side p_side, real_t p_drag_margin) { ERR_FAIL_INDEX((int)p_side, 4); drag_margin[p_side] = p_drag_margin; - update(); + queue_redraw(); } real_t Camera2D::get_drag_margin(Side p_side) const { @@ -623,7 +625,7 @@ Node *Camera2D::get_custom_viewport() const { void Camera2D::set_screen_drawing_enabled(bool enable) { screen_drawing_enabled = enable; #ifdef TOOLS_ENABLED - update(); + queue_redraw(); #endif } @@ -634,7 +636,7 @@ bool Camera2D::is_screen_drawing_enabled() const { void Camera2D::set_limit_drawing_enabled(bool enable) { limit_drawing_enabled = enable; #ifdef TOOLS_ENABLED - update(); + queue_redraw(); #endif } @@ -645,7 +647,7 @@ bool Camera2D::is_limit_drawing_enabled() const { void Camera2D::set_margin_drawing_enabled(bool enable) { margin_drawing_enabled = enable; #ifdef TOOLS_ENABLED - update(); + queue_redraw(); #endif } @@ -653,9 +655,9 @@ bool Camera2D::is_margin_drawing_enabled() const { return margin_drawing_enabled; } -void Camera2D::_validate_property(PropertyInfo &property) const { - if (!smoothing_enabled && property.name == "smoothing_speed") { - property.usage = PROPERTY_USAGE_NO_EDITOR; +void Camera2D::_validate_property(PropertyInfo &p_property) const { + if (!smoothing_enabled && p_property.name == "smoothing_speed") { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } } @@ -666,8 +668,8 @@ void Camera2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_anchor_mode", "anchor_mode"), &Camera2D::set_anchor_mode); ClassDB::bind_method(D_METHOD("get_anchor_mode"), &Camera2D::get_anchor_mode); - ClassDB::bind_method(D_METHOD("set_rotating", "rotating"), &Camera2D::set_rotating); - ClassDB::bind_method(D_METHOD("is_rotating"), &Camera2D::is_rotating); + ClassDB::bind_method(D_METHOD("set_ignore_rotation", "ignore"), &Camera2D::set_ignore_rotation); + ClassDB::bind_method(D_METHOD("is_ignoring_rotation"), &Camera2D::is_ignoring_rotation); ClassDB::bind_method(D_METHOD("_update_scroll"), &Camera2D::_update_scroll); @@ -699,8 +701,8 @@ void Camera2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_drag_margin", "margin", "drag_margin"), &Camera2D::set_drag_margin); ClassDB::bind_method(D_METHOD("get_drag_margin", "margin"), &Camera2D::get_drag_margin); - ClassDB::bind_method(D_METHOD("get_camera_position"), &Camera2D::get_camera_position); - ClassDB::bind_method(D_METHOD("get_camera_screen_center"), &Camera2D::get_camera_screen_center); + ClassDB::bind_method(D_METHOD("get_target_position"), &Camera2D::get_camera_position); + ClassDB::bind_method(D_METHOD("get_screen_center_position"), &Camera2D::get_camera_screen_center); ClassDB::bind_method(D_METHOD("set_zoom", "zoom"), &Camera2D::set_zoom); ClassDB::bind_method(D_METHOD("get_zoom"), &Camera2D::get_zoom); @@ -731,7 +733,7 @@ void Camera2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset", PROPERTY_HINT_NONE, "suffix:px"), "set_offset", "get_offset"); ADD_PROPERTY(PropertyInfo(Variant::INT, "anchor_mode", PROPERTY_HINT_ENUM, "Fixed TopLeft,Drag Center"), "set_anchor_mode", "get_anchor_mode"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rotating"), "set_rotating", "is_rotating"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_rotation"), "set_ignore_rotation", "is_ignoring_rotation"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "current"), "set_current", "is_current"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "zoom", PROPERTY_HINT_LINK), "set_zoom", "get_zoom"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "custom_viewport", PROPERTY_HINT_RESOURCE_TYPE, "Viewport", PROPERTY_USAGE_NONE), "set_custom_viewport", "get_custom_viewport"); diff --git a/scene/2d/camera_2d.h b/scene/2d/camera_2d.h index 294a6fcb80..1ce622388c 100644 --- a/scene/2d/camera_2d.h +++ b/scene/2d/camera_2d.h @@ -63,7 +63,7 @@ protected: Vector2 zoom = Vector2(1, 1); Vector2 zoom_scale = Vector2(1, 1); AnchorMode anchor_mode = ANCHOR_MODE_DRAG_CENTER; - bool rotating = false; + bool ignore_rotation = true; bool current = false; real_t smoothing = 5.0; bool smoothing_enabled = false; @@ -100,7 +100,7 @@ protected: void _notification(int p_what); static void _bind_methods(); - void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; public: void set_offset(const Vector2 &p_offset); @@ -109,8 +109,8 @@ public: void set_anchor_mode(AnchorMode p_anchor_mode); AnchorMode get_anchor_mode() const; - void set_rotating(bool p_rotating); - bool is_rotating() const; + void set_ignore_rotation(bool p_ignore); + bool is_ignoring_rotation() const; void set_limit(Side p_side, int p_limit); int get_limit(Side p_side) const; diff --git a/scene/2d/canvas_group.cpp b/scene/2d/canvas_group.cpp index bbf3fff0ad..d4182f85a7 100644 --- a/scene/2d/canvas_group.cpp +++ b/scene/2d/canvas_group.cpp @@ -36,7 +36,7 @@ void CanvasGroup::set_fit_margin(real_t p_fit_margin) { fit_margin = p_fit_margin; RS::get_singleton()->canvas_item_set_canvas_group_mode(get_canvas_item(), RS::CANVAS_GROUP_MODE_TRANSPARENT, clear_margin, true, fit_margin, use_mipmaps); - update(); + queue_redraw(); } real_t CanvasGroup::get_fit_margin() const { @@ -49,7 +49,7 @@ void CanvasGroup::set_clear_margin(real_t p_clear_margin) { clear_margin = p_clear_margin; RS::get_singleton()->canvas_item_set_canvas_group_mode(get_canvas_item(), RS::CANVAS_GROUP_MODE_TRANSPARENT, clear_margin, true, clear_margin, use_mipmaps); - update(); + queue_redraw(); } real_t CanvasGroup::get_clear_margin() const { diff --git a/scene/2d/canvas_group.h b/scene/2d/canvas_group.h index 9bc1772ee2..557e7e23dc 100644 --- a/scene/2d/canvas_group.h +++ b/scene/2d/canvas_group.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef CANVASGROUP_H -#define CANVASGROUP_H +#ifndef CANVAS_GROUP_H +#define CANVAS_GROUP_H #include "scene/2d/node_2d.h" @@ -56,4 +56,4 @@ public: ~CanvasGroup(); }; -#endif // CANVASGROUP_H +#endif // CANVAS_GROUP_H diff --git a/scene/2d/canvas_modulate.cpp b/scene/2d/canvas_modulate.cpp index 61a17a4845..330afe4a1b 100644 --- a/scene/2d/canvas_modulate.cpp +++ b/scene/2d/canvas_modulate.cpp @@ -78,8 +78,8 @@ Color CanvasModulate::get_color() const { return color; } -TypedArray<String> CanvasModulate::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray CanvasModulate::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (is_visible_in_tree() && is_inside_tree()) { List<Node *> nodes; diff --git a/scene/2d/canvas_modulate.h b/scene/2d/canvas_modulate.h index ec37449f8f..4f522ca1c7 100644 --- a/scene/2d/canvas_modulate.h +++ b/scene/2d/canvas_modulate.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef CANVASMODULATE_H -#define CANVASMODULATE_H +#ifndef CANVAS_MODULATE_H +#define CANVAS_MODULATE_H #include "scene/2d/node_2d.h" @@ -46,10 +46,10 @@ public: void set_color(const Color &p_color); Color get_color() const; - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; CanvasModulate(); ~CanvasModulate(); }; -#endif // CANVASMODULATE_H +#endif // CANVAS_MODULATE_H diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp index a8c12f4893..23948c2fd3 100644 --- a/scene/2d/collision_object_2d.cpp +++ b/scene/2d/collision_object_2d.cpp @@ -186,6 +186,17 @@ bool CollisionObject2D::get_collision_mask_value(int p_layer_number) const { return get_collision_mask() & (1 << (p_layer_number - 1)); } +void CollisionObject2D::set_collision_priority(real_t p_priority) { + collision_priority = p_priority; + if (!area) { + PhysicsServer2D::get_singleton()->body_set_collision_priority(get_rid(), p_priority); + } +} + +real_t CollisionObject2D::get_collision_priority() const { + return collision_priority; +} + void CollisionObject2D::set_disable_mode(DisableMode p_mode) { if (disable_mode == p_mode) { return; @@ -350,8 +361,8 @@ void CollisionObject2D::get_shape_owners(List<uint32_t> *r_owners) { } } -Array CollisionObject2D::_get_shape_owners() { - Array ret; +PackedInt32Array CollisionObject2D::_get_shape_owners() { + PackedInt32Array ret; for (const KeyValue<uint32_t, ShapeData> &E : shapes) { ret.push_back(E.key); } @@ -554,8 +565,8 @@ void CollisionObject2D::_update_pickable() { } } -TypedArray<String> CollisionObject2D::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray CollisionObject2D::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (shapes.is_empty()) { warnings.push_back(RTR("This node has no shape, so it can't collide or interact with other objects.\nConsider adding a CollisionShape2D or CollisionPolygon2D as a child to define its shape.")); @@ -574,6 +585,8 @@ void CollisionObject2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_collision_layer_value", "layer_number"), &CollisionObject2D::get_collision_layer_value); ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &CollisionObject2D::set_collision_mask_value); ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &CollisionObject2D::get_collision_mask_value); + ClassDB::bind_method(D_METHOD("set_collision_priority", "priority"), &CollisionObject2D::set_collision_priority); + ClassDB::bind_method(D_METHOD("get_collision_priority"), &CollisionObject2D::get_collision_priority); ClassDB::bind_method(D_METHOD("set_disable_mode", "mode"), &CollisionObject2D::set_disable_mode); ClassDB::bind_method(D_METHOD("get_disable_mode"), &CollisionObject2D::get_disable_mode); ClassDB::bind_method(D_METHOD("set_pickable", "enabled"), &CollisionObject2D::set_pickable); @@ -599,6 +612,10 @@ void CollisionObject2D::_bind_methods() { ClassDB::bind_method(D_METHOD("shape_find_owner", "shape_index"), &CollisionObject2D::shape_find_owner); GDVIRTUAL_BIND(_input_event, "viewport", "event", "shape_idx"); + GDVIRTUAL_BIND(_mouse_enter); + GDVIRTUAL_BIND(_mouse_exit); + GDVIRTUAL_BIND(_mouse_shape_enter, "shape_idx"); + GDVIRTUAL_BIND(_mouse_shape_exit, "shape_idx"); ADD_SIGNAL(MethodInfo("input_event", PropertyInfo(Variant::OBJECT, "viewport", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"), PropertyInfo(Variant::INT, "shape_idx"))); ADD_SIGNAL(MethodInfo("mouse_entered")); @@ -611,6 +628,7 @@ void CollisionObject2D::_bind_methods() { ADD_GROUP("Collision", "collision_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_layer", "get_collision_layer"); ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_mask", "get_collision_mask"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_priority"), "set_collision_priority", "get_collision_priority"); ADD_GROUP("Input", "input_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "input_pickable"), "set_pickable", "is_pickable"); diff --git a/scene/2d/collision_object_2d.h b/scene/2d/collision_object_2d.h index 997afee6c4..6b778d1b60 100644 --- a/scene/2d/collision_object_2d.h +++ b/scene/2d/collision_object_2d.h @@ -49,6 +49,7 @@ public: private: uint32_t collision_layer = 1; uint32_t collision_mask = 1; + real_t collision_priority = 1.0; bool area = false; RID rid; @@ -102,6 +103,10 @@ protected: void set_body_mode(PhysicsServer2D::BodyMode p_mode); GDVIRTUAL3(_input_event, Viewport *, Ref<InputEvent>, int) + GDVIRTUAL0(_mouse_enter) + GDVIRTUAL0(_mouse_exit) + GDVIRTUAL1(_mouse_shape_enter, int) + GDVIRTUAL1(_mouse_shape_exit, int) public: void set_collision_layer(uint32_t p_layer); uint32_t get_collision_layer() const; @@ -115,13 +120,16 @@ public: void set_collision_mask_value(int p_layer_number, bool p_value); bool get_collision_mask_value(int p_layer_number) const; + void set_collision_priority(real_t p_priority); + real_t get_collision_priority() const; + void set_disable_mode(DisableMode p_mode); DisableMode get_disable_mode() const; uint32_t create_shape_owner(Object *p_owner); void remove_shape_owner(uint32_t owner); void get_shape_owners(List<uint32_t> *r_owners); - Array _get_shape_owners(); + PackedInt32Array _get_shape_owners(); void shape_owner_set_transform(uint32_t p_owner, const Transform2D &p_transform); Transform2D shape_owner_get_transform(uint32_t p_owner) const; @@ -149,7 +157,7 @@ public: void set_pickable(bool p_enabled); bool is_pickable() const; - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; _FORCE_INLINE_ RID get_rid() const { return rid; } diff --git a/scene/2d/collision_polygon_2d.cpp b/scene/2d/collision_polygon_2d.cpp index 8df29851e5..d06461b566 100644 --- a/scene/2d/collision_polygon_2d.cpp +++ b/scene/2d/collision_polygon_2d.cpp @@ -198,7 +198,7 @@ void CollisionPolygon2D::set_polygon(const Vector<Point2> &p_polygon) { _build_polygon(); _update_in_shape_owner(); } - update(); + queue_redraw(); update_configuration_warnings(); } @@ -213,7 +213,7 @@ void CollisionPolygon2D::set_build_mode(BuildMode p_mode) { _build_polygon(); _update_in_shape_owner(); } - update(); + queue_redraw(); update_configuration_warnings(); } @@ -235,11 +235,11 @@ bool CollisionPolygon2D::_edit_is_selected_on_click(const Point2 &p_point, doubl } #endif -TypedArray<String> CollisionPolygon2D::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray CollisionPolygon2D::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (!Object::cast_to<CollisionObject2D>(get_parent())) { - warnings.push_back(RTR("CollisionPolygon2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidDynamicBody2D, CharacterBody2D, etc. to give them a shape.")); + warnings.push_back(RTR("CollisionPolygon2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidBody2D, CharacterBody2D, etc. to give them a shape.")); } int polygon_count = polygon.size(); @@ -264,7 +264,7 @@ TypedArray<String> CollisionPolygon2D::get_configuration_warnings() const { void CollisionPolygon2D::set_disabled(bool p_disabled) { disabled = p_disabled; - update(); + queue_redraw(); if (parent) { parent->shape_owner_set_disabled(owner_id, p_disabled); } @@ -276,7 +276,7 @@ bool CollisionPolygon2D::is_disabled() const { void CollisionPolygon2D::set_one_way_collision(bool p_enable) { one_way_collision = p_enable; - update(); + queue_redraw(); if (parent) { parent->shape_owner_set_one_way_collision(owner_id, p_enable); } diff --git a/scene/2d/collision_polygon_2d.h b/scene/2d/collision_polygon_2d.h index e18022ab7e..066f7271c6 100644 --- a/scene/2d/collision_polygon_2d.h +++ b/scene/2d/collision_polygon_2d.h @@ -77,7 +77,7 @@ public: void set_polygon(const Vector<Point2> &p_polygon); Vector<Point2> get_polygon() const; - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; void set_disabled(bool p_disabled); bool is_disabled() const; diff --git a/scene/2d/collision_shape_2d.cpp b/scene/2d/collision_shape_2d.cpp index 9c0c26f6d9..7e167a3807 100644 --- a/scene/2d/collision_shape_2d.cpp +++ b/scene/2d/collision_shape_2d.cpp @@ -36,7 +36,7 @@ #include "scene/resources/convex_polygon_shape_2d.h" void CollisionShape2D::_shape_changed() { - update(); + queue_redraw(); } void CollisionShape2D::_update_in_shape_owner(bool p_xform_only) { @@ -140,7 +140,7 @@ void CollisionShape2D::set_shape(const Ref<Shape2D> &p_shape) { shape->disconnect("changed", callable_mp(this, &CollisionShape2D::_shape_changed)); } shape = p_shape; - update(); + queue_redraw(); if (parent) { parent->shape_owner_clear_shapes(owner_id); if (shape.is_valid()) { @@ -168,11 +168,11 @@ bool CollisionShape2D::_edit_is_selected_on_click(const Point2 &p_point, double return shape->_edit_is_selected_on_click(p_point, p_tolerance); } -TypedArray<String> CollisionShape2D::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray CollisionShape2D::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (!Object::cast_to<CollisionObject2D>(get_parent())) { - warnings.push_back(RTR("CollisionShape2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidDynamicBody2D, CharacterBody2D, etc. to give them a shape.")); + warnings.push_back(RTR("CollisionShape2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidBody2D, CharacterBody2D, etc. to give them a shape.")); } if (!shape.is_valid()) { warnings.push_back(RTR("A shape must be provided for CollisionShape2D to function. Please create a shape resource for it!")); @@ -192,7 +192,7 @@ TypedArray<String> CollisionShape2D::get_configuration_warnings() const { void CollisionShape2D::set_disabled(bool p_disabled) { disabled = p_disabled; - update(); + queue_redraw(); if (parent) { parent->shape_owner_set_disabled(owner_id, p_disabled); } @@ -204,7 +204,7 @@ bool CollisionShape2D::is_disabled() const { void CollisionShape2D::set_one_way_collision(bool p_enable) { one_way_collision = p_enable; - update(); + queue_redraw(); if (parent) { parent->shape_owner_set_one_way_collision(owner_id, p_enable); } diff --git a/scene/2d/collision_shape_2d.h b/scene/2d/collision_shape_2d.h index dbc81e8424..5e50420e00 100644 --- a/scene/2d/collision_shape_2d.h +++ b/scene/2d/collision_shape_2d.h @@ -72,7 +72,7 @@ public: void set_one_way_collision_margin(real_t p_margin); real_t get_one_way_collision_margin() const; - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; CollisionShape2D(); }; diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp index 4155d0797f..eece90190b 100644 --- a/scene/2d/cpu_particles_2d.cpp +++ b/scene/2d/cpu_particles_2d.cpp @@ -32,7 +32,7 @@ #include "core/core_string_names.h" #include "scene/2d/gpu_particles_2d.h" -#include "scene/resources/particles_material.h" +#include "scene/resources/particle_process_material.h" void CPUParticles2D::set_emitting(bool p_emitting) { if (emitting == p_emitting) { @@ -211,13 +211,13 @@ void CPUParticles2D::set_texture(const Ref<Texture2D> &p_texture) { texture->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CPUParticles2D::_texture_changed)); } - update(); + queue_redraw(); _update_mesh_texture(); } void CPUParticles2D::_texture_changed() { if (texture.is_valid()) { - update(); + queue_redraw(); _update_mesh_texture(); } } @@ -242,8 +242,8 @@ bool CPUParticles2D::get_fractional_delta() const { return fractional_delta; } -TypedArray<String> CPUParticles2D::get_configuration_warnings() const { - TypedArray<String> warnings = Node2D::get_configuration_warnings(); +PackedStringArray CPUParticles2D::get_configuration_warnings() const { + PackedStringArray warnings = Node2D::get_configuration_warnings(); CanvasItemMaterial *mat = Object::cast_to<CanvasItemMaterial>(get_material().ptr()); @@ -503,32 +503,32 @@ bool CPUParticles2D::get_split_scale() { return split_scale; } -void CPUParticles2D::_validate_property(PropertyInfo &property) const { - if (property.name == "emission_sphere_radius" && (emission_shape != EMISSION_SHAPE_SPHERE && emission_shape != EMISSION_SHAPE_SPHERE_SURFACE)) { - property.usage = PROPERTY_USAGE_NONE; +void CPUParticles2D::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "emission_sphere_radius" && (emission_shape != EMISSION_SHAPE_SPHERE && emission_shape != EMISSION_SHAPE_SPHERE_SURFACE)) { + p_property.usage = PROPERTY_USAGE_NONE; } - if (property.name == "emission_rect_extents" && emission_shape != EMISSION_SHAPE_RECTANGLE) { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name == "emission_rect_extents" && emission_shape != EMISSION_SHAPE_RECTANGLE) { + p_property.usage = PROPERTY_USAGE_NONE; } - if ((property.name == "emission_point_texture" || property.name == "emission_color_texture") && (emission_shape < EMISSION_SHAPE_POINTS)) { - property.usage = PROPERTY_USAGE_NONE; + if ((p_property.name == "emission_point_texture" || p_property.name == "emission_color_texture") && (emission_shape < EMISSION_SHAPE_POINTS)) { + p_property.usage = PROPERTY_USAGE_NONE; } - if (property.name == "emission_normals" && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name == "emission_normals" && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) { + p_property.usage = PROPERTY_USAGE_NONE; } - if (property.name == "emission_points" && emission_shape != EMISSION_SHAPE_POINTS && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name == "emission_points" && emission_shape != EMISSION_SHAPE_POINTS && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) { + p_property.usage = PROPERTY_USAGE_NONE; } - if (property.name == "emission_colors" && emission_shape != EMISSION_SHAPE_POINTS && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name == "emission_colors" && emission_shape != EMISSION_SHAPE_POINTS && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) { + p_property.usage = PROPERTY_USAGE_NONE; } - if (property.name.begins_with("scale_curve_") && !split_scale) { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name.begins_with("scale_curve_") && !split_scale) { + p_property.usage = PROPERTY_USAGE_NONE; } } @@ -556,7 +556,7 @@ static real_t rand_from_seed(uint32_t &seed) { void CPUParticles2D::_update_internal() { if (particles.size() == 0 || !is_visible_in_tree()) { - _set_redraw(false); + _set_do_redraw(false); return; } @@ -567,7 +567,7 @@ void CPUParticles2D::_update_internal() { inactive_time += delta; if (inactive_time > lifetime * 1.2) { set_process_internal(false); - _set_redraw(false); + _set_do_redraw(false); //reset variables time = 0; @@ -577,7 +577,7 @@ void CPUParticles2D::_update_internal() { return; } } - _set_redraw(true); + _set_do_redraw(true); if (time == 0 && pre_process_time > 0.0) { double frame_time; @@ -719,17 +719,17 @@ void CPUParticles2D::_particles_process(double p_delta) { /*real_t tex_linear_velocity = 0; if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { - tex_linear_velocity = curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY]->interpolate(0); + tex_linear_velocity = curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY]->sample(0); }*/ real_t tex_angle = 0.0; if (curve_parameters[PARAM_ANGLE].is_valid()) { - tex_angle = curve_parameters[PARAM_ANGLE]->interpolate(tv); + tex_angle = curve_parameters[PARAM_ANGLE]->sample(tv); } real_t tex_anim_offset = 0.0; if (curve_parameters[PARAM_ANGLE].is_valid()) { - tex_anim_offset = curve_parameters[PARAM_ANGLE]->interpolate(tv); + tex_anim_offset = curve_parameters[PARAM_ANGLE]->sample(tv); } p.seed = Math::rand(); @@ -745,12 +745,12 @@ void CPUParticles2D::_particles_process(double p_delta) { p.start_color_rand = Color(1, 1, 1, 1); } - real_t angle1_rad = direction.angle() + Math::deg2rad((Math::randf() * 2.0 - 1.0) * spread); + real_t angle1_rad = direction.angle() + Math::deg_to_rad((Math::randf() * 2.0 - 1.0) * spread); Vector2 rot = Vector2(Math::cos(angle1_rad), Math::sin(angle1_rad)); p.velocity = rot * Math::lerp(parameters_min[PARAM_INITIAL_LINEAR_VELOCITY], parameters_max[PARAM_INITIAL_LINEAR_VELOCITY], (real_t)Math::randf()); real_t base_angle = tex_angle * Math::lerp(parameters_min[PARAM_ANGLE], parameters_max[PARAM_ANGLE], p.angle_rand); - p.rotation = Math::deg2rad(base_angle); + p.rotation = Math::deg_to_rad(base_angle); p.custom[0] = 0.0; // unused p.custom[1] = 0.0; // phase [0..1] @@ -825,51 +825,51 @@ void CPUParticles2D::_particles_process(double p_delta) { real_t tex_linear_velocity = 1.0; if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { - tex_linear_velocity = curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY]->interpolate(tv); + tex_linear_velocity = curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY]->sample(tv); } real_t tex_orbit_velocity = 1.0; if (curve_parameters[PARAM_ORBIT_VELOCITY].is_valid()) { - tex_orbit_velocity = curve_parameters[PARAM_ORBIT_VELOCITY]->interpolate(tv); + tex_orbit_velocity = curve_parameters[PARAM_ORBIT_VELOCITY]->sample(tv); } real_t tex_angular_velocity = 1.0; if (curve_parameters[PARAM_ANGULAR_VELOCITY].is_valid()) { - tex_angular_velocity = curve_parameters[PARAM_ANGULAR_VELOCITY]->interpolate(tv); + tex_angular_velocity = curve_parameters[PARAM_ANGULAR_VELOCITY]->sample(tv); } real_t tex_linear_accel = 1.0; if (curve_parameters[PARAM_LINEAR_ACCEL].is_valid()) { - tex_linear_accel = curve_parameters[PARAM_LINEAR_ACCEL]->interpolate(tv); + tex_linear_accel = curve_parameters[PARAM_LINEAR_ACCEL]->sample(tv); } real_t tex_tangential_accel = 1.0; if (curve_parameters[PARAM_TANGENTIAL_ACCEL].is_valid()) { - tex_tangential_accel = curve_parameters[PARAM_TANGENTIAL_ACCEL]->interpolate(tv); + tex_tangential_accel = curve_parameters[PARAM_TANGENTIAL_ACCEL]->sample(tv); } real_t tex_radial_accel = 1.0; if (curve_parameters[PARAM_RADIAL_ACCEL].is_valid()) { - tex_radial_accel = curve_parameters[PARAM_RADIAL_ACCEL]->interpolate(tv); + tex_radial_accel = curve_parameters[PARAM_RADIAL_ACCEL]->sample(tv); } real_t tex_damping = 1.0; if (curve_parameters[PARAM_DAMPING].is_valid()) { - tex_damping = curve_parameters[PARAM_DAMPING]->interpolate(tv); + tex_damping = curve_parameters[PARAM_DAMPING]->sample(tv); } real_t tex_angle = 1.0; if (curve_parameters[PARAM_ANGLE].is_valid()) { - tex_angle = curve_parameters[PARAM_ANGLE]->interpolate(tv); + tex_angle = curve_parameters[PARAM_ANGLE]->sample(tv); } real_t tex_anim_speed = 1.0; if (curve_parameters[PARAM_ANIM_SPEED].is_valid()) { - tex_anim_speed = curve_parameters[PARAM_ANIM_SPEED]->interpolate(tv); + tex_anim_speed = curve_parameters[PARAM_ANIM_SPEED]->sample(tv); } real_t tex_anim_offset = 1.0; if (curve_parameters[PARAM_ANIM_OFFSET].is_valid()) { - tex_anim_offset = curve_parameters[PARAM_ANIM_OFFSET]->interpolate(tv); + tex_anim_offset = curve_parameters[PARAM_ANIM_OFFSET]->sample(tv); } Vector2 force = gravity; @@ -890,7 +890,7 @@ void CPUParticles2D::_particles_process(double p_delta) { real_t orbit_amount = tex_orbit_velocity * Math::lerp(parameters_min[PARAM_ORBIT_VELOCITY], parameters_max[PARAM_ORBIT_VELOCITY], rand_from_seed(alt_seed)); if (orbit_amount != 0.0) { real_t ang = orbit_amount * local_delta * Math_TAU; - // Not sure why the ParticlesMaterial code uses a clockwise rotation matrix, + // Not sure why the ParticleProcessMaterial code uses a clockwise rotation matrix, // but we use -ang here to reproduce its behavior. Transform2D rot = Transform2D(-ang, Vector2()); p.transform[2] -= diff; @@ -912,7 +912,7 @@ void CPUParticles2D::_particles_process(double p_delta) { } real_t base_angle = (tex_angle)*Math::lerp(parameters_min[PARAM_ANGLE], parameters_max[PARAM_ANGLE], p.angle_rand); base_angle += p.custom[1] * lifetime * tex_angular_velocity * Math::lerp(parameters_min[PARAM_ANGULAR_VELOCITY], parameters_max[PARAM_ANGULAR_VELOCITY], rand_from_seed(alt_seed)); - p.rotation = Math::deg2rad(base_angle); //angle + p.rotation = Math::deg_to_rad(base_angle); //angle p.custom[2] = tex_anim_offset * Math::lerp(parameters_min[PARAM_ANIM_OFFSET], parameters_max[PARAM_ANIM_OFFSET], p.anim_offset_rand) + tv * tex_anim_speed * Math::lerp(parameters_min[PARAM_ANIM_SPEED], parameters_max[PARAM_ANIM_SPEED], rand_from_seed(alt_seed)); } //apply color @@ -921,18 +921,18 @@ void CPUParticles2D::_particles_process(double p_delta) { Vector2 tex_scale = Vector2(1.0, 1.0); if (split_scale) { if (scale_curve_x.is_valid()) { - tex_scale.x = scale_curve_x->interpolate(tv); + tex_scale.x = scale_curve_x->sample(tv); } else { tex_scale.x = 1.0; } if (scale_curve_y.is_valid()) { - tex_scale.y = scale_curve_y->interpolate(tv); + tex_scale.y = scale_curve_y->sample(tv); } else { tex_scale.y = 1.0; } } else { if (curve_parameters[PARAM_SCALE].is_valid()) { - real_t tmp_scale = curve_parameters[PARAM_SCALE]->interpolate(tv); + real_t tmp_scale = curve_parameters[PARAM_SCALE]->sample(tv); tex_scale.x = tmp_scale; tex_scale.y = tmp_scale; } @@ -940,7 +940,7 @@ void CPUParticles2D::_particles_process(double p_delta) { real_t tex_hue_variation = 0.0; if (curve_parameters[PARAM_HUE_VARIATION].is_valid()) { - tex_hue_variation = curve_parameters[PARAM_HUE_VARIATION]->interpolate(tv); + tex_hue_variation = curve_parameters[PARAM_HUE_VARIATION]->sample(tv); } real_t hue_rot_angle = (tex_hue_variation)*Math_TAU * Math::lerp(parameters_min[PARAM_HUE_VARIATION], parameters_max[PARAM_HUE_VARIATION], p.hue_rot_rand); @@ -1062,16 +1062,16 @@ void CPUParticles2D::_update_particle_data_buffer() { } } -void CPUParticles2D::_set_redraw(bool p_redraw) { - if (redraw == p_redraw) { +void CPUParticles2D::_set_do_redraw(bool p_do_redraw) { + if (do_redraw == p_do_redraw) { return; } - redraw = p_redraw; + do_redraw = p_do_redraw; { MutexLock lock(update_mutex); - if (redraw) { + if (do_redraw) { RS::get_singleton()->connect("frame_pre_draw", callable_mp(this, &CPUParticles2D::_update_render_thread)); RS::get_singleton()->canvas_item_set_update_when_visible(get_canvas_item(), true); @@ -1086,7 +1086,7 @@ void CPUParticles2D::_set_redraw(bool p_redraw) { } } - update(); // redraw to update render list + queue_redraw(); // redraw to update render list } void CPUParticles2D::_update_render_thread() { @@ -1102,7 +1102,7 @@ void CPUParticles2D::_notification(int p_what) { } break; case NOTIFICATION_EXIT_TREE: { - _set_redraw(false); + _set_do_redraw(false); } break; case NOTIFICATION_DRAW: { @@ -1111,7 +1111,7 @@ void CPUParticles2D::_notification(int p_what) { _update_internal(); } - if (!redraw) { + if (!do_redraw) { return; // don't add to render list } @@ -1184,7 +1184,7 @@ void CPUParticles2D::convert_from_particles(Node *p_particles) { set_material(mat); } - Ref<ParticlesMaterial> material = particles->get_process_material(); + Ref<ParticleProcessMaterial> material = particles->get_process_material(); if (material.is_null()) { return; } @@ -1205,14 +1205,14 @@ void CPUParticles2D::convert_from_particles(Node *p_particles) { set_color_initial_ramp(gti->get_gradient()); } - set_particle_flag(PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY, material->get_particle_flag(ParticlesMaterial::PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY)); + set_particle_flag(PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY, material->get_particle_flag(ParticleProcessMaterial::PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY)); set_emission_shape(EmissionShape(material->get_emission_shape())); set_emission_sphere_radius(material->get_emission_sphere_radius()); Vector2 rect_extents = Vector2(material->get_emission_box_extents().x, material->get_emission_box_extents().y); set_emission_rect_extents(rect_extents); - Ref<CurveXYZTexture> scale3D = material->get_param_texture(ParticlesMaterial::PARAM_SCALE); + Ref<CurveXYZTexture> scale3D = material->get_param_texture(ParticleProcessMaterial::PARAM_SCALE); if (scale3D.is_valid()) { split_scale = true; scale_curve_x = scale3D->get_curve_x(); @@ -1222,14 +1222,14 @@ void CPUParticles2D::convert_from_particles(Node *p_particles) { set_gravity(gravity); set_lifetime_randomness(material->get_lifetime_randomness()); -#define CONVERT_PARAM(m_param) \ - set_param_min(m_param, material->get_param_min(ParticlesMaterial::m_param)); \ - { \ - Ref<CurveTexture> ctex = material->get_param_texture(ParticlesMaterial::m_param); \ - if (ctex.is_valid()) \ - set_param_curve(m_param, ctex->get_curve()); \ - } \ - set_param_max(m_param, material->get_param_max(ParticlesMaterial::m_param)); +#define CONVERT_PARAM(m_param) \ + set_param_min(m_param, material->get_param_min(ParticleProcessMaterial::m_param)); \ + { \ + Ref<CurveTexture> ctex = material->get_param_texture(ParticleProcessMaterial::m_param); \ + if (ctex.is_valid()) \ + set_param_curve(m_param, ctex->get_curve()); \ + } \ + set_param_max(m_param, material->get_param_max(ParticleProcessMaterial::m_param)); CONVERT_PARAM(PARAM_INITIAL_LINEAR_VELOCITY); CONVERT_PARAM(PARAM_ANGULAR_VELOCITY); @@ -1383,32 +1383,32 @@ void CPUParticles2D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "initial_velocity_min", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater,suffix:px/s"), "set_param_min", "get_param_min", PARAM_INITIAL_LINEAR_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "initial_velocity_max", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater,suffix:px/s"), "set_param_max", "get_param_max", PARAM_INITIAL_LINEAR_VELOCITY); ADD_GROUP("Angular Velocity", "angular_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_velocity_min", PROPERTY_HINT_RANGE, "-720,720,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_ANGULAR_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_velocity_max", PROPERTY_HINT_RANGE, "-720,720,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_ANGULAR_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_velocity_min", PROPERTY_HINT_RANGE, "-720,720,0.01,or_less,or_greater"), "set_param_min", "get_param_min", PARAM_ANGULAR_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_velocity_max", PROPERTY_HINT_RANGE, "-720,720,0.01,or_less,or_greater"), "set_param_max", "get_param_max", PARAM_ANGULAR_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angular_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANGULAR_VELOCITY); ADD_GROUP("Orbit Velocity", "orbit_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "orbit_velocity_min", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_ORBIT_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "orbit_velocity_max", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_ORBIT_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "orbit_velocity_min", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_less,or_greater"), "set_param_min", "get_param_min", PARAM_ORBIT_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "orbit_velocity_max", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_less,or_greater"), "set_param_max", "get_param_max", PARAM_ORBIT_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "orbit_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ORBIT_VELOCITY); ADD_GROUP("Linear Accel", "linear_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_accel_min", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_LINEAR_ACCEL); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_accel_max", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_LINEAR_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_accel_min", PROPERTY_HINT_RANGE, "-100,100,0.01,or_less,or_greater"), "set_param_min", "get_param_min", PARAM_LINEAR_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_accel_max", PROPERTY_HINT_RANGE, "-100,100,0.01,or_less,or_greater"), "set_param_max", "get_param_max", PARAM_LINEAR_ACCEL); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "linear_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_LINEAR_ACCEL); ADD_GROUP("Radial Accel", "radial_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "radial_accel_min", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_RADIAL_ACCEL); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "radial_accel_max", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_RADIAL_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "radial_accel_min", PROPERTY_HINT_RANGE, "-100,100,0.01,or_less,or_greater"), "set_param_min", "get_param_min", PARAM_RADIAL_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "radial_accel_max", PROPERTY_HINT_RANGE, "-100,100,0.01,or_less,or_greater"), "set_param_max", "get_param_max", PARAM_RADIAL_ACCEL); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "radial_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_RADIAL_ACCEL); ADD_GROUP("Tangential Accel", "tangential_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "tangential_accel_min", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_TANGENTIAL_ACCEL); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "tangential_accel_max", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_TANGENTIAL_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "tangential_accel_min", PROPERTY_HINT_RANGE, "-100,100,0.01,or_less,or_greater"), "set_param_min", "get_param_min", PARAM_TANGENTIAL_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "tangential_accel_max", PROPERTY_HINT_RANGE, "-100,100,0.01,or_less,or_greater"), "set_param_max", "get_param_max", PARAM_TANGENTIAL_ACCEL); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "tangential_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_TANGENTIAL_ACCEL); ADD_GROUP("Damping", ""); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "damping_min", PROPERTY_HINT_RANGE, "0,100,0.01"), "set_param_min", "get_param_min", PARAM_DAMPING); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "damping_max", PROPERTY_HINT_RANGE, "0,100,0.01"), "set_param_max", "get_param_max", PARAM_DAMPING); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "damping_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_DAMPING); ADD_GROUP("Angle", ""); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle_min", PROPERTY_HINT_RANGE, "-720,720,0.1,or_lesser,or_greater,degrees"), "set_param_min", "get_param_min", PARAM_ANGLE); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle_max", PROPERTY_HINT_RANGE, "-720,720,0.1,or_lesser,or_greater,degrees"), "set_param_max", "get_param_max", PARAM_ANGLE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle_min", PROPERTY_HINT_RANGE, "-720,720,0.1,or_less,or_greater,degrees"), "set_param_min", "get_param_min", PARAM_ANGLE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle_max", PROPERTY_HINT_RANGE, "-720,720,0.1,or_less,or_greater,degrees"), "set_param_max", "get_param_max", PARAM_ANGLE); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angle_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANGLE); ADD_GROUP("Scale", ""); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "scale_amount_min", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param_min", "get_param_min", PARAM_SCALE); @@ -1428,8 +1428,8 @@ void CPUParticles2D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "hue_variation_max", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_param_max", "get_param_max", PARAM_HUE_VARIATION); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "hue_variation_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_HUE_VARIATION); ADD_GROUP("Animation", "anim_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_min", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater,or_lesser"), "set_param_min", "get_param_min", PARAM_ANIM_SPEED); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_max", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater,or_lesser"), "set_param_max", "get_param_max", PARAM_ANIM_SPEED); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_min", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater,or_less"), "set_param_min", "get_param_min", PARAM_ANIM_SPEED); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_max", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater,or_less"), "set_param_max", "get_param_max", PARAM_ANIM_SPEED); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_speed_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANIM_SPEED); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_min", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_min", "get_param_min", PARAM_ANIM_OFFSET); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_max", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_max", "get_param_max", PARAM_ANIM_OFFSET); @@ -1470,7 +1470,7 @@ CPUParticles2D::CPUParticles2D() { set_emitting(true); set_amount(8); - set_use_local_coordinates(true); + set_use_local_coordinates(false); set_param_min(PARAM_INITIAL_LINEAR_VELOCITY, 0); set_param_min(PARAM_ANGULAR_VELOCITY, 0); diff --git a/scene/2d/cpu_particles_2d.h b/scene/2d/cpu_particles_2d.h index 51d58723b4..ea735411a8 100644 --- a/scene/2d/cpu_particles_2d.h +++ b/scene/2d/cpu_particles_2d.h @@ -102,7 +102,7 @@ private: double inactive_time = 0.0; double frame_remainder = 0.0; int cycle = 0; - bool redraw = false; + bool do_redraw = false; RID mesh; RID multimesh; @@ -186,14 +186,14 @@ private: void _update_mesh_texture(); - void _set_redraw(bool p_redraw); + void _set_do_redraw(bool p_do_redraw); void _texture_changed(); protected: static void _bind_methods(); void _notification(int p_what); - virtual void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; public: void set_emitting(bool p_emitting); @@ -282,7 +282,7 @@ public: void set_gravity(const Vector2 &p_gravity); Vector2 get_gravity() const; - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; void restart(); diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp index a869cf2525..18f709f241 100644 --- a/scene/2d/gpu_particles_2d.cpp +++ b/scene/2d/gpu_particles_2d.cpp @@ -30,7 +30,8 @@ #include "gpu_particles_2d.h" -#include "scene/resources/particles_material.h" +#include "core/core_string_names.h" +#include "scene/resources/particle_process_material.h" #ifdef TOOLS_ENABLED #include "core/config/engine.h" @@ -99,7 +100,7 @@ void GPUParticles2D::set_visibility_rect(const Rect2 &p_visibility_rect) { RS::get_singleton()->particles_set_custom_aabb(particles, aabb); - update(); + queue_redraw(); } void GPUParticles2D::set_use_local_coordinates(bool p_enable) { @@ -123,10 +124,10 @@ void GPUParticles2D::_update_particle_emission_transform() { void GPUParticles2D::set_process_material(const Ref<Material> &p_material) { process_material = p_material; - Ref<ParticlesMaterial> pm = p_material; - if (pm.is_valid() && !pm->get_particle_flag(ParticlesMaterial::PARTICLE_FLAG_DISABLE_Z) && pm->get_gravity() == Vector3(0, -9.8, 0)) { + Ref<ParticleProcessMaterial> pm = p_material; + if (pm.is_valid() && !pm->get_particle_flag(ParticleProcessMaterial::PARTICLE_FLAG_DISABLE_Z) && pm->get_gravity() == Vector3(0, -9.8, 0)) { // Likely a new (3D) material, modify it to match 2D space - pm->set_particle_flag(ParticlesMaterial::PARTICLE_FLAG_DISABLE_Z, true); + pm->set_particle_flag(ParticleProcessMaterial::PARTICLE_FLAG_DISABLE_Z, true); pm->set_gravity(Vector3(0, 98, 0)); } RID material_rid; @@ -141,7 +142,7 @@ void GPUParticles2D::set_process_material(const Ref<Material> &p_material) { void GPUParticles2D::set_trail_enabled(bool p_enabled) { trail_enabled = p_enabled; RS::get_singleton()->particles_set_trails(particles, trail_enabled, trail_length); - update(); + queue_redraw(); RS::get_singleton()->particles_set_transform_align(particles, p_enabled ? RS::PARTICLES_TRANSFORM_ALIGN_Y_TO_VELOCITY : RS::PARTICLES_TRANSFORM_ALIGN_DISABLED); } @@ -150,7 +151,7 @@ void GPUParticles2D::set_trail_length(double p_seconds) { ERR_FAIL_COND(p_seconds < 0.001); trail_length = p_seconds; RS::get_singleton()->particles_set_trails(particles, trail_enabled, trail_length); - update(); + queue_redraw(); } void GPUParticles2D::set_trail_sections(int p_sections) { @@ -158,7 +159,7 @@ void GPUParticles2D::set_trail_sections(int p_sections) { ERR_FAIL_COND(p_sections > 128); trail_sections = p_sections; - update(); + queue_redraw(); } void GPUParticles2D::set_trail_section_subdivisions(int p_subdivisions) { @@ -166,13 +167,13 @@ void GPUParticles2D::set_trail_section_subdivisions(int p_subdivisions) { ERR_FAIL_COND(p_subdivisions > 1024); trail_section_subdivisions = p_subdivisions; - update(); + queue_redraw(); } #ifdef TOOLS_ENABLED void GPUParticles2D::set_show_visibility_rect(bool p_show_visibility_rect) { show_visibility_rect = p_show_visibility_rect; - update(); + queue_redraw(); } #endif @@ -295,8 +296,8 @@ bool GPUParticles2D::get_interpolate() const { return interpolate; } -TypedArray<String> GPUParticles2D::get_configuration_warnings() const { - TypedArray<String> warnings = Node2D::get_configuration_warnings(); +PackedStringArray GPUParticles2D::get_configuration_warnings() const { + PackedStringArray warnings = Node2D::get_configuration_warnings(); if (RenderingServer::get_singleton()->is_low_end()) { warnings.push_back(RTR("GPU-based particles are not supported by the OpenGL video driver.\nUse the CPUParticles2D node instead. You can use the \"Convert to CPUParticles2D\" option for this purpose.")); @@ -308,10 +309,10 @@ TypedArray<String> GPUParticles2D::get_configuration_warnings() const { CanvasItemMaterial *mat = Object::cast_to<CanvasItemMaterial>(get_material().ptr()); if (get_material().is_null() || (mat && !mat->get_particles_animation())) { - const ParticlesMaterial *process = Object::cast_to<ParticlesMaterial>(process_material.ptr()); + const ParticleProcessMaterial *process = Object::cast_to<ParticleProcessMaterial>(process_material.ptr()); if (process && - (process->get_param_max(ParticlesMaterial::PARAM_ANIM_SPEED) != 0.0 || process->get_param_max(ParticlesMaterial::PARAM_ANIM_OFFSET) != 0.0 || - process->get_param_texture(ParticlesMaterial::PARAM_ANIM_SPEED).is_valid() || process->get_param_texture(ParticlesMaterial::PARAM_ANIM_OFFSET).is_valid())) { + (process->get_param_max(ParticleProcessMaterial::PARAM_ANIM_SPEED) != 0.0 || process->get_param_max(ParticleProcessMaterial::PARAM_ANIM_OFFSET) != 0.0 || + process->get_param_texture(ParticleProcessMaterial::PARAM_ANIM_SPEED).is_valid() || process->get_param_texture(ParticleProcessMaterial::PARAM_ANIM_OFFSET).is_valid())) { warnings.push_back(RTR("Particles2D animation requires the usage of a CanvasItemMaterial with \"Particles Animation\" enabled.")); } } @@ -331,16 +332,24 @@ Rect2 GPUParticles2D::capture_rect() const { } void GPUParticles2D::set_texture(const Ref<Texture2D> &p_texture) { + if (texture.is_valid()) { + texture->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &GPUParticles2D::_texture_changed)); + } + texture = p_texture; + + if (texture.is_valid()) { + texture->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &GPUParticles2D::_texture_changed)); + } _update_collision_size(); - update(); + queue_redraw(); } Ref<Texture2D> GPUParticles2D::get_texture() const { return texture; } -void GPUParticles2D::_validate_property(PropertyInfo &property) const { +void GPUParticles2D::_validate_property(PropertyInfo &p_property) const { } void GPUParticles2D::emit_particle(const Transform2D &p_transform2d, const Vector2 &p_velocity2d, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) { @@ -363,6 +372,14 @@ void GPUParticles2D::_attach_sub_emitter() { } } +void GPUParticles2D::_texture_changed() { + // Changes to the texture need to trigger an update to make + // the editor redraw the sprite with the updated texture. + if (texture.is_valid()) { + queue_redraw(); + } +} + void GPUParticles2D::set_sub_emitter(const NodePath &p_path) { if (is_inside_tree()) { RS::get_singleton()->particles_set_subemitter(particles, RID()); @@ -480,12 +497,21 @@ void GPUParticles2D::_notification(int p_what) { Vector2(-size.x / 2.0, size.y / 2.0) }; - Vector<Vector2> uvs = { - Vector2(0, 0), - Vector2(1, 0), - Vector2(1, 1), - Vector2(0, 1) - }; + Vector<Vector2> uvs; + AtlasTexture *atlas_texure = Object::cast_to<AtlasTexture>(*texture); + if (atlas_texure && atlas_texure->get_atlas().is_valid()) { + Rect2 region_rect = atlas_texure->get_region(); + Size2 atlas_size = atlas_texure->get_atlas()->get_size(); + uvs.push_back(Vector2(region_rect.position.x / atlas_size.x, region_rect.position.y / atlas_size.y)); + uvs.push_back(Vector2((region_rect.position.x + region_rect.size.x) / atlas_size.x, region_rect.position.y / atlas_size.y)); + uvs.push_back(Vector2((region_rect.position.x + region_rect.size.x) / atlas_size.x, (region_rect.position.y + region_rect.size.y) / atlas_size.y)); + uvs.push_back(Vector2(region_rect.position.x / atlas_size.x, (region_rect.position.y + region_rect.size.y) / atlas_size.y)); + } else { + uvs.push_back(Vector2(0, 0)); + uvs.push_back(Vector2(1, 0)); + uvs.push_back(Vector2(1, 1)); + uvs.push_back(Vector2(0, 1)); + } Vector<int> indices = { 0, 1, 2, 0, 2, 3 }; @@ -625,7 +651,7 @@ void GPUParticles2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "trail_sections", PROPERTY_HINT_RANGE, "2,128,1"), "set_trail_sections", "get_trail_sections"); ADD_PROPERTY(PropertyInfo(Variant::INT, "trail_section_subdivisions", PROPERTY_HINT_RANGE, "1,1024,1"), "set_trail_section_subdivisions", "get_trail_section_subdivisions"); ADD_GROUP("Process Material", "process_"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "process_material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,ParticlesMaterial"), "set_process_material", "get_process_material"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "process_material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,ParticleProcessMaterial"), "set_process_material", "get_process_material"); ADD_GROUP("Textures", ""); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture"); @@ -660,7 +686,7 @@ GPUParticles2D::GPUParticles2D() { set_explosiveness_ratio(0); set_randomness_ratio(0); set_visibility_rect(Rect2(Vector2(-100, -100), Vector2(200, 200))); - set_use_local_coordinates(true); + set_use_local_coordinates(false); set_draw_order(DRAW_ORDER_LIFETIME); set_speed_scale(1); set_fixed_fps(30); diff --git a/scene/2d/gpu_particles_2d.h b/scene/2d/gpu_particles_2d.h index 3c7f4cd9b5..d613b4ef51 100644 --- a/scene/2d/gpu_particles_2d.h +++ b/scene/2d/gpu_particles_2d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef PARTICLES_2D_H -#define PARTICLES_2D_H +#ifndef GPU_PARTICLES_2D_H +#define GPU_PARTICLES_2D_H #include "scene/2d/node_2d.h" @@ -82,9 +82,11 @@ private: void _attach_sub_emitter(); + void _texture_changed(); + protected: static void _bind_methods(); - virtual void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; void _notification(int p_what); void _update_collision_size(); @@ -143,7 +145,7 @@ public: void set_texture(const Ref<Texture2D> &p_texture); Ref<Texture2D> get_texture() const; - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; void set_sub_emitter(const NodePath &p_path); NodePath get_sub_emitter() const; @@ -167,4 +169,4 @@ public: VARIANT_ENUM_CAST(GPUParticles2D::DrawOrder) VARIANT_ENUM_CAST(GPUParticles2D::EmitFlags) -#endif // PARTICLES_2D_H +#endif // GPU_PARTICLES_2D_H diff --git a/scene/2d/joint_2d.cpp b/scene/2d/joint_2d.cpp index 7b9f7e14ca..6000508f36 100644 --- a/scene/2d/joint_2d.cpp +++ b/scene/2d/joint_2d.cpp @@ -202,8 +202,8 @@ bool Joint2D::get_exclude_nodes_from_collision() const { return exclude_from_collision; } -TypedArray<String> Joint2D::get_configuration_warnings() const { - TypedArray<String> warnings = Node2D::get_configuration_warnings(); +PackedStringArray Joint2D::get_configuration_warnings() const { + PackedStringArray warnings = Node2D::get_configuration_warnings(); if (!warning.is_empty()) { warnings.push_back(warning); @@ -267,7 +267,7 @@ void PinJoint2D::_configure_joint(RID p_joint, PhysicsBody2D *body_a, PhysicsBod void PinJoint2D::set_softness(real_t p_softness) { softness = p_softness; - update(); + queue_redraw(); if (is_configured()) { PhysicsServer2D::get_singleton()->pin_joint_set_param(get_joint(), PhysicsServer2D::PIN_JOINT_SOFTNESS, p_softness); } @@ -321,7 +321,7 @@ void GrooveJoint2D::_configure_joint(RID p_joint, PhysicsBody2D *body_a, Physics void GrooveJoint2D::set_length(real_t p_length) { length = p_length; - update(); + queue_redraw(); } real_t GrooveJoint2D::get_length() const { @@ -330,7 +330,7 @@ real_t GrooveJoint2D::get_length() const { void GrooveJoint2D::set_initial_offset(real_t p_initial_offset) { initial_offset = p_initial_offset; - update(); + queue_redraw(); } real_t GrooveJoint2D::get_initial_offset() const { @@ -387,7 +387,7 @@ void DampedSpringJoint2D::_configure_joint(RID p_joint, PhysicsBody2D *body_a, P void DampedSpringJoint2D::set_length(real_t p_length) { length = p_length; - update(); + queue_redraw(); } real_t DampedSpringJoint2D::get_length() const { @@ -396,7 +396,7 @@ real_t DampedSpringJoint2D::get_length() const { void DampedSpringJoint2D::set_rest_length(real_t p_rest_length) { rest_length = p_rest_length; - update(); + queue_redraw(); if (is_configured()) { PhysicsServer2D::get_singleton()->damped_spring_joint_set_param(get_joint(), PhysicsServer2D::DAMPED_SPRING_REST_LENGTH, p_rest_length ? p_rest_length : length); } @@ -408,7 +408,7 @@ real_t DampedSpringJoint2D::get_rest_length() const { void DampedSpringJoint2D::set_stiffness(real_t p_stiffness) { stiffness = p_stiffness; - update(); + queue_redraw(); if (is_configured()) { PhysicsServer2D::get_singleton()->damped_spring_joint_set_param(get_joint(), PhysicsServer2D::DAMPED_SPRING_STIFFNESS, p_stiffness); } @@ -420,7 +420,7 @@ real_t DampedSpringJoint2D::get_stiffness() const { void DampedSpringJoint2D::set_damping(real_t p_damping) { damping = p_damping; - update(); + queue_redraw(); if (is_configured()) { PhysicsServer2D::get_singleton()->damped_spring_joint_set_param(get_joint(), PhysicsServer2D::DAMPED_SPRING_DAMPING, p_damping); } diff --git a/scene/2d/joint_2d.h b/scene/2d/joint_2d.h index e3cd600cbd..8b145be6ae 100644 --- a/scene/2d/joint_2d.h +++ b/scene/2d/joint_2d.h @@ -62,7 +62,7 @@ protected: _FORCE_INLINE_ bool is_configured() const { return configured; } public: - virtual TypedArray<String> get_configuration_warnings() const override; + virtual PackedStringArray get_configuration_warnings() const override; void set_node_a(const NodePath &p_node_a); NodePath get_node_a() const; diff --git a/scene/2d/light_2d.cpp b/scene/2d/light_2d.cpp index 0481a58431..90402260ed 100644 --- a/scene/2d/light_2d.cpp +++ b/scene/2d/light_2d.cpp @@ -227,9 +227,9 @@ real_t Light2D::get_shadow_smooth() const { return shadow_smooth; } -void Light2D::_validate_property(PropertyInfo &property) const { - if (!shadow && (property.name == "shadow_color" || property.name == "shadow_filter" || property.name == "shadow_filter_smooth" || property.name == "shadow_item_cull_mask")) { - property.usage = PROPERTY_USAGE_NO_EDITOR; +void Light2D::_validate_property(PropertyInfo &p_property) const { + if (!shadow && (p_property.name == "shadow_color" || p_property.name == "shadow_filter" || p_property.name == "shadow_filter_smooth" || p_property.name == "shadow_item_cull_mask")) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } } @@ -395,8 +395,8 @@ Vector2 PointLight2D::get_texture_offset() const { return texture_offset; } -TypedArray<String> PointLight2D::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray PointLight2D::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (!texture.is_valid()) { warnings.push_back(RTR("A texture with the shape of the light must be supplied to the \"Texture\" property.")); diff --git a/scene/2d/light_2d.h b/scene/2d/light_2d.h index a84b6516c0..29870923aa 100644 --- a/scene/2d/light_2d.h +++ b/scene/2d/light_2d.h @@ -79,7 +79,7 @@ protected: _FORCE_INLINE_ RID _get_light() const { return canvas_light; } void _notification(int p_what); static void _bind_methods(); - void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; public: void set_enabled(bool p_enabled); @@ -171,7 +171,7 @@ public: void set_texture_scale(real_t p_scale); real_t get_texture_scale() const; - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; PointLight2D(); }; diff --git a/scene/2d/light_occluder_2d.cpp b/scene/2d/light_occluder_2d.cpp index 14188d7120..67e82140e4 100644 --- a/scene/2d/light_occluder_2d.cpp +++ b/scene/2d/light_occluder_2d.cpp @@ -153,7 +153,7 @@ OccluderPolygon2D::~OccluderPolygon2D() { void LightOccluder2D::_poly_changed() { #ifdef DEBUG_ENABLED - update(); + queue_redraw(); #endif } @@ -229,7 +229,7 @@ void LightOccluder2D::set_occluder_polygon(const Ref<OccluderPolygon2D> &p_polyg if (occluder_polygon.is_valid()) { occluder_polygon->connect("changed", callable_mp(this, &LightOccluder2D::_poly_changed)); } - update(); + queue_redraw(); #endif } @@ -246,8 +246,8 @@ int LightOccluder2D::get_occluder_light_mask() const { return mask; } -TypedArray<String> LightOccluder2D::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray LightOccluder2D::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (!occluder_polygon.is_valid()) { warnings.push_back(RTR("An occluder polygon must be set (or drawn) for this occluder to take effect.")); diff --git a/scene/2d/light_occluder_2d.h b/scene/2d/light_occluder_2d.h index 4acfeaf781..ee4d87e54b 100644 --- a/scene/2d/light_occluder_2d.h +++ b/scene/2d/light_occluder_2d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef LIGHTOCCLUDER2D_H -#define LIGHTOCCLUDER2D_H +#ifndef LIGHT_OCCLUDER_2D_H +#define LIGHT_OCCLUDER_2D_H #include "scene/2d/node_2d.h" @@ -105,10 +105,10 @@ public: void set_as_sdf_collision(bool p_enable); bool is_set_as_sdf_collision() const; - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; LightOccluder2D(); ~LightOccluder2D(); }; -#endif // LIGHTOCCLUDER2D_H +#endif // LIGHT_OCCLUDER_2D_H diff --git a/scene/2d/line_2d.cpp b/scene/2d/line_2d.cpp index 06e5cbc97e..6a72280f3d 100644 --- a/scene/2d/line_2d.cpp +++ b/scene/2d/line_2d.cpp @@ -76,7 +76,7 @@ bool Line2D::_edit_is_selected_on_click(const Point2 &p_point, double p_toleranc void Line2D::set_points(const Vector<Vector2> &p_points) { _points = p_points; - update(); + queue_redraw(); } void Line2D::set_width(float p_width) { @@ -84,7 +84,7 @@ void Line2D::set_width(float p_width) { p_width = 0.0; } _width = p_width; - update(); + queue_redraw(); } float Line2D::get_width() const { @@ -104,7 +104,7 @@ void Line2D::set_curve(const Ref<Curve> &p_curve) { _curve->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Line2D::_curve_changed)); } - update(); + queue_redraw(); } Ref<Curve> Line2D::get_curve() const { @@ -118,7 +118,7 @@ Vector<Vector2> Line2D::get_points() const { void Line2D::set_point_position(int i, Vector2 p_pos) { ERR_FAIL_INDEX(i, _points.size()); _points.set(i, p_pos); - update(); + queue_redraw(); } Vector2 Line2D::get_point_position(int i) const { @@ -134,7 +134,7 @@ void Line2D::clear_points() { int count = _points.size(); if (count > 0) { _points.clear(); - update(); + queue_redraw(); } } @@ -144,17 +144,17 @@ void Line2D::add_point(Vector2 p_pos, int p_atpos) { } else { _points.insert(p_atpos, p_pos); } - update(); + queue_redraw(); } void Line2D::remove_point(int i) { _points.remove_at(i); - update(); + queue_redraw(); } void Line2D::set_default_color(Color p_color) { _default_color = p_color; - update(); + queue_redraw(); } Color Line2D::get_default_color() const { @@ -174,7 +174,7 @@ void Line2D::set_gradient(const Ref<Gradient> &p_gradient) { _gradient->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Line2D::_gradient_changed)); } - update(); + queue_redraw(); } Ref<Gradient> Line2D::get_gradient() const { @@ -183,7 +183,7 @@ Ref<Gradient> Line2D::get_gradient() const { void Line2D::set_texture(const Ref<Texture2D> &p_texture) { _texture = p_texture; - update(); + queue_redraw(); } Ref<Texture2D> Line2D::get_texture() const { @@ -192,7 +192,7 @@ Ref<Texture2D> Line2D::get_texture() const { void Line2D::set_texture_mode(const LineTextureMode p_mode) { _texture_mode = p_mode; - update(); + queue_redraw(); } Line2D::LineTextureMode Line2D::get_texture_mode() const { @@ -201,7 +201,7 @@ Line2D::LineTextureMode Line2D::get_texture_mode() const { void Line2D::set_joint_mode(LineJointMode p_mode) { _joint_mode = p_mode; - update(); + queue_redraw(); } Line2D::LineJointMode Line2D::get_joint_mode() const { @@ -210,7 +210,7 @@ Line2D::LineJointMode Line2D::get_joint_mode() const { void Line2D::set_begin_cap_mode(LineCapMode p_mode) { _begin_cap_mode = p_mode; - update(); + queue_redraw(); } Line2D::LineCapMode Line2D::get_begin_cap_mode() const { @@ -219,7 +219,7 @@ Line2D::LineCapMode Line2D::get_begin_cap_mode() const { void Line2D::set_end_cap_mode(LineCapMode p_mode) { _end_cap_mode = p_mode; - update(); + queue_redraw(); } Line2D::LineCapMode Line2D::get_end_cap_mode() const { @@ -239,7 +239,7 @@ void Line2D::set_sharp_limit(float p_limit) { p_limit = 0.f; } _sharp_limit = p_limit; - update(); + queue_redraw(); } float Line2D::get_sharp_limit() const { @@ -248,7 +248,7 @@ float Line2D::get_sharp_limit() const { void Line2D::set_round_precision(int p_precision) { _round_precision = MAX(1, p_precision); - update(); + queue_redraw(); } int Line2D::get_round_precision() const { @@ -257,7 +257,7 @@ int Line2D::get_round_precision() const { void Line2D::set_antialiased(bool p_antialiased) { _antialiased = p_antialiased; - update(); + queue_redraw(); } bool Line2D::get_antialiased() const { @@ -334,11 +334,11 @@ void Line2D::_draw() { } void Line2D::_gradient_changed() { - update(); + queue_redraw(); } void Line2D::_curve_changed() { - update(); + queue_redraw(); } // static @@ -346,13 +346,13 @@ void Line2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_points", "points"), &Line2D::set_points); ClassDB::bind_method(D_METHOD("get_points"), &Line2D::get_points); - ClassDB::bind_method(D_METHOD("set_point_position", "i", "position"), &Line2D::set_point_position); - ClassDB::bind_method(D_METHOD("get_point_position", "i"), &Line2D::get_point_position); + ClassDB::bind_method(D_METHOD("set_point_position", "index", "position"), &Line2D::set_point_position); + ClassDB::bind_method(D_METHOD("get_point_position", "index"), &Line2D::get_point_position); ClassDB::bind_method(D_METHOD("get_point_count"), &Line2D::get_point_count); - 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("add_point", "position", "index"), &Line2D::add_point, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("remove_point", "index"), &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 5322c5a5fe..27c510171a 100644 --- a/scene/2d/line_2d.h +++ b/scene/2d/line_2d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef LINE2D_H -#define LINE2D_H +#ifndef LINE_2D_H +#define LINE_2D_H #include "node_2d.h" @@ -138,4 +138,4 @@ private: bool _antialiased = false; }; -#endif // LINE2D_H +#endif // LINE_2D_H diff --git a/scene/2d/line_builder.cpp b/scene/2d/line_builder.cpp index 25eb9b9851..2bbe88b0e0 100644 --- a/scene/2d/line_builder.cpp +++ b/scene/2d/line_builder.cpp @@ -137,14 +137,14 @@ void LineBuilder::build() { // The line's outer length will be a little higher due to begin and end caps if (begin_cap_mode == Line2D::LINE_CAP_BOX || begin_cap_mode == Line2D::LINE_CAP_ROUND) { if (retrieve_curve) { - total_distance += width * curve->interpolate_baked(0.f) * 0.5f; + total_distance += width * curve->sample_baked(0.f) * 0.5f; } else { total_distance += width * 0.5f; } } if (end_cap_mode == Line2D::LINE_CAP_BOX || end_cap_mode == Line2D::LINE_CAP_ROUND) { if (retrieve_curve) { - total_distance += width * curve->interpolate_baked(1.f) * 0.5f; + total_distance += width * curve->sample_baked(1.f) * 0.5f; } else { total_distance += width * 0.5f; } @@ -160,7 +160,7 @@ void LineBuilder::build() { float uvx1 = 0.f; if (retrieve_curve) { - width_factor = curve->interpolate_baked(0.f); + width_factor = curve->sample_baked(0.f); } pos_up0 += u0 * hw * width_factor; @@ -219,7 +219,7 @@ void LineBuilder::build() { color1 = gradient->get_color_at_offset(current_distance1 / total_distance); } if (retrieve_curve) { - width_factor = curve->interpolate_baked(current_distance1 / total_distance); + width_factor = curve->sample_baked(current_distance1 / total_distance); } Vector2 inner_normal0, inner_normal1; @@ -383,7 +383,7 @@ void LineBuilder::build() { color1 = gradient->get_color(gradient->get_points_count() - 1); } if (retrieve_curve) { - width_factor = curve->interpolate_baked(1.f); + width_factor = curve->sample_baked(1.f); } Vector2 pos_up1 = pos1 + u0 * hw * width_factor; diff --git a/scene/2d/position_2d.cpp b/scene/2d/marker_2d.cpp index cfa4d0401e..d203c58ffd 100644 --- a/scene/2d/position_2d.cpp +++ b/scene/2d/marker_2d.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* position_2d.cpp */ +/* marker_2d.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,9 +28,9 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "position_2d.h" +#include "marker_2d.h" -void Position2D::_draw_cross() { +void Marker2D::_draw_cross() { const real_t extents = get_gizmo_extents(); // Add more points to create a "hard stop" in the color gradient. @@ -50,7 +50,7 @@ void Position2D::_draw_cross() { // Use the axis color which is brighter for the positive axis. // Use a darkened axis color for the negative axis. - // This makes it possible to see in which direction the Position3D node is rotated + // This makes it possible to see in which direction the Marker3D node is rotated // (which can be important depending on how it's used). // Axis colors are taken from `axis_x_color` and `axis_y_color` (defined in `editor/editor_themes.cpp`). const Color color_x = Color(0.96, 0.20, 0.32); @@ -73,20 +73,20 @@ void Position2D::_draw_cross() { } #ifdef TOOLS_ENABLED -Rect2 Position2D::_edit_get_rect() const { +Rect2 Marker2D::_edit_get_rect() const { real_t extents = get_gizmo_extents(); return Rect2(Point2(-extents, -extents), Size2(extents * 2, extents * 2)); } -bool Position2D::_edit_use_rect() const { +bool Marker2D::_edit_use_rect() const { return false; } #endif -void Position2D::_notification(int p_what) { +void Marker2D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { - update(); + queue_redraw(); } break; case NOTIFICATION_DRAW: { @@ -100,21 +100,21 @@ void Position2D::_notification(int p_what) { } } -void Position2D::set_gizmo_extents(real_t p_extents) { +void Marker2D::set_gizmo_extents(real_t p_extents) { gizmo_extents = p_extents; - update(); + queue_redraw(); } -real_t Position2D::get_gizmo_extents() const { +real_t Marker2D::get_gizmo_extents() const { return gizmo_extents; } -void Position2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_gizmo_extents", "extents"), &Position2D::set_gizmo_extents); - ClassDB::bind_method(D_METHOD("get_gizmo_extents"), &Position2D::get_gizmo_extents); +void Marker2D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_gizmo_extents", "extents"), &Marker2D::set_gizmo_extents); + ClassDB::bind_method(D_METHOD("get_gizmo_extents"), &Marker2D::get_gizmo_extents); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gizmo_extents", PROPERTY_HINT_RANGE, "0,1000,0.1,or_greater,suffix:px"), "set_gizmo_extents", "get_gizmo_extents"); } -Position2D::Position2D() { +Marker2D::Marker2D() { } diff --git a/scene/2d/position_2d.h b/scene/2d/marker_2d.h index 99b0266130..e287018dfc 100644 --- a/scene/2d/position_2d.h +++ b/scene/2d/marker_2d.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* position_2d.h */ +/* marker_2d.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,13 +28,13 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef POSITION_2D_H -#define POSITION_2D_H +#ifndef MARKER_2D_H +#define MARKER_2D_H #include "scene/2d/node_2d.h" -class Position2D : public Node2D { - GDCLASS(Position2D, Node2D); +class Marker2D : public Node2D { + GDCLASS(Marker2D, Node2D); real_t gizmo_extents = 10.0; @@ -53,7 +53,7 @@ public: void set_gizmo_extents(real_t p_extents); real_t get_gizmo_extents() const; - Position2D(); + Marker2D(); }; -#endif // POSITION_2D_H +#endif // MARKER_2D_H diff --git a/scene/2d/mesh_instance_2d.cpp b/scene/2d/mesh_instance_2d.cpp index 178addd62d..56099205d4 100644 --- a/scene/2d/mesh_instance_2d.cpp +++ b/scene/2d/mesh_instance_2d.cpp @@ -61,7 +61,7 @@ void MeshInstance2D::_bind_methods() { void MeshInstance2D::set_mesh(const Ref<Mesh> &p_mesh) { mesh = p_mesh; - update(); + queue_redraw(); } Ref<Mesh> MeshInstance2D::get_mesh() const { @@ -73,13 +73,13 @@ void MeshInstance2D::set_texture(const Ref<Texture2D> &p_texture) { return; } texture = p_texture; - update(); + queue_redraw(); emit_signal(SceneStringNames::get_singleton()->texture_changed); } void MeshInstance2D::set_normal_map(const Ref<Texture2D> &p_texture) { normal_map = p_texture; - update(); + queue_redraw(); } Ref<Texture2D> MeshInstance2D::get_normal_map() const { diff --git a/scene/2d/multimesh_instance_2d.cpp b/scene/2d/multimesh_instance_2d.cpp index 8f72ff1757..68d529fd32 100644 --- a/scene/2d/multimesh_instance_2d.cpp +++ b/scene/2d/multimesh_instance_2d.cpp @@ -61,7 +61,7 @@ void MultiMeshInstance2D::_bind_methods() { void MultiMeshInstance2D::set_multimesh(const Ref<MultiMesh> &p_multimesh) { multimesh = p_multimesh; - update(); + queue_redraw(); } Ref<MultiMesh> MultiMeshInstance2D::get_multimesh() const { @@ -73,7 +73,7 @@ void MultiMeshInstance2D::set_texture(const Ref<Texture2D> &p_texture) { return; } texture = p_texture; - update(); + queue_redraw(); emit_signal(SceneStringNames::get_singleton()->texture_changed); } @@ -83,7 +83,7 @@ Ref<Texture2D> MultiMeshInstance2D::get_texture() const { void MultiMeshInstance2D::set_normal_map(const Ref<Texture2D> &p_texture) { normal_map = p_texture; - update(); + queue_redraw(); } Ref<Texture2D> MultiMeshInstance2D::get_normal_map() const { diff --git a/scene/2d/navigation_agent_2d.cpp b/scene/2d/navigation_agent_2d.cpp index a5f7faffef..f077f7f5e6 100644 --- a/scene/2d/navigation_agent_2d.cpp +++ b/scene/2d/navigation_agent_2d.cpp @@ -49,8 +49,8 @@ void NavigationAgent2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_radius", "radius"), &NavigationAgent2D::set_radius); ClassDB::bind_method(D_METHOD("get_radius"), &NavigationAgent2D::get_radius); - ClassDB::bind_method(D_METHOD("set_neighbor_dist", "neighbor_dist"), &NavigationAgent2D::set_neighbor_dist); - ClassDB::bind_method(D_METHOD("get_neighbor_dist"), &NavigationAgent2D::get_neighbor_dist); + ClassDB::bind_method(D_METHOD("set_neighbor_distance", "neighbor_distance"), &NavigationAgent2D::set_neighbor_distance); + ClassDB::bind_method(D_METHOD("get_neighbor_distance"), &NavigationAgent2D::get_neighbor_distance); ClassDB::bind_method(D_METHOD("set_max_neighbors", "max_neighbors"), &NavigationAgent2D::set_max_neighbors); ClassDB::bind_method(D_METHOD("get_max_neighbors"), &NavigationAgent2D::get_max_neighbors); @@ -96,7 +96,7 @@ void NavigationAgent2D::_bind_methods() { ADD_GROUP("Avoidance", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "avoidance_enabled"), "set_avoidance_enabled", "get_avoidance_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.1,500,0.01,suffix:px"), "set_radius", "get_radius"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "neighbor_dist", PROPERTY_HINT_RANGE, "0.1,100000,0.01,suffix:px"), "set_neighbor_dist", "get_neighbor_dist"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "neighbor_distance", PROPERTY_HINT_RANGE, "0.1,100000,0.01,suffix:px"), "set_neighbor_distance", "get_neighbor_distance"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_neighbors", PROPERTY_HINT_RANGE, "1,10000,1"), "set_max_neighbors", "get_max_neighbors"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "time_horizon", PROPERTY_HINT_RANGE, "0.1,10000,0.01,suffix:s"), "set_time_horizon", "get_time_horizon"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_speed", PROPERTY_HINT_RANGE, "0.1,100000,0.01,suffix:px/s"), "set_max_speed", "get_max_speed"); @@ -173,11 +173,18 @@ void NavigationAgent2D::_notification(int p_what) { NavigationAgent2D::NavigationAgent2D() { agent = NavigationServer2D::get_singleton()->agent_create(); - set_neighbor_dist(500.0); + set_neighbor_distance(500.0); set_max_neighbors(10); set_time_horizon(20.0); set_radius(10.0); set_max_speed(200.0); + + // Preallocate query and result objects to improve performance. + navigation_query = Ref<NavigationPathQueryParameters2D>(); + navigation_query.instantiate(); + + navigation_result = Ref<NavigationPathQueryResult2D>(); + navigation_result.instantiate(); } NavigationAgent2D::~NavigationAgent2D() { @@ -275,9 +282,9 @@ void NavigationAgent2D::set_radius(real_t p_radius) { NavigationServer2D::get_singleton()->agent_set_radius(agent, radius); } -void NavigationAgent2D::set_neighbor_dist(real_t p_dist) { - neighbor_dist = p_dist; - NavigationServer2D::get_singleton()->agent_set_neighbor_dist(agent, neighbor_dist); +void NavigationAgent2D::set_neighbor_distance(real_t p_distance) { + neighbor_distance = p_distance; + NavigationServer2D::get_singleton()->agent_set_neighbor_distance(agent, neighbor_distance); } void NavigationAgent2D::set_max_neighbors(int p_count) { @@ -314,6 +321,8 @@ Vector2 NavigationAgent2D::get_target_location() const { Vector2 NavigationAgent2D::get_next_location() { update_navigation(); + + const Vector<Vector2> &navigation_path = navigation_result->get_path(); if (navigation_path.size() == 0) { ERR_FAIL_COND_V_MSG(agent_parent == nullptr, Vector2(), "The agent has no parent."); return agent_parent->get_global_position(); @@ -322,6 +331,10 @@ Vector2 NavigationAgent2D::get_next_location() { } } +const Vector<Vector2> &NavigationAgent2D::get_nav_path() const { + return navigation_result->get_path(); +} + real_t NavigationAgent2D::distance_to_target() const { ERR_FAIL_COND_V_MSG(agent_parent == nullptr, 0.0, "The agent has no parent."); return agent_parent->get_global_position().distance_to(target_location); @@ -342,6 +355,8 @@ bool NavigationAgent2D::is_navigation_finished() { Vector2 NavigationAgent2D::get_final_location() { update_navigation(); + + const Vector<Vector2> &navigation_path = navigation_result->get_path(); if (navigation_path.size() == 0) { return Vector2(); } @@ -368,8 +383,8 @@ void NavigationAgent2D::_avoidance_done(Vector3 p_new_velocity) { emit_signal(SNAME("velocity_computed"), velocity); } -TypedArray<String> NavigationAgent2D::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray NavigationAgent2D::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (!Object::cast_to<Node2D>(get_parent())) { warnings.push_back(RTR("The NavigationAgent2D can be used only under a Node2D inheriting parent node.")); @@ -391,22 +406,24 @@ void NavigationAgent2D::update_navigation() { update_frame_id = Engine::get_singleton()->get_physics_frames(); - Vector2 o = agent_parent->get_global_position(); + Vector2 origin = agent_parent->get_global_position(); bool reload_path = false; if (NavigationServer2D::get_singleton()->agent_is_map_changed(agent)) { reload_path = true; - } else if (navigation_path.size() == 0) { + } else if (navigation_result->get_path().size() == 0) { reload_path = true; } else { // Check if too far from the navigation path if (nav_path_index > 0) { + const Vector<Vector2> &navigation_path = navigation_result->get_path(); + Vector2 segment[2]; segment[0] = navigation_path[nav_path_index - 1]; segment[1] = navigation_path[nav_path_index]; - Vector2 p = Geometry2D::get_closest_point_to_segment(o, segment); - if (o.distance_to(p) >= path_max_distance) { + Vector2 p = Geometry2D::get_closest_point_to_segment(origin, segment); + if (origin.distance_to(p) >= path_max_distance) { // To faraway, reload path reload_path = true; } @@ -414,24 +431,31 @@ void NavigationAgent2D::update_navigation() { } if (reload_path) { + navigation_query->set_start_position(origin); + navigation_query->set_target_position(target_location); + navigation_query->set_navigation_layers(navigation_layers); + if (map_override.is_valid()) { - navigation_path = NavigationServer2D::get_singleton()->map_get_path(map_override, o, target_location, true, navigation_layers); + navigation_query->set_map(map_override); } else { - navigation_path = NavigationServer2D::get_singleton()->map_get_path(agent_parent->get_world_2d()->get_navigation_map(), o, target_location, true, navigation_layers); + navigation_query->set_map(agent_parent->get_world_2d()->get_navigation_map()); } + + NavigationServer2D::get_singleton()->query_path(navigation_query, navigation_result); navigation_finished = false; nav_path_index = 0; emit_signal(SNAME("path_changed")); } - if (navigation_path.size() == 0) { + if (navigation_result->get_path().size() == 0) { return; } // Check if we can advance the navigation path if (navigation_finished == false) { // Advances to the next far away location. - while (o.distance_to(navigation_path[nav_path_index]) < path_desired_distance) { + const Vector<Vector2> &navigation_path = navigation_result->get_path(); + while (origin.distance_to(navigation_path[nav_path_index]) < path_desired_distance) { nav_path_index += 1; if (nav_path_index == navigation_path.size()) { _check_distance_to_target(); @@ -445,7 +469,7 @@ void NavigationAgent2D::update_navigation() { } void NavigationAgent2D::_request_repath() { - navigation_path.clear(); + navigation_result->reset(); target_reached = false; navigation_finished = false; update_frame_id = 0; diff --git a/scene/2d/navigation_agent_2d.h b/scene/2d/navigation_agent_2d.h index 032a15cad2..5abd3c0317 100644 --- a/scene/2d/navigation_agent_2d.h +++ b/scene/2d/navigation_agent_2d.h @@ -34,6 +34,8 @@ #include "scene/main/node.h" class Node2D; +class NavigationPathQueryParameters2D; +class NavigationPathQueryResult2D; class NavigationAgent2D : public Node { GDCLASS(NavigationAgent2D, Node); @@ -50,7 +52,7 @@ class NavigationAgent2D : public Node { real_t path_desired_distance = 1.0; real_t target_desired_distance = 1.0; real_t radius = 0.0; - real_t neighbor_dist = 0.0; + real_t neighbor_distance = 0.0; int max_neighbors = 0; real_t time_horizon = 0.0; real_t max_speed = 0.0; @@ -58,7 +60,8 @@ class NavigationAgent2D : public Node { real_t path_max_distance = 3.0; Vector2 target_location; - Vector<Vector2> navigation_path; + Ref<NavigationPathQueryParameters2D> navigation_query; + Ref<NavigationPathQueryResult2D> navigation_result; int nav_path_index = 0; bool velocity_submitted = false; Vector2 prev_safe_velocity; @@ -110,9 +113,9 @@ public: return radius; } - void set_neighbor_dist(real_t p_dist); - real_t get_neighbor_dist() const { - return neighbor_dist; + void set_neighbor_distance(real_t p_distance); + real_t get_neighbor_distance() const { + return neighbor_distance; } void set_max_neighbors(int p_count); @@ -138,9 +141,7 @@ public: Vector2 get_next_location(); - Vector<Vector2> get_nav_path() const { - return navigation_path; - } + const Vector<Vector2> &get_nav_path() const; int get_nav_path_index() const { return nav_path_index; @@ -155,7 +156,7 @@ public: void set_velocity(Vector2 p_velocity); void _avoidance_done(Vector3 p_new_velocity); - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; private: void update_navigation(); @@ -163,4 +164,4 @@ private: void _check_distance_to_target(); }; -#endif +#endif // NAVIGATION_AGENT_2D_H diff --git a/scene/2d/navigation_link_2d.cpp b/scene/2d/navigation_link_2d.cpp new file mode 100644 index 0000000000..3f7e10eaea --- /dev/null +++ b/scene/2d/navigation_link_2d.cpp @@ -0,0 +1,288 @@ +/*************************************************************************/ +/* navigation_link_2d.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "navigation_link_2d.h" + +#include "core/math/geometry_2d.h" +#include "scene/resources/world_2d.h" +#include "servers/navigation_server_2d.h" +#include "servers/navigation_server_3d.h" + +void NavigationLink2D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &NavigationLink2D::set_enabled); + ClassDB::bind_method(D_METHOD("is_enabled"), &NavigationLink2D::is_enabled); + + ClassDB::bind_method(D_METHOD("set_bidirectional", "bidirectional"), &NavigationLink2D::set_bidirectional); + ClassDB::bind_method(D_METHOD("is_bidirectional"), &NavigationLink2D::is_bidirectional); + + ClassDB::bind_method(D_METHOD("set_navigation_layers", "navigation_layers"), &NavigationLink2D::set_navigation_layers); + ClassDB::bind_method(D_METHOD("get_navigation_layers"), &NavigationLink2D::get_navigation_layers); + + ClassDB::bind_method(D_METHOD("set_navigation_layer_value", "layer_number", "value"), &NavigationLink2D::set_navigation_layer_value); + ClassDB::bind_method(D_METHOD("get_navigation_layer_value", "layer_number"), &NavigationLink2D::get_navigation_layer_value); + + ClassDB::bind_method(D_METHOD("set_start_location", "location"), &NavigationLink2D::set_start_location); + ClassDB::bind_method(D_METHOD("get_start_location"), &NavigationLink2D::get_start_location); + + ClassDB::bind_method(D_METHOD("set_end_location", "location"), &NavigationLink2D::set_end_location); + ClassDB::bind_method(D_METHOD("get_end_location"), &NavigationLink2D::get_end_location); + + ClassDB::bind_method(D_METHOD("set_enter_cost", "enter_cost"), &NavigationLink2D::set_enter_cost); + ClassDB::bind_method(D_METHOD("get_enter_cost"), &NavigationLink2D::get_enter_cost); + + ClassDB::bind_method(D_METHOD("set_travel_cost", "travel_cost"), &NavigationLink2D::set_travel_cost); + ClassDB::bind_method(D_METHOD("get_travel_cost"), &NavigationLink2D::get_travel_cost); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bidirectional"), "set_bidirectional", "is_bidirectional"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_2D_NAVIGATION), "set_navigation_layers", "get_navigation_layers"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "start_location"), "set_start_location", "get_start_location"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "end_location"), "set_end_location", "get_end_location"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "enter_cost"), "set_enter_cost", "get_enter_cost"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "travel_cost"), "set_travel_cost", "get_travel_cost"); +} + +void NavigationLink2D::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + if (enabled) { + NavigationServer2D::get_singleton()->link_set_map(link, get_world_2d()->get_navigation_map()); + + // Update global positions for the link. + Transform2D gt = get_global_transform(); + NavigationServer2D::get_singleton()->link_set_start_location(link, gt.xform(start_location)); + NavigationServer2D::get_singleton()->link_set_end_location(link, gt.xform(end_location)); + } + } break; + case NOTIFICATION_TRANSFORM_CHANGED: { + // Update global positions for the link. + Transform2D gt = get_global_transform(); + NavigationServer2D::get_singleton()->link_set_start_location(link, gt.xform(start_location)); + NavigationServer2D::get_singleton()->link_set_end_location(link, gt.xform(end_location)); + } break; + case NOTIFICATION_EXIT_TREE: { + NavigationServer2D::get_singleton()->link_set_map(link, RID()); + } break; + case NOTIFICATION_DRAW: { +#ifdef DEBUG_ENABLED + if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || NavigationServer2D::get_singleton()->get_debug_enabled())) { + Color color; + if (enabled) { + color = NavigationServer2D::get_singleton()->get_debug_navigation_link_connection_color(); + } else { + color = NavigationServer2D::get_singleton()->get_debug_navigation_link_connection_disabled_color(); + } + + real_t radius = NavigationServer2D::get_singleton()->map_get_link_connection_radius(get_world_2d()->get_navigation_map()); + + draw_line(get_start_location(), get_end_location(), color); + draw_arc(get_start_location(), radius, 0, Math_TAU, 10, color); + draw_arc(get_end_location(), radius, 0, Math_TAU, 10, color); + } +#endif // DEBUG_ENABLED + } break; + } +} + +#ifdef TOOLS_ENABLED +Rect2 NavigationLink2D::_edit_get_rect() const { + if (!is_inside_tree()) { + return Rect2(); + } + + real_t radius = NavigationServer2D::get_singleton()->map_get_link_connection_radius(get_world_2d()->get_navigation_map()); + + Rect2 rect(get_start_location(), Size2()); + rect.expand_to(get_end_location()); + rect.grow_by(radius); + return rect; +} + +bool NavigationLink2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + Point2 segment[2] = { get_start_location(), get_end_location() }; + + Vector2 closest_point = Geometry2D::get_closest_point_to_segment(p_point, segment); + return p_point.distance_to(closest_point) < p_tolerance; +} +#endif // TOOLS_ENABLED + +void NavigationLink2D::set_enabled(bool p_enabled) { + if (enabled == p_enabled) { + return; + } + + enabled = p_enabled; + + if (!is_inside_tree()) { + return; + } + + if (!enabled) { + NavigationServer2D::get_singleton()->link_set_map(link, RID()); + } else { + NavigationServer2D::get_singleton()->link_set_map(link, get_world_2d()->get_navigation_map()); + } + +#ifdef DEBUG_ENABLED + if (Engine::get_singleton()->is_editor_hint() || NavigationServer2D::get_singleton()->get_debug_enabled()) { + queue_redraw(); + } +#endif // DEBUG_ENABLED +} + +void NavigationLink2D::set_bidirectional(bool p_bidirectional) { + if (bidirectional == p_bidirectional) { + return; + } + + bidirectional = p_bidirectional; + + NavigationServer2D::get_singleton()->link_set_bidirectional(link, bidirectional); +} + +void NavigationLink2D::set_navigation_layers(uint32_t p_navigation_layers) { + if (navigation_layers == p_navigation_layers) { + return; + } + + navigation_layers = p_navigation_layers; + + NavigationServer2D::get_singleton()->link_set_navigation_layers(link, navigation_layers); +} + +void NavigationLink2D::set_navigation_layer_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Navigation layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Navigation layer number must be between 1 and 32 inclusive."); + + uint32_t _navigation_layers = get_navigation_layers(); + + if (p_value) { + _navigation_layers |= 1 << (p_layer_number - 1); + } else { + _navigation_layers &= ~(1 << (p_layer_number - 1)); + } + + set_navigation_layers(_navigation_layers); +} + +bool NavigationLink2D::get_navigation_layer_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Navigation layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Navigation layer number must be between 1 and 32 inclusive."); + + return get_navigation_layers() & (1 << (p_layer_number - 1)); +} + +void NavigationLink2D::set_start_location(Vector2 p_location) { + if (start_location.is_equal_approx(p_location)) { + return; + } + + start_location = p_location; + + if (!is_inside_tree()) { + return; + } + + Transform2D gt = get_global_transform(); + NavigationServer2D::get_singleton()->link_set_start_location(link, gt.xform(start_location)); + + update_configuration_warnings(); + +#ifdef DEBUG_ENABLED + if (Engine::get_singleton()->is_editor_hint() || NavigationServer2D::get_singleton()->get_debug_enabled()) { + queue_redraw(); + } +#endif // DEBUG_ENABLED +} + +void NavigationLink2D::set_end_location(Vector2 p_location) { + if (end_location.is_equal_approx(p_location)) { + return; + } + + end_location = p_location; + + if (!is_inside_tree()) { + return; + } + + Transform2D gt = get_global_transform(); + NavigationServer2D::get_singleton()->link_set_end_location(link, gt.xform(end_location)); + + update_configuration_warnings(); + +#ifdef DEBUG_ENABLED + if (Engine::get_singleton()->is_editor_hint() || NavigationServer2D::get_singleton()->get_debug_enabled()) { + queue_redraw(); + } +#endif // DEBUG_ENABLED +} + +void NavigationLink2D::set_enter_cost(real_t p_enter_cost) { + ERR_FAIL_COND_MSG(p_enter_cost < 0.0, "The enter_cost must be positive."); + if (Math::is_equal_approx(enter_cost, p_enter_cost)) { + return; + } + + enter_cost = p_enter_cost; + + NavigationServer2D::get_singleton()->link_set_enter_cost(link, enter_cost); +} + +void NavigationLink2D::set_travel_cost(real_t p_travel_cost) { + ERR_FAIL_COND_MSG(p_travel_cost < 0.0, "The travel_cost must be positive."); + if (Math::is_equal_approx(travel_cost, p_travel_cost)) { + return; + } + + travel_cost = p_travel_cost; + + NavigationServer2D::get_singleton()->link_set_travel_cost(link, travel_cost); +} + +PackedStringArray NavigationLink2D::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); + + if (start_location.is_equal_approx(end_location)) { + warnings.push_back(RTR("NavigationLink2D start location should be different than the end location to be useful.")); + } + + return warnings; +} + +NavigationLink2D::NavigationLink2D() { + link = NavigationServer2D::get_singleton()->link_create(); + set_notify_transform(true); +} + +NavigationLink2D::~NavigationLink2D() { + NavigationServer2D::get_singleton()->free(link); + link = RID(); +} diff --git a/scene/2d/navigation_link_2d.h b/scene/2d/navigation_link_2d.h new file mode 100644 index 0000000000..2a5092216d --- /dev/null +++ b/scene/2d/navigation_link_2d.h @@ -0,0 +1,88 @@ +/*************************************************************************/ +/* navigation_link_2d.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef NAVIGATION_LINK_2D_H +#define NAVIGATION_LINK_2D_H + +#include "scene/2d/node_2d.h" + +class NavigationLink2D : public Node2D { + GDCLASS(NavigationLink2D, Node2D); + + bool enabled = true; + RID link = RID(); + bool bidirectional = true; + uint32_t navigation_layers = 1; + Vector2 end_location = Vector2(); + Vector2 start_location = Vector2(); + real_t enter_cost = 0.0; + real_t travel_cost = 1.0; + +protected: + static void _bind_methods(); + void _notification(int p_what); + +public: +#ifdef TOOLS_ENABLED + virtual Rect2 _edit_get_rect() const override; + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const override; +#endif + + void set_enabled(bool p_enabled); + bool is_enabled() const { return enabled; } + + void set_bidirectional(bool p_bidirectional); + bool is_bidirectional() const { return bidirectional; } + + void set_navigation_layers(uint32_t p_navigation_layers); + uint32_t get_navigation_layers() const { return navigation_layers; } + + void set_navigation_layer_value(int p_layer_number, bool p_value); + bool get_navigation_layer_value(int p_layer_number) const; + + void set_start_location(Vector2 p_location); + Vector2 get_start_location() const { return start_location; } + + void set_end_location(Vector2 p_location); + Vector2 get_end_location() const { return end_location; } + + void set_enter_cost(real_t p_enter_cost); + real_t get_enter_cost() const { return enter_cost; } + + void set_travel_cost(real_t p_travel_cost); + real_t get_travel_cost() const { return travel_cost; } + + PackedStringArray get_configuration_warnings() const override; + + NavigationLink2D(); + ~NavigationLink2D(); +}; + +#endif // NAVIGATION_LINK_2D_H diff --git a/scene/2d/navigation_obstacle_2d.cpp b/scene/2d/navigation_obstacle_2d.cpp index 0320c6c917..e46bb79551 100644 --- a/scene/2d/navigation_obstacle_2d.cpp +++ b/scene/2d/navigation_obstacle_2d.cpp @@ -38,6 +38,9 @@ void NavigationObstacle2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_rid"), &NavigationObstacle2D::get_rid); + ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &NavigationObstacle2D::set_navigation_map); + ClassDB::bind_method(D_METHOD("get_navigation_map"), &NavigationObstacle2D::get_navigation_map); + ClassDB::bind_method(D_METHOD("set_estimate_radius", "estimate_radius"), &NavigationObstacle2D::set_estimate_radius); ClassDB::bind_method(D_METHOD("is_radius_estimated"), &NavigationObstacle2D::is_radius_estimated); ClassDB::bind_method(D_METHOD("set_radius", "radius"), &NavigationObstacle2D::set_radius); @@ -57,28 +60,26 @@ void NavigationObstacle2D::_validate_property(PropertyInfo &p_property) const { void NavigationObstacle2D::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: { - parent_node2d = Object::cast_to<Node2D>(get_parent()); - reevaluate_agent_radius(); - if (parent_node2d != nullptr) { - // place agent on navigation map first or else the RVO agent callback creation fails silently later - NavigationServer2D::get_singleton()->agent_set_map(get_rid(), parent_node2d->get_world_2d()->get_navigation_map()); - } + case NOTIFICATION_POST_ENTER_TREE: { + set_agent_parent(get_parent()); set_physics_process_internal(true); } break; case NOTIFICATION_EXIT_TREE: { - parent_node2d = nullptr; + set_agent_parent(nullptr); set_physics_process_internal(false); } break; case NOTIFICATION_PARENTED: { - parent_node2d = Object::cast_to<Node2D>(get_parent()); - reevaluate_agent_radius(); + if (is_inside_tree() && (get_parent() != parent_node2d)) { + set_agent_parent(get_parent()); + set_physics_process_internal(true); + } } break; case NOTIFICATION_UNPARENTED: { - parent_node2d = nullptr; + set_agent_parent(nullptr); + set_physics_process_internal(false); } break; case NOTIFICATION_PAUSED: { @@ -119,15 +120,15 @@ NavigationObstacle2D::~NavigationObstacle2D() { agent = RID(); // Pointless } -TypedArray<String> NavigationObstacle2D::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray NavigationObstacle2D::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (!Object::cast_to<Node2D>(get_parent())) { warnings.push_back(RTR("The NavigationObstacle2D only serves to provide collision avoidance to a Node2D object.")); } if (Object::cast_to<StaticBody2D>(get_parent())) { - warnings.push_back(RTR("The NavigationObstacle2D is intended for constantly moving bodies like CharacterBody2D or RigidDynamicBody2D as it creates only an RVO avoidance radius and does not follow scene geometry exactly." + warnings.push_back(RTR("The NavigationObstacle2D is intended for constantly moving bodies like CharacterBody2D or RigidBody2D as it creates only an RVO avoidance radius and does not follow scene geometry exactly." "\nNot constantly moving or complete static objects should be captured with a refreshed NavigationPolygon so agents can not only avoid them but also move along those objects outline at high detail")); } @@ -135,7 +136,7 @@ TypedArray<String> NavigationObstacle2D::get_configuration_warnings() const { } void NavigationObstacle2D::initialize_agent() { - NavigationServer2D::get_singleton()->agent_set_neighbor_dist(agent, 0.0); + NavigationServer2D::get_singleton()->agent_set_neighbor_distance(agent, 0.0); NavigationServer2D::get_singleton()->agent_set_max_neighbors(agent, 0); NavigationServer2D::get_singleton()->agent_set_time_horizon(agent, 0.0); NavigationServer2D::get_singleton()->agent_set_max_speed(agent, 0.0); @@ -182,6 +183,35 @@ real_t NavigationObstacle2D::estimate_agent_radius() const { return 1.0; // Never a 0 radius } +void NavigationObstacle2D::set_agent_parent(Node *p_agent_parent) { + if (Object::cast_to<Node2D>(p_agent_parent) != nullptr) { + parent_node2d = Object::cast_to<Node2D>(p_agent_parent); + if (map_override.is_valid()) { + NavigationServer2D::get_singleton()->agent_set_map(get_rid(), map_override); + } else { + NavigationServer2D::get_singleton()->agent_set_map(get_rid(), parent_node2d->get_world_2d()->get_navigation_map()); + } + reevaluate_agent_radius(); + } else { + parent_node2d = nullptr; + NavigationServer2D::get_singleton()->agent_set_map(get_rid(), RID()); + } +} + +void NavigationObstacle2D::set_navigation_map(RID p_navigation_map) { + map_override = p_navigation_map; + NavigationServer2D::get_singleton()->agent_set_map(agent, map_override); +} + +RID NavigationObstacle2D::get_navigation_map() const { + if (map_override.is_valid()) { + return map_override; + } else if (parent_node2d != nullptr) { + return parent_node2d->get_world_2d()->get_navigation_map(); + } + return RID(); +} + void NavigationObstacle2D::set_estimate_radius(bool p_estimate_radius) { estimate_radius = p_estimate_radius; notify_property_list_changed(); diff --git a/scene/2d/navigation_obstacle_2d.h b/scene/2d/navigation_obstacle_2d.h index 948cf5b61a..d4c1df343f 100644 --- a/scene/2d/navigation_obstacle_2d.h +++ b/scene/2d/navigation_obstacle_2d.h @@ -38,15 +38,17 @@ class NavigationObstacle2D : public Node { GDCLASS(NavigationObstacle2D, Node); Node2D *parent_node2d = nullptr; + RID agent; RID map_before_pause; + RID map_override; bool estimate_radius = true; real_t radius = 1.0; protected: static void _bind_methods(); - void _validate_property(PropertyInfo &p_property) const override; + void _validate_property(PropertyInfo &p_property) const; void _notification(int p_what); public: @@ -57,6 +59,11 @@ public: return agent; } + void set_agent_parent(Node *p_agent_parent); + + void set_navigation_map(RID p_navigation_map); + RID get_navigation_map() const; + void set_estimate_radius(bool p_estimate_radius); bool is_radius_estimated() const { return estimate_radius; @@ -66,7 +73,7 @@ public: return radius; } - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; private: void initialize_agent(); @@ -74,4 +81,4 @@ private: real_t estimate_agent_radius() const; }; -#endif +#endif // NAVIGATION_OBSTACLE_2D_H diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp index 6e8fd891cb..6e8ecb13b1 100644 --- a/scene/2d/navigation_region_2d.cpp +++ b/scene/2d/navigation_region_2d.cpp @@ -35,6 +35,7 @@ #include "core/os/mutex.h" #include "scene/resources/world_2d.h" #include "servers/navigation_server_2d.h" +#include "servers/navigation_server_3d.h" #include "thirdparty/misc/polypartition.h" @@ -353,10 +354,13 @@ void NavigationPolygon::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "outlines", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_outlines", "_get_outlines"); } +///////////////////////////// + void NavigationRegion2D::set_enabled(bool p_enabled) { if (enabled == p_enabled) { return; } + enabled = p_enabled; if (!is_inside_tree()) { @@ -371,9 +375,11 @@ void NavigationRegion2D::set_enabled(bool p_enabled) { NavigationServer2D::get_singleton_mut()->connect("map_changed", callable_mp(this, &NavigationRegion2D::_map_changed)); } - if (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint()) { - update(); +#ifdef DEBUG_ENABLED + if (Engine::get_singleton()->is_editor_hint() || NavigationServer3D::get_singleton()->get_debug_enabled()) { + queue_redraw(); } +#endif // DEBUG_ENABLED } bool NavigationRegion2D::is_enabled() const { @@ -381,35 +387,50 @@ bool NavigationRegion2D::is_enabled() const { } void NavigationRegion2D::set_navigation_layers(uint32_t p_navigation_layers) { - NavigationServer2D::get_singleton()->region_set_navigation_layers(region, p_navigation_layers); + if (navigation_layers == p_navigation_layers) { + return; + } + + navigation_layers = p_navigation_layers; + + NavigationServer2D::get_singleton()->region_set_navigation_layers(region, navigation_layers); } uint32_t NavigationRegion2D::get_navigation_layers() const { - return NavigationServer2D::get_singleton()->region_get_navigation_layers(region); + return navigation_layers; } void NavigationRegion2D::set_navigation_layer_value(int p_layer_number, bool p_value) { ERR_FAIL_COND_MSG(p_layer_number < 1, "Navigation layer number must be between 1 and 32 inclusive."); ERR_FAIL_COND_MSG(p_layer_number > 32, "Navigation layer number must be between 1 and 32 inclusive."); + uint32_t _navigation_layers = get_navigation_layers(); + if (p_value) { _navigation_layers |= 1 << (p_layer_number - 1); } else { _navigation_layers &= ~(1 << (p_layer_number - 1)); } + set_navigation_layers(_navigation_layers); } bool NavigationRegion2D::get_navigation_layer_value(int p_layer_number) const { ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Navigation layer number must be between 1 and 32 inclusive."); ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Navigation layer number must be between 1 and 32 inclusive."); + return get_navigation_layers() & (1 << (p_layer_number - 1)); } void NavigationRegion2D::set_enter_cost(real_t p_enter_cost) { ERR_FAIL_COND_MSG(p_enter_cost < 0.0, "The enter_cost must be positive."); - enter_cost = MAX(p_enter_cost, 0.0); - NavigationServer2D::get_singleton()->region_set_enter_cost(region, p_enter_cost); + if (Math::is_equal_approx(enter_cost, p_enter_cost)) { + return; + } + + enter_cost = p_enter_cost; + + NavigationServer2D::get_singleton()->region_set_enter_cost(region, enter_cost); } real_t NavigationRegion2D::get_enter_cost() const { @@ -418,8 +439,13 @@ real_t NavigationRegion2D::get_enter_cost() const { void NavigationRegion2D::set_travel_cost(real_t p_travel_cost) { ERR_FAIL_COND_MSG(p_travel_cost < 0.0, "The travel_cost must be positive."); - travel_cost = MAX(p_travel_cost, 0.0); - NavigationServer2D::get_singleton()->region_set_enter_cost(region, travel_cost); + if (Math::is_equal_approx(travel_cost, p_travel_cost)) { + return; + } + + travel_cost = p_travel_cost; + + NavigationServer2D::get_singleton()->region_set_travel_cost(region, travel_cost); } real_t NavigationRegion2D::get_travel_cost() const { @@ -430,7 +456,6 @@ RID NavigationRegion2D::get_region_rid() const { return region; } -///////////////////////////// #ifdef TOOLS_ENABLED Rect2 NavigationRegion2D::_edit_get_rect() const { return navpoly.is_valid() ? navpoly->_edit_get_rect() : Rect2(); @@ -462,7 +487,8 @@ void NavigationRegion2D::_notification(int p_what) { } break; case NOTIFICATION_DRAW: { - if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint()) && navpoly.is_valid()) { +#ifdef DEBUG_ENABLED + if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || NavigationServer3D::get_singleton()->get_debug_enabled()) && navpoly.is_valid()) { Vector<Vector2> verts = navpoly->get_vertices(); if (verts.size() < 3) { return; @@ -470,11 +496,11 @@ void NavigationRegion2D::_notification(int p_what) { Color color; if (enabled) { - color = get_tree()->get_debug_navigation_color(); + color = NavigationServer3D::get_singleton()->get_debug_navigation_geometry_face_color(); } else { - color = get_tree()->get_debug_navigation_disabled_color(); + color = NavigationServer3D::get_singleton()->get_debug_navigation_geometry_face_disabled_color(); } - Color doors_color = color.lightened(0.2); + Color doors_color = NavigationServer3D::get_singleton()->get_debug_navigation_edge_connection_color(); RandomPCG rand; @@ -490,7 +516,7 @@ void NavigationRegion2D::_notification(int p_what) { // Generate the polygon color, slightly randomly modified from the settings one. Color random_variation_color; - random_variation_color.set_hsv(color.get_h() + rand.random(-1.0, 1.0) * 0.05, color.get_s(), color.get_v() + rand.random(-1.0, 1.0) * 0.1); + random_variation_color.set_hsv(color.get_h() + rand.random(-1.0, 1.0) * 0.1, color.get_s(), color.get_v() + rand.random(-1.0, 1.0) * 0.2); random_variation_color.a = color.a; Vector<Color> colors; colors.push_back(random_variation_color); @@ -516,6 +542,7 @@ void NavigationRegion2D::_notification(int p_what) { draw_arc(b, radius, angle - Math_PI / 2.0, angle + Math_PI / 2.0, 10, doors_color); } } +#endif // DEBUG_ENABLED } break; } } @@ -546,20 +573,23 @@ Ref<NavigationPolygon> NavigationRegion2D::get_navigation_polygon() const { void NavigationRegion2D::_navpoly_changed() { if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint())) { - update(); + queue_redraw(); } if (navpoly.is_valid()) { NavigationServer2D::get_singleton()->region_set_navpoly(region, navpoly); } } + void NavigationRegion2D::_map_changed(RID p_map) { - if (enabled && get_world_2d()->get_navigation_map() == p_map) { - update(); +#ifdef DEBUG_ENABLED + if (is_inside_tree() && get_world_2d()->get_navigation_map() == p_map) { + queue_redraw(); } +#endif // DEBUG_ENABLED } -TypedArray<String> NavigationRegion2D::get_configuration_warnings() const { - TypedArray<String> warnings = Node2D::get_configuration_warnings(); +PackedStringArray NavigationRegion2D::get_configuration_warnings() const { + PackedStringArray warnings = Node2D::get_configuration_warnings(); if (is_visible_in_tree() && is_inside_tree()) { if (!navpoly.is_valid()) { @@ -605,8 +635,18 @@ NavigationRegion2D::NavigationRegion2D() { region = NavigationServer2D::get_singleton()->region_create(); NavigationServer2D::get_singleton()->region_set_enter_cost(region, get_enter_cost()); NavigationServer2D::get_singleton()->region_set_travel_cost(region, get_travel_cost()); + +#ifdef DEBUG_ENABLED + NavigationServer3D::get_singleton_mut()->connect("map_changed", callable_mp(this, &NavigationRegion2D::_map_changed)); + NavigationServer3D::get_singleton_mut()->connect("navigation_debug_changed", callable_mp(this, &NavigationRegion2D::_map_changed)); +#endif // DEBUG_ENABLED } NavigationRegion2D::~NavigationRegion2D() { NavigationServer2D::get_singleton()->free(region); + +#ifdef DEBUG_ENABLED + NavigationServer3D::get_singleton_mut()->disconnect("map_changed", callable_mp(this, &NavigationRegion2D::_map_changed)); + NavigationServer3D::get_singleton_mut()->disconnect("navigation_debug_changed", callable_mp(this, &NavigationRegion2D::_map_changed)); +#endif // DEBUG_ENABLED } diff --git a/scene/2d/navigation_region_2d.h b/scene/2d/navigation_region_2d.h index 3c9df91fe3..c630e20780 100644 --- a/scene/2d/navigation_region_2d.h +++ b/scene/2d/navigation_region_2d.h @@ -96,10 +96,10 @@ class NavigationRegion2D : public Node2D { bool enabled = true; RID region; - Ref<NavigationPolygon> navpoly; - + uint32_t navigation_layers = 1; real_t enter_cost = 0.0; real_t travel_cost = 1.0; + Ref<NavigationPolygon> navpoly; void _navpoly_changed(); void _map_changed(RID p_RID); @@ -134,7 +134,7 @@ public: void set_navigation_polygon(const Ref<NavigationPolygon> &p_navpoly); Ref<NavigationPolygon> get_navigation_polygon() const; - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; NavigationRegion2D(); ~NavigationRegion2D(); diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp index 4599785ce4..7765533016 100644 --- a/scene/2d/node_2d.cpp +++ b/scene/2d/node_2d.cpp @@ -437,8 +437,8 @@ void Node2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_relative_transform_to_parent", "parent"), &Node2D::get_relative_transform_to_parent); ADD_GROUP("Transform", ""); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position", PROPERTY_HINT_RANGE, "-99999,99999,0.001,or_lesser,or_greater,no_slider,suffix:px"), "set_position", "get_position"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_lesser,or_greater,radians"), "set_rotation", "get_rotation"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position", PROPERTY_HINT_RANGE, "-99999,99999,0.001,or_less,or_greater,no_slider,suffix:px"), "set_position", "get_position"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_less,or_greater,radians"), "set_rotation", "get_rotation"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scale", PROPERTY_HINT_LINK), "set_scale", "get_scale"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "skew", PROPERTY_HINT_RANGE, "-89.9,89.9,0.1,radians"), "set_skew", "get_skew"); ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "transform", PROPERTY_HINT_NONE, "suffix:px", PROPERTY_USAGE_NONE), "set_transform", "get_transform"); diff --git a/scene/2d/node_2d.h b/scene/2d/node_2d.h index 473c34768f..0d8a31e6bb 100644 --- a/scene/2d/node_2d.h +++ b/scene/2d/node_2d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef NODE2D_H -#define NODE2D_H +#ifndef NODE_2D_H +#define NODE_2D_H #include "scene/main/canvas_item.h" @@ -124,4 +124,4 @@ public: Node2D() {} }; -#endif // NODE2D_H +#endif // NODE_2D_H diff --git a/scene/2d/parallax_background.cpp b/scene/2d/parallax_background.cpp index bd5a01f5a4..f1a28b7852 100644 --- a/scene/2d/parallax_background.cpp +++ b/scene/2d/parallax_background.cpp @@ -102,9 +102,9 @@ void ParallaxBackground::_update_scroll() { } if (ignore_camera_zoom) { - l->set_base_offset_and_scale((ofs + screen_offset * (scale - 1)) / scale, 1.0, screen_offset); + l->set_base_offset_and_scale((ofs + screen_offset * (scale - 1)) / scale, 1.0); } else { - l->set_base_offset_and_scale(ofs, scale, screen_offset); + l->set_base_offset_and_scale(ofs, scale); } } } diff --git a/scene/2d/parallax_layer.cpp b/scene/2d/parallax_layer.cpp index f0aad1b8a4..01e4bf19f3 100644 --- a/scene/2d/parallax_layer.cpp +++ b/scene/2d/parallax_layer.cpp @@ -39,7 +39,7 @@ void ParallaxLayer::set_motion_scale(const Size2 &p_scale) { if (pb && is_inside_tree()) { Vector2 ofs = pb->get_final_offset(); real_t scale = pb->get_scroll_scale(); - set_base_offset_and_scale(ofs, scale, screen_offset); + set_base_offset_and_scale(ofs, scale); } } @@ -54,7 +54,7 @@ void ParallaxLayer::set_motion_offset(const Size2 &p_offset) { if (pb && is_inside_tree()) { Vector2 ofs = pb->get_final_offset(); real_t scale = pb->get_scroll_scale(); - set_base_offset_and_scale(ofs, scale, screen_offset); + set_base_offset_and_scale(ofs, scale); } } @@ -111,9 +111,7 @@ void ParallaxLayer::_notification(int p_what) { } } -void ParallaxLayer::set_base_offset_and_scale(const Point2 &p_offset, real_t p_scale, const Point2 &p_screen_offset) { - screen_offset = p_screen_offset; - +void ParallaxLayer::set_base_offset_and_scale(const Point2 &p_offset, real_t p_scale) { if (!is_inside_tree()) { return; } @@ -121,7 +119,7 @@ void ParallaxLayer::set_base_offset_and_scale(const Point2 &p_offset, real_t p_s return; } - Point2 new_ofs = (screen_offset + (p_offset - screen_offset) * motion_scale) + motion_offset * p_scale + orig_offset * p_scale; + Point2 new_ofs = p_offset * motion_scale + motion_offset * p_scale + orig_offset * p_scale; if (mirroring.x) { real_t den = mirroring.x * p_scale; @@ -139,8 +137,8 @@ void ParallaxLayer::set_base_offset_and_scale(const Point2 &p_offset, real_t p_s _update_mirroring(); } -TypedArray<String> ParallaxLayer::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray ParallaxLayer::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (!Object::cast_to<ParallaxBackground>(get_parent())) { warnings.push_back(RTR("ParallaxLayer node only works when set as child of a ParallaxBackground node.")); diff --git a/scene/2d/parallax_layer.h b/scene/2d/parallax_layer.h index b4dcf0ea61..6471f56c5c 100644 --- a/scene/2d/parallax_layer.h +++ b/scene/2d/parallax_layer.h @@ -43,8 +43,6 @@ class ParallaxLayer : public Node2D { Vector2 mirroring; void _update_mirroring(); - Point2 screen_offset; - protected: void _notification(int p_what); static void _bind_methods(); @@ -59,9 +57,9 @@ public: void set_mirroring(const Size2 &p_mirroring); Size2 get_mirroring() const; - void set_base_offset_and_scale(const Point2 &p_offset, real_t p_scale, const Point2 &p_screen_offset); + void set_base_offset_and_scale(const Point2 &p_offset, real_t p_scale); - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; ParallaxLayer(); }; diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp index 8eb48ffb30..c1044fdf5b 100644 --- a/scene/2d/path_2d.cpp +++ b/scene/2d/path_2d.cpp @@ -47,7 +47,7 @@ Rect2 Path2D::_edit_get_rect() const { for (int i = 0; i < curve->get_point_count(); i++) { for (int j = 0; j <= 8; j++) { real_t frac = j / 8.0; - Vector2 p = curve->interpolate(i, frac); + Vector2 p = curve->sample(i, frac); aabb.expand_to(p); } } @@ -70,7 +70,7 @@ bool Path2D::_edit_is_selected_on_click(const Point2 &p_point, double p_toleranc for (int j = 1; j <= 8; j++) { real_t frac = j / 8.0; - s[1] = curve->interpolate(i, frac); + s[1] = curve->sample(i, frac); Vector2 p = Geometry2D::get_closest_point_to_segment(p_point, s); if (p.distance_to(p_point) <= p_tolerance) { @@ -112,7 +112,7 @@ void Path2D::_notification(int p_what) { for (int i = 0; i < curve->get_point_count(); i++) { for (int j = 0; j < 8; j++) { real_t frac = j * (1.0 / 8.0); - Vector2 p = curve->interpolate(i, frac); + Vector2 p = curve->sample(i, frac); _cached_draw_pts.set(count++, p); } } @@ -131,7 +131,7 @@ void Path2D::_curve_changed() { return; } - update(); + queue_redraw(); } void Path2D::set_curve(const Ref<Curve2D> &p_curve) { @@ -175,10 +175,10 @@ void PathFollow2D::_update_transform() { if (path_length == 0) { return; } - Vector2 pos = c->interpolate_baked(offset, cubic); + Vector2 pos = c->sample_baked(progress, cubic); if (rotates) { - real_t ahead = offset + lookahead; + real_t ahead = progress + lookahead; if (loop && ahead >= path_length) { // If our lookahead will loop, we need to check if the path is closed. @@ -195,14 +195,14 @@ void PathFollow2D::_update_transform() { } } - Vector2 ahead_pos = c->interpolate_baked(ahead, cubic); + Vector2 ahead_pos = c->sample_baked(ahead, cubic); Vector2 tangent_to_curve; if (ahead_pos == pos) { // This will happen at the end of non-looping or non-closed paths. // We'll try a look behind instead, in order to get a meaningful angle. tangent_to_curve = - (pos - c->interpolate_baked(offset - lookahead, cubic)).normalized(); + (pos - c->sample_baked(progress - lookahead, cubic)).normalized(); } else { tangent_to_curve = (ahead_pos - pos).normalized(); } @@ -245,19 +245,19 @@ bool PathFollow2D::get_cubic_interpolation() const { return cubic; } -void PathFollow2D::_validate_property(PropertyInfo &property) const { - if (property.name == "offset") { +void PathFollow2D::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "offset") { real_t max = 10000.0; if (path && path->get_curve().is_valid()) { max = path->get_curve()->get_baked_length(); } - property.hint_string = "0," + rtos(max) + ",0.01,or_lesser,or_greater"; + p_property.hint_string = "0," + rtos(max) + ",0.01,or_less,or_greater"; } } -TypedArray<String> PathFollow2D::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray PathFollow2D::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (is_visible_in_tree() && is_inside_tree()) { if (!Object::cast_to<Path2D>(get_parent())) { @@ -269,8 +269,8 @@ TypedArray<String> PathFollow2D::get_configuration_warnings() const { } void PathFollow2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_offset", "offset"), &PathFollow2D::set_offset); - ClassDB::bind_method(D_METHOD("get_offset"), &PathFollow2D::get_offset); + ClassDB::bind_method(D_METHOD("set_progress", "progress"), &PathFollow2D::set_progress); + ClassDB::bind_method(D_METHOD("get_progress"), &PathFollow2D::get_progress); ClassDB::bind_method(D_METHOD("set_h_offset", "h_offset"), &PathFollow2D::set_h_offset); ClassDB::bind_method(D_METHOD("get_h_offset"), &PathFollow2D::get_h_offset); @@ -278,8 +278,8 @@ void PathFollow2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_v_offset", "v_offset"), &PathFollow2D::set_v_offset); ClassDB::bind_method(D_METHOD("get_v_offset"), &PathFollow2D::get_v_offset); - ClassDB::bind_method(D_METHOD("set_unit_offset", "unit_offset"), &PathFollow2D::set_unit_offset); - ClassDB::bind_method(D_METHOD("get_unit_offset"), &PathFollow2D::get_unit_offset); + ClassDB::bind_method(D_METHOD("set_progress_ratio", "ratio"), &PathFollow2D::set_progress_ratio); + ClassDB::bind_method(D_METHOD("get_progress_ratio"), &PathFollow2D::get_progress_ratio); ClassDB::bind_method(D_METHOD("set_rotates", "enable"), &PathFollow2D::set_rotates); ClassDB::bind_method(D_METHOD("is_rotating"), &PathFollow2D::is_rotating); @@ -293,8 +293,8 @@ void PathFollow2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_lookahead", "lookahead"), &PathFollow2D::set_lookahead); ClassDB::bind_method(D_METHOD("get_lookahead"), &PathFollow2D::get_lookahead); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "offset", PROPERTY_HINT_RANGE, "0,10000,0.01,or_lesser,or_greater,suffix:px"), "set_offset", "get_offset"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "unit_offset", PROPERTY_HINT_RANGE, "0,1,0.0001,or_lesser,or_greater", PROPERTY_USAGE_EDITOR), "set_unit_offset", "get_unit_offset"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "progress", PROPERTY_HINT_RANGE, "0,10000,0.01,or_less,or_greater,suffix:px"), "set_progress", "get_progress"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "progress_ratio", PROPERTY_HINT_RANGE, "0,1,0.0001,or_less,or_greater", PROPERTY_USAGE_EDITOR), "set_progress_ratio", "get_progress_ratio"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "h_offset"), "set_h_offset", "get_h_offset"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "v_offset"), "set_v_offset", "get_v_offset"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rotates"), "set_rotates", "is_rotating"); @@ -303,19 +303,20 @@ void PathFollow2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lookahead", PROPERTY_HINT_RANGE, "0.001,1024.0,0.001"), "set_lookahead", "get_lookahead"); } -void PathFollow2D::set_offset(real_t p_offset) { - offset = p_offset; +void PathFollow2D::set_progress(real_t p_progress) { + ERR_FAIL_COND(!isfinite(p_progress)); + progress = p_progress; if (path) { if (path->get_curve().is_valid()) { real_t path_length = path->get_curve()->get_baked_length(); if (loop && path_length) { - offset = Math::fposmod(offset, path_length); - if (!Math::is_zero_approx(p_offset) && Math::is_zero_approx(offset)) { - offset = path_length; + progress = Math::fposmod(progress, path_length); + if (!Math::is_zero_approx(p_progress) && Math::is_zero_approx(progress)) { + progress = path_length; } } else { - offset = CLAMP(offset, 0, path_length); + progress = CLAMP(progress, 0, path_length); } } @@ -345,19 +346,19 @@ real_t PathFollow2D::get_v_offset() const { return v_offset; } -real_t PathFollow2D::get_offset() const { - return offset; +real_t PathFollow2D::get_progress() const { + return progress; } -void PathFollow2D::set_unit_offset(real_t p_unit_offset) { +void PathFollow2D::set_progress_ratio(real_t p_ratio) { if (path && path->get_curve().is_valid() && path->get_curve()->get_baked_length()) { - set_offset(p_unit_offset * path->get_curve()->get_baked_length()); + set_progress(p_ratio * path->get_curve()->get_baked_length()); } } -real_t PathFollow2D::get_unit_offset() const { +real_t PathFollow2D::get_progress_ratio() const { if (path && path->get_curve().is_valid() && path->get_curve()->get_baked_length()) { - return get_offset() / path->get_curve()->get_baked_length(); + return get_progress() / path->get_curve()->get_baked_length(); } else { return 0; } diff --git a/scene/2d/path_2d.h b/scene/2d/path_2d.h index bc55f84831..5e436fb9f6 100644 --- a/scene/2d/path_2d.h +++ b/scene/2d/path_2d.h @@ -65,7 +65,7 @@ class PathFollow2D : public Node2D { public: private: Path2D *path = nullptr; - real_t offset = 0.0; + real_t progress = 0.0; real_t h_offset = 0.0; real_t v_offset = 0.0; real_t lookahead = 4.0; @@ -76,14 +76,14 @@ private: void _update_transform(); protected: - virtual void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; void _notification(int p_what); static void _bind_methods(); public: - void set_offset(real_t p_offset); - real_t get_offset() const; + void set_progress(real_t p_progress); + real_t get_progress() const; void set_h_offset(real_t p_h_offset); real_t get_h_offset() const; @@ -91,8 +91,8 @@ public: void set_v_offset(real_t p_v_offset); real_t get_v_offset() const; - void set_unit_offset(real_t p_unit_offset); - real_t get_unit_offset() const; + void set_progress_ratio(real_t p_ratio); + real_t get_progress_ratio() const; void set_lookahead(real_t p_lookahead); real_t get_lookahead() const; @@ -106,7 +106,7 @@ public: void set_cubic_interpolation(bool p_enable); bool get_cubic_interpolation() const; - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; PathFollow2D() {} }; diff --git a/scene/2d/physical_bone_2d.cpp b/scene/2d/physical_bone_2d.cpp index 2999736d64..5ff706ebb7 100644 --- a/scene/2d/physical_bone_2d.cpp +++ b/scene/2d/physical_bone_2d.cpp @@ -35,7 +35,7 @@ void PhysicalBone2D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { - // Position the RigidDynamicBody in the correct position. + // Position the RigidBody in the correct position. if (follow_bone_when_simulating) { _position_at_bone2d(); } @@ -106,8 +106,8 @@ void PhysicalBone2D::_find_joint_child() { } } -TypedArray<String> PhysicalBone2D::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray PhysicalBone2D::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (!parent_skeleton) { warnings.push_back(RTR("A PhysicalBone2D only works with a Skeleton2D or another PhysicalBone2D as a parent node!")); @@ -158,6 +158,7 @@ void PhysicalBone2D::_start_physics_simulation() { // Apply the layers and masks. PhysicsServer2D::get_singleton()->body_set_collision_layer(get_rid(), get_collision_layer()); PhysicsServer2D::get_singleton()->body_set_collision_mask(get_rid(), get_collision_mask()); + PhysicsServer2D::get_singleton()->body_set_collision_priority(get_rid(), get_collision_priority()); // Apply the correct mode. _apply_body_mode(); @@ -176,6 +177,7 @@ void PhysicalBone2D::_stop_physics_simulation() { set_physics_process_internal(false); PhysicsServer2D::get_singleton()->body_set_collision_layer(get_rid(), 0); PhysicsServer2D::get_singleton()->body_set_collision_mask(get_rid(), 0); + PhysicsServer2D::get_singleton()->body_set_collision_priority(get_rid(), 1.0); PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BodyMode::BODY_MODE_STATIC); } } @@ -285,7 +287,7 @@ void PhysicalBone2D::_bind_methods() { } PhysicalBone2D::PhysicalBone2D() { - // Stop the RigidDynamicBody from executing its force integration. + // Stop the RigidBody from executing its force integration. PhysicsServer2D::get_singleton()->body_set_collision_layer(get_rid(), 0); PhysicsServer2D::get_singleton()->body_set_collision_mask(get_rid(), 0); PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BodyMode::BODY_MODE_STATIC); diff --git a/scene/2d/physical_bone_2d.h b/scene/2d/physical_bone_2d.h index 22d329c320..33ac0d9935 100644 --- a/scene/2d/physical_bone_2d.h +++ b/scene/2d/physical_bone_2d.h @@ -36,8 +36,8 @@ class Joint2D; -class PhysicalBone2D : public RigidDynamicBody2D { - GDCLASS(PhysicalBone2D, RigidDynamicBody2D); +class PhysicalBone2D : public RigidBody2D { + GDCLASS(PhysicalBone2D, RigidBody2D); protected: void _notification(int p_what); @@ -79,7 +79,7 @@ public: void set_follow_bone_when_simulating(bool p_follow); bool get_follow_bone_when_simulating() const; - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; PhysicalBone2D(); ~PhysicalBone2D(); diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index e60a5ed034..4e6b37a860 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -34,8 +34,8 @@ #include "scene/scene_string_names.h" void PhysicsBody2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("move_and_collide", "distance", "test_only", "safe_margin"), &PhysicsBody2D::_move, DEFVAL(false), DEFVAL(0.08)); - ClassDB::bind_method(D_METHOD("test_move", "from", "distance", "collision", "safe_margin"), &PhysicsBody2D::test_move, DEFVAL(Variant()), DEFVAL(0.08)); + ClassDB::bind_method(D_METHOD("move_and_collide", "distance", "test_only", "safe_margin", "recovery_as_collision"), &PhysicsBody2D::_move, DEFVAL(false), DEFVAL(0.08), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("test_move", "from", "distance", "collision", "safe_margin", "recovery_as_collision"), &PhysicsBody2D::test_move, DEFVAL(Variant()), DEFVAL(0.08), DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_collision_exceptions"), &PhysicsBody2D::get_collision_exceptions); ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body"), &PhysicsBody2D::add_collision_exception_with); @@ -54,15 +54,15 @@ PhysicsBody2D::~PhysicsBody2D() { } } -Ref<KinematicCollision2D> PhysicsBody2D::_move(const Vector2 &p_distance, bool p_test_only, real_t p_margin) { +Ref<KinematicCollision2D> PhysicsBody2D::_move(const Vector2 &p_distance, bool p_test_only, real_t p_margin, bool p_recovery_as_collision) { PhysicsServer2D::MotionParameters parameters(get_global_transform(), p_distance, p_margin); - parameters.recovery_as_collision = false; // Don't report collisions generated only from recovery. + parameters.recovery_as_collision = p_recovery_as_collision; PhysicsServer2D::MotionResult result; if (move_and_collide(parameters, result, p_test_only)) { // Create a new instance when the cached reference is invalid or still in use in script. - if (motion_cache.is_null() || motion_cache->reference_get_count() > 1) { + if (motion_cache.is_null() || motion_cache->get_reference_count() > 1) { motion_cache.instantiate(); motion_cache->owner = this; } @@ -128,7 +128,7 @@ bool PhysicsBody2D::move_and_collide(const PhysicsServer2D::MotionParameters &p_ return colliding; } -bool PhysicsBody2D::test_move(const Transform2D &p_from, const Vector2 &p_distance, const Ref<KinematicCollision2D> &r_collision, real_t p_margin) { +bool PhysicsBody2D::test_move(const Transform2D &p_from, const Vector2 &p_distance, const Ref<KinematicCollision2D> &r_collision, real_t p_margin, bool p_recovery_as_collision) { ERR_FAIL_COND_V(!is_inside_tree(), false); PhysicsServer2D::MotionResult *r = nullptr; @@ -141,7 +141,7 @@ bool PhysicsBody2D::test_move(const Transform2D &p_from, const Vector2 &p_distan } PhysicsServer2D::MotionParameters parameters(p_from, p_distance, p_margin); - parameters.recovery_as_collision = false; // Don't report collisions generated only from recovery. + parameters.recovery_as_collision = p_recovery_as_collision; return PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), parameters, r); } @@ -262,21 +262,16 @@ void AnimatableBody2D::_update_kinematic_motion() { #endif if (sync_to_physics) { - PhysicsServer2D::get_singleton()->body_set_state_sync_callback(get_rid(), this, _body_state_changed_callback); + PhysicsServer2D::get_singleton()->body_set_state_sync_callback(get_rid(), callable_mp(this, &AnimatableBody2D::_body_state_changed)); set_only_update_transform_changes(true); set_notify_local_transform(true); } else { - PhysicsServer2D::get_singleton()->body_set_state_sync_callback(get_rid(), nullptr, nullptr); + PhysicsServer2D::get_singleton()->body_set_state_sync_callback(get_rid(), Callable()); set_only_update_transform_changes(false); set_notify_local_transform(false); } } -void AnimatableBody2D::_body_state_changed_callback(void *p_instance, PhysicsDirectBodyState2D *p_state) { - AnimatableBody2D *body = static_cast<AnimatableBody2D *>(p_instance); - body->_body_state_changed(p_state); -} - void AnimatableBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) { if (!sync_to_physics) { return; @@ -325,7 +320,7 @@ AnimatableBody2D::AnimatableBody2D() : StaticBody2D(PhysicsServer2D::BODY_MODE_KINEMATIC) { } -void RigidDynamicBody2D::_body_enter_tree(ObjectID p_id) { +void RigidBody2D::_body_enter_tree(ObjectID p_id) { Object *obj = ObjectDB::get_instance(p_id); Node *node = Object::cast_to<Node>(obj); ERR_FAIL_COND(!node); @@ -347,7 +342,7 @@ void RigidDynamicBody2D::_body_enter_tree(ObjectID p_id) { contact_monitor->locked = false; } -void RigidDynamicBody2D::_body_exit_tree(ObjectID p_id) { +void RigidBody2D::_body_exit_tree(ObjectID p_id) { Object *obj = ObjectDB::get_instance(p_id); Node *node = Object::cast_to<Node>(obj); ERR_FAIL_COND(!node); @@ -368,7 +363,7 @@ void RigidDynamicBody2D::_body_exit_tree(ObjectID p_id) { contact_monitor->locked = false; } -void RigidDynamicBody2D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, int p_body_shape, int p_local_shape) { +void RigidBody2D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, int p_body_shape, int p_local_shape) { bool body_in = p_status == 1; ObjectID objid = p_instance; @@ -387,8 +382,8 @@ void RigidDynamicBody2D::_body_inout(int p_status, const RID &p_body, ObjectID p //E->value.rc=0; E->value.in_scene = node && node->is_inside_tree(); if (node) { - node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidDynamicBody2D::_body_enter_tree), make_binds(objid)); - node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidDynamicBody2D::_body_exit_tree), make_binds(objid)); + node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidBody2D::_body_enter_tree).bind(objid)); + node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidBody2D::_body_exit_tree).bind(objid)); if (E->value.in_scene) { emit_signal(SceneStringNames::get_singleton()->body_entered, node); } @@ -416,8 +411,8 @@ void RigidDynamicBody2D::_body_inout(int p_status, const RID &p_body, ObjectID p if (E->value.shapes.is_empty()) { if (node) { - node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidDynamicBody2D::_body_enter_tree)); - node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidDynamicBody2D::_body_exit_tree)); + node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidBody2D::_body_enter_tree)); + node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidBody2D::_body_exit_tree)); if (in_scene) { emit_signal(SceneStringNames::get_singleton()->body_exited, node); } @@ -431,19 +426,14 @@ void RigidDynamicBody2D::_body_inout(int p_status, const RID &p_body, ObjectID p } } -struct _RigidDynamicBody2DInOut { +struct _RigidBody2DInOut { RID rid; ObjectID id; int shape = 0; int local_shape = 0; }; -void RigidDynamicBody2D::_body_state_changed_callback(void *p_instance, PhysicsDirectBodyState2D *p_state) { - RigidDynamicBody2D *body = static_cast<RigidDynamicBody2D *>(p_instance); - body->_body_state_changed(p_state); -} - -void RigidDynamicBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) { +void RigidBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) { set_block_transform_notify(true); // don't want notify (would feedback loop) if (!freeze || freeze_mode != FREEZE_MODE_KINEMATIC) { set_global_transform(p_state->get_transform()); @@ -473,9 +463,9 @@ void RigidDynamicBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) } } - _RigidDynamicBody2DInOut *toadd = (_RigidDynamicBody2DInOut *)alloca(p_state->get_contact_count() * sizeof(_RigidDynamicBody2DInOut)); + _RigidBody2DInOut *toadd = (_RigidBody2DInOut *)alloca(p_state->get_contact_count() * sizeof(_RigidBody2DInOut)); int toadd_count = 0; //state->get_contact_count(); - RigidDynamicBody2D_RemoveAction *toremove = (RigidDynamicBody2D_RemoveAction *)alloca(rc * sizeof(RigidDynamicBody2D_RemoveAction)); + RigidBody2D_RemoveAction *toremove = (RigidBody2D_RemoveAction *)alloca(rc * sizeof(RigidBody2D_RemoveAction)); int toremove_count = 0; //put the ones to add @@ -539,7 +529,7 @@ void RigidDynamicBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) } } -void RigidDynamicBody2D::_apply_body_mode() { +void RigidBody2D::_apply_body_mode() { if (freeze) { switch (freeze_mode) { case FREEZE_MODE_STATIC: { @@ -550,13 +540,13 @@ void RigidDynamicBody2D::_apply_body_mode() { } break; } } else if (lock_rotation) { - set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC_LINEAR); + set_body_mode(PhysicsServer2D::BODY_MODE_RIGID_LINEAR); } else { - set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC); + set_body_mode(PhysicsServer2D::BODY_MODE_RIGID); } } -void RigidDynamicBody2D::set_lock_rotation_enabled(bool p_lock_rotation) { +void RigidBody2D::set_lock_rotation_enabled(bool p_lock_rotation) { if (p_lock_rotation == lock_rotation) { return; } @@ -565,11 +555,11 @@ void RigidDynamicBody2D::set_lock_rotation_enabled(bool p_lock_rotation) { _apply_body_mode(); } -bool RigidDynamicBody2D::is_lock_rotation_enabled() const { +bool RigidBody2D::is_lock_rotation_enabled() const { return lock_rotation; } -void RigidDynamicBody2D::set_freeze_enabled(bool p_freeze) { +void RigidBody2D::set_freeze_enabled(bool p_freeze) { if (p_freeze == freeze) { return; } @@ -578,11 +568,11 @@ void RigidDynamicBody2D::set_freeze_enabled(bool p_freeze) { _apply_body_mode(); } -bool RigidDynamicBody2D::is_freeze_enabled() const { +bool RigidBody2D::is_freeze_enabled() const { return freeze; } -void RigidDynamicBody2D::set_freeze_mode(FreezeMode p_freeze_mode) { +void RigidBody2D::set_freeze_mode(FreezeMode p_freeze_mode) { if (p_freeze_mode == freeze_mode) { return; } @@ -591,31 +581,31 @@ void RigidDynamicBody2D::set_freeze_mode(FreezeMode p_freeze_mode) { _apply_body_mode(); } -RigidDynamicBody2D::FreezeMode RigidDynamicBody2D::get_freeze_mode() const { +RigidBody2D::FreezeMode RigidBody2D::get_freeze_mode() const { return freeze_mode; } -void RigidDynamicBody2D::set_mass(real_t p_mass) { +void RigidBody2D::set_mass(real_t p_mass) { ERR_FAIL_COND(p_mass <= 0); mass = p_mass; PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_MASS, mass); } -real_t RigidDynamicBody2D::get_mass() const { +real_t RigidBody2D::get_mass() const { return mass; } -void RigidDynamicBody2D::set_inertia(real_t p_inertia) { +void RigidBody2D::set_inertia(real_t p_inertia) { ERR_FAIL_COND(p_inertia < 0); inertia = p_inertia; PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_INERTIA, inertia); } -real_t RigidDynamicBody2D::get_inertia() const { +real_t RigidBody2D::get_inertia() const { return inertia; } -void RigidDynamicBody2D::set_center_of_mass_mode(CenterOfMassMode p_mode) { +void RigidBody2D::set_center_of_mass_mode(CenterOfMassMode p_mode) { if (center_of_mass_mode == p_mode) { return; } @@ -637,11 +627,11 @@ void RigidDynamicBody2D::set_center_of_mass_mode(CenterOfMassMode p_mode) { } } -RigidDynamicBody2D::CenterOfMassMode RigidDynamicBody2D::get_center_of_mass_mode() const { +RigidBody2D::CenterOfMassMode RigidBody2D::get_center_of_mass_mode() const { return center_of_mass_mode; } -void RigidDynamicBody2D::set_center_of_mass(const Vector2 &p_center_of_mass) { +void RigidBody2D::set_center_of_mass(const Vector2 &p_center_of_mass) { if (center_of_mass == p_center_of_mass) { return; } @@ -652,102 +642,102 @@ void RigidDynamicBody2D::set_center_of_mass(const Vector2 &p_center_of_mass) { PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_CENTER_OF_MASS, center_of_mass); } -const Vector2 &RigidDynamicBody2D::get_center_of_mass() const { +const Vector2 &RigidBody2D::get_center_of_mass() const { return center_of_mass; } -void RigidDynamicBody2D::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) { +void RigidBody2D::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) { if (physics_material_override.is_valid()) { - if (physics_material_override->is_connected(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidDynamicBody2D::_reload_physics_characteristics))) { - physics_material_override->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidDynamicBody2D::_reload_physics_characteristics)); + if (physics_material_override->is_connected(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidBody2D::_reload_physics_characteristics))) { + physics_material_override->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidBody2D::_reload_physics_characteristics)); } } physics_material_override = p_physics_material_override; if (physics_material_override.is_valid()) { - physics_material_override->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidDynamicBody2D::_reload_physics_characteristics)); + physics_material_override->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidBody2D::_reload_physics_characteristics)); } _reload_physics_characteristics(); } -Ref<PhysicsMaterial> RigidDynamicBody2D::get_physics_material_override() const { +Ref<PhysicsMaterial> RigidBody2D::get_physics_material_override() const { return physics_material_override; } -void RigidDynamicBody2D::set_gravity_scale(real_t p_gravity_scale) { +void RigidBody2D::set_gravity_scale(real_t p_gravity_scale) { gravity_scale = p_gravity_scale; PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_GRAVITY_SCALE, gravity_scale); } -real_t RigidDynamicBody2D::get_gravity_scale() const { +real_t RigidBody2D::get_gravity_scale() const { return gravity_scale; } -void RigidDynamicBody2D::set_linear_damp_mode(DampMode p_mode) { +void RigidBody2D::set_linear_damp_mode(DampMode p_mode) { linear_damp_mode = p_mode; PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_LINEAR_DAMP_MODE, linear_damp_mode); } -RigidDynamicBody2D::DampMode RigidDynamicBody2D::get_linear_damp_mode() const { +RigidBody2D::DampMode RigidBody2D::get_linear_damp_mode() const { return linear_damp_mode; } -void RigidDynamicBody2D::set_angular_damp_mode(DampMode p_mode) { +void RigidBody2D::set_angular_damp_mode(DampMode p_mode) { angular_damp_mode = p_mode; PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_ANGULAR_DAMP_MODE, angular_damp_mode); } -RigidDynamicBody2D::DampMode RigidDynamicBody2D::get_angular_damp_mode() const { +RigidBody2D::DampMode RigidBody2D::get_angular_damp_mode() const { return angular_damp_mode; } -void RigidDynamicBody2D::set_linear_damp(real_t p_linear_damp) { +void RigidBody2D::set_linear_damp(real_t p_linear_damp) { ERR_FAIL_COND(p_linear_damp < -1); linear_damp = p_linear_damp; PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_LINEAR_DAMP, linear_damp); } -real_t RigidDynamicBody2D::get_linear_damp() const { +real_t RigidBody2D::get_linear_damp() const { return linear_damp; } -void RigidDynamicBody2D::set_angular_damp(real_t p_angular_damp) { +void RigidBody2D::set_angular_damp(real_t p_angular_damp) { ERR_FAIL_COND(p_angular_damp < -1); angular_damp = p_angular_damp; PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_ANGULAR_DAMP, angular_damp); } -real_t RigidDynamicBody2D::get_angular_damp() const { +real_t RigidBody2D::get_angular_damp() const { return angular_damp; } -void RigidDynamicBody2D::set_axis_velocity(const Vector2 &p_axis) { +void RigidBody2D::set_axis_velocity(const Vector2 &p_axis) { Vector2 axis = p_axis.normalized(); linear_velocity -= axis * axis.dot(linear_velocity); linear_velocity += p_axis; PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_LINEAR_VELOCITY, linear_velocity); } -void RigidDynamicBody2D::set_linear_velocity(const Vector2 &p_velocity) { +void RigidBody2D::set_linear_velocity(const Vector2 &p_velocity) { linear_velocity = p_velocity; PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_LINEAR_VELOCITY, linear_velocity); } -Vector2 RigidDynamicBody2D::get_linear_velocity() const { +Vector2 RigidBody2D::get_linear_velocity() const { return linear_velocity; } -void RigidDynamicBody2D::set_angular_velocity(real_t p_velocity) { +void RigidBody2D::set_angular_velocity(real_t p_velocity) { angular_velocity = p_velocity; PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_ANGULAR_VELOCITY, angular_velocity); } -real_t RigidDynamicBody2D::get_angular_velocity() const { +real_t RigidBody2D::get_angular_velocity() const { return angular_velocity; } -void RigidDynamicBody2D::set_use_custom_integrator(bool p_enable) { +void RigidBody2D::set_use_custom_integrator(bool p_enable) { if (custom_integrator == p_enable) { return; } @@ -756,100 +746,106 @@ void RigidDynamicBody2D::set_use_custom_integrator(bool p_enable) { PhysicsServer2D::get_singleton()->body_set_omit_force_integration(get_rid(), p_enable); } -bool RigidDynamicBody2D::is_using_custom_integrator() { +bool RigidBody2D::is_using_custom_integrator() { return custom_integrator; } -void RigidDynamicBody2D::set_sleeping(bool p_sleeping) { +void RigidBody2D::set_sleeping(bool p_sleeping) { sleeping = p_sleeping; PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_SLEEPING, sleeping); } -void RigidDynamicBody2D::set_can_sleep(bool p_active) { +void RigidBody2D::set_can_sleep(bool p_active) { can_sleep = p_active; PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_CAN_SLEEP, p_active); } -bool RigidDynamicBody2D::is_able_to_sleep() const { +bool RigidBody2D::is_able_to_sleep() const { return can_sleep; } -bool RigidDynamicBody2D::is_sleeping() const { +bool RigidBody2D::is_sleeping() const { return sleeping; } -void RigidDynamicBody2D::set_max_contacts_reported(int p_amount) { +void RigidBody2D::set_max_contacts_reported(int p_amount) { max_contacts_reported = p_amount; PhysicsServer2D::get_singleton()->body_set_max_contacts_reported(get_rid(), p_amount); } -int RigidDynamicBody2D::get_max_contacts_reported() const { +int RigidBody2D::get_max_contacts_reported() const { return max_contacts_reported; } -void RigidDynamicBody2D::apply_central_impulse(const Vector2 &p_impulse) { +int RigidBody2D::get_contact_count() const { + PhysicsDirectBodyState2D *bs = PhysicsServer2D::get_singleton()->body_get_direct_state(get_rid()); + ERR_FAIL_NULL_V(bs, 0); + return bs->get_contact_count(); +} + +void RigidBody2D::apply_central_impulse(const Vector2 &p_impulse) { PhysicsServer2D::get_singleton()->body_apply_central_impulse(get_rid(), p_impulse); } -void RigidDynamicBody2D::apply_impulse(const Vector2 &p_impulse, const Vector2 &p_position) { +void RigidBody2D::apply_impulse(const Vector2 &p_impulse, const Vector2 &p_position) { PhysicsServer2D::get_singleton()->body_apply_impulse(get_rid(), p_impulse, p_position); } -void RigidDynamicBody2D::apply_torque_impulse(real_t p_torque) { +void RigidBody2D::apply_torque_impulse(real_t p_torque) { PhysicsServer2D::get_singleton()->body_apply_torque_impulse(get_rid(), p_torque); } -void RigidDynamicBody2D::apply_central_force(const Vector2 &p_force) { +void RigidBody2D::apply_central_force(const Vector2 &p_force) { PhysicsServer2D::get_singleton()->body_apply_central_force(get_rid(), p_force); } -void RigidDynamicBody2D::apply_force(const Vector2 &p_force, const Vector2 &p_position) { +void RigidBody2D::apply_force(const Vector2 &p_force, const Vector2 &p_position) { PhysicsServer2D::get_singleton()->body_apply_force(get_rid(), p_force, p_position); } -void RigidDynamicBody2D::apply_torque(real_t p_torque) { +void RigidBody2D::apply_torque(real_t p_torque) { PhysicsServer2D::get_singleton()->body_apply_torque(get_rid(), p_torque); } -void RigidDynamicBody2D::add_constant_central_force(const Vector2 &p_force) { +void RigidBody2D::add_constant_central_force(const Vector2 &p_force) { PhysicsServer2D::get_singleton()->body_add_constant_central_force(get_rid(), p_force); } -void RigidDynamicBody2D::add_constant_force(const Vector2 &p_force, const Vector2 &p_position) { +void RigidBody2D::add_constant_force(const Vector2 &p_force, const Vector2 &p_position) { PhysicsServer2D::get_singleton()->body_add_constant_force(get_rid(), p_force, p_position); } -void RigidDynamicBody2D::add_constant_torque(const real_t p_torque) { +void RigidBody2D::add_constant_torque(const real_t p_torque) { PhysicsServer2D::get_singleton()->body_add_constant_torque(get_rid(), p_torque); } -void RigidDynamicBody2D::set_constant_force(const Vector2 &p_force) { +void RigidBody2D::set_constant_force(const Vector2 &p_force) { PhysicsServer2D::get_singleton()->body_set_constant_force(get_rid(), p_force); } -Vector2 RigidDynamicBody2D::get_constant_force() const { +Vector2 RigidBody2D::get_constant_force() const { return PhysicsServer2D::get_singleton()->body_get_constant_force(get_rid()); } -void RigidDynamicBody2D::set_constant_torque(real_t p_torque) { +void RigidBody2D::set_constant_torque(real_t p_torque) { PhysicsServer2D::get_singleton()->body_set_constant_torque(get_rid(), p_torque); } -real_t RigidDynamicBody2D::get_constant_torque() const { +real_t RigidBody2D::get_constant_torque() const { return PhysicsServer2D::get_singleton()->body_get_constant_torque(get_rid()); } -void RigidDynamicBody2D::set_continuous_collision_detection_mode(CCDMode p_mode) { +void RigidBody2D::set_continuous_collision_detection_mode(CCDMode p_mode) { ccd_mode = p_mode; PhysicsServer2D::get_singleton()->body_set_continuous_collision_detection_mode(get_rid(), PhysicsServer2D::CCDMode(p_mode)); } -RigidDynamicBody2D::CCDMode RigidDynamicBody2D::get_continuous_collision_detection_mode() const { +RigidBody2D::CCDMode RigidBody2D::get_continuous_collision_detection_mode() const { return ccd_mode; } -TypedArray<Node2D> RigidDynamicBody2D::get_colliding_bodies() const { - ERR_FAIL_COND_V(!contact_monitor, Array()); +TypedArray<Node2D> RigidBody2D::get_colliding_bodies() const { + ERR_FAIL_COND_V(!contact_monitor, TypedArray<Node2D>()); TypedArray<Node2D> ret; ret.resize(contact_monitor->body_map.size()); @@ -866,7 +862,7 @@ TypedArray<Node2D> RigidDynamicBody2D::get_colliding_bodies() const { return ret; } -void RigidDynamicBody2D::set_contact_monitor(bool p_enabled) { +void RigidBody2D::set_contact_monitor(bool p_enabled) { if (p_enabled == is_contact_monitor_enabled()) { return; } @@ -880,8 +876,8 @@ void RigidDynamicBody2D::set_contact_monitor(bool p_enabled) { Node *node = Object::cast_to<Node>(obj); if (node) { - node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidDynamicBody2D::_body_enter_tree)); - node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidDynamicBody2D::_body_exit_tree)); + node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidBody2D::_body_enter_tree)); + node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidBody2D::_body_exit_tree)); } } @@ -893,11 +889,11 @@ void RigidDynamicBody2D::set_contact_monitor(bool p_enabled) { } } -bool RigidDynamicBody2D::is_contact_monitor_enabled() const { +bool RigidBody2D::is_contact_monitor_enabled() const { return contact_monitor != nullptr; } -void RigidDynamicBody2D::_notification(int p_what) { +void RigidBody2D::_notification(int p_what) { #ifdef TOOLS_ENABLED switch (p_what) { case NOTIFICATION_ENTER_TREE: { @@ -915,115 +911,116 @@ void RigidDynamicBody2D::_notification(int p_what) { #endif } -TypedArray<String> RigidDynamicBody2D::get_configuration_warnings() const { +PackedStringArray RigidBody2D::get_configuration_warnings() const { Transform2D t = get_transform(); - TypedArray<String> warnings = CollisionObject2D::get_configuration_warnings(); + PackedStringArray warnings = CollisionObject2D::get_configuration_warnings(); if (ABS(t.columns[0].length() - 1.0) > 0.05 || ABS(t.columns[1].length() - 1.0) > 0.05) { - warnings.push_back(RTR("Size changes to RigidDynamicBody2D will be overridden by the physics engine when running.\nChange the size in children collision shapes instead.")); + warnings.push_back(RTR("Size changes to RigidBody2D will be overridden by the physics engine when running.\nChange the size in children collision shapes instead.")); } return warnings; } -void RigidDynamicBody2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_mass", "mass"), &RigidDynamicBody2D::set_mass); - ClassDB::bind_method(D_METHOD("get_mass"), &RigidDynamicBody2D::get_mass); +void RigidBody2D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_mass", "mass"), &RigidBody2D::set_mass); + ClassDB::bind_method(D_METHOD("get_mass"), &RigidBody2D::get_mass); - ClassDB::bind_method(D_METHOD("get_inertia"), &RigidDynamicBody2D::get_inertia); - ClassDB::bind_method(D_METHOD("set_inertia", "inertia"), &RigidDynamicBody2D::set_inertia); + ClassDB::bind_method(D_METHOD("get_inertia"), &RigidBody2D::get_inertia); + ClassDB::bind_method(D_METHOD("set_inertia", "inertia"), &RigidBody2D::set_inertia); - ClassDB::bind_method(D_METHOD("set_center_of_mass_mode", "mode"), &RigidDynamicBody2D::set_center_of_mass_mode); - ClassDB::bind_method(D_METHOD("get_center_of_mass_mode"), &RigidDynamicBody2D::get_center_of_mass_mode); + ClassDB::bind_method(D_METHOD("set_center_of_mass_mode", "mode"), &RigidBody2D::set_center_of_mass_mode); + ClassDB::bind_method(D_METHOD("get_center_of_mass_mode"), &RigidBody2D::get_center_of_mass_mode); - ClassDB::bind_method(D_METHOD("set_center_of_mass", "center_of_mass"), &RigidDynamicBody2D::set_center_of_mass); - ClassDB::bind_method(D_METHOD("get_center_of_mass"), &RigidDynamicBody2D::get_center_of_mass); + ClassDB::bind_method(D_METHOD("set_center_of_mass", "center_of_mass"), &RigidBody2D::set_center_of_mass); + ClassDB::bind_method(D_METHOD("get_center_of_mass"), &RigidBody2D::get_center_of_mass); - ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &RigidDynamicBody2D::set_physics_material_override); - ClassDB::bind_method(D_METHOD("get_physics_material_override"), &RigidDynamicBody2D::get_physics_material_override); + ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &RigidBody2D::set_physics_material_override); + ClassDB::bind_method(D_METHOD("get_physics_material_override"), &RigidBody2D::get_physics_material_override); - ClassDB::bind_method(D_METHOD("set_gravity_scale", "gravity_scale"), &RigidDynamicBody2D::set_gravity_scale); - ClassDB::bind_method(D_METHOD("get_gravity_scale"), &RigidDynamicBody2D::get_gravity_scale); + ClassDB::bind_method(D_METHOD("set_gravity_scale", "gravity_scale"), &RigidBody2D::set_gravity_scale); + ClassDB::bind_method(D_METHOD("get_gravity_scale"), &RigidBody2D::get_gravity_scale); - ClassDB::bind_method(D_METHOD("set_linear_damp_mode", "linear_damp_mode"), &RigidDynamicBody2D::set_linear_damp_mode); - ClassDB::bind_method(D_METHOD("get_linear_damp_mode"), &RigidDynamicBody2D::get_linear_damp_mode); + ClassDB::bind_method(D_METHOD("set_linear_damp_mode", "linear_damp_mode"), &RigidBody2D::set_linear_damp_mode); + ClassDB::bind_method(D_METHOD("get_linear_damp_mode"), &RigidBody2D::get_linear_damp_mode); - ClassDB::bind_method(D_METHOD("set_angular_damp_mode", "angular_damp_mode"), &RigidDynamicBody2D::set_angular_damp_mode); - ClassDB::bind_method(D_METHOD("get_angular_damp_mode"), &RigidDynamicBody2D::get_angular_damp_mode); + ClassDB::bind_method(D_METHOD("set_angular_damp_mode", "angular_damp_mode"), &RigidBody2D::set_angular_damp_mode); + ClassDB::bind_method(D_METHOD("get_angular_damp_mode"), &RigidBody2D::get_angular_damp_mode); - ClassDB::bind_method(D_METHOD("set_linear_damp", "linear_damp"), &RigidDynamicBody2D::set_linear_damp); - ClassDB::bind_method(D_METHOD("get_linear_damp"), &RigidDynamicBody2D::get_linear_damp); + ClassDB::bind_method(D_METHOD("set_linear_damp", "linear_damp"), &RigidBody2D::set_linear_damp); + ClassDB::bind_method(D_METHOD("get_linear_damp"), &RigidBody2D::get_linear_damp); - ClassDB::bind_method(D_METHOD("set_angular_damp", "angular_damp"), &RigidDynamicBody2D::set_angular_damp); - ClassDB::bind_method(D_METHOD("get_angular_damp"), &RigidDynamicBody2D::get_angular_damp); + ClassDB::bind_method(D_METHOD("set_angular_damp", "angular_damp"), &RigidBody2D::set_angular_damp); + ClassDB::bind_method(D_METHOD("get_angular_damp"), &RigidBody2D::get_angular_damp); - ClassDB::bind_method(D_METHOD("set_linear_velocity", "linear_velocity"), &RigidDynamicBody2D::set_linear_velocity); - ClassDB::bind_method(D_METHOD("get_linear_velocity"), &RigidDynamicBody2D::get_linear_velocity); + ClassDB::bind_method(D_METHOD("set_linear_velocity", "linear_velocity"), &RigidBody2D::set_linear_velocity); + ClassDB::bind_method(D_METHOD("get_linear_velocity"), &RigidBody2D::get_linear_velocity); - ClassDB::bind_method(D_METHOD("set_angular_velocity", "angular_velocity"), &RigidDynamicBody2D::set_angular_velocity); - ClassDB::bind_method(D_METHOD("get_angular_velocity"), &RigidDynamicBody2D::get_angular_velocity); + ClassDB::bind_method(D_METHOD("set_angular_velocity", "angular_velocity"), &RigidBody2D::set_angular_velocity); + ClassDB::bind_method(D_METHOD("get_angular_velocity"), &RigidBody2D::get_angular_velocity); - ClassDB::bind_method(D_METHOD("set_max_contacts_reported", "amount"), &RigidDynamicBody2D::set_max_contacts_reported); - ClassDB::bind_method(D_METHOD("get_max_contacts_reported"), &RigidDynamicBody2D::get_max_contacts_reported); + ClassDB::bind_method(D_METHOD("set_max_contacts_reported", "amount"), &RigidBody2D::set_max_contacts_reported); + ClassDB::bind_method(D_METHOD("get_max_contacts_reported"), &RigidBody2D::get_max_contacts_reported); + ClassDB::bind_method(D_METHOD("get_contact_count"), &RigidBody2D::get_contact_count); - ClassDB::bind_method(D_METHOD("set_use_custom_integrator", "enable"), &RigidDynamicBody2D::set_use_custom_integrator); - ClassDB::bind_method(D_METHOD("is_using_custom_integrator"), &RigidDynamicBody2D::is_using_custom_integrator); + ClassDB::bind_method(D_METHOD("set_use_custom_integrator", "enable"), &RigidBody2D::set_use_custom_integrator); + ClassDB::bind_method(D_METHOD("is_using_custom_integrator"), &RigidBody2D::is_using_custom_integrator); - ClassDB::bind_method(D_METHOD("set_contact_monitor", "enabled"), &RigidDynamicBody2D::set_contact_monitor); - ClassDB::bind_method(D_METHOD("is_contact_monitor_enabled"), &RigidDynamicBody2D::is_contact_monitor_enabled); + ClassDB::bind_method(D_METHOD("set_contact_monitor", "enabled"), &RigidBody2D::set_contact_monitor); + ClassDB::bind_method(D_METHOD("is_contact_monitor_enabled"), &RigidBody2D::is_contact_monitor_enabled); - ClassDB::bind_method(D_METHOD("set_continuous_collision_detection_mode", "mode"), &RigidDynamicBody2D::set_continuous_collision_detection_mode); - ClassDB::bind_method(D_METHOD("get_continuous_collision_detection_mode"), &RigidDynamicBody2D::get_continuous_collision_detection_mode); + ClassDB::bind_method(D_METHOD("set_continuous_collision_detection_mode", "mode"), &RigidBody2D::set_continuous_collision_detection_mode); + ClassDB::bind_method(D_METHOD("get_continuous_collision_detection_mode"), &RigidBody2D::get_continuous_collision_detection_mode); - ClassDB::bind_method(D_METHOD("set_axis_velocity", "axis_velocity"), &RigidDynamicBody2D::set_axis_velocity); - ClassDB::bind_method(D_METHOD("apply_central_impulse", "impulse"), &RigidDynamicBody2D::apply_central_impulse, Vector2()); - ClassDB::bind_method(D_METHOD("apply_impulse", "impulse", "position"), &RigidDynamicBody2D::apply_impulse, Vector2()); - ClassDB::bind_method(D_METHOD("apply_torque_impulse", "torque"), &RigidDynamicBody2D::apply_torque_impulse); + ClassDB::bind_method(D_METHOD("set_axis_velocity", "axis_velocity"), &RigidBody2D::set_axis_velocity); + ClassDB::bind_method(D_METHOD("apply_central_impulse", "impulse"), &RigidBody2D::apply_central_impulse, Vector2()); + ClassDB::bind_method(D_METHOD("apply_impulse", "impulse", "position"), &RigidBody2D::apply_impulse, Vector2()); + ClassDB::bind_method(D_METHOD("apply_torque_impulse", "torque"), &RigidBody2D::apply_torque_impulse); - ClassDB::bind_method(D_METHOD("apply_central_force", "force"), &RigidDynamicBody2D::apply_central_force); - ClassDB::bind_method(D_METHOD("apply_force", "force", "position"), &RigidDynamicBody2D::apply_force, Vector2()); - ClassDB::bind_method(D_METHOD("apply_torque", "torque"), &RigidDynamicBody2D::apply_torque); + ClassDB::bind_method(D_METHOD("apply_central_force", "force"), &RigidBody2D::apply_central_force); + ClassDB::bind_method(D_METHOD("apply_force", "force", "position"), &RigidBody2D::apply_force, Vector2()); + ClassDB::bind_method(D_METHOD("apply_torque", "torque"), &RigidBody2D::apply_torque); - ClassDB::bind_method(D_METHOD("add_constant_central_force", "force"), &RigidDynamicBody2D::add_constant_central_force); - ClassDB::bind_method(D_METHOD("add_constant_force", "force", "position"), &RigidDynamicBody2D::add_constant_force, Vector2()); - ClassDB::bind_method(D_METHOD("add_constant_torque", "torque"), &RigidDynamicBody2D::add_constant_torque); + ClassDB::bind_method(D_METHOD("add_constant_central_force", "force"), &RigidBody2D::add_constant_central_force); + ClassDB::bind_method(D_METHOD("add_constant_force", "force", "position"), &RigidBody2D::add_constant_force, Vector2()); + ClassDB::bind_method(D_METHOD("add_constant_torque", "torque"), &RigidBody2D::add_constant_torque); - ClassDB::bind_method(D_METHOD("set_constant_force", "force"), &RigidDynamicBody2D::set_constant_force); - ClassDB::bind_method(D_METHOD("get_constant_force"), &RigidDynamicBody2D::get_constant_force); + ClassDB::bind_method(D_METHOD("set_constant_force", "force"), &RigidBody2D::set_constant_force); + ClassDB::bind_method(D_METHOD("get_constant_force"), &RigidBody2D::get_constant_force); - ClassDB::bind_method(D_METHOD("set_constant_torque", "torque"), &RigidDynamicBody2D::set_constant_torque); - ClassDB::bind_method(D_METHOD("get_constant_torque"), &RigidDynamicBody2D::get_constant_torque); + ClassDB::bind_method(D_METHOD("set_constant_torque", "torque"), &RigidBody2D::set_constant_torque); + ClassDB::bind_method(D_METHOD("get_constant_torque"), &RigidBody2D::get_constant_torque); - ClassDB::bind_method(D_METHOD("set_sleeping", "sleeping"), &RigidDynamicBody2D::set_sleeping); - ClassDB::bind_method(D_METHOD("is_sleeping"), &RigidDynamicBody2D::is_sleeping); + ClassDB::bind_method(D_METHOD("set_sleeping", "sleeping"), &RigidBody2D::set_sleeping); + ClassDB::bind_method(D_METHOD("is_sleeping"), &RigidBody2D::is_sleeping); - ClassDB::bind_method(D_METHOD("set_can_sleep", "able_to_sleep"), &RigidDynamicBody2D::set_can_sleep); - ClassDB::bind_method(D_METHOD("is_able_to_sleep"), &RigidDynamicBody2D::is_able_to_sleep); + ClassDB::bind_method(D_METHOD("set_can_sleep", "able_to_sleep"), &RigidBody2D::set_can_sleep); + ClassDB::bind_method(D_METHOD("is_able_to_sleep"), &RigidBody2D::is_able_to_sleep); - ClassDB::bind_method(D_METHOD("set_lock_rotation_enabled", "lock_rotation"), &RigidDynamicBody2D::set_lock_rotation_enabled); - ClassDB::bind_method(D_METHOD("is_lock_rotation_enabled"), &RigidDynamicBody2D::is_lock_rotation_enabled); + ClassDB::bind_method(D_METHOD("set_lock_rotation_enabled", "lock_rotation"), &RigidBody2D::set_lock_rotation_enabled); + ClassDB::bind_method(D_METHOD("is_lock_rotation_enabled"), &RigidBody2D::is_lock_rotation_enabled); - ClassDB::bind_method(D_METHOD("set_freeze_enabled", "freeze_mode"), &RigidDynamicBody2D::set_freeze_enabled); - ClassDB::bind_method(D_METHOD("is_freeze_enabled"), &RigidDynamicBody2D::is_freeze_enabled); + ClassDB::bind_method(D_METHOD("set_freeze_enabled", "freeze_mode"), &RigidBody2D::set_freeze_enabled); + ClassDB::bind_method(D_METHOD("is_freeze_enabled"), &RigidBody2D::is_freeze_enabled); - ClassDB::bind_method(D_METHOD("set_freeze_mode", "freeze_mode"), &RigidDynamicBody2D::set_freeze_mode); - ClassDB::bind_method(D_METHOD("get_freeze_mode"), &RigidDynamicBody2D::get_freeze_mode); + ClassDB::bind_method(D_METHOD("set_freeze_mode", "freeze_mode"), &RigidBody2D::set_freeze_mode); + ClassDB::bind_method(D_METHOD("get_freeze_mode"), &RigidBody2D::get_freeze_mode); - ClassDB::bind_method(D_METHOD("get_colliding_bodies"), &RigidDynamicBody2D::get_colliding_bodies); + ClassDB::bind_method(D_METHOD("get_colliding_bodies"), &RigidBody2D::get_colliding_bodies); GDVIRTUAL_BIND(_integrate_forces, "state"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass", PROPERTY_HINT_RANGE, "0.01,1000,0.01,or_greater,exp,suffix:kg"), "set_mass", "get_mass"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "inertia", PROPERTY_HINT_RANGE, U"0,1000,0.01,or_greater,exp,suffix:kg\u22C5px\u00B2"), "set_inertia", "get_inertia"); ADD_PROPERTY(PropertyInfo(Variant::INT, "center_of_mass_mode", PROPERTY_HINT_ENUM, "Auto,Custom", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_center_of_mass_mode", "get_center_of_mass_mode"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "center_of_mass", PROPERTY_HINT_RANGE, "-10,10,0.01,or_lesser,or_greater,suffix:px"), "set_center_of_mass", "get_center_of_mass"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "center_of_mass", PROPERTY_HINT_RANGE, "-10,10,0.01,or_less,or_greater,suffix:px"), "set_center_of_mass", "get_center_of_mass"); ADD_LINKED_PROPERTY("center_of_mass_mode", "center_of_mass"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_scale", PROPERTY_HINT_RANGE, "-128,128,0.01"), "set_gravity_scale", "get_gravity_scale"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "custom_integrator"), "set_use_custom_integrator", "is_using_custom_integrator"); ADD_PROPERTY(PropertyInfo(Variant::INT, "continuous_cd", PROPERTY_HINT_ENUM, "Disabled,Cast Ray,Cast Shape"), "set_continuous_collision_detection_mode", "get_continuous_collision_detection_mode"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "contacts_reported", PROPERTY_HINT_RANGE, "0,64,1,or_greater"), "set_max_contacts_reported", "get_max_contacts_reported"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "max_contacts_reported", PROPERTY_HINT_RANGE, "0,64,1,or_greater"), "set_max_contacts_reported", "get_max_contacts_reported"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "contact_monitor"), "set_contact_monitor", "is_contact_monitor_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sleeping"), "set_sleeping", "is_sleeping"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "can_sleep"), "set_can_sleep", "is_able_to_sleep"); @@ -1062,26 +1059,26 @@ void RigidDynamicBody2D::_bind_methods() { BIND_ENUM_CONSTANT(CCD_MODE_CAST_SHAPE); } -void RigidDynamicBody2D::_validate_property(PropertyInfo &property) const { +void RigidBody2D::_validate_property(PropertyInfo &p_property) const { if (center_of_mass_mode != CENTER_OF_MASS_MODE_CUSTOM) { - if (property.name == "center_of_mass") { - property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + if (p_property.name == "center_of_mass") { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } } } -RigidDynamicBody2D::RigidDynamicBody2D() : - PhysicsBody2D(PhysicsServer2D::BODY_MODE_DYNAMIC) { - PhysicsServer2D::get_singleton()->body_set_state_sync_callback(get_rid(), this, _body_state_changed_callback); +RigidBody2D::RigidBody2D() : + PhysicsBody2D(PhysicsServer2D::BODY_MODE_RIGID) { + PhysicsServer2D::get_singleton()->body_set_state_sync_callback(get_rid(), callable_mp(this, &RigidBody2D::_body_state_changed)); } -RigidDynamicBody2D::~RigidDynamicBody2D() { +RigidBody2D::~RigidBody2D() { if (contact_monitor) { memdelete(contact_monitor); } } -void RigidDynamicBody2D::_reload_physics_characteristics() { +void RigidBody2D::_reload_physics_characteristics() { if (physics_material_override.is_null()) { PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_BOUNCE, 0); PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_FRICTION, 1); @@ -1107,9 +1104,9 @@ bool CharacterBody2D::move_and_slide() { if ((on_floor || on_wall) && platform_rid.is_valid()) { bool excluded = false; if (on_floor) { - excluded = (moving_platform_floor_layers & platform_layer) == 0; + excluded = (platform_floor_layers & platform_layer) == 0; } else if (on_wall) { - excluded = (moving_platform_wall_layers & platform_layer) == 0; + excluded = (platform_wall_layers & platform_layer) == 0; } if (!excluded) { //this approach makes sure there is less delay between the actual body velocity and the one we saved @@ -1135,9 +1132,8 @@ bool CharacterBody2D::move_and_slide() { on_ceiling = false; on_wall = false; - if (!current_platform_velocity.is_equal_approx(Vector2())) { + if (!current_platform_velocity.is_zero_approx()) { PhysicsServer2D::MotionParameters parameters(get_global_transform(), current_platform_velocity * delta, margin); - parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. parameters.exclude_bodies.insert(platform_rid); if (platform_object_id.is_valid()) { parameters.exclude_objects.insert(platform_object_id); @@ -1159,10 +1155,10 @@ bool CharacterBody2D::move_and_slide() { // Compute real velocity. real_velocity = get_position_delta() / delta; - if (moving_platform_apply_velocity_on_leave != PLATFORM_VEL_ON_LEAVE_NEVER) { + if (platform_on_leave != PLATFORM_ON_LEAVE_DO_NOTHING) { // Add last platform velocity when just left a moving platform. if (!on_floor && !on_wall) { - if (moving_platform_apply_velocity_on_leave == PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY && current_platform_velocity.dot(up_direction) < 0) { + if (platform_on_leave == PLATFORM_ON_LEAVE_ADD_UPWARD_VELOCITY && current_platform_velocity.dot(up_direction) < 0) { current_platform_velocity = current_platform_velocity.slide(up_direction); } velocity += current_platform_velocity; @@ -1196,7 +1192,6 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo for (int iteration = 0; iteration < max_slides; ++iteration) { PhysicsServer2D::MotionParameters parameters(get_global_transform(), motion, margin); - parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. Vector2 prev_position = parameters.from.columns[2]; @@ -1234,7 +1229,7 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo break; } - if (result.remainder.is_equal_approx(Vector2())) { + if (result.remainder.is_zero_approx()) { motion = Vector2(); break; } @@ -1318,7 +1313,7 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo sliding_enabled = true; first_slide = false; - if (!collided || motion.is_equal_approx(Vector2())) { + if (!collided || motion.is_zero_approx()) { break; } } @@ -1353,7 +1348,6 @@ void CharacterBody2D::_move_and_slide_floating(double p_delta) { bool first_slide = true; for (int iteration = 0; iteration < max_slides; ++iteration) { PhysicsServer2D::MotionParameters parameters(get_global_transform(), motion, margin); - parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. PhysicsServer2D::MotionResult result; bool collided = move_and_collide(parameters, result, false, false); @@ -1364,7 +1358,7 @@ void CharacterBody2D::_move_and_slide_floating(double p_delta) { motion_results.push_back(result); _set_collision_direction(result); - if (result.remainder.is_equal_approx(Vector2())) { + if (result.remainder.is_zero_approx()) { motion = Vector2(); break; } @@ -1383,7 +1377,7 @@ void CharacterBody2D::_move_and_slide_floating(double p_delta) { } } - if (!collided || motion.is_equal_approx(Vector2())) { + if (!collided || motion.is_zero_approx()) { break; } @@ -1400,7 +1394,7 @@ void CharacterBody2D::_snap_on_floor(bool p_was_on_floor, bool p_vel_dir_facing_ real_t length = MAX(floor_snap_length, margin); PhysicsServer2D::MotionParameters parameters(get_global_transform(), -up_direction * length, margin); - parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. + parameters.recovery_as_collision = true; // Report margin recovery as collision to improve floor detection. parameters.collide_separation_ray = true; PhysicsServer2D::MotionResult result; @@ -1436,7 +1430,7 @@ bool CharacterBody2D::_on_floor_if_snapped(bool p_was_on_floor, bool p_vel_dir_f real_t length = MAX(floor_snap_length, margin); PhysicsServer2D::MotionParameters parameters(get_global_transform(), -up_direction * length, margin); - parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. + parameters.recovery_as_collision = true; // Report margin recovery as collision to improve floor detection. parameters.collide_separation_ray = true; PhysicsServer2D::MotionResult result; @@ -1550,7 +1544,7 @@ Ref<KinematicCollision2D> CharacterBody2D::_get_slide_collision(int p_bounce) { } // Create a new instance when the cached reference is invalid or still in use in script. - if (slide_colliders[p_bounce].is_null() || slide_colliders[p_bounce]->reference_get_count() > 1) { + if (slide_colliders[p_bounce].is_null() || slide_colliders[p_bounce]->get_reference_count() > 1) { slide_colliders.write[p_bounce].instantiate(); slide_colliders.write[p_bounce]->owner = this; } @@ -1606,20 +1600,20 @@ void CharacterBody2D::set_slide_on_ceiling_enabled(bool p_enabled) { slide_on_ceiling = p_enabled; } -uint32_t CharacterBody2D::get_moving_platform_floor_layers() const { - return moving_platform_floor_layers; +uint32_t CharacterBody2D::get_platform_floor_layers() const { + return platform_floor_layers; } -void CharacterBody2D::set_moving_platform_floor_layers(uint32_t p_exclude_layers) { - moving_platform_floor_layers = p_exclude_layers; +void CharacterBody2D::set_platform_floor_layers(uint32_t p_exclude_layers) { + platform_floor_layers = p_exclude_layers; } -uint32_t CharacterBody2D::get_moving_platform_wall_layers() const { - return moving_platform_wall_layers; +uint32_t CharacterBody2D::get_platform_wall_layers() const { + return platform_wall_layers; } -void CharacterBody2D::set_moving_platform_wall_layers(uint32_t p_exclude_layers) { - moving_platform_wall_layers = p_exclude_layers; +void CharacterBody2D::set_platform_wall_layers(uint32_t p_exclude_layers) { + platform_wall_layers = p_exclude_layers; } void CharacterBody2D::set_motion_mode(MotionMode p_mode) { @@ -1630,12 +1624,12 @@ CharacterBody2D::MotionMode CharacterBody2D::get_motion_mode() const { return motion_mode; } -void CharacterBody2D::set_moving_platform_apply_velocity_on_leave(MovingPlatformApplyVelocityOnLeave p_on_leave_apply_velocity) { - moving_platform_apply_velocity_on_leave = p_on_leave_apply_velocity; +void CharacterBody2D::set_platform_on_leave(PlatformOnLeave p_on_leave_apply_velocity) { + platform_on_leave = p_on_leave_apply_velocity; } -CharacterBody2D::MovingPlatformApplyVelocityOnLeave CharacterBody2D::get_moving_platform_apply_velocity_on_leave() const { - return moving_platform_apply_velocity_on_leave; +CharacterBody2D::PlatformOnLeave CharacterBody2D::get_platform_on_leave() const { + return platform_on_leave; } int CharacterBody2D::get_max_slides() const { @@ -1702,7 +1696,7 @@ void CharacterBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &CharacterBody2D::set_velocity); ClassDB::bind_method(D_METHOD("get_velocity"), &CharacterBody2D::get_velocity); - ClassDB::bind_method(D_METHOD("set_safe_margin", "pixels"), &CharacterBody2D::set_safe_margin); + ClassDB::bind_method(D_METHOD("set_safe_margin", "margin"), &CharacterBody2D::set_safe_margin); ClassDB::bind_method(D_METHOD("get_safe_margin"), &CharacterBody2D::get_safe_margin); ClassDB::bind_method(D_METHOD("is_floor_stop_on_slope_enabled"), &CharacterBody2D::is_floor_stop_on_slope_enabled); ClassDB::bind_method(D_METHOD("set_floor_stop_on_slope_enabled", "enabled"), &CharacterBody2D::set_floor_stop_on_slope_enabled); @@ -1713,10 +1707,10 @@ void CharacterBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_slide_on_ceiling_enabled", "enabled"), &CharacterBody2D::set_slide_on_ceiling_enabled); ClassDB::bind_method(D_METHOD("is_slide_on_ceiling_enabled"), &CharacterBody2D::is_slide_on_ceiling_enabled); - ClassDB::bind_method(D_METHOD("set_moving_platform_floor_layers", "exclude_layer"), &CharacterBody2D::set_moving_platform_floor_layers); - ClassDB::bind_method(D_METHOD("get_moving_platform_floor_layers"), &CharacterBody2D::get_moving_platform_floor_layers); - ClassDB::bind_method(D_METHOD("set_moving_platform_wall_layers", "exclude_layer"), &CharacterBody2D::set_moving_platform_wall_layers); - ClassDB::bind_method(D_METHOD("get_moving_platform_wall_layers"), &CharacterBody2D::get_moving_platform_wall_layers); + ClassDB::bind_method(D_METHOD("set_platform_floor_layers", "exclude_layer"), &CharacterBody2D::set_platform_floor_layers); + ClassDB::bind_method(D_METHOD("get_platform_floor_layers"), &CharacterBody2D::get_platform_floor_layers); + ClassDB::bind_method(D_METHOD("set_platform_wall_layers", "exclude_layer"), &CharacterBody2D::set_platform_wall_layers); + ClassDB::bind_method(D_METHOD("get_platform_wall_layers"), &CharacterBody2D::get_platform_wall_layers); ClassDB::bind_method(D_METHOD("get_max_slides"), &CharacterBody2D::get_max_slides); ClassDB::bind_method(D_METHOD("set_max_slides", "max_slides"), &CharacterBody2D::set_max_slides); @@ -1730,8 +1724,8 @@ void CharacterBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_up_direction", "up_direction"), &CharacterBody2D::set_up_direction); ClassDB::bind_method(D_METHOD("set_motion_mode", "mode"), &CharacterBody2D::set_motion_mode); ClassDB::bind_method(D_METHOD("get_motion_mode"), &CharacterBody2D::get_motion_mode); - ClassDB::bind_method(D_METHOD("set_moving_platform_apply_velocity_on_leave", "on_leave_apply_velocity"), &CharacterBody2D::set_moving_platform_apply_velocity_on_leave); - ClassDB::bind_method(D_METHOD("get_moving_platform_apply_velocity_on_leave"), &CharacterBody2D::get_moving_platform_apply_velocity_on_leave); + ClassDB::bind_method(D_METHOD("set_platform_on_leave", "on_leave_apply_velocity"), &CharacterBody2D::set_platform_on_leave); + ClassDB::bind_method(D_METHOD("get_platform_on_leave"), &CharacterBody2D::get_platform_on_leave); ClassDB::bind_method(D_METHOD("is_on_floor"), &CharacterBody2D::is_on_floor); ClassDB::bind_method(D_METHOD("is_on_floor_only"), &CharacterBody2D::is_on_floor_only); @@ -1756,34 +1750,38 @@ void CharacterBody2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slide_on_ceiling"), "set_slide_on_ceiling_enabled", "is_slide_on_ceiling_enabled"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_max_slides", "get_max_slides"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wall_min_slide_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians", PROPERTY_USAGE_DEFAULT), "set_wall_min_slide_angle", "get_wall_min_slide_angle"); + ADD_GROUP("Floor", "floor_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_stop_on_slope"), "set_floor_stop_on_slope_enabled", "is_floor_stop_on_slope_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_constant_speed"), "set_floor_constant_speed_enabled", "is_floor_constant_speed_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_block_on_wall"), "set_floor_block_on_wall_enabled", "is_floor_block_on_wall_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_max_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians"), "set_floor_max_angle", "get_floor_max_angle"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_snap_length", PROPERTY_HINT_RANGE, "0,32,0.1,or_greater,suffix:px"), "set_floor_snap_length", "get_floor_snap_length"); - ADD_GROUP("Moving Platform", "moving_platform"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_apply_velocity_on_leave", PROPERTY_HINT_ENUM, "Always,Upward Only,Never", PROPERTY_USAGE_DEFAULT), "set_moving_platform_apply_velocity_on_leave", "get_moving_platform_apply_velocity_on_leave"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_floor_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_moving_platform_floor_layers", "get_moving_platform_floor_layers"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_wall_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_moving_platform_wall_layers", "get_moving_platform_wall_layers"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001,suffix:px"), "set_safe_margin", "get_safe_margin"); + + ADD_GROUP("Moving Platform", "platform"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "platform_on_leave", PROPERTY_HINT_ENUM, "Add Velocity,Add Upward Velocity,Do Nothing", PROPERTY_USAGE_DEFAULT), "set_platform_on_leave", "get_platform_on_leave"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "platform_floor_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_platform_floor_layers", "get_platform_floor_layers"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "platform_wall_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_platform_wall_layers", "get_platform_wall_layers"); + + ADD_GROUP("Collision", ""); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001,suffix:px"), "set_safe_margin", "get_safe_margin"); BIND_ENUM_CONSTANT(MOTION_MODE_GROUNDED); BIND_ENUM_CONSTANT(MOTION_MODE_FLOATING); - BIND_ENUM_CONSTANT(PLATFORM_VEL_ON_LEAVE_ALWAYS); - BIND_ENUM_CONSTANT(PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY); - BIND_ENUM_CONSTANT(PLATFORM_VEL_ON_LEAVE_NEVER); + BIND_ENUM_CONSTANT(PLATFORM_ON_LEAVE_ADD_VELOCITY); + BIND_ENUM_CONSTANT(PLATFORM_ON_LEAVE_ADD_UPWARD_VELOCITY); + BIND_ENUM_CONSTANT(PLATFORM_ON_LEAVE_DO_NOTHING); } -void CharacterBody2D::_validate_property(PropertyInfo &property) const { +void CharacterBody2D::_validate_property(PropertyInfo &p_property) const { if (motion_mode == MOTION_MODE_FLOATING) { - if (property.name.begins_with("floor_") || property.name == "up_direction" || property.name == "slide_on_ceiling") { - property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + if (p_property.name.begins_with("floor_") || p_property.name == "up_direction" || p_property.name == "slide_on_ceiling") { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } } else { - if (property.name == "wall_min_slide_angle") { - property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + if (p_property.name == "wall_min_slide_angle") { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } } } @@ -1823,6 +1821,10 @@ real_t KinematicCollision2D::get_angle(const Vector2 &p_up_direction) const { return result.get_angle(p_up_direction); } +real_t KinematicCollision2D::get_depth() const { + return result.collision_depth; +} + Object *KinematicCollision2D::get_local_shape() const { if (!owner) { return nullptr; @@ -1874,6 +1876,7 @@ void KinematicCollision2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_travel"), &KinematicCollision2D::get_travel); ClassDB::bind_method(D_METHOD("get_remainder"), &KinematicCollision2D::get_remainder); ClassDB::bind_method(D_METHOD("get_angle", "up_direction"), &KinematicCollision2D::get_angle, DEFVAL(Vector2(0.0, -1.0))); + ClassDB::bind_method(D_METHOD("get_depth"), &KinematicCollision2D::get_depth); ClassDB::bind_method(D_METHOD("get_local_shape"), &KinematicCollision2D::get_local_shape); ClassDB::bind_method(D_METHOD("get_collider"), &KinematicCollision2D::get_collider); ClassDB::bind_method(D_METHOD("get_collider_id"), &KinematicCollision2D::get_collider_id); diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h index 7401fc7578..932ec1de16 100644 --- a/scene/2d/physics_body_2d.h +++ b/scene/2d/physics_body_2d.h @@ -47,11 +47,11 @@ protected: Ref<KinematicCollision2D> motion_cache; - Ref<KinematicCollision2D> _move(const Vector2 &p_distance, bool p_test_only = false, real_t p_margin = 0.08); + Ref<KinematicCollision2D> _move(const Vector2 &p_distance, bool p_test_only = false, real_t p_margin = 0.08, bool p_recovery_as_collision = false); public: bool move_and_collide(const PhysicsServer2D::MotionParameters &p_parameters, PhysicsServer2D::MotionResult &r_result, bool p_test_only = false, bool p_cancel_sliding = true); - bool test_move(const Transform2D &p_from, const Vector2 &p_distance, const Ref<KinematicCollision2D> &r_collision = Ref<KinematicCollision2D>(), real_t p_margin = 0.08); + bool test_move(const Transform2D &p_from, const Vector2 &p_distance, const Ref<KinematicCollision2D> &r_collision = Ref<KinematicCollision2D>(), real_t p_margin = 0.08, bool p_recovery_as_collision = false); TypedArray<PhysicsBody2D> get_collision_exceptions(); void add_collision_exception_with(Node *p_node); //must be physicsbody @@ -113,8 +113,8 @@ private: bool is_sync_to_physics_enabled() const; }; -class RigidDynamicBody2D : public PhysicsBody2D { - GDCLASS(RigidDynamicBody2D, PhysicsBody2D); +class RigidBody2D : public PhysicsBody2D { + GDCLASS(RigidBody2D, PhysicsBody2D); public: enum FreezeMode { @@ -186,7 +186,7 @@ private: local_shape = p_ls; } }; - struct RigidDynamicBody2D_RemoveAction { + struct RigidBody2D_RemoveAction { RID rid; ObjectID body_id; ShapePair pair; @@ -216,7 +216,7 @@ protected: void _notification(int p_what); static void _bind_methods(); - virtual void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; GDVIRTUAL1(_integrate_forces, PhysicsDirectBodyState2D *) @@ -284,6 +284,7 @@ public: void set_max_contacts_reported(int p_amount); int get_max_contacts_reported() const; + int get_contact_count() const; void set_continuous_collision_detection_mode(CCDMode p_mode); CCDMode get_continuous_collision_detection_mode() const; @@ -308,19 +309,19 @@ public: TypedArray<Node2D> get_colliding_bodies() const; //function for script - virtual TypedArray<String> get_configuration_warnings() const override; + virtual PackedStringArray get_configuration_warnings() const override; - RigidDynamicBody2D(); - ~RigidDynamicBody2D(); + RigidBody2D(); + ~RigidBody2D(); private: void _reload_physics_characteristics(); }; -VARIANT_ENUM_CAST(RigidDynamicBody2D::FreezeMode); -VARIANT_ENUM_CAST(RigidDynamicBody2D::CenterOfMassMode); -VARIANT_ENUM_CAST(RigidDynamicBody2D::DampMode); -VARIANT_ENUM_CAST(RigidDynamicBody2D::CCDMode); +VARIANT_ENUM_CAST(RigidBody2D::FreezeMode); +VARIANT_ENUM_CAST(RigidBody2D::CenterOfMassMode); +VARIANT_ENUM_CAST(RigidBody2D::DampMode); +VARIANT_ENUM_CAST(RigidBody2D::CCDMode); class CharacterBody2D : public PhysicsBody2D { GDCLASS(CharacterBody2D, PhysicsBody2D); @@ -330,10 +331,10 @@ public: MOTION_MODE_GROUNDED, MOTION_MODE_FLOATING, }; - enum MovingPlatformApplyVelocityOnLeave { - PLATFORM_VEL_ON_LEAVE_ALWAYS, - PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY, - PLATFORM_VEL_ON_LEAVE_NEVER, + enum PlatformOnLeave { + PLATFORM_ON_LEAVE_ADD_VELOCITY, + PLATFORM_ON_LEAVE_ADD_UPWARD_VELOCITY, + PLATFORM_ON_LEAVE_DO_NOTHING, }; bool move_and_slide(); @@ -364,7 +365,7 @@ public: private: real_t margin = 0.08; MotionMode motion_mode = MOTION_MODE_GROUNDED; - MovingPlatformApplyVelocityOnLeave moving_platform_apply_velocity_on_leave = PLATFORM_VEL_ON_LEAVE_ALWAYS; + PlatformOnLeave platform_on_leave = PLATFORM_ON_LEAVE_ADD_VELOCITY; bool floor_constant_speed = false; bool floor_stop_on_slope = true; @@ -372,12 +373,12 @@ private: bool slide_on_ceiling = true; int max_slides = 4; int platform_layer = 0; - real_t floor_max_angle = Math::deg2rad((real_t)45.0); + real_t floor_max_angle = Math::deg_to_rad((real_t)45.0); real_t floor_snap_length = 1; - real_t wall_min_slide_angle = Math::deg2rad((real_t)15.0); + real_t wall_min_slide_angle = Math::deg_to_rad((real_t)15.0); Vector2 up_direction = Vector2(0.0, -1.0); - uint32_t moving_platform_floor_layers = UINT32_MAX; - uint32_t moving_platform_wall_layers = 0; + uint32_t platform_floor_layers = UINT32_MAX; + uint32_t platform_wall_layers = 0; Vector2 velocity; Vector2 floor_normal; @@ -423,17 +424,17 @@ private: real_t get_wall_min_slide_angle() const; void set_wall_min_slide_angle(real_t p_radians); - uint32_t get_moving_platform_floor_layers() const; - void set_moving_platform_floor_layers(const uint32_t p_exclude_layer); + uint32_t get_platform_floor_layers() const; + void set_platform_floor_layers(const uint32_t p_exclude_layer); - uint32_t get_moving_platform_wall_layers() const; - void set_moving_platform_wall_layers(const uint32_t p_exclude_layer); + uint32_t get_platform_wall_layers() const; + void set_platform_wall_layers(const uint32_t p_exclude_layer); void set_motion_mode(MotionMode p_mode); MotionMode get_motion_mode() const; - void set_moving_platform_apply_velocity_on_leave(MovingPlatformApplyVelocityOnLeave p_on_leave_velocity); - MovingPlatformApplyVelocityOnLeave get_moving_platform_apply_velocity_on_leave() const; + void set_platform_on_leave(PlatformOnLeave p_on_leave_velocity); + PlatformOnLeave get_platform_on_leave() const; void _move_and_slide_floating(double p_delta); void _move_and_slide_grounded(double p_delta, bool p_was_on_floor); @@ -450,11 +451,11 @@ private: protected: void _notification(int p_what); static void _bind_methods(); - virtual void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; }; VARIANT_ENUM_CAST(CharacterBody2D::MotionMode); -VARIANT_ENUM_CAST(CharacterBody2D::MovingPlatformApplyVelocityOnLeave); +VARIANT_ENUM_CAST(CharacterBody2D::PlatformOnLeave); class KinematicCollision2D : public RefCounted { GDCLASS(KinematicCollision2D, RefCounted); @@ -473,6 +474,7 @@ public: Vector2 get_travel() const; Vector2 get_remainder() const; real_t get_angle(const Vector2 &p_up_direction = Vector2(0.0, -1.0)) const; + real_t get_depth() const; Object *get_local_shape() const; Object *get_collider() const; ObjectID get_collider_id() const; diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp index ba62941d3a..5e77902977 100644 --- a/scene/2d/polygon_2d.cpp +++ b/scene/2d/polygon_2d.cpp @@ -90,14 +90,14 @@ bool Polygon2D::_edit_is_selected_on_click(const Point2 &p_point, double p_toler } #endif -void Polygon2D::_validate_property(PropertyInfo &property) const { - if (!invert && property.name == "invert_border") { - property.usage = PROPERTY_USAGE_NO_EDITOR; +void Polygon2D::_validate_property(PropertyInfo &p_property) const { + if (!invert && p_property.name == "invert_border") { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } } void Polygon2D::_skeleton_bone_setup_changed() { - update(); + queue_redraw(); } void Polygon2D::_notification(int p_what) { @@ -375,7 +375,7 @@ void Polygon2D::_notification(int p_what) { void Polygon2D::set_polygon(const Vector<Vector2> &p_polygon) { polygon = p_polygon; rect_cache_dirty = true; - update(); + queue_redraw(); } Vector<Vector2> Polygon2D::get_polygon() const { @@ -392,7 +392,7 @@ int Polygon2D::get_internal_vertex_count() const { void Polygon2D::set_uv(const Vector<Vector2> &p_uv) { uv = p_uv; - update(); + queue_redraw(); } Vector<Vector2> Polygon2D::get_uv() const { @@ -401,7 +401,7 @@ Vector<Vector2> Polygon2D::get_uv() const { void Polygon2D::set_polygons(const Array &p_polygons) { polygons = p_polygons; - update(); + queue_redraw(); } Array Polygon2D::get_polygons() const { @@ -410,7 +410,7 @@ Array Polygon2D::get_polygons() const { void Polygon2D::set_color(const Color &p_color) { color = p_color; - update(); + queue_redraw(); } Color Polygon2D::get_color() const { @@ -419,7 +419,7 @@ Color Polygon2D::get_color() const { void Polygon2D::set_vertex_colors(const Vector<Color> &p_colors) { vertex_colors = p_colors; - update(); + queue_redraw(); } Vector<Color> Polygon2D::get_vertex_colors() const { @@ -428,7 +428,7 @@ Vector<Color> Polygon2D::get_vertex_colors() const { void Polygon2D::set_texture(const Ref<Texture2D> &p_texture) { texture = p_texture; - update(); + queue_redraw(); } Ref<Texture2D> Polygon2D::get_texture() const { @@ -437,7 +437,7 @@ Ref<Texture2D> Polygon2D::get_texture() const { void Polygon2D::set_texture_offset(const Vector2 &p_offset) { tex_ofs = p_offset; - update(); + queue_redraw(); } Vector2 Polygon2D::get_texture_offset() const { @@ -446,7 +446,7 @@ Vector2 Polygon2D::get_texture_offset() const { void Polygon2D::set_texture_rotation(real_t p_rot) { tex_rot = p_rot; - update(); + queue_redraw(); } real_t Polygon2D::get_texture_rotation() const { @@ -455,7 +455,7 @@ real_t Polygon2D::get_texture_rotation() const { void Polygon2D::set_texture_scale(const Size2 &p_scale) { tex_scale = p_scale; - update(); + queue_redraw(); } Size2 Polygon2D::get_texture_scale() const { @@ -464,7 +464,7 @@ Size2 Polygon2D::get_texture_scale() const { void Polygon2D::set_invert(bool p_invert) { invert = p_invert; - update(); + queue_redraw(); notify_property_list_changed(); } @@ -474,7 +474,7 @@ bool Polygon2D::get_invert() const { void Polygon2D::set_antialiased(bool p_antialiased) { antialiased = p_antialiased; - update(); + queue_redraw(); } bool Polygon2D::get_antialiased() const { @@ -483,7 +483,7 @@ bool Polygon2D::get_antialiased() const { void Polygon2D::set_invert_border(real_t p_invert_border) { invert_border = p_invert_border; - update(); + queue_redraw(); } real_t Polygon2D::get_invert_border() const { @@ -493,7 +493,7 @@ real_t Polygon2D::get_invert_border() const { void Polygon2D::set_offset(const Vector2 &p_offset) { offset = p_offset; rect_cache_dirty = true; - update(); + queue_redraw(); } Vector2 Polygon2D::get_offset() const { @@ -533,13 +533,13 @@ void Polygon2D::clear_bones() { void Polygon2D::set_bone_weights(int p_index, const Vector<float> &p_weights) { ERR_FAIL_INDEX(p_index, bone_weights.size()); bone_weights.write[p_index].weights = p_weights; - update(); + queue_redraw(); } void Polygon2D::set_bone_path(int p_index, const NodePath &p_path) { ERR_FAIL_INDEX(p_index, bone_weights.size()); bone_weights.write[p_index].path = p_path; - update(); + queue_redraw(); } Array Polygon2D::_get_bones() const { @@ -567,7 +567,7 @@ void Polygon2D::set_skeleton(const NodePath &p_skeleton) { return; } skeleton = p_skeleton; - update(); + queue_redraw(); } NodePath Polygon2D::get_skeleton() const { @@ -602,8 +602,8 @@ void Polygon2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_texture_scale", "texture_scale"), &Polygon2D::set_texture_scale); ClassDB::bind_method(D_METHOD("get_texture_scale"), &Polygon2D::get_texture_scale); - ClassDB::bind_method(D_METHOD("set_invert", "invert"), &Polygon2D::set_invert); - ClassDB::bind_method(D_METHOD("get_invert"), &Polygon2D::get_invert); + ClassDB::bind_method(D_METHOD("set_invert_enabled", "invert"), &Polygon2D::set_invert); + ClassDB::bind_method(D_METHOD("get_invert_enabled"), &Polygon2D::get_invert); ClassDB::bind_method(D_METHOD("set_antialiased", "antialiased"), &Polygon2D::set_antialiased); ClassDB::bind_method(D_METHOD("get_antialiased"), &Polygon2D::get_antialiased); @@ -640,13 +640,13 @@ void Polygon2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "texture_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_texture_offset", "get_texture_offset"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "texture_scale", PROPERTY_HINT_LINK), "set_texture_scale", "get_texture_scale"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "texture_rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_lesser,or_greater,radians"), "set_texture_rotation", "get_texture_rotation"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "texture_rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_less,or_greater,radians"), "set_texture_rotation", "get_texture_rotation"); ADD_GROUP("Skeleton", ""); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "skeleton", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton2D"), "set_skeleton", "get_skeleton"); ADD_GROUP("Invert", "invert_"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "invert_enable"), "set_invert", "get_invert"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "invert_enabled"), "set_invert_enabled", "get_invert_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "invert_border", PROPERTY_HINT_RANGE, "0.1,16384,0.1,suffix:px"), "set_invert_border", "get_invert_border"); ADD_GROUP("Data", ""); diff --git a/scene/2d/polygon_2d.h b/scene/2d/polygon_2d.h index d6a1be0f6d..d333152f62 100644 --- a/scene/2d/polygon_2d.h +++ b/scene/2d/polygon_2d.h @@ -77,7 +77,7 @@ class Polygon2D : public Node2D { protected: void _notification(int p_what); static void _bind_methods(); - void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; public: #ifdef TOOLS_ENABLED diff --git a/scene/2d/ray_cast_2d.cpp b/scene/2d/ray_cast_2d.cpp index 8953813452..2c8a2e715a 100644 --- a/scene/2d/ray_cast_2d.cpp +++ b/scene/2d/ray_cast_2d.cpp @@ -36,7 +36,7 @@ void RayCast2D::set_target_position(const Vector2 &p_point) { target_position = p_point; if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_collisions_hint())) { - update(); + queue_redraw(); } } @@ -82,6 +82,10 @@ Object *RayCast2D::get_collider() const { return ObjectDB::get_instance(against); } +RID RayCast2D::get_collider_rid() const { + return against_rid; +} + int RayCast2D::get_collider_shape() const { return against_shape; } @@ -96,7 +100,7 @@ Vector2 RayCast2D::get_collision_normal() const { void RayCast2D::set_enabled(bool p_enabled) { enabled = p_enabled; - update(); + queue_redraw(); if (is_inside_tree() && !Engine::get_singleton()->is_editor_hint()) { set_physics_process_internal(p_enabled); } @@ -203,17 +207,19 @@ void RayCast2D::_update_raycast_state() { if (dss->intersect_ray(ray_params, rr)) { collided = true; against = rr.collider_id; + against_rid = rr.rid; collision_point = rr.position; collision_normal = rr.normal; against_shape = rr.shape; } else { collided = false; against = ObjectID(); + against_rid = RID(); against_shape = 0; } if (prev_collision_state != collided) { - update(); + queue_redraw(); } } @@ -240,7 +246,7 @@ void RayCast2D::_draw_debug_shape() { Transform2D xf; xf.rotate(target_position.angle()); - xf.translate(Vector2(no_line ? 0 : target_position.length() - arrow_size, 0)); + xf.translate_local(Vector2(no_line ? 0 : target_position.length() - arrow_size, 0)); Vector<Vector2> pts = { xf.xform(Vector2(arrow_size, 0)), @@ -321,6 +327,7 @@ void RayCast2D::_bind_methods() { ClassDB::bind_method(D_METHOD("force_raycast_update"), &RayCast2D::force_raycast_update); ClassDB::bind_method(D_METHOD("get_collider"), &RayCast2D::get_collider); + ClassDB::bind_method(D_METHOD("get_collider_rid"), &RayCast2D::get_collider_rid); ClassDB::bind_method(D_METHOD("get_collider_shape"), &RayCast2D::get_collider_shape); ClassDB::bind_method(D_METHOD("get_collision_point"), &RayCast2D::get_collision_point); ClassDB::bind_method(D_METHOD("get_collision_normal"), &RayCast2D::get_collision_normal); diff --git a/scene/2d/ray_cast_2d.h b/scene/2d/ray_cast_2d.h index 1fb97d89fe..57f993fe8d 100644 --- a/scene/2d/ray_cast_2d.h +++ b/scene/2d/ray_cast_2d.h @@ -41,6 +41,7 @@ class RayCast2D : public Node2D { bool enabled = true; bool collided = false; ObjectID against; + RID against_rid; int against_shape = 0; Vector2 collision_point; Vector2 collision_normal; @@ -91,6 +92,7 @@ public: bool is_colliding() const; Object *get_collider() const; + RID get_collider_rid() const; int get_collider_shape() const; Vector2 get_collision_point() const; Vector2 get_collision_normal() const; diff --git a/scene/2d/remote_transform_2d.cpp b/scene/2d/remote_transform_2d.cpp index 6c4bfd58ce..f4343e4c03 100644 --- a/scene/2d/remote_transform_2d.cpp +++ b/scene/2d/remote_transform_2d.cpp @@ -183,8 +183,8 @@ void RemoteTransform2D::force_update_cache() { _update_cache(); } -TypedArray<String> RemoteTransform2D::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray RemoteTransform2D::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (!has_node(remote_node) || !Object::cast_to<Node2D>(get_node(remote_node))) { warnings.push_back(RTR("Path property must point to a valid Node2D node to work.")); diff --git a/scene/2d/remote_transform_2d.h b/scene/2d/remote_transform_2d.h index bd352e1054..f98eec75c6 100644 --- a/scene/2d/remote_transform_2d.h +++ b/scene/2d/remote_transform_2d.h @@ -70,7 +70,7 @@ public: void force_update_cache(); - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; RemoteTransform2D(); }; diff --git a/scene/2d/shape_cast_2d.cpp b/scene/2d/shape_cast_2d.cpp index ae810156a2..6222b0db14 100644 --- a/scene/2d/shape_cast_2d.cpp +++ b/scene/2d/shape_cast_2d.cpp @@ -40,7 +40,7 @@ void ShapeCast2D::set_target_position(const Vector2 &p_point) { target_position = p_point; if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_collisions_hint())) { - update(); + queue_redraw(); } } @@ -132,7 +132,7 @@ real_t ShapeCast2D::get_closest_collision_unsafe_fraction() const { void ShapeCast2D::set_enabled(bool p_enabled) { enabled = p_enabled; - update(); + queue_redraw(); if (is_inside_tree() && !Engine::get_singleton()->is_editor_hint()) { set_physics_process_internal(p_enabled); } @@ -152,7 +152,7 @@ void ShapeCast2D::set_shape(const Ref<Shape2D> &p_shape) { shape_rid = shape->get_rid(); } update_configuration_warnings(); - update(); + queue_redraw(); } Ref<Shape2D> ShapeCast2D::get_shape() const { @@ -182,7 +182,7 @@ bool ShapeCast2D::get_exclude_parent_body() const { } void ShapeCast2D::_redraw_shape() { - update(); + queue_redraw(); } void ShapeCast2D::_notification(int p_what) { @@ -217,7 +217,7 @@ void ShapeCast2D::_notification(int p_what) { if (shape.is_null()) { break; } - Color draw_col = get_tree()->get_debug_collisions_color(); + Color draw_col = collided ? Color(1.0, 0.01, 0) : get_tree()->get_debug_collisions_color(); if (!enabled) { float g = draw_col.get_v(); draw_col.r = g; @@ -235,18 +235,25 @@ void ShapeCast2D::_notification(int p_what) { // Draw an arrow indicating where the ShapeCast is pointing to. if (target_position != Vector2()) { - Transform2D xf; - xf.rotate(target_position.angle()); - xf.translate(Vector2(target_position.length(), 0)); + const real_t max_arrow_size = 6; + const real_t line_width = 1.4; + bool no_line = target_position.length() < line_width; + real_t arrow_size = CLAMP(target_position.length() * 2 / 3, line_width, max_arrow_size); - draw_line(Vector2(), target_position, draw_col, 2); + if (no_line) { + arrow_size = target_position.length(); + } else { + draw_line(Vector2(), target_position - target_position.normalized() * arrow_size, draw_col, line_width); + } - float tsize = 8; + Transform2D xf; + xf.rotate(target_position.angle()); + xf.translate_local(Vector2(no_line ? 0 : target_position.length() - arrow_size, 0)); Vector<Vector2> pts = { - xf.xform(Vector2(tsize, 0)), - xf.xform(Vector2(0, Math_SQRT12 * tsize)), - xf.xform(Vector2(0, -Math_SQRT12 * tsize)) + xf.xform(Vector2(arrow_size, 0)), + xf.xform(Vector2(0, 0.5 * arrow_size)), + xf.xform(Vector2(0, -0.5 * arrow_size)) }; Vector<Color> cols = { draw_col, draw_col, draw_col }; @@ -291,6 +298,8 @@ void ShapeCast2D::_update_shapecast_state() { collision_safe_fraction = 0.0; collision_unsafe_fraction = 0.0; + bool prev_collision_state = collided; + if (target_position != Vector2()) { dss->cast_motion(params, collision_safe_fraction, collision_unsafe_fraction); if (collision_unsafe_fraction < 1.0) { @@ -314,6 +323,10 @@ void ShapeCast2D::_update_shapecast_state() { } } collided = !result.is_empty(); + + if (prev_collision_state != collided) { + queue_redraw(); + } } void ShapeCast2D::force_shapecast_update() { @@ -378,8 +391,8 @@ Array ShapeCast2D::_get_collision_result() const { return ret; } -TypedArray<String> ShapeCast2D::get_configuration_warnings() const { - TypedArray<String> warnings = Node2D::get_configuration_warnings(); +PackedStringArray ShapeCast2D::get_configuration_warnings() const { + PackedStringArray warnings = Node2D::get_configuration_warnings(); if (shape.is_null()) { warnings.push_back(RTR("This node cannot interact with other objects unless a Shape2D is assigned.")); diff --git a/scene/2d/shape_cast_2d.h b/scene/2d/shape_cast_2d.h index 7ff080aed0..7b55566b01 100644 --- a/scene/2d/shape_cast_2d.h +++ b/scene/2d/shape_cast_2d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SHAPE_CAST_2D -#define SHAPE_CAST_2D +#ifndef SHAPE_CAST_2D_H +#define SHAPE_CAST_2D_H #include "scene/2d/node_2d.h" #include "scene/resources/shape_2d.h" @@ -117,7 +117,7 @@ public: void remove_exception(const CollisionObject2D *p_node); void clear_exceptions(); - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; }; -#endif +#endif // SHAPE_CAST_2D_H diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index cbacb7f579..b5759c54f7 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -44,7 +44,7 @@ bool Bone2D::_set(const StringName &p_path, const Variant &p_value) { } else if (path.begins_with("length")) { set_length(p_value); } else if (path.begins_with("bone_angle")) { - set_bone_angle(Math::deg2rad(real_t(p_value))); + set_bone_angle(Math::deg_to_rad(real_t(p_value))); } else if (path.begins_with("default_length")) { set_length(p_value); } @@ -66,7 +66,7 @@ bool Bone2D::_get(const StringName &p_path, Variant &r_ret) const { } else if (path.begins_with("length")) { r_ret = get_length(); } else if (path.begins_with("bone_angle")) { - r_ret = Math::rad2deg(get_bone_angle()); + r_ret = Math::rad_to_deg(get_bone_angle()); } else if (path.begins_with("default_length")) { r_ret = get_length(); } @@ -126,7 +126,7 @@ void Bone2D::_notification(int p_what) { return; } - update(); + queue_redraw(); #endif // TOOLS_ENABLED } break; @@ -143,12 +143,12 @@ void Bone2D::_notification(int p_what) { return; } - update(); + queue_redraw(); if (get_parent()) { Bone2D *parent_bone = Object::cast_to<Bone2D>(get_parent()); if (parent_bone) { - parent_bone->update(); + parent_bone->queue_redraw(); } } #endif // TOOLS_ENABLED @@ -365,7 +365,7 @@ bool Bone2D::_editor_get_bone_shape(Vector<Vector2> *p_shape, Vector<Vector2> *p void Bone2D::_editor_set_show_bone_gizmo(bool p_show_gizmo) { _editor_show_bone_gizmo = p_show_gizmo; - update(); + queue_redraw(); } bool Bone2D::_editor_get_show_bone_gizmo() const { @@ -434,8 +434,8 @@ int Bone2D::get_index_in_skeleton() const { return skeleton_index; } -TypedArray<String> Bone2D::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray Bone2D::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (!skeleton) { if (parent_bone) { warnings.push_back(RTR("This Bone2D chain should end at a Skeleton2D node.")); @@ -493,7 +493,7 @@ void Bone2D::set_length(real_t p_length) { length = p_length; #ifdef TOOLS_ENABLED - update(); + queue_redraw(); #endif // TOOLS_ENABLED } @@ -505,7 +505,7 @@ void Bone2D::set_bone_angle(real_t p_angle) { bone_angle = p_angle; #ifdef TOOLS_ENABLED - update(); + queue_redraw(); #endif // TOOLS_ENABLED } diff --git a/scene/2d/skeleton_2d.h b/scene/2d/skeleton_2d.h index 98fb867d99..580aed97ce 100644 --- a/scene/2d/skeleton_2d.h +++ b/scene/2d/skeleton_2d.h @@ -78,7 +78,7 @@ public: void apply_rest(); Transform2D get_skeleton_rest() const; - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; void set_default_length(real_t p_length); real_t get_default_length() const; diff --git a/scene/2d/sprite_2d.cpp b/scene/2d/sprite_2d.cpp index b3062ca02a..0ecf8333a0 100644 --- a/scene/2d/sprite_2d.cpp +++ b/scene/2d/sprite_2d.cpp @@ -146,7 +146,7 @@ void Sprite2D::set_texture(const Ref<Texture2D> &p_texture) { texture->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Sprite2D::_texture_changed)); } - update(); + queue_redraw(); emit_signal(SceneStringNames::get_singleton()->texture_changed); item_rect_changed(); } @@ -157,7 +157,7 @@ Ref<Texture2D> Sprite2D::get_texture() const { void Sprite2D::set_centered(bool p_center) { centered = p_center; - update(); + queue_redraw(); item_rect_changed(); } @@ -167,7 +167,7 @@ bool Sprite2D::is_centered() const { void Sprite2D::set_offset(const Point2 &p_offset) { offset = p_offset; - update(); + queue_redraw(); item_rect_changed(); } @@ -177,7 +177,7 @@ Point2 Sprite2D::get_offset() const { void Sprite2D::set_flip_h(bool p_flip) { hflip = p_flip; - update(); + queue_redraw(); } bool Sprite2D::is_flipped_h() const { @@ -186,7 +186,7 @@ bool Sprite2D::is_flipped_h() const { void Sprite2D::set_flip_v(bool p_flip) { vflip = p_flip; - update(); + queue_redraw(); } bool Sprite2D::is_flipped_v() const { @@ -199,7 +199,7 @@ void Sprite2D::set_region_enabled(bool p_region_enabled) { } region_enabled = p_region_enabled; - update(); + queue_redraw(); notify_property_list_changed(); } @@ -225,7 +225,7 @@ Rect2 Sprite2D::get_region_rect() const { void Sprite2D::set_region_filter_clip_enabled(bool p_region_filter_clip_enabled) { region_filter_clip_enabled = p_region_filter_clip_enabled; - update(); + queue_redraw(); } bool Sprite2D::is_region_filter_clip_enabled() const { @@ -262,7 +262,7 @@ Vector2i Sprite2D::get_frame_coords() const { void Sprite2D::set_vframes(int p_amount) { ERR_FAIL_COND_MSG(p_amount < 1, "Amount of vframes cannot be smaller than 1."); vframes = p_amount; - update(); + queue_redraw(); item_rect_changed(); notify_property_list_changed(); } @@ -274,7 +274,7 @@ int Sprite2D::get_vframes() const { void Sprite2D::set_hframes(int p_amount) { ERR_FAIL_COND_MSG(p_amount < 1, "Amount of hframes cannot be smaller than 1."); hframes = p_amount; - update(); + queue_redraw(); item_rect_changed(); notify_property_list_changed(); } @@ -368,19 +368,19 @@ Rect2 Sprite2D::get_rect() const { return Rect2(ofs, s); } -void Sprite2D::_validate_property(PropertyInfo &property) const { - if (property.name == "frame") { - property.hint = PROPERTY_HINT_RANGE; - property.hint_string = "0," + itos(vframes * hframes - 1) + ",1"; - property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS; +void Sprite2D::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "frame") { + p_property.hint = PROPERTY_HINT_RANGE; + p_property.hint_string = "0," + itos(vframes * hframes - 1) + ",1"; + p_property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS; } - if (property.name == "frame_coords") { - property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS; + if (p_property.name == "frame_coords") { + p_property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS; } - if (!region_enabled && (property.name == "region_rect" || property.name == "region_filter_clip")) { - property.usage = PROPERTY_USAGE_NO_EDITOR; + if (!region_enabled && (p_property.name == "region_rect" || p_property.name == "region_filter_clip")) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } } @@ -388,7 +388,7 @@ void Sprite2D::_texture_changed() { // Changes to the texture need to trigger an update to make // the editor redraw the sprite with the updated texture. if (texture.is_valid()) { - update(); + queue_redraw(); } } diff --git a/scene/2d/sprite_2d.h b/scene/2d/sprite_2d.h index 6893e92d4a..60f5940cfe 100644 --- a/scene/2d/sprite_2d.h +++ b/scene/2d/sprite_2d.h @@ -64,7 +64,7 @@ protected: static void _bind_methods(); - virtual void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; public: #ifdef TOOLS_ENABLED @@ -125,4 +125,4 @@ public: ~Sprite2D(); }; -#endif // SPRITE_H +#endif // SPRITE_2D_H diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index cf8b6b8f94..bdcd1f2f28 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -34,6 +34,10 @@ #include "scene/resources/world_2d.h" #include "servers/navigation_server_2d.h" +#ifdef DEBUG_ENABLED +#include "servers/navigation_server_3d.h" +#endif // DEBUG_ENABLED + HashMap<Vector2i, TileSet::CellNeighbor> TileMap::TerrainConstraint::get_overlapping_coords_and_peering_bits() const { HashMap<Vector2i, TileSet::CellNeighbor> output; @@ -553,7 +557,7 @@ int TileMap::get_layers_count() const { void TileMap::add_layer(int p_to_pos) { if (p_to_pos < 0) { - p_to_pos = layers.size(); + p_to_pos = layers.size() + p_to_pos + 1; } ERR_FAIL_INDEX(p_to_pos, (int)layers.size() + 1); @@ -612,6 +616,9 @@ void TileMap::remove_layer(int p_layer) { } void TileMap::set_layer_name(int p_layer, String p_name) { + if (p_layer < 0) { + p_layer = layers.size() + p_layer; + } ERR_FAIL_INDEX(p_layer, (int)layers.size()); layers[p_layer].name = p_name; emit_signal(SNAME("changed")); @@ -623,6 +630,9 @@ String TileMap::get_layer_name(int p_layer) const { } void TileMap::set_layer_enabled(int p_layer, bool p_enabled) { + if (p_layer < 0) { + p_layer = layers.size() + p_layer; + } ERR_FAIL_INDEX(p_layer, (int)layers.size()); layers[p_layer].enabled = p_enabled; _clear_layer_internals(p_layer); @@ -638,6 +648,9 @@ bool TileMap::is_layer_enabled(int p_layer) const { } void TileMap::set_layer_modulate(int p_layer, Color p_modulate) { + if (p_layer < 0) { + p_layer = layers.size() + p_layer; + } ERR_FAIL_INDEX(p_layer, (int)layers.size()); layers[p_layer].modulate = p_modulate; _clear_layer_internals(p_layer); @@ -651,6 +664,9 @@ Color TileMap::get_layer_modulate(int p_layer) const { } void TileMap::set_layer_y_sort_enabled(int p_layer, bool p_y_sort_enabled) { + if (p_layer < 0) { + p_layer = layers.size() + p_layer; + } ERR_FAIL_INDEX(p_layer, (int)layers.size()); layers[p_layer].y_sort_enabled = p_y_sort_enabled; _clear_layer_internals(p_layer); @@ -666,6 +682,9 @@ bool TileMap::is_layer_y_sort_enabled(int p_layer) const { } void TileMap::set_layer_y_sort_origin(int p_layer, int p_y_sort_origin) { + if (p_layer < 0) { + p_layer = layers.size() + p_layer; + } ERR_FAIL_INDEX(p_layer, (int)layers.size()); layers[p_layer].y_sort_origin = p_y_sort_origin; _clear_layer_internals(p_layer); @@ -679,6 +698,9 @@ int TileMap::get_layer_y_sort_origin(int p_layer) const { } void TileMap::set_layer_z_index(int p_layer, int p_z_index) { + if (p_layer < 0) { + p_layer = layers.size() + p_layer; + } ERR_FAIL_INDEX(p_layer, (int)layers.size()); layers[p_layer].z_index = p_z_index; _clear_layer_internals(p_layer); @@ -810,13 +832,13 @@ void TileMap::_update_dirty_quadrants() { // Update the coords cache. for (SelfList<TileMapQuadrant> *q = dirty_quadrant_list.first(); q; q = q->next()) { - q->self()->map_to_world.clear(); - q->self()->world_to_map.clear(); + q->self()->map_to_local.clear(); + q->self()->local_to_map.clear(); for (const Vector2i &E : q->self()->cells) { Vector2i pk = E; - Vector2i pk_world_coords = map_to_world(pk); - q->self()->map_to_world[pk] = pk_world_coords; - q->self()->world_to_map[pk_world_coords] = pk; + Vector2i pk_local_coords = map_to_local(pk); + q->self()->map_to_local[pk] = pk_local_coords; + q->self()->local_to_map[pk_local_coords] = pk; } } @@ -834,7 +856,7 @@ void TileMap::_update_dirty_quadrants() { for (SelfList<TileMapQuadrant> *q = dirty_quadrant_list.first(); q; q = q->next()) { rs->canvas_item_clear(q->self()->debug_canvas_item); Transform2D xform; - xform.set_origin(map_to_world(q->self()->coords * get_effective_quadrant_size(layer))); + xform.set_origin(map_to_local(q->self()->coords * get_effective_quadrant_size(layer))); rs->canvas_item_set_transform(q->self()->debug_canvas_item, xform); _rendering_draw_quadrant_debug(q->self()); @@ -960,10 +982,10 @@ void TileMap::_recompute_rect_cache() { for (unsigned int layer = 0; layer < layers.size(); layer++) { for (const KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) { Rect2 r; - r.position = map_to_world(E.key * get_effective_quadrant_size(layer)); - r.expand_to(map_to_world((E.key + Vector2i(1, 0)) * get_effective_quadrant_size(layer))); - r.expand_to(map_to_world((E.key + Vector2i(1, 1)) * get_effective_quadrant_size(layer))); - r.expand_to(map_to_world((E.key + Vector2i(0, 1)) * get_effective_quadrant_size(layer))); + r.position = map_to_local(E.key * get_effective_quadrant_size(layer)); + r.expand_to(map_to_local((E.key + Vector2i(1, 0)) * get_effective_quadrant_size(layer))); + r.expand_to(map_to_local((E.key + Vector2i(1, 1)) * get_effective_quadrant_size(layer))); + r.expand_to(map_to_local((E.key + Vector2i(0, 1)) * get_effective_quadrant_size(layer))); if (first) { r_total = r; first = false; @@ -992,7 +1014,7 @@ void TileMap::_rendering_notification(int p_what) { TileMapQuadrant &q = E_quadrant.value; // Update occluders transform. - for (const KeyValue<Vector2i, Vector2i> &E_cell : q.world_to_map) { + for (const KeyValue<Vector2i, Vector2i> &E_cell : q.local_to_map) { Transform2D xform; xform.set_origin(E_cell.key); for (const RID &occluder : q.occluders) { @@ -1012,7 +1034,7 @@ void TileMap::_rendering_notification(int p_what) { TileMapQuadrant &q = E_quadrant.value; // Update occluders transform. - for (const KeyValue<Vector2i, Vector2i> &E_cell : q.world_to_map) { + for (const KeyValue<Vector2i, Vector2i> &E_cell : q.local_to_map) { Transform2D xform; xform.set_origin(E_cell.key); for (const RID &occluder : q.occluders) { @@ -1108,7 +1130,7 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List } // Iterate over the cells of the quadrant. - for (const KeyValue<Vector2i, Vector2i> &E_cell : q.world_to_map) { + for (const KeyValue<Vector2i, Vector2i> &E_cell : q.local_to_map) { TileMapCell c = get_cell(q.layer, E_cell.value, true); TileSetSource *source; @@ -1133,7 +1155,7 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List int z_index = tile_data->get_z_index(); // Quandrant pos. - Vector2 position = map_to_world(q.coords * get_effective_quadrant_size(q.layer)); + Vector2 position = map_to_local(q.coords * get_effective_quadrant_size(q.layer)); if (is_y_sort_enabled() && layers[q.layer].y_sort_enabled) { // When Y-sorting, the quandrant size is sure to be 1, we can thus offset the CanvasItem. position.y += layers[q.layer].y_sort_origin + tile_data->get_y_sort_origin(); @@ -1205,14 +1227,14 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List int index = -(int64_t)0x80000000; //always must be drawn below children. for (int layer = 0; layer < (int)layers.size(); layer++) { - // Sort the quadrants coords per world coordinates - RBMap<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator> world_to_map; + // Sort the quadrants coords per local coordinates. + RBMap<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator> local_to_map; for (const KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) { - world_to_map[map_to_world(E.key)] = E.key; + local_to_map[map_to_local(E.key)] = E.key; } - // Sort the quadrants - for (const KeyValue<Vector2i, Vector2i> &E : world_to_map) { + // Sort the quadrants. + for (const KeyValue<Vector2i, Vector2i> &E : local_to_map) { TileMapQuadrant &q = layers[layer].quadrant_map[E.value]; for (const RID &ci : q.canvas_items) { RS::get_singleton()->canvas_item_set_draw_index(ci, index++); @@ -1252,7 +1274,7 @@ void TileMap::_rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { // Draw a placeholder for scenes needing one. RenderingServer *rs = RenderingServer::get_singleton(); - Vector2 quadrant_pos = map_to_world(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer)); + Vector2 quadrant_pos = map_to_local(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer)); for (const Vector2i &E_cell : p_quadrant->cells) { const TileMapCell &c = get_cell(p_quadrant->layer, E_cell, true); @@ -1284,7 +1306,7 @@ void TileMap::_rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { // Draw a placeholder tile. Transform2D xform; - xform.set_origin(map_to_world(E_cell) - quadrant_pos); + xform.set_origin(map_to_local(E_cell) - quadrant_pos); rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform); rs->canvas_item_add_circle(p_quadrant->debug_canvas_item, Vector2(), MIN(tile_set->get_tile_size().x, tile_set->get_tile_size().y) / 4.0, color); } @@ -1405,7 +1427,7 @@ void TileMap::_physics_notification(int p_what) { for (RID body : q.bodies) { Transform2D xform; - xform.set_origin(map_to_world(bodies_coords[body])); + xform.set_origin(map_to_local(bodies_coords[body])); xform = global_transform * xform; PhysicsServer2D::get_singleton()->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform); } @@ -1428,7 +1450,7 @@ void TileMap::_physics_notification(int p_what) { for (RID body : q.bodies) { Transform2D xform; - xform.set_origin(map_to_world(bodies_coords[body])); + xform.set_origin(map_to_local(bodies_coords[body])); xform = new_transform * xform; PhysicsServer2D::get_singleton()->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform); @@ -1498,7 +1520,7 @@ void TileMap::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r ps->body_set_space(body, space); Transform2D xform; - xform.set_origin(map_to_world(E_cell)); + xform.set_origin(map_to_local(E_cell)); xform = global_transform * xform; ps->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform); @@ -1584,7 +1606,7 @@ void TileMap::_physics_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { Vector<Color> color; color.push_back(debug_collision_color); - Vector2 quadrant_pos = map_to_world(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer)); + Vector2 quadrant_pos = map_to_local(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer)); Transform2D qudrant_xform; qudrant_xform.set_origin(quadrant_pos); Transform2D global_transform_inv = (get_global_transform() * qudrant_xform).affine_inverse(); @@ -1623,7 +1645,7 @@ void TileMap::_navigation_notification(int p_what) { continue; } Transform2D tile_transform; - tile_transform.set_origin(map_to_world(E_region.key)); + tile_transform.set_origin(map_to_local(E_region.key)); NavigationServer2D::get_singleton()->region_set_transform(region, tilemap_xform * tile_transform); } } @@ -1638,14 +1660,6 @@ void TileMap::_navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List ERR_FAIL_COND(!is_inside_tree()); ERR_FAIL_COND(!tile_set.is_valid()); - // Get colors for debug. - SceneTree *st = SceneTree::get_singleton(); - Color debug_navigation_color; - bool debug_navigation = st && st->is_debugging_navigation_hint(); - if (debug_navigation) { - debug_navigation_color = st->get_debug_navigation_color(); - } - Transform2D tilemap_xform = get_global_transform(); SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first(); while (q_list_element) { @@ -1691,7 +1705,7 @@ void TileMap::_navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List if (navpoly.is_valid()) { Transform2D tile_transform; - tile_transform.set_origin(map_to_world(E_cell)); + tile_transform.set_origin(map_to_local(E_cell)); RID region = NavigationServer2D::get_singleton()->region_create(); NavigationServer2D::get_singleton()->region_set_map(region, get_world_2d()->get_navigation_map()); @@ -1748,10 +1762,13 @@ void TileMap::_navigation_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { RenderingServer *rs = RenderingServer::get_singleton(); - Color color = get_tree()->get_debug_navigation_color(); + Color color = Color(0.5, 1.0, 1.0, 1.0); +#ifdef DEBUG_ENABLED + color = NavigationServer3D::get_singleton()->get_debug_navigation_geometry_face_color(); +#endif // DEBUG_ENABLED RandomPCG rand; - Vector2 quadrant_pos = map_to_world(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer)); + Vector2 quadrant_pos = map_to_local(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer)); for (const Vector2i &E_cell : p_quadrant->cells) { TileMapCell c = get_cell(p_quadrant->layer, E_cell, true); @@ -1774,7 +1791,7 @@ void TileMap::_navigation_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { } Transform2D xform; - xform.set_origin(map_to_world(E_cell) - quadrant_pos); + xform.set_origin(map_to_local(E_cell) - quadrant_pos); rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform); for (int layer_index = 0; layer_index < tile_set->get_navigation_layers_count(); layer_index++) { @@ -1844,16 +1861,16 @@ void TileMap::_scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_ Ref<PackedScene> packed_scene = scenes_collection_source->get_scene_tile_scene(c.alternative_tile); if (packed_scene.is_valid()) { Node *scene = packed_scene->instantiate(); - add_child(scene); Control *scene_as_control = Object::cast_to<Control>(scene); Node2D *scene_as_node2d = Object::cast_to<Node2D>(scene); if (scene_as_control) { - scene_as_control->set_position(map_to_world(E_cell) + scene_as_control->get_position()); + scene_as_control->set_position(map_to_local(E_cell) + scene_as_control->get_position()); } else if (scene_as_node2d) { Transform2D xform; - xform.set_origin(map_to_world(E_cell)); + xform.set_origin(map_to_local(E_cell)); scene_as_node2d->set_transform(xform * scene_as_node2d->get_transform()); } + add_child(scene); q.scenes[E_cell] = scene->get_name(); } } @@ -1885,7 +1902,7 @@ void TileMap::_scenes_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { // Draw a placeholder for scenes needing one. RenderingServer *rs = RenderingServer::get_singleton(); - Vector2 quadrant_pos = map_to_world(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer)); + Vector2 quadrant_pos = map_to_local(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer)); for (const Vector2i &E_cell : p_quadrant->cells) { const TileMapCell &c = get_cell(p_quadrant->layer, E_cell, true); @@ -1915,7 +1932,7 @@ void TileMap::_scenes_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { // Draw a placeholder tile. Transform2D xform; - xform.set_origin(map_to_world(E_cell) - quadrant_pos); + xform.set_origin(map_to_local(E_cell) - quadrant_pos); rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform); rs->canvas_item_add_circle(p_quadrant->debug_canvas_item, Vector2(), MIN(tile_set->get_tile_size().x, tile_set->get_tile_size().y) / 4.0, color); } @@ -2063,6 +2080,18 @@ int TileMap::get_cell_alternative_tile(int p_layer, const Vector2i &p_coords, bo return E->value.alternative_tile; } +TileData *TileMap::get_cell_tile_data(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const { + int source_id = get_cell_source_id(p_layer, p_coords, p_use_proxies); + ERR_FAIL_COND_V_MSG(source_id == TileSet::INVALID_SOURCE, nullptr, vformat("Invalid TileSetSource at cell %s. Make sure a tile exists at this cell.", p_coords)); + + Ref<TileSetAtlasSource> source = tile_set->get_source(source_id); + if (source.is_valid()) { + return source->get_tile_data(get_cell_atlas_coords(p_layer, p_coords, p_use_proxies), get_cell_alternative_tile(p_layer, p_coords, p_use_proxies)); + } + + return nullptr; +} + Ref<TileMapPattern> TileMap::get_pattern(int p_layer, TypedArray<Vector2i> p_coords_array) { ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), nullptr); ERR_FAIL_COND_V(!tile_set.is_valid(), nullptr); @@ -2652,7 +2681,7 @@ void TileMap::clear_layer(int p_layer) { // Remove all tiles. _clear_layer_internals(p_layer); layers[p_layer].tile_map.clear(); - + _recreate_layer_internals(p_layer); used_rect_cache_dirty = true; } @@ -2662,6 +2691,7 @@ void TileMap::clear() { for (unsigned int i = 0; i < layers.size(); i++) { layers[i].tile_map.clear(); } + _recreate_internals(); used_rect_cache_dirty = true; } @@ -2791,7 +2821,7 @@ void TileMap::_build_runtime_update_tile_data(SelfList<TileMapQuadrant>::List &r while (q_list_element) { TileMapQuadrant &q = *q_list_element->self(); // Iterate over the cells of the quadrant. - for (const KeyValue<Vector2i, Vector2i> &E_cell : q.world_to_map) { + for (const KeyValue<Vector2i, Vector2i> &E_cell : q.local_to_map) { TileMapCell c = get_cell(q.layer, E_cell.value, true); TileSetSource *source; @@ -2950,7 +2980,7 @@ void TileMap::_get_property_list(List<PropertyInfo> *p_list) const { } } -Vector2 TileMap::map_to_world(const Vector2i &p_pos) const { +Vector2 TileMap::map_to_local(const Vector2i &p_pos) const { // SHOULD RETURN THE CENTER OF THE CELL ERR_FAIL_COND_V(!tile_set.is_valid(), Vector2()); @@ -3027,10 +3057,10 @@ Vector2 TileMap::map_to_world(const Vector2i &p_pos) const { return (ret + Vector2(0.5, 0.5)) * tile_set->get_tile_size(); } -Vector2i TileMap::world_to_map(const Vector2 &p_pos) const { +Vector2i TileMap::local_to_map(const Vector2 &p_local_position) const { ERR_FAIL_COND_V(!tile_set.is_valid(), Vector2i()); - Vector2 ret = p_pos; + Vector2 ret = p_local_position; ret /= tile_set->get_tile_size(); TileSet::TileShape tile_shape = tile_set->get_tile_shape(); @@ -3055,7 +3085,7 @@ Vector2i TileMap::world_to_map(const Vector2 &p_pos) const { ret.x /= overlapping_ratio; } - // For each half-offset shape, we check if we are in the corner of the tile, and thus should correct the world position accordingly. + // For each half-offset shape, we check if we are in the corner of the tile, and thus should correct the local position accordingly. if (tile_shape == TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE || tile_shape == TileSet::TILE_SHAPE_HEXAGON || tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) { // Technically, those 3 shapes are equivalent, as they are basically half-offset, but with different levels or overlap. // square = no overlap, hexagon = 0.25 overlap, isometric = 0.5 overlap @@ -3585,7 +3615,7 @@ TypedArray<Vector2i> TileMap::get_used_cells(int p_layer) const { return a; } -Rect2 TileMap::get_used_rect() { // Not const because of cache +Rect2i TileMap::get_used_rect() { // Not const because of cache // Return the rect of the currently used area if (used_rect_cache_dirty) { bool first = true; @@ -3740,7 +3770,7 @@ void TileMap::draw_cells_outline(Control *p_control, RBSet<Vector2i> p_cells, Co TileSet::TileShape shape = tile_set->get_tile_shape(); for (const Vector2i &E : p_cells) { - Vector2 center = map_to_world(E); + Vector2 center = map_to_local(E); #define DRAW_SIDE_IF_NEEDED(side, polygon_index_from, polygon_index_to) \ if (!p_cells.has(get_neighbor_cell(E, side))) { \ @@ -3789,8 +3819,8 @@ void TileMap::draw_cells_outline(Control *p_control, RBSet<Vector2i> p_cells, Co #undef DRAW_SIDE_IF_NEEDED } -TypedArray<String> TileMap::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray TileMap::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); // Retrieve the set of Z index values with a Y-sorted layer. RBSet<int> y_sorted_z_index; @@ -3826,7 +3856,7 @@ void TileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("get_layer_name", "layer"), &TileMap::get_layer_name); ClassDB::bind_method(D_METHOD("set_layer_enabled", "layer", "enabled"), &TileMap::set_layer_enabled); ClassDB::bind_method(D_METHOD("is_layer_enabled", "layer"), &TileMap::is_layer_enabled); - ClassDB::bind_method(D_METHOD("set_layer_modulate", "layer", "enabled"), &TileMap::set_layer_modulate); + ClassDB::bind_method(D_METHOD("set_layer_modulate", "layer", "modulate"), &TileMap::set_layer_modulate); ClassDB::bind_method(D_METHOD("get_layer_modulate", "layer"), &TileMap::get_layer_modulate); ClassDB::bind_method(D_METHOD("set_layer_y_sort_enabled", "layer", "y_sort_enabled"), &TileMap::set_layer_y_sort_enabled); ClassDB::bind_method(D_METHOD("is_layer_y_sort_enabled", "layer"), &TileMap::is_layer_y_sort_enabled); @@ -3845,9 +3875,10 @@ void TileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("set_cell", "layer", "coords", "source_id", "atlas_coords", "alternative_tile"), &TileMap::set_cell, DEFVAL(TileSet::INVALID_SOURCE), DEFVAL(TileSetSource::INVALID_ATLAS_COORDS), DEFVAL(0)); ClassDB::bind_method(D_METHOD("erase_cell", "layer", "coords"), &TileMap::erase_cell); - ClassDB::bind_method(D_METHOD("get_cell_source_id", "layer", "coords", "use_proxies"), &TileMap::get_cell_source_id); - ClassDB::bind_method(D_METHOD("get_cell_atlas_coords", "layer", "coords", "use_proxies"), &TileMap::get_cell_atlas_coords); - ClassDB::bind_method(D_METHOD("get_cell_alternative_tile", "layer", "coords", "use_proxies"), &TileMap::get_cell_alternative_tile); + ClassDB::bind_method(D_METHOD("get_cell_source_id", "layer", "coords", "use_proxies"), &TileMap::get_cell_source_id, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_cell_atlas_coords", "layer", "coords", "use_proxies"), &TileMap::get_cell_atlas_coords, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_cell_alternative_tile", "layer", "coords", "use_proxies"), &TileMap::get_cell_alternative_tile, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_cell_tile_data", "layer", "coords", "use_proxies"), &TileMap::get_cell_tile_data, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_coords_for_body_rid", "body"), &TileMap::get_coords_for_body_rid); @@ -3869,8 +3900,8 @@ void TileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("get_used_cells", "layer"), &TileMap::get_used_cells); ClassDB::bind_method(D_METHOD("get_used_rect"), &TileMap::get_used_rect); - ClassDB::bind_method(D_METHOD("map_to_world", "map_position"), &TileMap::map_to_world); - ClassDB::bind_method(D_METHOD("world_to_map", "world_position"), &TileMap::world_to_map); + ClassDB::bind_method(D_METHOD("map_to_local", "map_position"), &TileMap::map_to_local); + ClassDB::bind_method(D_METHOD("local_to_map", "local_position"), &TileMap::local_to_map); ClassDB::bind_method(D_METHOD("get_neighbor_cell", "coords", "neighbor"), &TileMap::get_neighbor_cell); diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index 0ac94b9d45..902926291d 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -40,7 +40,7 @@ class TileSetAtlasSource; struct TileMapQuadrant { struct CoordsWorldComparator { _ALWAYS_INLINE_ bool operator()(const Vector2i &p_a, const Vector2i &p_b) const { - // We sort the cells by their world coords, as it is needed by rendering. + // We sort the cells by their local coords, as it is needed by rendering. if (p_a.y == p_b.y) { return p_a.x > p_b.x; } else { @@ -49,7 +49,7 @@ struct TileMapQuadrant { } }; - // Dirty list element + // Dirty list element. SelfList<TileMapQuadrant> dirty_list_element; // Quadrant layer and coords. @@ -58,10 +58,10 @@ struct TileMapQuadrant { // TileMapCells RBSet<Vector2i> cells; - // We need those two maps to sort by world position for rendering + // We need those two maps to sort by local position for rendering // This is kind of workaround, it would be better to sort the cells directly in the "cells" set instead. - RBMap<Vector2i, Vector2i> map_to_world; - RBMap<Vector2i, Vector2i, CoordsWorldComparator> world_to_map; + RBMap<Vector2i, Vector2i> map_to_local; + RBMap<Vector2i, Vector2i, CoordsWorldComparator> local_to_map; // Debug. RID debug_canvas_item; @@ -130,7 +130,7 @@ public: } String to_string() const { - return vformat("Constraint {pos:%s, bit:%d, terrain:%d, priotity:%d}", base_cell_coords, bit, terrain, priority); + return vformat("Constraint {pos:%s, bit:%d, terrain:%d, priority:%d}", base_cell_coords, bit, terrain, priority); } Vector2i get_base_cell_coords() const { @@ -343,6 +343,8 @@ public: int get_cell_source_id(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const; Vector2i get_cell_atlas_coords(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const; int get_cell_alternative_tile(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const; + // Helper method to make accessing the data easier. + TileData *get_cell_tile_data(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const; // Patterns. Ref<TileMapPattern> get_pattern(int p_layer, TypedArray<Vector2i> p_coords_array); @@ -366,14 +368,14 @@ public: virtual void set_y_sort_enabled(bool p_enable) override; - Vector2 map_to_world(const Vector2i &p_pos) const; - Vector2i world_to_map(const Vector2 &p_pos) const; + Vector2 map_to_local(const Vector2i &p_pos) const; + Vector2i local_to_map(const Vector2 &p_pos) const; bool is_existing_neighbor(TileSet::CellNeighbor p_cell_neighbor) const; Vector2i get_neighbor_cell(const Vector2i &p_coords, TileSet::CellNeighbor p_cell_neighbor) const; TypedArray<Vector2i> get_used_cells(int p_layer) const; - Rect2 get_used_rect(); // Not const because of cache + Rect2i get_used_rect(); // Not const because of cache // Override some methods of the CanvasItem class to pass the changes to the quadrants CanvasItems virtual void set_light_mask(int p_light_mask) override; @@ -404,7 +406,7 @@ public: GDVIRTUAL3(_tile_data_runtime_update, int, Vector2i, TileData *); // Configuration warnings. - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; TileMap(); ~TileMap(); diff --git a/scene/2d/touch_screen_button.cpp b/scene/2d/touch_screen_button.cpp index 4a4a2a1da0..a02f322ef1 100644 --- a/scene/2d/touch_screen_button.cpp +++ b/scene/2d/touch_screen_button.cpp @@ -34,7 +34,7 @@ void TouchScreenButton::set_texture_normal(const Ref<Texture2D> &p_texture) { texture_normal = p_texture; - update(); + queue_redraw(); } Ref<Texture2D> TouchScreenButton::get_texture_normal() const { @@ -43,7 +43,7 @@ Ref<Texture2D> TouchScreenButton::get_texture_normal() const { void TouchScreenButton::set_texture_pressed(const Ref<Texture2D> &p_texture_pressed) { texture_pressed = p_texture_pressed; - update(); + queue_redraw(); } Ref<Texture2D> TouchScreenButton::get_texture_pressed() const { @@ -60,16 +60,16 @@ Ref<BitMap> TouchScreenButton::get_bitmask() const { void TouchScreenButton::set_shape(const Ref<Shape2D> &p_shape) { if (shape.is_valid()) { - shape->disconnect("changed", callable_mp((CanvasItem *)this, &CanvasItem::update)); + shape->disconnect("changed", callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw)); } shape = p_shape; if (shape.is_valid()) { - shape->connect("changed", callable_mp((CanvasItem *)this, &CanvasItem::update)); + shape->connect("changed", callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw)); } - update(); + queue_redraw(); } Ref<Shape2D> TouchScreenButton::get_shape() const { @@ -78,7 +78,7 @@ Ref<Shape2D> TouchScreenButton::get_shape() const { void TouchScreenButton::set_shape_centered(bool p_shape_centered) { shape_centered = p_shape_centered; - update(); + queue_redraw(); } bool TouchScreenButton::is_shape_visible() const { @@ -87,7 +87,7 @@ bool TouchScreenButton::is_shape_visible() const { void TouchScreenButton::set_shape_visible(bool p_shape_visible) { shape_visible = p_shape_visible; - update(); + queue_redraw(); } bool TouchScreenButton::is_shape_centered() const { @@ -131,7 +131,7 @@ void TouchScreenButton::_notification(int p_what) { pos = texture_normal->get_size() * 0.5; } - draw_set_transform_matrix(get_canvas_transform().translated(pos)); + draw_set_transform_matrix(get_canvas_transform().translated_local(pos)); shape->draw(get_canvas_item(), draw_col); } } break; @@ -140,7 +140,7 @@ void TouchScreenButton::_notification(int p_what) { if (!Engine::get_singleton()->is_editor_hint() && !!DisplayServer::get_singleton()->screen_is_touchscreen(DisplayServer::get_singleton()->window_get_current_screen(get_viewport()->get_window_id())) && visibility == VISIBILITY_TOUCHSCREEN_ONLY) { return; } - update(); + queue_redraw(); if (!Engine::get_singleton()->is_editor_hint()) { set_process_input(is_visible_in_tree()); @@ -258,13 +258,13 @@ bool TouchScreenButton::_is_point_inside(const Point2 &p_point) { pos = texture_normal->get_size() * 0.5; } - touched = shape->collide(Transform2D().translated(pos), unit_rect, Transform2D(0, coord + Vector2(0.5, 0.5))); + touched = shape->collide(Transform2D().translated_local(pos), unit_rect, Transform2D(0, coord + Vector2(0.5, 0.5))); } if (bitmask.is_valid()) { check_rect = false; if (!touched && Rect2(Point2(), bitmask->get_size()).has_point(coord)) { - if (bitmask->get_bit(coord)) { + if (bitmask->get_bitv(coord)) { touched = true; } } @@ -292,7 +292,7 @@ void TouchScreenButton::_press(int p_finger_pressed) { } emit_signal(SNAME("pressed")); - update(); + queue_redraw(); } void TouchScreenButton::_release(bool p_exiting_tree) { @@ -311,7 +311,7 @@ void TouchScreenButton::_release(bool p_exiting_tree) { if (!p_exiting_tree) { emit_signal(SNAME("released")); - update(); + queue_redraw(); } } @@ -339,7 +339,7 @@ Rect2 TouchScreenButton::get_anchorable_rect() const { void TouchScreenButton::set_visibility_mode(VisibilityMode p_mode) { visibility = p_mode; - update(); + queue_redraw(); } TouchScreenButton::VisibilityMode TouchScreenButton::get_visibility_mode() const { diff --git a/scene/2d/visible_on_screen_notifier_2d.cpp b/scene/2d/visible_on_screen_notifier_2d.cpp index 1971dc1240..263c3a12a2 100644 --- a/scene/2d/visible_on_screen_notifier_2d.cpp +++ b/scene/2d/visible_on_screen_notifier_2d.cpp @@ -66,7 +66,7 @@ void VisibleOnScreenNotifier2D::set_rect(const Rect2 &p_rect) { if (is_inside_tree()) { RS::get_singleton()->canvas_item_set_visibility_notifier(get_canvas_item(), true, rect, callable_mp(this, &VisibleOnScreenNotifier2D::_visibility_enter), callable_mp(this, &VisibleOnScreenNotifier2D::_visibility_exit)); } - update(); + queue_redraw(); } Rect2 VisibleOnScreenNotifier2D::get_rect() const { diff --git a/scene/2d/visible_on_screen_notifier_2d.h b/scene/2d/visible_on_screen_notifier_2d.h index 38b508e2f6..ac7fad95a5 100644 --- a/scene/2d/visible_on_screen_notifier_2d.h +++ b/scene/2d/visible_on_screen_notifier_2d.h @@ -102,4 +102,4 @@ public: VARIANT_ENUM_CAST(VisibleOnScreenEnabler2D::EnableMode); -#endif // VISIBILITY_NOTIFIER_2D_H +#endif // VISIBLE_ON_SCREEN_NOTIFIER_2D_H diff --git a/scene/3d/area_3d.cpp b/scene/3d/area_3d.cpp index fb0c59daa1..cefa9eceff 100644 --- a/scene/3d/area_3d.cpp +++ b/scene/3d/area_3d.cpp @@ -239,8 +239,8 @@ void Area3D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, i E->value.rc = 0; E->value.in_tree = node && node->is_inside_tree(); if (node) { - node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area3D::_body_enter_tree), make_binds(objid)); - node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area3D::_body_exit_tree), make_binds(objid)); + node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area3D::_body_enter_tree).bind(objid)); + node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area3D::_body_exit_tree).bind(objid)); if (E->value.in_tree) { emit_signal(SceneStringNames::get_singleton()->body_entered, node); } @@ -426,8 +426,8 @@ void Area3D::_area_inout(int p_status, const RID &p_area, ObjectID p_instance, i E->value.rc = 0; E->value.in_tree = node && node->is_inside_tree(); if (node) { - node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area3D::_area_enter_tree), make_binds(objid)); - node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area3D::_area_exit_tree), make_binds(objid)); + node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area3D::_area_enter_tree).bind(objid)); + node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area3D::_area_exit_tree).bind(objid)); if (E->value.in_tree) { emit_signal(SceneStringNames::get_singleton()->area_entered, node); } @@ -473,22 +473,27 @@ bool Area3D::is_monitoring() const { } TypedArray<Node3D> Area3D::get_overlapping_bodies() const { - ERR_FAIL_COND_V(!monitoring, Array()); - Array ret; + TypedArray<Node3D> ret; + ERR_FAIL_COND_V_MSG(!monitoring, ret, "Can't find overlapping bodies when monitoring is off."); ret.resize(body_map.size()); int idx = 0; for (const KeyValue<ObjectID, BodyState> &E : body_map) { Object *obj = ObjectDB::get_instance(E.key); - if (!obj) { - ret.resize(ret.size() - 1); //ops - } else { - ret[idx++] = obj; + if (obj) { + ret[idx] = obj; + idx++; } } + ret.resize(idx); return ret; } +bool Area3D::has_overlapping_bodies() const { + ERR_FAIL_COND_V_MSG(!monitoring, false, "Can't find overlapping bodies when monitoring is off."); + return !body_map.is_empty(); +} + void Area3D::set_monitorable(bool p_enable) { ERR_FAIL_COND_MSG(locked || (is_inside_tree() && PhysicsServer3D::get_singleton()->is_flushing_queries()), "Function blocked during in/out signal. Use set_deferred(\"monitorable\", true/false)."); @@ -506,22 +511,26 @@ bool Area3D::is_monitorable() const { } TypedArray<Area3D> Area3D::get_overlapping_areas() const { - ERR_FAIL_COND_V(!monitoring, Array()); - Array ret; + TypedArray<Area3D> ret; + ERR_FAIL_COND_V_MSG(!monitoring, ret, "Can't find overlapping areas when monitoring is off."); ret.resize(area_map.size()); int idx = 0; for (const KeyValue<ObjectID, AreaState> &E : area_map) { Object *obj = ObjectDB::get_instance(E.key); - if (!obj) { - ret.resize(ret.size() - 1); //ops - } else { - ret[idx++] = obj; + if (obj) { + ret[idx] = obj; + idx++; } } - + ret.resize(idx); return ret; } +bool Area3D::has_overlapping_areas() const { + ERR_FAIL_COND_V_MSG(!monitoring, false, "Can't find overlapping areas when monitoring is off."); + return !area_map.is_empty(); +} + bool Area3D::overlaps_area(Node *p_area) const { ERR_FAIL_NULL_V(p_area, false); HashMap<ObjectID, AreaState>::ConstIterator E = area_map.find(p_area->get_instance_id()); @@ -598,8 +607,8 @@ float Area3D::get_reverb_uniformity() const { return reverb_uniformity; } -void Area3D::_validate_property(PropertyInfo &property) const { - if (property.name == "audio_bus_name" || property.name == "reverb_bus_name") { +void Area3D::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "audio_bus_name" || p_property.name == "reverb_bus_name") { String options; for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) { if (i > 0) { @@ -609,32 +618,30 @@ void Area3D::_validate_property(PropertyInfo &property) const { options += name; } - property.hint_string = options; - } else if (property.name.begins_with("gravity") && property.name != "gravity_space_override") { + p_property.hint_string = options; + } else if (p_property.name.begins_with("gravity") && p_property.name != "gravity_space_override") { if (gravity_space_override == SPACE_OVERRIDE_DISABLED) { - property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } else { if (gravity_is_point) { - if (property.name == "gravity_direction") { - property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + if (p_property.name == "gravity_direction") { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } } else { - if (property.name.begins_with("gravity_point_")) { - property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + if (p_property.name.begins_with("gravity_point_")) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } } } - } else if (property.name.begins_with("linear_damp") && property.name != "linear_damp_space_override") { + } else if (p_property.name.begins_with("linear_damp") && p_property.name != "linear_damp_space_override") { if (linear_damp_space_override == SPACE_OVERRIDE_DISABLED) { - property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } - } else if (property.name.begins_with("angular_damp") && property.name != "angular_damp_space_override") { + } else if (p_property.name.begins_with("angular_damp") && p_property.name != "angular_damp_space_override") { if (angular_damp_space_override == SPACE_OVERRIDE_DISABLED) { - property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } } - - CollisionObject3D::_validate_property(property); } void Area3D::_bind_methods() { @@ -689,6 +696,9 @@ void Area3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_overlapping_bodies"), &Area3D::get_overlapping_bodies); ClassDB::bind_method(D_METHOD("get_overlapping_areas"), &Area3D::get_overlapping_areas); + ClassDB::bind_method(D_METHOD("has_overlapping_bodies"), &Area3D::has_overlapping_bodies); + ClassDB::bind_method(D_METHOD("has_overlapping_areas"), &Area3D::has_overlapping_areas); + ClassDB::bind_method(D_METHOD("overlaps_body", "body"), &Area3D::overlaps_body); ClassDB::bind_method(D_METHOD("overlaps_area", "area"), &Area3D::overlaps_area); @@ -730,7 +740,7 @@ void Area3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_point_distance_scale", PROPERTY_HINT_RANGE, "0,1024,0.001,or_greater,exp"), "set_gravity_point_distance_scale", "get_gravity_point_distance_scale"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity_point_center", PROPERTY_HINT_NONE, "suffix:m"), "set_gravity_point_center", "get_gravity_point_center"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity_direction"), "set_gravity_direction", "get_gravity_direction"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity", PROPERTY_HINT_RANGE, U"-32,32,0.001,or_lesser,or_greater,suffix:m/s\u00B2"), "set_gravity", "get_gravity"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity", PROPERTY_HINT_RANGE, U"-32,32,0.001,or_less,or_greater,suffix:m/s\u00B2"), "set_gravity", "get_gravity"); ADD_GROUP("Linear Damp", "linear_damp_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "linear_damp_space_override", PROPERTY_HINT_ENUM, "Disabled,Combine,Combine-Replace,Replace,Replace-Combine", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_linear_damp_space_override_mode", "get_linear_damp_space_override_mode"); diff --git a/scene/3d/area_3d.h b/scene/3d/area_3d.h index 3b892baf57..0f0bcc7ce0 100644 --- a/scene/3d/area_3d.h +++ b/scene/3d/area_3d.h @@ -141,7 +141,7 @@ private: float reverb_amount = 0.0; float reverb_uniformity = 0.0; - void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; void _initialize_wind(); @@ -201,6 +201,9 @@ public: TypedArray<Node3D> get_overlapping_bodies() const; TypedArray<Area3D> get_overlapping_areas() const; //function for script + bool has_overlapping_bodies() const; + bool has_overlapping_areas() const; + bool overlaps_area(Node *p_area) const; bool overlaps_body(Node *p_body) const; diff --git a/scene/3d/audio_listener_3d.h b/scene/3d/audio_listener_3d.h index ebc37673ed..44c49f526e 100644 --- a/scene/3d/audio_listener_3d.h +++ b/scene/3d/audio_listener_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef LISTENER_3D_H -#define LISTENER_3D_H +#ifndef AUDIO_LISTENER_3D_H +#define AUDIO_LISTENER_3D_H #include "scene/3d/node_3d.h" @@ -67,4 +67,4 @@ public: ~AudioListener3D(); }; -#endif +#endif // AUDIO_LISTENER_3D_H diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp index 824ea0407e..21cf3bdb3b 100644 --- a/scene/3d/audio_stream_player_3d.cpp +++ b/scene/3d/audio_stream_player_3d.cpp @@ -149,7 +149,7 @@ void AudioStreamPlayer3D::_calc_reverb_vol(Area3D *area, Vector3 listener_area_p if (uniformity > 0.0) { float distance = listener_area_pos.length(); - float attenuation = Math::db2linear(_get_attenuation_db(distance)); + float attenuation = Math::db_to_linear(_get_attenuation_db(distance)); // Determine the fraction of sound that would come from each speaker if they were all driven uniformly. float center_val[3] = { 0.5f, 0.25f, 0.16666f }; @@ -213,12 +213,12 @@ float AudioStreamPlayer3D::_get_attenuation_db(float p_distance) const { float att = 0; switch (attenuation_model) { case ATTENUATION_INVERSE_DISTANCE: { - att = Math::linear2db(1.0 / ((p_distance / unit_size) + CMP_EPSILON)); + att = Math::linear_to_db(1.0 / ((p_distance / unit_size) + CMP_EPSILON)); } break; case ATTENUATION_INVERSE_SQUARE_DISTANCE: { float d = (p_distance / unit_size); d *= d; - att = Math::linear2db(1.0 / (d + CMP_EPSILON)); + att = Math::linear_to_db(1.0 / (d + CMP_EPSILON)); } break; case ATTENUATION_LOGARITHMIC: { att = -20 * Math::log(p_distance / unit_size + CMP_EPSILON); @@ -247,13 +247,18 @@ void AudioStreamPlayer3D::_notification(int p_what) { if (autoplay && !Engine::get_singleton()->is_editor_hint()) { play(); } + set_stream_paused(false); } break; case NOTIFICATION_EXIT_TREE: { - stop(); + set_stream_paused(true); AudioServer::get_singleton()->remove_listener_changed_callback(_listener_changed_cb, this); } break; + case NOTIFICATION_PREDELETE: { + stop(); + } break; + case NOTIFICATION_PAUSED: { if (!can_process()) { // Node can't process so we start fading out to silence. @@ -281,7 +286,7 @@ void AudioStreamPlayer3D::_notification(int p_what) { if (setplay.get() >= 0 && stream.is_valid()) { active.set(); - Ref<AudioStreamPlayback> new_playback = stream->instance_playback(); + Ref<AudioStreamPlayback> new_playback = stream->instantiate_playback(); ERR_FAIL_COND_MSG(new_playback.is_null(), "Failed to instantiate playback."); HashMap<StringName, Vector<AudioFrame>> bus_map; bus_map[_get_actual_bus()] = volume_vector; @@ -443,7 +448,7 @@ Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() { } } - float multiplier = Math::db2linear(_get_attenuation_db(dist)); + float multiplier = Math::db_to_linear(_get_attenuation_db(dist)); if (max_distance > 0) { multiplier *= MAX(0, 1.0 - (dist / max_distance)); } @@ -453,13 +458,13 @@ Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() { if (emission_angle_enabled) { Vector3 listenertopos = global_pos - listener_node->get_global_transform().origin; float c = listenertopos.normalized().dot(get_global_transform().basis.get_column(2).normalized()); //it's z negative - float angle = Math::rad2deg(Math::acos(c)); + float angle = Math::rad_to_deg(Math::acos(c)); if (angle > emission_angle) { db_att -= -emission_angle_filter_attenuation_db; } } - linear_attenuation = Math::db2linear(db_att); + linear_attenuation = Math::db_to_linear(db_att); for (Ref<AudioStreamPlayback> &playback : stream_playbacks) { AudioServer::get_singleton()->set_playback_highshelf_params(playback, linear_attenuation, attenuation_filter_cutoff_hz); } @@ -648,8 +653,8 @@ bool AudioStreamPlayer3D::_is_active() const { return active.is_set(); } -void AudioStreamPlayer3D::_validate_property(PropertyInfo &property) const { - if (property.name == "bus") { +void AudioStreamPlayer3D::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "bus") { String options; for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) { if (i > 0) { @@ -659,10 +664,8 @@ void AudioStreamPlayer3D::_validate_property(PropertyInfo &property) const { options += name; } - property.hint_string = options; + p_property.hint_string = options; } - - Node3D::_validate_property(property); } void AudioStreamPlayer3D::_bus_layout_changed() { diff --git a/scene/3d/audio_stream_player_3d.h b/scene/3d/audio_stream_player_3d.h index 85ece6d8d5..ef48269544 100644 --- a/scene/3d/audio_stream_player_3d.h +++ b/scene/3d/audio_stream_player_3d.h @@ -120,7 +120,7 @@ private: float cached_global_panning_strength = 1.0f; protected: - void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; void _notification(int p_what); static void _bind_methods(); @@ -196,4 +196,5 @@ public: VARIANT_ENUM_CAST(AudioStreamPlayer3D::AttenuationModel) VARIANT_ENUM_CAST(AudioStreamPlayer3D::DopplerTracking) + #endif // AUDIO_STREAM_PLAYER_3D_H diff --git a/scene/3d/bone_attachment_3d.cpp b/scene/3d/bone_attachment_3d.cpp index fbd5b5b65b..7b0a6c7e3e 100644 --- a/scene/3d/bone_attachment_3d.cpp +++ b/scene/3d/bone_attachment_3d.cpp @@ -30,8 +30,8 @@ #include "bone_attachment_3d.h" -void BoneAttachment3D::_validate_property(PropertyInfo &property) const { - if (property.name == "bone_name") { +void BoneAttachment3D::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "bone_name") { // Because it is a constant function, we cannot use the _get_skeleton_3d function. const Skeleton3D *parent = nullptr; if (use_external_skeleton) { @@ -51,15 +51,13 @@ void BoneAttachment3D::_validate_property(PropertyInfo &property) const { names += parent->get_bone_name(i); } - property.hint = PROPERTY_HINT_ENUM; - property.hint_string = names; + p_property.hint = PROPERTY_HINT_ENUM; + p_property.hint_string = names; } else { - property.hint = PROPERTY_HINT_NONE; - property.hint_string = ""; + p_property.hint = PROPERTY_HINT_NONE; + p_property.hint_string = ""; } } - - Node3D::_validate_property(property); } bool BoneAttachment3D::_set(const StringName &p_path, const Variant &p_value) { @@ -102,8 +100,8 @@ void BoneAttachment3D::_get_property_list(List<PropertyInfo> *p_list) const { } } -TypedArray<String> BoneAttachment3D::get_configuration_warnings() const { - TypedArray<String> warnings = Node3D::get_configuration_warnings(); +PackedStringArray BoneAttachment3D::get_configuration_warnings() const { + PackedStringArray warnings = Node3D::get_configuration_warnings(); if (use_external_skeleton) { if (external_skeleton_node_cache.is_null()) { diff --git a/scene/3d/bone_attachment_3d.h b/scene/3d/bone_attachment_3d.h index 137360b141..2db6ba6268 100644 --- a/scene/3d/bone_attachment_3d.h +++ b/scene/3d/bone_attachment_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef BONE_ATTACHMENT_H -#define BONE_ATTACHMENT_H +#ifndef BONE_ATTACHMENT_3D_H +#define BONE_ATTACHMENT_3D_H #include "scene/3d/skeleton_3d.h" #ifdef TOOLS_ENABLED @@ -64,7 +64,7 @@ class BoneAttachment3D : public Node3D { Skeleton3D *_get_skeleton3d(); protected: - virtual void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; bool _get(const StringName &p_path, Variant &r_ret) const; bool _set(const StringName &p_path, const Variant &p_value); void _get_property_list(List<PropertyInfo> *p_list) const; @@ -76,7 +76,7 @@ protected: #endif // TOOLS_ENABLED public: - virtual TypedArray<String> get_configuration_warnings() const override; + virtual PackedStringArray get_configuration_warnings() const override; void set_bone_name(const String &p_name); String get_bone_name() const; @@ -99,4 +99,4 @@ public: BoneAttachment3D(); }; -#endif // BONE_ATTACHMENT_H +#endif // BONE_ATTACHMENT_3D_H diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp index 10348b1eb6..304e56326d 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -31,7 +31,8 @@ #include "camera_3d.h" #include "collision_object_3d.h" -#include "core/math/camera_matrix.h" +#include "core/core_string_names.h" +#include "core/math/projection.h" #include "scene/main/viewport.h" void Camera3D::_update_audio_listener_state() { @@ -72,6 +73,15 @@ void Camera3D::_validate_property(PropertyInfo &p_property) const { } } + if (attributes.is_valid()) { + const CameraAttributesPhysical *physical_attributes = Object::cast_to<CameraAttributesPhysical>(attributes.ptr()); + if (physical_attributes) { + if (p_property.name == "near" || p_property.name == "far" || p_property.name == "fov" || p_property.name == "keep_aspect") { + p_property.usage = PROPERTY_USAGE_READ_ONLY | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR; + } + } + } + Node3D::_validate_property(p_property); } @@ -197,7 +207,7 @@ void Camera3D::set_frustum(real_t p_size, Vector2 p_offset, real_t p_z_near, rea update_gizmos(); } -void Camera3D::set_projection(Camera3D::Projection p_mode) { +void Camera3D::set_projection(ProjectionType p_mode) { if (p_mode == PROJECTION_PERSPECTIVE || p_mode == PROJECTION_ORTHOGONAL || p_mode == PROJECTION_FRUSTUM) { mode = p_mode; _update_camera_mode(); @@ -265,7 +275,7 @@ Vector3 Camera3D::project_local_ray_normal(const Point2 &p_pos) const { if (mode == PROJECTION_ORTHOGONAL) { ray = Vector3(0, 0, -1); } else { - CameraMatrix cm; + Projection cm; cm.set_perspective(fov, viewport_size.aspect(), near, far, keep_aspect == KEEP_WIDTH); Vector2 screen_he = cm.get_viewport_half_extents(); ray = Vector3(((cpos.x / viewport_size.width) * 2.0 - 1.0) * screen_he.x, ((1.0 - (cpos.y / viewport_size.height)) * 2.0 - 1.0) * screen_he.y, -near).normalized(); @@ -314,7 +324,7 @@ Vector<Vector3> Camera3D::get_near_plane_points() const { Size2 viewport_size = get_viewport()->get_visible_rect().size; - CameraMatrix cm; + Projection cm; if (mode == PROJECTION_ORTHOGONAL) { cm.set_orthogonal(size, viewport_size.aspect(), near, far, keep_aspect == KEEP_WIDTH); @@ -340,7 +350,7 @@ Point2 Camera3D::unproject_position(const Vector3 &p_pos) const { Size2 viewport_size = get_viewport()->get_visible_rect().size; - CameraMatrix cm; + Projection cm; if (mode == PROJECTION_ORTHOGONAL) { cm.set_orthogonal(size, viewport_size.aspect(), near, far, keep_aspect == KEEP_WIDTH); @@ -368,7 +378,7 @@ Vector3 Camera3D::project_position(const Point2 &p_point, real_t p_z_depth) cons } Size2 viewport_size = get_viewport()->get_visible_rect().size; - CameraMatrix cm; + Projection cm; if (mode == PROJECTION_ORTHOGONAL) { cm.set_orthogonal(size, viewport_size.aspect(), p_z_depth, far, keep_aspect == KEEP_WIDTH); @@ -402,18 +412,44 @@ Ref<Environment> Camera3D::get_environment() const { return environment; } -void Camera3D::set_effects(const Ref<CameraEffects> &p_effects) { - effects = p_effects; - if (effects.is_valid()) { - RS::get_singleton()->camera_set_camera_effects(camera, effects->get_rid()); +void Camera3D::set_attributes(const Ref<CameraAttributes> &p_attributes) { + if (attributes.is_valid()) { + CameraAttributesPhysical *physical_attributes = Object::cast_to<CameraAttributesPhysical>(attributes.ptr()); + if (physical_attributes) { + attributes->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Camera3D::_attributes_changed)); + } + } + + attributes = p_attributes; + + if (attributes.is_valid()) { + CameraAttributesPhysical *physical_attributes = Object::cast_to<CameraAttributesPhysical>(attributes.ptr()); + if (physical_attributes) { + attributes->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Camera3D::_attributes_changed)); + _attributes_changed(); + } + + RS::get_singleton()->camera_set_camera_attributes(camera, attributes->get_rid()); } else { - RS::get_singleton()->camera_set_camera_effects(camera, RID()); + RS::get_singleton()->camera_set_camera_attributes(camera, RID()); } - _update_camera_mode(); + + notify_property_list_changed(); } -Ref<CameraEffects> Camera3D::get_effects() const { - return effects; +Ref<CameraAttributes> Camera3D::get_attributes() const { + return attributes; +} + +void Camera3D::_attributes_changed() { + CameraAttributesPhysical *physical_attributes = Object::cast_to<CameraAttributesPhysical>(attributes.ptr()); + ERR_FAIL_COND(!physical_attributes); + + fov = physical_attributes->get_fov(); + near = physical_attributes->get_near(); + far = physical_attributes->get_far(); + keep_aspect = KEEP_HEIGHT; + _update_camera_mode(); } void Camera3D::set_keep_aspect_mode(KeepAspect p_aspect) { @@ -481,13 +517,13 @@ void Camera3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_cull_mask"), &Camera3D::get_cull_mask); ClassDB::bind_method(D_METHOD("set_environment", "env"), &Camera3D::set_environment); ClassDB::bind_method(D_METHOD("get_environment"), &Camera3D::get_environment); - ClassDB::bind_method(D_METHOD("set_effects", "env"), &Camera3D::set_effects); - ClassDB::bind_method(D_METHOD("get_effects"), &Camera3D::get_effects); + ClassDB::bind_method(D_METHOD("set_attributes", "env"), &Camera3D::set_attributes); + ClassDB::bind_method(D_METHOD("get_attributes"), &Camera3D::get_attributes); ClassDB::bind_method(D_METHOD("set_keep_aspect_mode", "mode"), &Camera3D::set_keep_aspect_mode); ClassDB::bind_method(D_METHOD("get_keep_aspect_mode"), &Camera3D::get_keep_aspect_mode); ClassDB::bind_method(D_METHOD("set_doppler_tracking", "mode"), &Camera3D::set_doppler_tracking); ClassDB::bind_method(D_METHOD("get_doppler_tracking"), &Camera3D::get_doppler_tracking); - ClassDB::bind_method(D_METHOD("get_frustum"), &Camera3D::get_frustum); + ClassDB::bind_method(D_METHOD("get_frustum"), &Camera3D::_get_frustum); ClassDB::bind_method(D_METHOD("is_position_in_frustum", "world_point"), &Camera3D::is_position_in_frustum); ClassDB::bind_method(D_METHOD("get_camera_rid"), &Camera3D::get_camera); ClassDB::bind_method(D_METHOD("get_pyramid_shape_rid"), &Camera3D::get_pyramid_shape_rid); @@ -500,14 +536,14 @@ void Camera3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "keep_aspect", PROPERTY_HINT_ENUM, "Keep Width,Keep Height"), "set_keep_aspect_mode", "get_keep_aspect_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "environment", PROPERTY_HINT_RESOURCE_TYPE, "Environment"), "set_environment", "get_environment"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "effects", PROPERTY_HINT_RESOURCE_TYPE, "CameraEffects"), "set_effects", "get_effects"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "attributes", PROPERTY_HINT_RESOURCE_TYPE, "CameraAttributesPractical,CameraAttributesPhysical"), "set_attributes", "get_attributes"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "h_offset", PROPERTY_HINT_NONE, "suffix:m"), "set_h_offset", "get_h_offset"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "v_offset", PROPERTY_HINT_NONE, "suffix:m"), "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,Frustum"), "set_projection", "get_projection"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "current"), "set_current", "is_current"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fov", PROPERTY_HINT_RANGE, "1,179,0.1,degrees"), "set_fov", "get_fov"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size", PROPERTY_HINT_RANGE, "0.001,16384,0.01,suffix:m"), "set_size", "get_size"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size", PROPERTY_HINT_RANGE, "0.001,16384,0.001,suffix:m"), "set_size", "get_size"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "frustum_offset", PROPERTY_HINT_NONE, "suffix:m"), "set_frustum_offset", "get_frustum_offset"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "near", PROPERTY_HINT_RANGE, "0.001,10,0.001,or_greater,exp,suffix:m"), "set_near", "get_near"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "far", PROPERTY_HINT_RANGE, "0.01,4000,0.01,or_greater,exp,suffix:m"), "set_far", "get_far"); @@ -544,7 +580,7 @@ real_t Camera3D::get_far() const { return far; } -Camera3D::Projection Camera3D::get_projection() const { +Camera3D::ProjectionType Camera3D::get_projection() const { return mode; } @@ -607,7 +643,7 @@ Vector<Plane> Camera3D::get_frustum() const { ERR_FAIL_COND_V(!is_inside_world(), Vector<Plane>()); Size2 viewport_size = get_viewport()->get_visible_rect().size; - CameraMatrix cm; + Projection cm; if (mode == PROJECTION_PERSPECTIVE) { cm.set_perspective(fov, viewport_size.aspect(), near, far, keep_aspect == KEEP_WIDTH); } else { @@ -617,6 +653,11 @@ Vector<Plane> Camera3D::get_frustum() const { return cm.get_projection_planes(get_camera_transform()); } +TypedArray<Plane> Camera3D::_get_frustum() const { + Variant ret = get_frustum(); + return ret; +} + bool Camera3D::is_position_in_frustum(const Vector3 &p_position) const { Vector<Plane> frustum = get_frustum(); for (int i = 0; i < frustum.size(); i++) { diff --git a/scene/3d/camera_3d.h b/scene/3d/camera_3d.h index 9f2f8ceed1..f150a23e27 100644 --- a/scene/3d/camera_3d.h +++ b/scene/3d/camera_3d.h @@ -33,14 +33,14 @@ #include "scene/3d/node_3d.h" #include "scene/3d/velocity_tracker_3d.h" -#include "scene/resources/camera_effects.h" +#include "scene/resources/camera_attributes.h" #include "scene/resources/environment.h" class Camera3D : public Node3D { GDCLASS(Camera3D, Node3D); public: - enum Projection { + enum ProjectionType { PROJECTION_PERSPECTIVE, PROJECTION_ORTHOGONAL, PROJECTION_FRUSTUM @@ -62,13 +62,13 @@ private: bool current = false; Viewport *viewport = nullptr; - Projection mode = PROJECTION_PERSPECTIVE; + ProjectionType mode = PROJECTION_PERSPECTIVE; - real_t fov = 0.0; + real_t fov = 75.0; real_t size = 1.0; Vector2 frustum_offset; - real_t near = 0.0; - real_t far = 0.0; + real_t near = 0.05; + real_t far = 4000.0; real_t v_offset = 0.0; real_t h_offset = 0.0; KeepAspect keep_aspect = KEEP_HEIGHT; @@ -81,11 +81,13 @@ private: uint32_t layers = 0xfffff; Ref<Environment> environment; - Ref<CameraEffects> effects; + Ref<CameraAttributes> attributes; + void _attributes_changed(); // void _camera_make_current(Node *p_camera); friend class Viewport; void _update_audio_listener_state(); + TypedArray<Plane> _get_frustum() const; DopplerTracking doppler_tracking = DOPPLER_TRACKING_DISABLED; Ref<VelocityTracker3D> velocity_tracker; @@ -99,7 +101,7 @@ protected: void _update_camera_mode(); void _notification(int p_what); - virtual void _validate_property(PropertyInfo &p_property) const override; + void _validate_property(PropertyInfo &p_property) const; static void _bind_methods(); @@ -112,7 +114,7 @@ public: void set_perspective(real_t p_fovy_degrees, real_t p_z_near, real_t p_z_far); void set_orthogonal(real_t p_size, real_t p_z_near, real_t p_z_far); void set_frustum(real_t p_size, Vector2 p_offset, real_t p_z_near, real_t p_z_far); - void set_projection(Camera3D::Projection p_mode); + void set_projection(Camera3D::ProjectionType p_mode); void make_current(); void clear_current(bool p_enable_next = true); @@ -127,7 +129,7 @@ public: real_t get_near() const; Vector2 get_frustum_offset() const; - Projection get_projection() const; + ProjectionType get_projection() const; void set_fov(real_t p_fov); void set_size(real_t p_size); @@ -158,8 +160,8 @@ public: void set_environment(const Ref<Environment> &p_environment); Ref<Environment> get_environment() const; - void set_effects(const Ref<CameraEffects> &p_effects); - Ref<CameraEffects> get_effects() const; + void set_attributes(const Ref<CameraAttributes> &p_effects); + Ref<CameraAttributes> get_attributes() const; void set_keep_aspect_mode(KeepAspect p_aspect); KeepAspect get_keep_aspect_mode() const; @@ -181,8 +183,8 @@ public: ~Camera3D(); }; -VARIANT_ENUM_CAST(Camera3D::Projection); +VARIANT_ENUM_CAST(Camera3D::ProjectionType); VARIANT_ENUM_CAST(Camera3D::KeepAspect); VARIANT_ENUM_CAST(Camera3D::DopplerTracking); -#endif +#endif // CAMERA_3D_H diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp index a36357555a..c3c1c8ba36 100644 --- a/scene/3d/collision_object_3d.cpp +++ b/scene/3d/collision_object_3d.cpp @@ -183,6 +183,17 @@ bool CollisionObject3D::get_collision_mask_value(int p_layer_number) const { return get_collision_mask() & (1 << (p_layer_number - 1)); } +void CollisionObject3D::set_collision_priority(real_t p_priority) { + collision_priority = p_priority; + if (!area) { + PhysicsServer3D::get_singleton()->body_set_collision_priority(get_rid(), p_priority); + } +} + +real_t CollisionObject3D::get_collision_priority() const { + return collision_priority; +} + void CollisionObject3D::set_disable_mode(DisableMode p_mode) { if (disable_mode == p_mode) { return; @@ -319,7 +330,7 @@ bool CollisionObject3D::_are_collision_shapes_visible() { void CollisionObject3D::_update_shape_data(uint32_t p_owner) { if (_are_collision_shapes_visible()) { if (debug_shapes_to_update.is_empty()) { - callable_mp(this, &CollisionObject3D::_update_debug_shapes).call_deferred({}, 0); + callable_mp(this, &CollisionObject3D::_update_debug_shapes).call_deferredp({}, 0); } debug_shapes_to_update.insert(p_owner); } @@ -365,8 +376,7 @@ void CollisionObject3D::_update_debug_shapes() { RS::get_singleton()->instance_set_scenario(s.debug_shape, get_world_3d()->get_scenario()); if (!s.shape->is_connected("changed", callable_mp(this, &CollisionObject3D::_shape_changed))) { - s.shape->connect("changed", callable_mp(this, &CollisionObject3D::_shape_changed), - varray(s.shape), CONNECT_DEFERRED); + s.shape->connect("changed", callable_mp(this, &CollisionObject3D::_shape_changed).bind(s.shape), CONNECT_DEFERRED); } ++debug_shapes_count; @@ -404,6 +414,9 @@ void CollisionObject3D::_on_transform_changed() { debug_shape_old_transform = get_global_transform(); for (KeyValue<uint32_t, ShapeData> &E : shapes) { ShapeData &shapedata = E.value; + if (shapedata.disabled) { + continue; // If disabled then there are no debug shapes to update. + } const ShapeData::ShapeBase *shapes = shapedata.shapes.ptr(); for (int i = 0; i < shapedata.shapes.size(); i++) { RS::get_singleton()->instance_set_transform(shapes[i].debug_shape, debug_shape_old_transform * shapedata.xform); @@ -430,6 +443,8 @@ void CollisionObject3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_collision_layer_value", "layer_number"), &CollisionObject3D::get_collision_layer_value); ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &CollisionObject3D::set_collision_mask_value); ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &CollisionObject3D::get_collision_mask_value); + ClassDB::bind_method(D_METHOD("set_collision_priority", "priority"), &CollisionObject3D::set_collision_priority); + ClassDB::bind_method(D_METHOD("get_collision_priority"), &CollisionObject3D::get_collision_priority); ClassDB::bind_method(D_METHOD("set_disable_mode", "mode"), &CollisionObject3D::set_disable_mode); ClassDB::bind_method(D_METHOD("get_disable_mode"), &CollisionObject3D::get_disable_mode); ClassDB::bind_method(D_METHOD("set_ray_pickable", "ray_pickable"), &CollisionObject3D::set_ray_pickable); @@ -454,6 +469,8 @@ void CollisionObject3D::_bind_methods() { ClassDB::bind_method(D_METHOD("shape_find_owner", "shape_index"), &CollisionObject3D::shape_find_owner); GDVIRTUAL_BIND(_input_event, "camera", "event", "position", "normal", "shape_idx"); + GDVIRTUAL_BIND(_mouse_enter); + GDVIRTUAL_BIND(_mouse_exit); ADD_SIGNAL(MethodInfo("input_event", PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"), PropertyInfo(Variant::VECTOR3, "position"), PropertyInfo(Variant::VECTOR3, "normal"), PropertyInfo(Variant::INT, "shape_idx"))); ADD_SIGNAL(MethodInfo("mouse_entered")); @@ -464,6 +481,7 @@ void CollisionObject3D::_bind_methods() { ADD_GROUP("Collision", "collision_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer", "get_collision_layer"); ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_priority"), "set_collision_priority", "get_collision_priority"); ADD_GROUP("Input", "input_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "input_ray_pickable"), "set_ray_pickable", "is_ray_pickable"); @@ -530,8 +548,8 @@ void CollisionObject3D::get_shape_owners(List<uint32_t> *r_owners) { } } -Array CollisionObject3D::_get_shape_owners() { - Array ret; +PackedInt32Array CollisionObject3D::_get_shape_owners() { + PackedInt32Array ret; for (const KeyValue<uint32_t, ShapeData> &E : shapes) { ret.push_back(E.key); } @@ -685,8 +703,8 @@ bool CollisionObject3D::get_capture_input_on_drag() const { return capture_input_on_drag; } -TypedArray<String> CollisionObject3D::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray CollisionObject3D::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (shapes.is_empty()) { warnings.push_back(RTR("This node has no shape, so it can't collide or interact with other objects.\nConsider adding a CollisionShape3D or CollisionPolygon3D as a child to define its shape.")); diff --git a/scene/3d/collision_object_3d.h b/scene/3d/collision_object_3d.h index 098f573551..1406e6c698 100644 --- a/scene/3d/collision_object_3d.h +++ b/scene/3d/collision_object_3d.h @@ -47,6 +47,7 @@ public: private: uint32_t collision_layer = 1; uint32_t collision_mask = 1; + real_t collision_priority = 1.0; bool area = false; @@ -112,6 +113,8 @@ protected: bool is_only_update_transform_changes_enabled() const; GDVIRTUAL5(_input_event, Camera3D *, Ref<InputEvent>, Vector3, Vector3, int) + GDVIRTUAL0(_mouse_enter) + GDVIRTUAL0(_mouse_exit) public: void set_collision_layer(uint32_t p_layer); uint32_t get_collision_layer() const; @@ -125,13 +128,16 @@ public: void set_collision_mask_value(int p_layer_number, bool p_value); bool get_collision_mask_value(int p_layer_number) const; + void set_collision_priority(real_t p_priority); + real_t get_collision_priority() const; + void set_disable_mode(DisableMode p_mode); DisableMode get_disable_mode() const; uint32_t create_shape_owner(Object *p_owner); void remove_shape_owner(uint32_t owner); void get_shape_owners(List<uint32_t> *r_owners); - Array _get_shape_owners(); + PackedInt32Array _get_shape_owners(); void shape_owner_set_transform(uint32_t p_owner, const Transform3D &p_transform); Transform3D shape_owner_get_transform(uint32_t p_owner) const; @@ -158,7 +164,7 @@ public: _FORCE_INLINE_ RID get_rid() const { return rid; } - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; CollisionObject3D(); ~CollisionObject3D(); @@ -166,4 +172,4 @@ public: VARIANT_ENUM_CAST(CollisionObject3D::DisableMode); -#endif // COLLISION_OBJECT__H +#endif // COLLISION_OBJECT_3D_H diff --git a/scene/3d/collision_polygon_3d.cpp b/scene/3d/collision_polygon_3d.cpp index bd6a70e566..81b2c85de4 100644 --- a/scene/3d/collision_polygon_3d.cpp +++ b/scene/3d/collision_polygon_3d.cpp @@ -167,11 +167,11 @@ void CollisionPolygon3D::set_margin(real_t p_margin) { } } -TypedArray<String> CollisionPolygon3D::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray CollisionPolygon3D::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (!Object::cast_to<CollisionObject3D>(get_parent())) { - warnings.push_back(RTR("CollisionPolygon3D only serves to provide a collision shape to a CollisionObject3D derived node. Please only use it as a child of Area3D, StaticBody3D, RigidDynamicBody3D, CharacterBody3D, etc. to give them a shape.")); + warnings.push_back(RTR("CollisionPolygon3D only serves to provide a collision shape to a CollisionObject3D derived node. Please only use it as a child of Area3D, StaticBody3D, RigidBody3D, CharacterBody3D, etc. to give them a shape.")); } if (polygon.is_empty()) { diff --git a/scene/3d/collision_polygon_3d.h b/scene/3d/collision_polygon_3d.h index a24d485af2..bbcea539b2 100644 --- a/scene/3d/collision_polygon_3d.h +++ b/scene/3d/collision_polygon_3d.h @@ -74,9 +74,9 @@ public: real_t get_margin() const; void set_margin(real_t p_margin); - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; CollisionPolygon3D(); }; -#endif // COLLISION_POLYGON_H +#endif // COLLISION_POLYGON_3D_H diff --git a/scene/3d/collision_shape_3d.cpp b/scene/3d/collision_shape_3d.cpp index 759997de7b..7a0001bc6f 100644 --- a/scene/3d/collision_shape_3d.cpp +++ b/scene/3d/collision_shape_3d.cpp @@ -114,11 +114,11 @@ void CollisionShape3D::resource_changed(Ref<Resource> res) { update_gizmos(); } -TypedArray<String> CollisionShape3D::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray CollisionShape3D::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (!Object::cast_to<CollisionObject3D>(get_parent())) { - warnings.push_back(RTR("CollisionShape3D only serves to provide a collision shape to a CollisionObject3D derived node. Please only use it as a child of Area3D, StaticBody3D, RigidDynamicBody3D, CharacterBody3D, etc. to give them a shape.")); + warnings.push_back(RTR("CollisionShape3D only serves to provide a collision shape to a CollisionObject3D derived node. Please only use it as a child of Area3D, StaticBody3D, RigidBody3D, CharacterBody3D, etc. to give them a shape.")); } if (!shape.is_valid()) { @@ -126,9 +126,9 @@ TypedArray<String> CollisionShape3D::get_configuration_warnings() const { } if (shape.is_valid() && - Object::cast_to<RigidDynamicBody3D>(get_parent()) && + Object::cast_to<RigidBody3D>(get_parent()) && Object::cast_to<ConcavePolygonShape3D>(*shape)) { - warnings.push_back(RTR("ConcavePolygonShape3D doesn't support RigidDynamicBody3D in another mode than static.")); + warnings.push_back(RTR("ConcavePolygonShape3D doesn't support RigidBody3D in another mode than static.")); } return warnings; diff --git a/scene/3d/collision_shape_3d.h b/scene/3d/collision_shape_3d.h index 5c32230942..70653daa19 100644 --- a/scene/3d/collision_shape_3d.h +++ b/scene/3d/collision_shape_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef COLLISION_SHAPE_H -#define COLLISION_SHAPE_H +#ifndef COLLISION_SHAPE_3D_H +#define COLLISION_SHAPE_3D_H #include "scene/3d/node_3d.h" #include "scene/resources/shape_3d.h" @@ -62,10 +62,10 @@ public: void set_disabled(bool p_disabled); bool is_disabled() const; - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; CollisionShape3D(); ~CollisionShape3D(); }; -#endif // BODY_VOLUME_H +#endif // COLLISION_SHAPE_3D_H diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp index 4df0c37ad6..ef373cf9ca 100644 --- a/scene/3d/cpu_particles_3d.cpp +++ b/scene/3d/cpu_particles_3d.cpp @@ -33,7 +33,7 @@ #include "scene/3d/camera_3d.h" #include "scene/3d/gpu_particles_3d.h" #include "scene/main/viewport.h" -#include "scene/resources/particles_material.h" +#include "scene/resources/particle_process_material.h" AABB CPUParticles3D::get_aabb() const { return AABB(); @@ -188,8 +188,8 @@ bool CPUParticles3D::get_fractional_delta() const { return fractional_delta; } -TypedArray<String> CPUParticles3D::get_configuration_warnings() const { - TypedArray<String> warnings = GeometryInstance3D::get_configuration_warnings(); +PackedStringArray CPUParticles3D::get_configuration_warnings() const { + PackedStringArray warnings = GeometryInstance3D::get_configuration_warnings(); bool mesh_found = false; bool anim_material_found = false; @@ -516,36 +516,34 @@ bool CPUParticles3D::get_split_scale() { return split_scale; } -void CPUParticles3D::_validate_property(PropertyInfo &property) const { - if (property.name == "emission_sphere_radius" && (emission_shape != EMISSION_SHAPE_SPHERE && emission_shape != EMISSION_SHAPE_SPHERE_SURFACE)) { - property.usage = PROPERTY_USAGE_NONE; +void CPUParticles3D::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "emission_sphere_radius" && (emission_shape != EMISSION_SHAPE_SPHERE && emission_shape != EMISSION_SHAPE_SPHERE_SURFACE)) { + p_property.usage = PROPERTY_USAGE_NONE; } - if (property.name == "emission_box_extents" && emission_shape != EMISSION_SHAPE_BOX) { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name == "emission_box_extents" && emission_shape != EMISSION_SHAPE_BOX) { + p_property.usage = PROPERTY_USAGE_NONE; } - if ((property.name == "emission_point_texture" || property.name == "emission_color_texture" || property.name == "emission_points") && (emission_shape != EMISSION_SHAPE_POINTS && (emission_shape != EMISSION_SHAPE_DIRECTED_POINTS))) { - property.usage = PROPERTY_USAGE_NONE; + if ((p_property.name == "emission_point_texture" || p_property.name == "emission_color_texture" || p_property.name == "emission_points") && (emission_shape != EMISSION_SHAPE_POINTS && (emission_shape != EMISSION_SHAPE_DIRECTED_POINTS))) { + p_property.usage = PROPERTY_USAGE_NONE; } - if (property.name == "emission_normals" && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name == "emission_normals" && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) { + p_property.usage = PROPERTY_USAGE_NONE; } - if (property.name.begins_with("emission_ring_") && emission_shape != EMISSION_SHAPE_RING) { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name.begins_with("emission_ring_") && emission_shape != EMISSION_SHAPE_RING) { + p_property.usage = PROPERTY_USAGE_NONE; } - if (property.name.begins_with("orbit_") && !particle_flags[PARTICLE_FLAG_DISABLE_Z]) { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name.begins_with("orbit_") && !particle_flags[PARTICLE_FLAG_DISABLE_Z]) { + p_property.usage = PROPERTY_USAGE_NONE; } - if (property.name.begins_with("scale_curve_") && !split_scale) { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name.begins_with("scale_curve_") && !split_scale) { + p_property.usage = PROPERTY_USAGE_NONE; } - - Node3D::_validate_property(property); } static uint32_t idhash(uint32_t x) { @@ -741,17 +739,17 @@ void CPUParticles3D::_particles_process(double p_delta) { /*real_t tex_linear_velocity = 0; if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { - tex_linear_velocity = curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY]->interpolate(0); + tex_linear_velocity = curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY]->sample(0); }*/ real_t tex_angle = 0.0; if (curve_parameters[PARAM_ANGLE].is_valid()) { - tex_angle = curve_parameters[PARAM_ANGLE]->interpolate(tv); + tex_angle = curve_parameters[PARAM_ANGLE]->sample(tv); } real_t tex_anim_offset = 0.0; if (curve_parameters[PARAM_ANGLE].is_valid()) { - tex_anim_offset = curve_parameters[PARAM_ANGLE]->interpolate(tv); + tex_anim_offset = curve_parameters[PARAM_ANGLE]->sample(tv); } p.seed = Math::rand(); @@ -768,13 +766,13 @@ void CPUParticles3D::_particles_process(double p_delta) { } if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { - real_t angle1_rad = Math::atan2(direction.y, direction.x) + Math::deg2rad((Math::randf() * 2.0 - 1.0) * spread); + real_t angle1_rad = Math::atan2(direction.y, direction.x) + Math::deg_to_rad((Math::randf() * 2.0 - 1.0) * spread); Vector3 rot = Vector3(Math::cos(angle1_rad), Math::sin(angle1_rad), 0.0); p.velocity = rot * Math::lerp(parameters_min[PARAM_INITIAL_LINEAR_VELOCITY], parameters_max[PARAM_INITIAL_LINEAR_VELOCITY], (real_t)Math::randf()); } else { //initiate velocity spread in 3D - real_t angle1_rad = Math::deg2rad((Math::randf() * (real_t)2.0 - (real_t)1.0) * spread); - real_t angle2_rad = Math::deg2rad((Math::randf() * (real_t)2.0 - (real_t)1.0) * ((real_t)1.0 - flatness) * spread); + real_t angle1_rad = Math::deg_to_rad((Math::randf() * (real_t)2.0 - (real_t)1.0) * spread); + real_t angle2_rad = Math::deg_to_rad((Math::randf() * (real_t)2.0 - (real_t)1.0) * ((real_t)1.0 - flatness) * spread); Vector3 direction_xz = Vector3(Math::sin(angle1_rad), 0, Math::cos(angle1_rad)); Vector3 direction_yz = Vector3(0, Math::sin(angle2_rad), Math::cos(angle2_rad)); @@ -798,7 +796,7 @@ void CPUParticles3D::_particles_process(double p_delta) { } real_t base_angle = tex_angle * Math::lerp(parameters_min[PARAM_ANGLE], parameters_max[PARAM_ANGLE], p.angle_rand); - p.custom[0] = Math::deg2rad(base_angle); //angle + p.custom[0] = Math::deg_to_rad(base_angle); //angle p.custom[1] = 0.0; //phase p.custom[2] = tex_anim_offset * Math::lerp(parameters_min[PARAM_ANIM_OFFSET], parameters_max[PARAM_ANIM_OFFSET], p.anim_offset_rand); //animation offset (0-1) p.transform = Transform3D(); @@ -909,53 +907,53 @@ void CPUParticles3D::_particles_process(double p_delta) { real_t tex_linear_velocity = 1.0; if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { - tex_linear_velocity = curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY]->interpolate(tv); + tex_linear_velocity = curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY]->sample(tv); } real_t tex_orbit_velocity = 1.0; if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { if (curve_parameters[PARAM_ORBIT_VELOCITY].is_valid()) { - tex_orbit_velocity = curve_parameters[PARAM_ORBIT_VELOCITY]->interpolate(tv); + tex_orbit_velocity = curve_parameters[PARAM_ORBIT_VELOCITY]->sample(tv); } } real_t tex_angular_velocity = 1.0; if (curve_parameters[PARAM_ANGULAR_VELOCITY].is_valid()) { - tex_angular_velocity = curve_parameters[PARAM_ANGULAR_VELOCITY]->interpolate(tv); + tex_angular_velocity = curve_parameters[PARAM_ANGULAR_VELOCITY]->sample(tv); } real_t tex_linear_accel = 1.0; if (curve_parameters[PARAM_LINEAR_ACCEL].is_valid()) { - tex_linear_accel = curve_parameters[PARAM_LINEAR_ACCEL]->interpolate(tv); + tex_linear_accel = curve_parameters[PARAM_LINEAR_ACCEL]->sample(tv); } real_t tex_tangential_accel = 1.0; if (curve_parameters[PARAM_TANGENTIAL_ACCEL].is_valid()) { - tex_tangential_accel = curve_parameters[PARAM_TANGENTIAL_ACCEL]->interpolate(tv); + tex_tangential_accel = curve_parameters[PARAM_TANGENTIAL_ACCEL]->sample(tv); } real_t tex_radial_accel = 1.0; if (curve_parameters[PARAM_RADIAL_ACCEL].is_valid()) { - tex_radial_accel = curve_parameters[PARAM_RADIAL_ACCEL]->interpolate(tv); + tex_radial_accel = curve_parameters[PARAM_RADIAL_ACCEL]->sample(tv); } real_t tex_damping = 1.0; if (curve_parameters[PARAM_DAMPING].is_valid()) { - tex_damping = curve_parameters[PARAM_DAMPING]->interpolate(tv); + tex_damping = curve_parameters[PARAM_DAMPING]->sample(tv); } real_t tex_angle = 1.0; if (curve_parameters[PARAM_ANGLE].is_valid()) { - tex_angle = curve_parameters[PARAM_ANGLE]->interpolate(tv); + tex_angle = curve_parameters[PARAM_ANGLE]->sample(tv); } real_t tex_anim_speed = 1.0; if (curve_parameters[PARAM_ANIM_SPEED].is_valid()) { - tex_anim_speed = curve_parameters[PARAM_ANIM_SPEED]->interpolate(tv); + tex_anim_speed = curve_parameters[PARAM_ANIM_SPEED]->sample(tv); } real_t tex_anim_offset = 1.0; if (curve_parameters[PARAM_ANIM_OFFSET].is_valid()) { - tex_anim_offset = curve_parameters[PARAM_ANIM_OFFSET]->interpolate(tv); + tex_anim_offset = curve_parameters[PARAM_ANIM_OFFSET]->sample(tv); } Vector3 force = gravity; @@ -985,7 +983,7 @@ void CPUParticles3D::_particles_process(double p_delta) { real_t orbit_amount = tex_orbit_velocity * Math::lerp(parameters_min[PARAM_ORBIT_VELOCITY], parameters_max[PARAM_ORBIT_VELOCITY], rand_from_seed(alt_seed)); if (orbit_amount != 0.0) { real_t ang = orbit_amount * local_delta * Math_TAU; - // Not sure why the ParticlesMaterial code uses a clockwise rotation matrix, + // Not sure why the ParticleProcessMaterial code uses a clockwise rotation matrix, // but we use -ang here to reproduce its behavior. Transform2D rot = Transform2D(-ang, Vector2()); Vector2 rotv = rot.basis_xform(Vector2(diff.x, diff.y)); @@ -1009,7 +1007,7 @@ void CPUParticles3D::_particles_process(double p_delta) { } real_t base_angle = (tex_angle)*Math::lerp(parameters_min[PARAM_ANGLE], parameters_max[PARAM_ANGLE], p.angle_rand); base_angle += p.custom[1] * lifetime * tex_angular_velocity * Math::lerp(parameters_min[PARAM_ANGULAR_VELOCITY], parameters_max[PARAM_ANGULAR_VELOCITY], rand_from_seed(alt_seed)); - p.custom[0] = Math::deg2rad(base_angle); //angle + p.custom[0] = Math::deg_to_rad(base_angle); //angle p.custom[2] = tex_anim_offset * Math::lerp(parameters_min[PARAM_ANIM_OFFSET], parameters_max[PARAM_ANIM_OFFSET], p.anim_offset_rand) + tv * tex_anim_speed * Math::lerp(parameters_min[PARAM_ANIM_SPEED], parameters_max[PARAM_ANIM_SPEED], rand_from_seed(alt_seed)); //angle } //apply color @@ -1018,23 +1016,23 @@ void CPUParticles3D::_particles_process(double p_delta) { Vector3 tex_scale = Vector3(1.0, 1.0, 1.0); if (split_scale) { if (scale_curve_x.is_valid()) { - tex_scale.x = scale_curve_x->interpolate(tv); + tex_scale.x = scale_curve_x->sample(tv); } else { tex_scale.x = 1.0; } if (scale_curve_y.is_valid()) { - tex_scale.y = scale_curve_y->interpolate(tv); + tex_scale.y = scale_curve_y->sample(tv); } else { tex_scale.y = 1.0; } if (scale_curve_z.is_valid()) { - tex_scale.z = scale_curve_z->interpolate(tv); + tex_scale.z = scale_curve_z->sample(tv); } else { tex_scale.z = 1.0; } } else { if (curve_parameters[PARAM_SCALE].is_valid()) { - float tmp_scale = curve_parameters[PARAM_SCALE]->interpolate(tv); + float tmp_scale = curve_parameters[PARAM_SCALE]->sample(tv); tex_scale.x = tmp_scale; tex_scale.y = tmp_scale; tex_scale.z = tmp_scale; @@ -1043,7 +1041,7 @@ void CPUParticles3D::_particles_process(double p_delta) { real_t tex_hue_variation = 0.0; if (curve_parameters[PARAM_HUE_VARIATION].is_valid()) { - tex_hue_variation = curve_parameters[PARAM_HUE_VARIATION]->interpolate(tv); + tex_hue_variation = curve_parameters[PARAM_HUE_VARIATION]->sample(tv); } real_t hue_rot_angle = (tex_hue_variation)*Math_TAU * Math::lerp(parameters_min[PARAM_HUE_VARIATION], parameters_max[PARAM_HUE_VARIATION], p.hue_rot_rand); @@ -1345,7 +1343,7 @@ void CPUParticles3D::convert_from_particles(Node *p_particles) { set_draw_order(DrawOrder(particles->get_draw_order())); set_mesh(particles->get_draw_pass_mesh(0)); - Ref<ParticlesMaterial> material = particles->get_process_material(); + Ref<ParticleProcessMaterial> material = particles->get_process_material(); if (material.is_null()) { return; } @@ -1366,14 +1364,14 @@ void CPUParticles3D::convert_from_particles(Node *p_particles) { set_color_initial_ramp(gti->get_gradient()); } - set_particle_flag(PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY, material->get_particle_flag(ParticlesMaterial::PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY)); - set_particle_flag(PARTICLE_FLAG_ROTATE_Y, material->get_particle_flag(ParticlesMaterial::PARTICLE_FLAG_ROTATE_Y)); - set_particle_flag(PARTICLE_FLAG_DISABLE_Z, material->get_particle_flag(ParticlesMaterial::PARTICLE_FLAG_DISABLE_Z)); + set_particle_flag(PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY, material->get_particle_flag(ParticleProcessMaterial::PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY)); + set_particle_flag(PARTICLE_FLAG_ROTATE_Y, material->get_particle_flag(ParticleProcessMaterial::PARTICLE_FLAG_ROTATE_Y)); + set_particle_flag(PARTICLE_FLAG_DISABLE_Z, material->get_particle_flag(ParticleProcessMaterial::PARTICLE_FLAG_DISABLE_Z)); set_emission_shape(EmissionShape(material->get_emission_shape())); set_emission_sphere_radius(material->get_emission_sphere_radius()); set_emission_box_extents(material->get_emission_box_extents()); - Ref<CurveXYZTexture> scale3D = material->get_param_texture(ParticlesMaterial::PARAM_SCALE); + Ref<CurveXYZTexture> scale3D = material->get_param_texture(ParticleProcessMaterial::PARAM_SCALE); if (scale3D.is_valid()) { split_scale = true; scale_curve_x = scale3D->get_curve_x(); @@ -1384,14 +1382,14 @@ void CPUParticles3D::convert_from_particles(Node *p_particles) { set_gravity(material->get_gravity()); set_lifetime_randomness(material->get_lifetime_randomness()); -#define CONVERT_PARAM(m_param) \ - set_param_min(m_param, material->get_param_min(ParticlesMaterial::m_param)); \ - { \ - Ref<CurveTexture> ctex = material->get_param_texture(ParticlesMaterial::m_param); \ - if (ctex.is_valid()) \ - set_param_curve(m_param, ctex->get_curve()); \ - } \ - set_param_max(m_param, material->get_param_max(ParticlesMaterial::m_param)); +#define CONVERT_PARAM(m_param) \ + set_param_min(m_param, material->get_param_min(ParticleProcessMaterial::m_param)); \ + { \ + Ref<CurveTexture> ctex = material->get_param_texture(ParticleProcessMaterial::m_param); \ + if (ctex.is_valid()) \ + set_param_curve(m_param, ctex->get_curve()); \ + } \ + set_param_max(m_param, material->get_param_max(ParticleProcessMaterial::m_param)); CONVERT_PARAM(PARAM_INITIAL_LINEAR_VELOCITY); CONVERT_PARAM(PARAM_ANGULAR_VELOCITY); @@ -1570,32 +1568,32 @@ void CPUParticles3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "initial_velocity_min", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param_min", "get_param_min", PARAM_INITIAL_LINEAR_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "initial_velocity_max", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param_max", "get_param_max", PARAM_INITIAL_LINEAR_VELOCITY); ADD_GROUP("Angular Velocity", "angular_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_velocity_min", PROPERTY_HINT_RANGE, "-720,720,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_ANGULAR_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_velocity_max", PROPERTY_HINT_RANGE, "-720,720,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_ANGULAR_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_velocity_min", PROPERTY_HINT_RANGE, "-720,720,0.01,or_less,or_greater"), "set_param_min", "get_param_min", PARAM_ANGULAR_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_velocity_max", PROPERTY_HINT_RANGE, "-720,720,0.01,or_less,or_greater"), "set_param_max", "get_param_max", PARAM_ANGULAR_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angular_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANGULAR_VELOCITY); ADD_GROUP("Orbit Velocity", "orbit_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "orbit_velocity_min", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_ORBIT_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "orbit_velocity_max", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_ORBIT_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "orbit_velocity_min", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_less,or_greater"), "set_param_min", "get_param_min", PARAM_ORBIT_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "orbit_velocity_max", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_less,or_greater"), "set_param_max", "get_param_max", PARAM_ORBIT_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "orbit_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ORBIT_VELOCITY); ADD_GROUP("Linear Accel", "linear_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_accel_min", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_LINEAR_ACCEL); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_accel_max", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_LINEAR_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_accel_min", PROPERTY_HINT_RANGE, "-100,100,0.01,or_less,or_greater"), "set_param_min", "get_param_min", PARAM_LINEAR_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_accel_max", PROPERTY_HINT_RANGE, "-100,100,0.01,or_less,or_greater"), "set_param_max", "get_param_max", PARAM_LINEAR_ACCEL); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "linear_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_LINEAR_ACCEL); ADD_GROUP("Radial Accel", "radial_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "radial_accel_min", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_RADIAL_ACCEL); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "radial_accel_max", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_RADIAL_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "radial_accel_min", PROPERTY_HINT_RANGE, "-100,100,0.01,or_less,or_greater"), "set_param_min", "get_param_min", PARAM_RADIAL_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "radial_accel_max", PROPERTY_HINT_RANGE, "-100,100,0.01,or_less,or_greater"), "set_param_max", "get_param_max", PARAM_RADIAL_ACCEL); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "radial_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_RADIAL_ACCEL); ADD_GROUP("Tangential Accel", "tangential_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "tangential_accel_min", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_TANGENTIAL_ACCEL); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "tangential_accel_max", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_TANGENTIAL_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "tangential_accel_min", PROPERTY_HINT_RANGE, "-100,100,0.01,or_less,or_greater"), "set_param_min", "get_param_min", PARAM_TANGENTIAL_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "tangential_accel_max", PROPERTY_HINT_RANGE, "-100,100,0.01,or_less,or_greater"), "set_param_max", "get_param_max", PARAM_TANGENTIAL_ACCEL); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "tangential_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_TANGENTIAL_ACCEL); ADD_GROUP("Damping", ""); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "damping_min", PROPERTY_HINT_RANGE, "0,100,0.01"), "set_param_min", "get_param_min", PARAM_DAMPING); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "damping_max", PROPERTY_HINT_RANGE, "0,100,0.01"), "set_param_max", "get_param_max", PARAM_DAMPING); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "damping_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_DAMPING); ADD_GROUP("Angle", ""); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle_min", PROPERTY_HINT_RANGE, "-720,720,0.1,or_lesser,or_greater,degrees"), "set_param_min", "get_param_min", PARAM_ANGLE); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle_max", PROPERTY_HINT_RANGE, "-720,720,0.1,or_lesser,or_greater,degrees"), "set_param_max", "get_param_max", PARAM_ANGLE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle_min", PROPERTY_HINT_RANGE, "-720,720,0.1,or_less,or_greater,degrees"), "set_param_min", "get_param_min", PARAM_ANGLE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle_max", PROPERTY_HINT_RANGE, "-720,720,0.1,or_less,or_greater,degrees"), "set_param_max", "get_param_max", PARAM_ANGLE); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angle_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANGLE); ADD_GROUP("Scale", ""); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "scale_amount_min", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param_min", "get_param_min", PARAM_SCALE); @@ -1615,8 +1613,8 @@ void CPUParticles3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "hue_variation_max", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_param_max", "get_param_max", PARAM_HUE_VARIATION); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "hue_variation_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_HUE_VARIATION); ADD_GROUP("Animation", "anim_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_min", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater,or_lesser"), "set_param_min", "get_param_min", PARAM_ANIM_SPEED); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_max", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater,or_lesser"), "set_param_max", "get_param_max", PARAM_ANIM_SPEED); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_min", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater,or_less"), "set_param_min", "get_param_min", PARAM_ANIM_SPEED); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_max", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater,or_less"), "set_param_max", "get_param_max", PARAM_ANIM_SPEED); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_speed_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANIM_SPEED); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_min", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_min", "get_param_min", PARAM_ANIM_OFFSET); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_max", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_max", "get_param_max", PARAM_ANIM_OFFSET); diff --git a/scene/3d/cpu_particles_3d.h b/scene/3d/cpu_particles_3d.h index 7f225ee98d..26c702172b 100644 --- a/scene/3d/cpu_particles_3d.h +++ b/scene/3d/cpu_particles_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef CPU_PARTICLES_H -#define CPU_PARTICLES_H +#ifndef CPU_PARTICLES_3D_H +#define CPU_PARTICLES_3D_H #include "scene/3d/visual_instance_3d.h" @@ -138,7 +138,7 @@ private: real_t randomness_ratio = 0.0; double lifetime_randomness = 0.0; double speed_scale = 1.0; - bool local_coords = true; + bool local_coords = false; int fixed_fps = 0; bool fractional_delta = true; @@ -198,7 +198,7 @@ private: protected: static void _bind_methods(); void _notification(int p_what); - virtual void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; public: AABB get_aabb() const override; @@ -302,7 +302,7 @@ public: void set_gravity(const Vector3 &p_gravity); Vector3 get_gravity() const; - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; void restart(); @@ -317,4 +317,4 @@ VARIANT_ENUM_CAST(CPUParticles3D::Parameter) VARIANT_ENUM_CAST(CPUParticles3D::ParticleFlags) VARIANT_ENUM_CAST(CPUParticles3D::EmissionShape) -#endif // CPU_PARTICLES_H +#endif // CPU_PARTICLES_3D_H diff --git a/scene/3d/decal.cpp b/scene/3d/decal.cpp index 01cab493ec..fc442986a8 100644 --- a/scene/3d/decal.cpp +++ b/scene/3d/decal.cpp @@ -152,15 +152,14 @@ AABB Decal::get_aabb() const { return aabb; } -void Decal::_validate_property(PropertyInfo &property) const { - if (!distance_fade_enabled && (property.name == "distance_fade_begin" || property.name == "distance_fade_length")) { - property.usage = PROPERTY_USAGE_NO_EDITOR; +void Decal::_validate_property(PropertyInfo &p_property) const { + if (!distance_fade_enabled && (p_property.name == "distance_fade_begin" || p_property.name == "distance_fade_length")) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } - VisualInstance3D::_validate_property(property); } -TypedArray<String> Decal::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray Decal::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (textures[TEXTURE_ALBEDO].is_null() && textures[TEXTURE_NORMAL].is_null() && textures[TEXTURE_ORM].is_null() && textures[TEXTURE_EMISSION].is_null()) { warnings.push_back(RTR("The decal has no textures loaded into any of its texture properties, and will therefore not be visible.")); @@ -215,11 +214,13 @@ void Decal::_bind_methods() { ClassDB::bind_method(D_METHOD("get_cull_mask"), &Decal::get_cull_mask); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0,1024,0.001,or_greater,suffix:m"), "set_extents", "get_extents"); + ADD_GROUP("Textures", "texture_"); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "texture_albedo", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture", TEXTURE_ALBEDO); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "texture_normal", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture", TEXTURE_NORMAL); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "texture_orm", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture", TEXTURE_ORM); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "texture_emission", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture", TEXTURE_EMISSION); + ADD_GROUP("Parameters", ""); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_energy", PROPERTY_HINT_RANGE, "0,128,0.01"), "set_emission_energy", "get_emission_energy"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate"), "set_modulate", "get_modulate"); @@ -227,13 +228,16 @@ void Decal::_bind_methods() { // A Normal Fade of 1.0 causes the decal to be invisible even if fully perpendicular to a surface. // Due to this, limit Normal Fade to 0.999. ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "normal_fade", PROPERTY_HINT_RANGE, "0,0.999,0.001"), "set_normal_fade", "get_normal_fade"); + ADD_GROUP("Vertical Fade", ""); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "upper_fade", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_upper_fade", "get_upper_fade"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lower_fade", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_lower_fade", "get_lower_fade"); + ADD_GROUP("Distance Fade", "distance_fade_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "distance_fade_enabled"), "set_enable_distance_fade", "is_distance_fade_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_begin", PROPERTY_HINT_NONE, "suffix:m"), "set_distance_fade_begin", "get_distance_fade_begin"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_length", PROPERTY_HINT_NONE, "suffix:m"), "set_distance_fade_length", "get_distance_fade_length"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_begin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), "set_distance_fade_begin", "get_distance_fade_begin"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_length", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), "set_distance_fade_length", "get_distance_fade_length"); + ADD_GROUP("Cull Mask", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask"); diff --git a/scene/3d/decal.h b/scene/3d/decal.h index d5990272c6..ab39350b75 100644 --- a/scene/3d/decal.h +++ b/scene/3d/decal.h @@ -57,15 +57,15 @@ private: real_t upper_fade = 0.3; real_t lower_fade = 0.3; bool distance_fade_enabled = false; - real_t distance_fade_begin = 10.0; - real_t distance_fade_length = 1.0; + real_t distance_fade_begin = 40.0; + real_t distance_fade_length = 10.0; protected: static void _bind_methods(); - void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; public: - virtual TypedArray<String> get_configuration_warnings() const override; + virtual PackedStringArray get_configuration_warnings() const override; void set_extents(const Vector3 &p_extents); Vector3 get_extents() const; diff --git a/scene/3d/fog_volume.cpp b/scene/3d/fog_volume.cpp index 1b329143b6..4606e70310 100644 --- a/scene/3d/fog_volume.cpp +++ b/scene/3d/fog_volume.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "fog_volume.h" +#include "scene/resources/environment.h" /////////////////////////// @@ -45,12 +46,11 @@ void FogVolume::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "FogMaterial,ShaderMaterial"), "set_material", "get_material"); } -void FogVolume::_validate_property(PropertyInfo &property) const { - if (property.name == "extents" && shape == RS::FOG_VOLUME_SHAPE_WORLD) { - property.usage = PROPERTY_USAGE_NONE; +void FogVolume::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "extents" && shape == RS::FOG_VOLUME_SHAPE_WORLD) { + p_property.usage = PROPERTY_USAGE_NONE; return; } - VisualInstance3D::_validate_property(property); } void FogVolume::set_extents(const Vector3 &p_extents) { @@ -99,8 +99,8 @@ AABB FogVolume::get_aabb() const { return AABB(); } -TypedArray<String> FogVolume::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray FogVolume::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); Ref<Environment> environment = get_viewport()->find_world_3d()->get_environment(); diff --git a/scene/3d/fog_volume.h b/scene/3d/fog_volume.h index 556a92ad3f..d79836be0e 100644 --- a/scene/3d/fog_volume.h +++ b/scene/3d/fog_volume.h @@ -49,7 +49,7 @@ class FogVolume : public VisualInstance3D { protected: _FORCE_INLINE_ RID _get_volume() { return volume; } static void _bind_methods(); - virtual void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; public: void set_extents(const Vector3 &p_extents); @@ -62,7 +62,7 @@ public: Ref<Material> get_material() const; virtual AABB get_aabb() const override; - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; FogVolume(); ~FogVolume(); diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp index b352114c7f..dbbf196f7a 100644 --- a/scene/3d/gpu_particles_3d.cpp +++ b/scene/3d/gpu_particles_3d.cpp @@ -30,7 +30,7 @@ #include "gpu_particles_3d.h" -#include "scene/resources/particles_material.h" +#include "scene/resources/particle_process_material.h" AABB GPUParticles3D::get_aabb() const { return AABB(); @@ -222,7 +222,7 @@ void GPUParticles3D::set_draw_pass_mesh(int p_pass, const Ref<Mesh> &p_mesh) { draw_passes.write[p_pass] = p_mesh; if (Engine::get_singleton()->is_editor_hint() && draw_passes.write[p_pass].is_valid()) { - draw_passes.write[p_pass]->connect("changed", callable_mp((Node *)this, &Node::update_configuration_warnings), varray(), CONNECT_DEFERRED); + draw_passes.write[p_pass]->connect("changed", callable_mp((Node *)this, &Node::update_configuration_warnings), CONNECT_DEFERRED); } RID mesh_rid; @@ -269,8 +269,8 @@ bool GPUParticles3D::get_interpolate() const { return interpolate; } -TypedArray<String> GPUParticles3D::get_configuration_warnings() const { - TypedArray<String> warnings = GeometryInstance3D::get_configuration_warnings(); +PackedStringArray GPUParticles3D::get_configuration_warnings() const { + PackedStringArray warnings = GeometryInstance3D::get_configuration_warnings(); if (RenderingServer::get_singleton()->is_low_end()) { warnings.push_back(RTR("GPU-based particles are not supported by the OpenGL video driver.\nUse the CPUParticles3D node instead. You can use the \"Convert to CPUParticles3D\" option for this purpose.")); @@ -306,10 +306,10 @@ TypedArray<String> GPUParticles3D::get_configuration_warnings() const { if (process_material.is_null()) { warnings.push_back(RTR("A material to process the particles is not assigned, so no behavior is imprinted.")); } else { - const ParticlesMaterial *process = Object::cast_to<ParticlesMaterial>(process_material.ptr()); + const ParticleProcessMaterial *process = Object::cast_to<ParticleProcessMaterial>(process_material.ptr()); if (!anim_material_found && process && - (process->get_param_max(ParticlesMaterial::PARAM_ANIM_SPEED) != 0.0 || process->get_param_max(ParticlesMaterial::PARAM_ANIM_OFFSET) != 0.0 || - process->get_param_texture(ParticlesMaterial::PARAM_ANIM_SPEED).is_valid() || process->get_param_texture(ParticlesMaterial::PARAM_ANIM_OFFSET).is_valid())) { + (process->get_param_max(ParticleProcessMaterial::PARAM_ANIM_SPEED) != 0.0 || process->get_param_max(ParticleProcessMaterial::PARAM_ANIM_OFFSET) != 0.0 || + process->get_param_texture(ParticleProcessMaterial::PARAM_ANIM_SPEED).is_valid() || process->get_param_texture(ParticleProcessMaterial::PARAM_ANIM_OFFSET).is_valid())) { warnings.push_back(RTR("Particles animation requires the usage of a BaseMaterial3D whose Billboard Mode is set to \"Particle Billboard\".")); } } @@ -376,16 +376,14 @@ AABB GPUParticles3D::capture_aabb() const { return RS::get_singleton()->particles_get_current_aabb(particles); } -void GPUParticles3D::_validate_property(PropertyInfo &property) const { - if (property.name.begins_with("draw_pass_")) { - int index = property.name.get_slicec('_', 2).to_int() - 1; +void GPUParticles3D::_validate_property(PropertyInfo &p_property) const { + if (p_property.name.begins_with("draw_pass_")) { + int index = p_property.name.get_slicec('_', 2).to_int() - 1; if (index >= draw_passes.size()) { - property.usage = PROPERTY_USAGE_NONE; + p_property.usage = PROPERTY_USAGE_NONE; return; } } - - GeometryInstance3D::_validate_property(property); } void GPUParticles3D::emit_particle(const Transform3D &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) { @@ -587,7 +585,7 @@ void GPUParticles3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "trail_enabled"), "set_trail_enabled", "is_trail_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "trail_length_secs", PROPERTY_HINT_RANGE, "0.01,10,0.01,suffix:s"), "set_trail_length", "get_trail_length"); ADD_GROUP("Process Material", ""); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "process_material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,ParticlesMaterial"), "set_process_material", "get_process_material"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "process_material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,ParticleProcessMaterial"), "set_process_material", "get_process_material"); ADD_GROUP("Draw Passes", "draw_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "draw_passes", PROPERTY_HINT_RANGE, "0," + itos(MAX_DRAW_PASSES) + ",1"), "set_draw_passes", "get_draw_passes"); for (int i = 0; i < MAX_DRAW_PASSES; i++) { @@ -631,7 +629,7 @@ GPUParticles3D::GPUParticles3D() { set_randomness_ratio(0); set_trail_length(0.3); set_visibility_aabb(AABB(Vector3(-4, -4, -4), Vector3(8, 8, 8))); - set_use_local_coordinates(true); + set_use_local_coordinates(false); set_draw_passes(1); set_draw_order(DRAW_ORDER_INDEX); set_speed_scale(1); diff --git a/scene/3d/gpu_particles_3d.h b/scene/3d/gpu_particles_3d.h index adce45a0a9..ef92218e4d 100644 --- a/scene/3d/gpu_particles_3d.h +++ b/scene/3d/gpu_particles_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef PARTICLES_H -#define PARTICLES_H +#ifndef GPU_PARTICLES_3D_H +#define GPU_PARTICLES_3D_H #include "scene/3d/visual_instance_3d.h" #include "scene/resources/skin.h" @@ -94,7 +94,7 @@ private: protected: static void _bind_methods(); void _notification(int p_what); - virtual void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; public: AABB get_aabb() const override; @@ -147,7 +147,7 @@ public: void set_draw_pass_mesh(int p_pass, const Ref<Mesh> &p_mesh); Ref<Mesh> get_draw_pass_mesh(int p_pass) const; - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; void set_sub_emitter(const NodePath &p_path); NodePath get_sub_emitter() const; @@ -179,4 +179,4 @@ VARIANT_ENUM_CAST(GPUParticles3D::DrawOrder) VARIANT_ENUM_CAST(GPUParticles3D::TransformAlign) VARIANT_ENUM_CAST(GPUParticles3D::EmitFlags) -#endif // PARTICLES_H +#endif // GPU_PARTICLES_3D_H diff --git a/scene/3d/gpu_particles_collision_3d.cpp b/scene/3d/gpu_particles_collision_3d.cpp index da0789ccd5..d3f53d9c0d 100644 --- a/scene/3d/gpu_particles_collision_3d.cpp +++ b/scene/3d/gpu_particles_collision_3d.cpp @@ -30,6 +30,7 @@ #include "gpu_particles_collision_3d.h" +#include "core/object/worker_thread_pool.h" #include "mesh_instance_3d.h" #include "scene/3d/camera_3d.h" #include "scene/main/viewport.h" @@ -126,6 +127,10 @@ GPUParticlesCollisionBox3D::~GPUParticlesCollisionBox3D() { void GPUParticlesCollisionSDF3D::_find_meshes(const AABB &p_aabb, Node *p_at_node, List<PlotMesh> &plot_meshes) { MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_at_node); if (mi && mi->is_visible_in_tree()) { + if ((mi->get_layer_mask() & bake_mask) == 0) { + return; + } + Ref<Mesh> mesh = mi->get_mesh(); if (mesh.is_valid()) { AABB aabb = mesh->get_aabb(); @@ -339,15 +344,12 @@ void GPUParticlesCollisionSDF3D::_compute_sdf_z(uint32_t p_z, ComputeSDFParams * } void GPUParticlesCollisionSDF3D::_compute_sdf(ComputeSDFParams *params) { - ThreadWorkPool work_pool; - work_pool.init(); - work_pool.begin_work(params->size.z, this, &GPUParticlesCollisionSDF3D::_compute_sdf_z, params); - while (!work_pool.is_done_dispatching()) { + WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &GPUParticlesCollisionSDF3D::_compute_sdf_z, params, params->size.z); + while (!WorkerThreadPool::get_singleton()->is_group_task_completed(group_task)) { OS::get_singleton()->delay_usec(10000); - bake_step_function(work_pool.get_work_index() * 100 / params->size.z, "Baking SDF"); + bake_step_function(WorkerThreadPool::get_singleton()->get_group_processed_element_count(group_task) * 100 / params->size.z, "Baking SDF"); } - work_pool.end_work(); - work_pool.finish(); + WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task); } Vector3i GPUParticlesCollisionSDF3D::get_estimated_cell_size() const { @@ -447,7 +449,7 @@ Ref<Image> GPUParticlesCollisionSDF3D::bake() { //compute bvh - ERR_FAIL_COND_V(faces.size() <= 1, Ref<Image>()); + ERR_FAIL_COND_V_MSG(faces.size() <= 1, Ref<Image>(), "No faces detected during GPUParticlesCollisionSDF3D bake. Check whether there are visible meshes matching the bake mask within its extents."); LocalVector<FacePos> face_pos; @@ -501,6 +503,16 @@ Ref<Image> GPUParticlesCollisionSDF3D::bake() { return ret; } +PackedStringArray GPUParticlesCollisionSDF3D::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); + + if (bake_mask == 0) { + warnings.push_back(RTR("The Bake Mask has no bits enabled, which means baking will not produce any collision for this GPUParticlesCollisionSDF3D.\nTo resolve this, enable at least one bit in the Bake Mask property.")); + } + + return warnings; +} + void GPUParticlesCollisionSDF3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_extents", "extents"), &GPUParticlesCollisionSDF3D::set_extents); ClassDB::bind_method(D_METHOD("get_extents"), &GPUParticlesCollisionSDF3D::get_extents); @@ -514,9 +526,15 @@ void GPUParticlesCollisionSDF3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_thickness", "thickness"), &GPUParticlesCollisionSDF3D::set_thickness); ClassDB::bind_method(D_METHOD("get_thickness"), &GPUParticlesCollisionSDF3D::get_thickness); + ClassDB::bind_method(D_METHOD("set_bake_mask", "mask"), &GPUParticlesCollisionSDF3D::set_bake_mask); + ClassDB::bind_method(D_METHOD("get_bake_mask"), &GPUParticlesCollisionSDF3D::get_bake_mask); + ClassDB::bind_method(D_METHOD("set_bake_mask_value", "layer_number", "value"), &GPUParticlesCollisionSDF3D::set_bake_mask_value); + ClassDB::bind_method(D_METHOD("get_bake_mask_value", "layer_number"), &GPUParticlesCollisionSDF3D::get_bake_mask_value); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater,suffix:m"), "set_extents", "get_extents"); ADD_PROPERTY(PropertyInfo(Variant::INT, "resolution", PROPERTY_HINT_ENUM, "16,32,64,128,256,512,suffix:px"), "set_resolution", "get_resolution"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "thickness", PROPERTY_HINT_RANGE, "0.0,2.0,0.01,suffix:m"), "set_thickness", "get_thickness"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "bake_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_bake_mask", "get_bake_mask"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture3D"), "set_texture", "get_texture"); BIND_ENUM_CONSTANT(RESOLUTION_16); @@ -555,6 +573,31 @@ GPUParticlesCollisionSDF3D::Resolution GPUParticlesCollisionSDF3D::get_resolutio return resolution; } +void GPUParticlesCollisionSDF3D::set_bake_mask(uint32_t p_mask) { + bake_mask = p_mask; + update_configuration_warnings(); +} + +uint32_t GPUParticlesCollisionSDF3D::get_bake_mask() const { + return bake_mask; +} + +void GPUParticlesCollisionSDF3D::set_bake_mask_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1 || p_layer_number > 20, vformat("The render layer number (%d) must be between 1 and 20 (inclusive).", p_layer_number)); + uint32_t mask = get_bake_mask(); + if (p_value) { + mask |= 1 << (p_layer_number - 1); + } else { + mask &= ~(1 << (p_layer_number - 1)); + } + set_bake_mask(mask); +} + +bool GPUParticlesCollisionSDF3D::get_bake_mask_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1 || p_layer_number > 20, false, vformat("The render layer number (%d) must be between 1 and 20 (inclusive).", p_layer_number)); + return bake_mask & (1 << (p_layer_number - 1)); +} + void GPUParticlesCollisionSDF3D::set_texture(const Ref<Texture3D> &p_texture) { texture = p_texture; RID tex = texture.is_valid() ? texture->get_rid() : RID(); @@ -764,7 +807,7 @@ void GPUParticlesAttractor3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_directionality", "amount"), &GPUParticlesAttractor3D::set_directionality); ClassDB::bind_method(D_METHOD("get_directionality"), &GPUParticlesAttractor3D::get_directionality); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "strength", PROPERTY_HINT_RANGE, "-128,128,0.01,or_greater,or_lesser"), "set_strength", "get_strength"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "strength", PROPERTY_HINT_RANGE, "-128,128,0.01,or_greater,or_less"), "set_strength", "get_strength"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "attenuation", PROPERTY_HINT_EXP_EASING, "0,8,0.01"), "set_attenuation", "get_attenuation"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "directionality", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_directionality", "get_directionality"); ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask"); diff --git a/scene/3d/gpu_particles_collision_3d.h b/scene/3d/gpu_particles_collision_3d.h index 4b2cb930fa..548552bb70 100644 --- a/scene/3d/gpu_particles_collision_3d.h +++ b/scene/3d/gpu_particles_collision_3d.h @@ -110,6 +110,7 @@ public: private: Vector3 extents = Vector3(1, 1, 1); Resolution resolution = RESOLUTION_64; + uint32_t bake_mask = 0xFFFFFFFF; Ref<Texture3D> texture; float thickness = 1.0; @@ -161,6 +162,8 @@ protected: static void _bind_methods(); public: + virtual PackedStringArray get_configuration_warnings() const override; + void set_thickness(float p_thickness); float get_thickness() const; @@ -170,6 +173,12 @@ public: void set_resolution(Resolution p_resolution); Resolution get_resolution() const; + void set_bake_mask(uint32_t p_mask); + uint32_t get_bake_mask() const; + + void set_bake_mask_value(int p_layer_number, bool p_enable); + bool get_bake_mask_value(int p_layer_number) const; + void set_texture(const Ref<Texture3D> &p_texture); Ref<Texture3D> get_texture() const; diff --git a/scene/3d/importer_mesh_instance_3d.h b/scene/3d/importer_mesh_instance_3d.h index 3daf06771d..223b6fc80a 100644 --- a/scene/3d/importer_mesh_instance_3d.h +++ b/scene/3d/importer_mesh_instance_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SCENE_IMPORTER_MESH_INSTANCE_3D_H -#define SCENE_IMPORTER_MESH_INSTANCE_3D_H +#ifndef IMPORTER_MESH_INSTANCE_3D_H +#define IMPORTER_MESH_INSTANCE_3D_H #include "scene/3d/node_3d.h" #include "scene/resources/immediate_mesh.h" @@ -61,4 +61,5 @@ public: void set_skeleton_path(const NodePath &p_path); NodePath get_skeleton_path() const; }; -#endif + +#endif // IMPORTER_MESH_INSTANCE_3D_H diff --git a/scene/3d/joint_3d.cpp b/scene/3d/joint_3d.cpp index 0b824ef28b..1a18f43e7b 100644 --- a/scene/3d/joint_3d.cpp +++ b/scene/3d/joint_3d.cpp @@ -198,8 +198,8 @@ bool Joint3D::get_exclude_nodes_from_collision() const { return exclude_from_collision; } -TypedArray<String> Joint3D::get_configuration_warnings() const { - TypedArray<String> warnings = Node3D::get_configuration_warnings(); +PackedStringArray Joint3D::get_configuration_warnings() const { + PackedStringArray warnings = Node3D::get_configuration_warnings(); if (!warning.is_empty()) { warnings.push_back(warning); @@ -221,11 +221,11 @@ void Joint3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_exclude_nodes_from_collision", "enable"), &Joint3D::set_exclude_nodes_from_collision); ClassDB::bind_method(D_METHOD("get_exclude_nodes_from_collision"), &Joint3D::get_exclude_nodes_from_collision); - ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "nodes/node_a", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "PhysicsBody3D"), "set_node_a", "get_node_a"); - ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "nodes/node_b", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "PhysicsBody3D"), "set_node_b", "get_node_b"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "solver/priority", PROPERTY_HINT_RANGE, "1,8,1"), "set_solver_priority", "get_solver_priority"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "node_a", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "PhysicsBody3D"), "set_node_a", "get_node_a"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "node_b", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "PhysicsBody3D"), "set_node_b", "get_node_b"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "solver_priority", PROPERTY_HINT_RANGE, "1,8,1"), "set_solver_priority", "get_solver_priority"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collision/exclude_nodes"), "set_exclude_nodes_from_collision", "get_exclude_nodes_from_collision"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "exclude_nodes_from_collision"), "set_exclude_nodes_from_collision", "get_exclude_nodes_from_collision"); } Joint3D::Joint3D() { @@ -292,22 +292,6 @@ PinJoint3D::PinJoint3D() { /////////////////////////////////// -void HingeJoint3D::_set_upper_limit(real_t p_limit) { - set_param(PARAM_LIMIT_UPPER, Math::deg2rad(p_limit)); -} - -real_t HingeJoint3D::_get_upper_limit() const { - return Math::rad2deg(get_param(PARAM_LIMIT_UPPER)); -} - -void HingeJoint3D::_set_lower_limit(real_t p_limit) { - set_param(PARAM_LIMIT_LOWER, Math::deg2rad(p_limit)); -} - -real_t HingeJoint3D::_get_lower_limit() const { - return Math::rad2deg(get_param(PARAM_LIMIT_LOWER)); -} - void HingeJoint3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_param", "param", "value"), &HingeJoint3D::set_param); ClassDB::bind_method(D_METHOD("get_param", "param"), &HingeJoint3D::get_param); @@ -315,23 +299,17 @@ void HingeJoint3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_flag", "flag", "enabled"), &HingeJoint3D::set_flag); ClassDB::bind_method(D_METHOD("get_flag", "flag"), &HingeJoint3D::get_flag); - ClassDB::bind_method(D_METHOD("_set_upper_limit", "upper_limit"), &HingeJoint3D::_set_upper_limit); - ClassDB::bind_method(D_METHOD("_get_upper_limit"), &HingeJoint3D::_get_upper_limit); - - ClassDB::bind_method(D_METHOD("_set_lower_limit", "lower_limit"), &HingeJoint3D::_set_lower_limit); - ClassDB::bind_method(D_METHOD("_get_lower_limit"), &HingeJoint3D::_get_lower_limit); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "params/bias", PROPERTY_HINT_RANGE, "0.00,0.99,0.01"), "set_param", "get_param", PARAM_BIAS); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_limit/enable"), "set_flag", "get_flag", FLAG_USE_LIMIT); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_limit/upper", PROPERTY_HINT_RANGE, "-180,180,0.1"), "_set_upper_limit", "_get_upper_limit"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_limit/lower", PROPERTY_HINT_RANGE, "-180,180,0.1"), "_set_lower_limit", "_get_lower_limit"); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit/upper", PROPERTY_HINT_RANGE, "-180,180,0.1,radians"), "set_param", "get_param", PARAM_LIMIT_UPPER); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit/lower", PROPERTY_HINT_RANGE, "-180,180,0.1,radians"), "set_param", "get_param", PARAM_LIMIT_LOWER); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit/bias", PROPERTY_HINT_RANGE, "0.01,0.99,0.01"), "set_param", "get_param", PARAM_LIMIT_BIAS); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit/softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param", "get_param", PARAM_LIMIT_SOFTNESS); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit/relaxation", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param", "get_param", PARAM_LIMIT_RELAXATION); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "motor/enable"), "set_flag", "get_flag", FLAG_ENABLE_MOTOR); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "motor/target_velocity", PROPERTY_HINT_RANGE, "-200,200,0.01,or_greater,or_lesser,suffix:m/s"), "set_param", "get_param", PARAM_MOTOR_TARGET_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "motor/target_velocity", PROPERTY_HINT_RANGE, "-200,200,0.01,or_greater,or_less,suffix:m/s"), "set_param", "get_param", PARAM_MOTOR_TARGET_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "motor/max_impulse", PROPERTY_HINT_RANGE, "0.01,1024,0.01"), "set_param", "get_param", PARAM_MOTOR_MAX_IMPULSE); BIND_ENUM_CONSTANT(PARAM_BIAS); @@ -420,34 +398,10 @@ HingeJoint3D::HingeJoint3D() { ///////////////////////////////////////////////// -////////////////////////////////// - -void SliderJoint3D::_set_upper_limit_angular(real_t p_limit_angular) { - set_param(PARAM_ANGULAR_LIMIT_UPPER, Math::deg2rad(p_limit_angular)); -} - -real_t SliderJoint3D::_get_upper_limit_angular() const { - return Math::rad2deg(get_param(PARAM_ANGULAR_LIMIT_UPPER)); -} - -void SliderJoint3D::_set_lower_limit_angular(real_t p_limit_angular) { - set_param(PARAM_ANGULAR_LIMIT_LOWER, Math::deg2rad(p_limit_angular)); -} - -real_t SliderJoint3D::_get_lower_limit_angular() const { - return Math::rad2deg(get_param(PARAM_ANGULAR_LIMIT_LOWER)); -} - void SliderJoint3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_param", "param", "value"), &SliderJoint3D::set_param); ClassDB::bind_method(D_METHOD("get_param", "param"), &SliderJoint3D::get_param); - ClassDB::bind_method(D_METHOD("_set_upper_limit_angular", "upper_limit_angular"), &SliderJoint3D::_set_upper_limit_angular); - ClassDB::bind_method(D_METHOD("_get_upper_limit_angular"), &SliderJoint3D::_get_upper_limit_angular); - - ClassDB::bind_method(D_METHOD("_set_lower_limit_angular", "lower_limit_angular"), &SliderJoint3D::_set_lower_limit_angular); - ClassDB::bind_method(D_METHOD("_get_lower_limit_angular"), &SliderJoint3D::_get_lower_limit_angular); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit/upper_distance", PROPERTY_HINT_RANGE, "-1024,1024,0.01,suffix:m"), "set_param", "get_param", PARAM_LINEAR_LIMIT_UPPER); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit/lower_distance", PROPERTY_HINT_RANGE, "-1024,1024,0.01,suffix:m"), "set_param", "get_param", PARAM_LINEAR_LIMIT_LOWER); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit/softness", PROPERTY_HINT_RANGE, "0.01,16.0,0.01"), "set_param", "get_param", PARAM_LINEAR_LIMIT_SOFTNESS); @@ -460,8 +414,8 @@ void SliderJoint3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_ortho/restitution", PROPERTY_HINT_RANGE, "0.01,16.0,0.01"), "set_param", "get_param", PARAM_LINEAR_ORTHOGONAL_RESTITUTION); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_ortho/damping", PROPERTY_HINT_RANGE, "0,16.0,0.01"), "set_param", "get_param", PARAM_LINEAR_ORTHOGONAL_DAMPING); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_limit/upper_angle", PROPERTY_HINT_RANGE, "-180,180,0.1"), "_set_upper_limit_angular", "_get_upper_limit_angular"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_limit/lower_angle", PROPERTY_HINT_RANGE, "-180,180,0.1"), "_set_lower_limit_angular", "_get_lower_limit_angular"); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit/upper_angle", PROPERTY_HINT_RANGE, "-180,180,0.1,radians"), "set_param", "get_param", PARAM_ANGULAR_LIMIT_UPPER); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit/lower_angle", PROPERTY_HINT_RANGE, "-180,180,0.1,radians"), "set_param", "get_param", PARAM_ANGULAR_LIMIT_LOWER); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit/softness", PROPERTY_HINT_RANGE, "0.01,16.0,0.01"), "set_param", "get_param", PARAM_ANGULAR_LIMIT_SOFTNESS); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit/restitution", PROPERTY_HINT_RANGE, "0.01,16.0,0.01"), "set_param", "get_param", PARAM_ANGULAR_LIMIT_RESTITUTION); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit/damping", PROPERTY_HINT_RANGE, "0,16.0,0.01"), "set_param", "get_param", PARAM_ANGULAR_LIMIT_DAMPING); @@ -562,34 +516,12 @@ SliderJoint3D::SliderJoint3D() { ////////////////////////////////// -void ConeTwistJoint3D::_set_swing_span(real_t p_limit_angular) { - set_param(PARAM_SWING_SPAN, Math::deg2rad(p_limit_angular)); -} - -real_t ConeTwistJoint3D::_get_swing_span() const { - return Math::rad2deg(get_param(PARAM_SWING_SPAN)); -} - -void ConeTwistJoint3D::_set_twist_span(real_t p_limit_angular) { - set_param(PARAM_TWIST_SPAN, Math::deg2rad(p_limit_angular)); -} - -real_t ConeTwistJoint3D::_get_twist_span() const { - return Math::rad2deg(get_param(PARAM_TWIST_SPAN)); -} - void ConeTwistJoint3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_param", "param", "value"), &ConeTwistJoint3D::set_param); ClassDB::bind_method(D_METHOD("get_param", "param"), &ConeTwistJoint3D::get_param); - ClassDB::bind_method(D_METHOD("_set_swing_span", "swing_span"), &ConeTwistJoint3D::_set_swing_span); - ClassDB::bind_method(D_METHOD("_get_swing_span"), &ConeTwistJoint3D::_get_swing_span); - - ClassDB::bind_method(D_METHOD("_set_twist_span", "twist_span"), &ConeTwistJoint3D::_set_twist_span); - ClassDB::bind_method(D_METHOD("_get_twist_span"), &ConeTwistJoint3D::_get_twist_span); - - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "swing_span", PROPERTY_HINT_RANGE, "-180,180,0.1"), "_set_swing_span", "_get_swing_span"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "twist_span", PROPERTY_HINT_RANGE, "-40000,40000,0.1"), "_set_twist_span", "_get_twist_span"); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "swing_span", PROPERTY_HINT_RANGE, "-180,180,0.1,radians"), "set_param", "get_param", PARAM_SWING_SPAN); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "twist_span", PROPERTY_HINT_RANGE, "-40000,40000,0.1,radians"), "set_param", "get_param", PARAM_TWIST_SPAN); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "bias", PROPERTY_HINT_RANGE, "0.01,16.0,0.01"), "set_param", "get_param", PARAM_BIAS); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "softness", PROPERTY_HINT_RANGE, "0.01,16.0,0.01"), "set_param", "get_param", PARAM_SOFTNESS); @@ -620,8 +552,6 @@ real_t ConeTwistJoint3D::get_param(Param p_param) const { void ConeTwistJoint3D::_configure_joint(RID p_joint, PhysicsBody3D *body_a, PhysicsBody3D *body_b) { Transform3D gt = get_global_transform(); - //Vector3 cone_twistpos = gt.origin; - //Vector3 cone_twistdir = gt.basis.get_axis(2); Transform3D ainv = body_a->get_global_transform().affine_inverse(); @@ -652,73 +582,7 @@ ConeTwistJoint3D::ConeTwistJoint3D() { ///////////////////////////////////////////////////////////////////// -void Generic6DOFJoint3D::_set_angular_hi_limit_x(real_t p_limit_angular) { - set_param_x(PARAM_ANGULAR_UPPER_LIMIT, Math::deg2rad(p_limit_angular)); -} - -real_t Generic6DOFJoint3D::_get_angular_hi_limit_x() const { - return Math::rad2deg(get_param_x(PARAM_ANGULAR_UPPER_LIMIT)); -} - -void Generic6DOFJoint3D::_set_angular_lo_limit_x(real_t p_limit_angular) { - set_param_x(PARAM_ANGULAR_LOWER_LIMIT, Math::deg2rad(p_limit_angular)); -} - -real_t Generic6DOFJoint3D::_get_angular_lo_limit_x() const { - return Math::rad2deg(get_param_x(PARAM_ANGULAR_LOWER_LIMIT)); -} - -void Generic6DOFJoint3D::_set_angular_hi_limit_y(real_t p_limit_angular) { - set_param_y(PARAM_ANGULAR_UPPER_LIMIT, Math::deg2rad(p_limit_angular)); -} - -real_t Generic6DOFJoint3D::_get_angular_hi_limit_y() const { - return Math::rad2deg(get_param_y(PARAM_ANGULAR_UPPER_LIMIT)); -} - -void Generic6DOFJoint3D::_set_angular_lo_limit_y(real_t p_limit_angular) { - set_param_y(PARAM_ANGULAR_LOWER_LIMIT, Math::deg2rad(p_limit_angular)); -} - -real_t Generic6DOFJoint3D::_get_angular_lo_limit_y() const { - return Math::rad2deg(get_param_y(PARAM_ANGULAR_LOWER_LIMIT)); -} - -void Generic6DOFJoint3D::_set_angular_hi_limit_z(real_t p_limit_angular) { - set_param_z(PARAM_ANGULAR_UPPER_LIMIT, Math::deg2rad(p_limit_angular)); -} - -real_t Generic6DOFJoint3D::_get_angular_hi_limit_z() const { - return Math::rad2deg(get_param_z(PARAM_ANGULAR_UPPER_LIMIT)); -} - -void Generic6DOFJoint3D::_set_angular_lo_limit_z(real_t p_limit_angular) { - set_param_z(PARAM_ANGULAR_LOWER_LIMIT, Math::deg2rad(p_limit_angular)); -} - -real_t Generic6DOFJoint3D::_get_angular_lo_limit_z() const { - return Math::rad2deg(get_param_z(PARAM_ANGULAR_LOWER_LIMIT)); -} - void Generic6DOFJoint3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("_set_angular_hi_limit_x", "angle"), &Generic6DOFJoint3D::_set_angular_hi_limit_x); - ClassDB::bind_method(D_METHOD("_get_angular_hi_limit_x"), &Generic6DOFJoint3D::_get_angular_hi_limit_x); - - ClassDB::bind_method(D_METHOD("_set_angular_lo_limit_x", "angle"), &Generic6DOFJoint3D::_set_angular_lo_limit_x); - ClassDB::bind_method(D_METHOD("_get_angular_lo_limit_x"), &Generic6DOFJoint3D::_get_angular_lo_limit_x); - - ClassDB::bind_method(D_METHOD("_set_angular_hi_limit_y", "angle"), &Generic6DOFJoint3D::_set_angular_hi_limit_y); - ClassDB::bind_method(D_METHOD("_get_angular_hi_limit_y"), &Generic6DOFJoint3D::_get_angular_hi_limit_y); - - ClassDB::bind_method(D_METHOD("_set_angular_lo_limit_y", "angle"), &Generic6DOFJoint3D::_set_angular_lo_limit_y); - ClassDB::bind_method(D_METHOD("_get_angular_lo_limit_y"), &Generic6DOFJoint3D::_get_angular_lo_limit_y); - - ClassDB::bind_method(D_METHOD("_set_angular_hi_limit_z", "angle"), &Generic6DOFJoint3D::_set_angular_hi_limit_z); - ClassDB::bind_method(D_METHOD("_get_angular_hi_limit_z"), &Generic6DOFJoint3D::_get_angular_hi_limit_z); - - ClassDB::bind_method(D_METHOD("_set_angular_lo_limit_z", "angle"), &Generic6DOFJoint3D::_set_angular_lo_limit_z); - ClassDB::bind_method(D_METHOD("_get_angular_lo_limit_z"), &Generic6DOFJoint3D::_get_angular_lo_limit_z); - ClassDB::bind_method(D_METHOD("set_param_x", "param", "value"), &Generic6DOFJoint3D::set_param_x); ClassDB::bind_method(D_METHOD("get_param_x", "param"), &Generic6DOFJoint3D::get_param_x); @@ -737,7 +601,8 @@ void Generic6DOFJoint3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_flag_z", "flag", "value"), &Generic6DOFJoint3D::set_flag_z); ClassDB::bind_method(D_METHOD("get_flag_z", "flag"), &Generic6DOFJoint3D::get_flag_z); - // X + ADD_GROUP("Linear Limit", "linear_limit_"); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_limit_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_LINEAR_LIMIT); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_x/upper_distance", PROPERTY_HINT_NONE, "suffix:m"), "set_param_x", "get_param_x", PARAM_LINEAR_UPPER_LIMIT); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_x/lower_distance", PROPERTY_HINT_NONE, "suffix:m"), "set_param_x", "get_param_x", PARAM_LINEAR_LOWER_LIMIT); @@ -745,34 +610,6 @@ void Generic6DOFJoint3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_x/restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_x", "get_param_x", PARAM_LINEAR_RESTITUTION); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_x/damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_x", "get_param_x", PARAM_LINEAR_DAMPING); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_motor_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_LINEAR_MOTOR); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_x/target_velocity", PROPERTY_HINT_NONE, "suffix:m/s"), "set_param_x", "get_param_x", PARAM_LINEAR_MOTOR_TARGET_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_x/force_limit"), "set_param_x", "get_param_x", PARAM_LINEAR_MOTOR_FORCE_LIMIT); - - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_spring_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_LINEAR_SPRING); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_x/stiffness"), "set_param_x", "get_param_x", PARAM_LINEAR_SPRING_STIFFNESS); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_x/damping"), "set_param_x", "get_param_x", PARAM_LINEAR_SPRING_DAMPING); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_x/equilibrium_point"), "set_param_x", "get_param_x", PARAM_LINEAR_SPRING_EQUILIBRIUM_POINT); - - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_limit_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_ANGULAR_LIMIT); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_limit_x/upper_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_hi_limit_x", "_get_angular_hi_limit_x"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_limit_x/lower_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_lo_limit_x", "_get_angular_lo_limit_x"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_x/softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_x", "get_param_x", PARAM_ANGULAR_LIMIT_SOFTNESS); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_x/restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_x", "get_param_x", PARAM_ANGULAR_RESTITUTION); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_x/damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_x", "get_param_x", PARAM_ANGULAR_DAMPING); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_x/force_limit"), "set_param_x", "get_param_x", PARAM_ANGULAR_FORCE_LIMIT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_x/erp"), "set_param_x", "get_param_x", PARAM_ANGULAR_ERP); - - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_motor_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_MOTOR); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_x/target_velocity"), "set_param_x", "get_param_x", PARAM_ANGULAR_MOTOR_TARGET_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_x/force_limit"), "set_param_x", "get_param_x", PARAM_ANGULAR_MOTOR_FORCE_LIMIT); - - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_spring_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_ANGULAR_SPRING); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_x/stiffness"), "set_param_x", "get_param_x", PARAM_ANGULAR_SPRING_STIFFNESS); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_x/damping"), "set_param_x", "get_param_x", PARAM_ANGULAR_SPRING_DAMPING); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_x/equilibrium_point"), "set_param_x", "get_param_x", PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT); - - // Y ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_limit_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_LINEAR_LIMIT); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_y/upper_distance", PROPERTY_HINT_NONE, "suffix:m"), "set_param_y", "get_param_y", PARAM_LINEAR_UPPER_LIMIT); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_y/lower_distance", PROPERTY_HINT_NONE, "suffix:m"), "set_param_y", "get_param_y", PARAM_LINEAR_LOWER_LIMIT); @@ -780,35 +617,6 @@ void Generic6DOFJoint3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_y/restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_LINEAR_RESTITUTION); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_y/damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_LINEAR_DAMPING); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_motor_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_LINEAR_MOTOR); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_y/target_velocity", PROPERTY_HINT_NONE, "suffix:m/s"), "set_param_y", "get_param_y", PARAM_LINEAR_MOTOR_TARGET_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_y/force_limit"), "set_param_y", "get_param_y", PARAM_LINEAR_MOTOR_FORCE_LIMIT); - - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_spring_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_LINEAR_SPRING); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_y/stiffness"), "set_param_y", "get_param_y", PARAM_LINEAR_SPRING_STIFFNESS); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_y/damping"), "set_param_y", "get_param_y", PARAM_LINEAR_SPRING_DAMPING); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_y/equilibrium_point"), "set_param_y", "get_param_y", PARAM_LINEAR_SPRING_EQUILIBRIUM_POINT); - - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_limit_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_ANGULAR_LIMIT); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_limit_y/upper_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_hi_limit_y", "_get_angular_hi_limit_y"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_limit_y/lower_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_lo_limit_y", "_get_angular_lo_limit_y"); - - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_y/softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_ANGULAR_LIMIT_SOFTNESS); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_y/restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_ANGULAR_RESTITUTION); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_y/damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_ANGULAR_DAMPING); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_y/force_limit"), "set_param_y", "get_param_y", PARAM_ANGULAR_FORCE_LIMIT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_y/erp"), "set_param_y", "get_param_y", PARAM_ANGULAR_ERP); - - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_motor_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_MOTOR); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_y/target_velocity"), "set_param_y", "get_param_y", PARAM_ANGULAR_MOTOR_TARGET_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_y/force_limit"), "set_param_y", "get_param_y", PARAM_ANGULAR_MOTOR_FORCE_LIMIT); - - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_spring_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_ANGULAR_SPRING); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_y/stiffness"), "set_param_y", "get_param_y", PARAM_ANGULAR_SPRING_STIFFNESS); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_y/damping"), "set_param_y", "get_param_y", PARAM_ANGULAR_SPRING_DAMPING); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_y/equilibrium_point"), "set_param_y", "get_param_y", PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT); - - // Z ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_limit_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_LINEAR_LIMIT); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_z/upper_distance", PROPERTY_HINT_NONE, "suffix:m"), "set_param_z", "get_param_z", PARAM_LINEAR_UPPER_LIMIT); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_z/lower_distance", PROPERTY_HINT_NONE, "suffix:m"), "set_param_z", "get_param_z", PARAM_LINEAR_LOWER_LIMIT); @@ -816,28 +624,92 @@ void Generic6DOFJoint3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_z/restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_z", "get_param_z", PARAM_LINEAR_RESTITUTION); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_z/damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_z", "get_param_z", PARAM_LINEAR_DAMPING); + ADD_GROUP("Linear Motor", "linear_motor_"); + + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_motor_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_LINEAR_MOTOR); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_x/target_velocity", PROPERTY_HINT_NONE, "suffix:m/s"), "set_param_x", "get_param_x", PARAM_LINEAR_MOTOR_TARGET_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_x/force_limit"), "set_param_x", "get_param_x", PARAM_LINEAR_MOTOR_FORCE_LIMIT); + + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_motor_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_LINEAR_MOTOR); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_y/target_velocity", PROPERTY_HINT_NONE, "suffix:m/s"), "set_param_y", "get_param_y", PARAM_LINEAR_MOTOR_TARGET_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_y/force_limit"), "set_param_y", "get_param_y", PARAM_LINEAR_MOTOR_FORCE_LIMIT); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_motor_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_LINEAR_MOTOR); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_z/target_velocity", PROPERTY_HINT_NONE, "suffix:m/s"), "set_param_z", "get_param_z", PARAM_LINEAR_MOTOR_TARGET_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_z/force_limit"), "set_param_z", "get_param_z", PARAM_LINEAR_MOTOR_FORCE_LIMIT); + ADD_GROUP("Linear Spring", "linear_spring_"); + + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_spring_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_LINEAR_SPRING); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_x/stiffness"), "set_param_x", "get_param_x", PARAM_LINEAR_SPRING_STIFFNESS); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_x/damping"), "set_param_x", "get_param_x", PARAM_LINEAR_SPRING_DAMPING); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_x/equilibrium_point"), "set_param_x", "get_param_x", PARAM_LINEAR_SPRING_EQUILIBRIUM_POINT); + + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_spring_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_LINEAR_SPRING); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_y/stiffness"), "set_param_y", "get_param_y", PARAM_LINEAR_SPRING_STIFFNESS); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_y/damping"), "set_param_y", "get_param_y", PARAM_LINEAR_SPRING_DAMPING); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_y/equilibrium_point"), "set_param_y", "get_param_y", PARAM_LINEAR_SPRING_EQUILIBRIUM_POINT); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_spring_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_LINEAR_SPRING); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_z/stiffness"), "set_param_z", "get_param_z", PARAM_LINEAR_SPRING_STIFFNESS); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_z/damping"), "set_param_z", "get_param_z", PARAM_LINEAR_SPRING_DAMPING); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_z/equilibrium_point"), "set_param_z", "get_param_z", PARAM_LINEAR_SPRING_EQUILIBRIUM_POINT); + ADD_GROUP("Angular Limit", "angular_limit_"); + + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_limit_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_ANGULAR_LIMIT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_x/upper_angle", PROPERTY_HINT_RANGE, "-180,180,0.01,radians"), "set_param_x", "get_param_x", PARAM_ANGULAR_UPPER_LIMIT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_x/lower_angle", PROPERTY_HINT_RANGE, "-180,180,0.01,radians"), "set_param_x", "get_param_x", PARAM_ANGULAR_LOWER_LIMIT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_x/softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_x", "get_param_x", PARAM_ANGULAR_LIMIT_SOFTNESS); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_x/restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_x", "get_param_x", PARAM_ANGULAR_RESTITUTION); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_x/damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_x", "get_param_x", PARAM_ANGULAR_DAMPING); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_x/force_limit"), "set_param_x", "get_param_x", PARAM_ANGULAR_FORCE_LIMIT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_x/erp"), "set_param_x", "get_param_x", PARAM_ANGULAR_ERP); + + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_limit_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_ANGULAR_LIMIT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_y/upper_angle", PROPERTY_HINT_RANGE, "-180,180,0.01,radians"), "set_param_y", "get_param_y", PARAM_ANGULAR_UPPER_LIMIT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_y/lower_angle", PROPERTY_HINT_RANGE, "-180,180,0.01,radians"), "set_param_y", "get_param_y", PARAM_ANGULAR_LOWER_LIMIT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_y/softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_ANGULAR_LIMIT_SOFTNESS); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_y/restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_ANGULAR_RESTITUTION); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_y/damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_ANGULAR_DAMPING); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_y/force_limit"), "set_param_y", "get_param_y", PARAM_ANGULAR_FORCE_LIMIT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_y/erp"), "set_param_y", "get_param_y", PARAM_ANGULAR_ERP); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_limit_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_ANGULAR_LIMIT); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_limit_z/upper_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_hi_limit_z", "_get_angular_hi_limit_z"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_limit_z/lower_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_lo_limit_z", "_get_angular_lo_limit_z"); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_z/upper_angle", PROPERTY_HINT_RANGE, "-180,180,0.01,radians"), "set_param_z", "get_param_z", PARAM_ANGULAR_UPPER_LIMIT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_z/lower_angle", PROPERTY_HINT_RANGE, "-180,180,0.01,radians"), "set_param_z", "get_param_z", PARAM_ANGULAR_LOWER_LIMIT); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_z/softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_z", "get_param_z", PARAM_ANGULAR_LIMIT_SOFTNESS); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_z/restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_z", "get_param_z", PARAM_ANGULAR_RESTITUTION); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_z/damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_z", "get_param_z", PARAM_ANGULAR_DAMPING); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_z/force_limit"), "set_param_z", "get_param_z", PARAM_ANGULAR_FORCE_LIMIT); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_z/erp"), "set_param_z", "get_param_z", PARAM_ANGULAR_ERP); + ADD_GROUP("Angular Motor", "angular_motor_"); + + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_motor_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_MOTOR); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_x/target_velocity"), "set_param_x", "get_param_x", PARAM_ANGULAR_MOTOR_TARGET_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_x/force_limit"), "set_param_x", "get_param_x", PARAM_ANGULAR_MOTOR_FORCE_LIMIT); + + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_motor_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_MOTOR); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_y/target_velocity"), "set_param_y", "get_param_y", PARAM_ANGULAR_MOTOR_TARGET_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_y/force_limit"), "set_param_y", "get_param_y", PARAM_ANGULAR_MOTOR_FORCE_LIMIT); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_motor_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_MOTOR); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_z/target_velocity"), "set_param_z", "get_param_z", PARAM_ANGULAR_MOTOR_TARGET_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_z/force_limit"), "set_param_z", "get_param_z", PARAM_ANGULAR_MOTOR_FORCE_LIMIT); + ADD_GROUP("Angular Spring", "angular_spring_"); + + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_spring_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_ANGULAR_SPRING); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_x/stiffness"), "set_param_x", "get_param_x", PARAM_ANGULAR_SPRING_STIFFNESS); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_x/damping"), "set_param_x", "get_param_x", PARAM_ANGULAR_SPRING_DAMPING); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_x/equilibrium_point"), "set_param_x", "get_param_x", PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT); + + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_spring_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_ANGULAR_SPRING); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_y/stiffness"), "set_param_y", "get_param_y", PARAM_ANGULAR_SPRING_STIFFNESS); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_y/damping"), "set_param_y", "get_param_y", PARAM_ANGULAR_SPRING_DAMPING); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_y/equilibrium_point"), "set_param_y", "get_param_y", PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_spring_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_ANGULAR_SPRING); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_z/stiffness"), "set_param_z", "get_param_z", PARAM_ANGULAR_SPRING_STIFFNESS); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_z/damping"), "set_param_z", "get_param_z", PARAM_ANGULAR_SPRING_DAMPING); diff --git a/scene/3d/joint_3d.h b/scene/3d/joint_3d.h index ea356ef3b7..5af427446f 100644 --- a/scene/3d/joint_3d.h +++ b/scene/3d/joint_3d.h @@ -63,7 +63,7 @@ protected: _FORCE_INLINE_ bool is_configured() const { return configured; } public: - virtual TypedArray<String> get_configuration_warnings() const override; + virtual PackedStringArray get_configuration_warnings() const override; void set_node_a(const NodePath &p_node_a); NodePath get_node_a() const; @@ -136,12 +136,6 @@ protected: virtual void _configure_joint(RID p_joint, PhysicsBody3D *body_a, PhysicsBody3D *body_b) override; static void _bind_methods(); - void _set_upper_limit(real_t p_limit); - real_t _get_upper_limit() const; - - void _set_lower_limit(real_t p_limit); - real_t _get_lower_limit() const; - public: void set_param(Param p_param, real_t p_value); real_t get_param(Param p_param) const; @@ -188,12 +182,6 @@ public: }; protected: - void _set_upper_limit_angular(real_t p_limit_angular); - real_t _get_upper_limit_angular() const; - - void _set_lower_limit_angular(real_t p_limit_angular); - real_t _get_lower_limit_angular() const; - real_t params[PARAM_MAX]; virtual void _configure_joint(RID p_joint, PhysicsBody3D *body_a, PhysicsBody3D *body_b) override; static void _bind_methods(); @@ -221,12 +209,6 @@ public: }; protected: - void _set_swing_span(real_t p_limit_angular); - real_t _get_swing_span() const; - - void _set_twist_span(real_t p_limit_angular); - real_t _get_twist_span() const; - real_t params[PARAM_MAX]; virtual void _configure_joint(RID p_joint, PhysicsBody3D *body_a, PhysicsBody3D *body_b) override; static void _bind_methods(); @@ -281,24 +263,6 @@ public: }; protected: - void _set_angular_hi_limit_x(real_t p_limit_angular); - real_t _get_angular_hi_limit_x() const; - - void _set_angular_hi_limit_y(real_t p_limit_angular); - real_t _get_angular_hi_limit_y() const; - - void _set_angular_hi_limit_z(real_t p_limit_angular); - real_t _get_angular_hi_limit_z() const; - - void _set_angular_lo_limit_x(real_t p_limit_angular); - real_t _get_angular_lo_limit_x() const; - - void _set_angular_lo_limit_y(real_t p_limit_angular); - real_t _get_angular_lo_limit_y() const; - - void _set_angular_lo_limit_z(real_t p_limit_angular); - real_t _get_angular_lo_limit_z() const; - real_t params_x[PARAM_MAX]; bool flags_x[FLAG_MAX]; real_t params_y[PARAM_MAX]; diff --git a/scene/3d/label_3d.cpp b/scene/3d/label_3d.cpp index 0e5771bdb1..d977874911 100644 --- a/scene/3d/label_3d.cpp +++ b/scene/3d/label_3d.cpp @@ -31,8 +31,10 @@ #include "label_3d.h" #include "core/core_string_names.h" +#include "scene/main/viewport.h" #include "scene/resources/theme.h" #include "scene/scene_string_names.h" +#include "scene/theme/theme_db.h" void Label3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_horizontal_alignment", "alignment"), &Label3D::set_horizontal_alignment); @@ -126,7 +128,7 @@ void Label3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "no_depth_test"), "set_draw_flag", "get_draw_flag", FLAG_DISABLE_DEPTH_TEST); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "fixed_size"), "set_draw_flag", "get_draw_flag", FLAG_FIXED_SIZE); ADD_PROPERTY(PropertyInfo(Variant::INT, "alpha_cut", PROPERTY_HINT_ENUM, "Disabled,Discard,Opaque Pre-Pass"), "set_alpha_cut_mode", "get_alpha_cut_mode"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_scissor_threshold", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_alpha_scissor_threshold", "get_alpha_scissor_threshold"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_scissor_threshold", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_alpha_scissor_threshold", "get_alpha_scissor_threshold"); ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,Nearest Mipmap,Linear Mipmap,Nearest Mipmap Anisotropic,Linear Mipmap Anisotropic"), "set_texture_filter", "get_texture_filter"); ADD_PROPERTY(PropertyInfo(Variant::INT, "render_priority", PROPERTY_HINT_RANGE, itos(RS::MATERIAL_RENDER_PRIORITY_MIN) + "," + itos(RS::MATERIAL_RENDER_PRIORITY_MAX) + ",1"), "set_render_priority", "get_render_priority"); ADD_PROPERTY(PropertyInfo(Variant::INT, "outline_render_priority", PROPERTY_HINT_RANGE, itos(RS::MATERIAL_RENDER_PRIORITY_MIN) + "," + itos(RS::MATERIAL_RENDER_PRIORITY_MAX) + ",1"), "set_outline_render_priority", "get_outline_render_priority"); @@ -162,9 +164,19 @@ void Label3D::_bind_methods() { BIND_ENUM_CONSTANT(ALPHA_CUT_OPAQUE_PREPASS); } -void Label3D::_validate_property(PropertyInfo &property) const { - if (property.name == "material_override" || property.name == "material_overlay") { - property.usage = PROPERTY_USAGE_NO_EDITOR; +void Label3D::_validate_property(PropertyInfo &p_property) const { + if ( + p_property.name == "material_override" || + p_property.name == "material_overlay" || + p_property.name == "lod_bias" || + p_property.name == "gi_mode" || + p_property.name == "gi_lightmap_scale") { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } + + if (p_property.name == "cast_shadow" && alpha_cut == ALPHA_CUT_DISABLED) { + // Alpha-blended materials can't cast shadows. + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } } @@ -174,6 +186,14 @@ void Label3D::_notification(int p_what) { if (!pending_update) { _im_update(); } + Viewport *viewport = get_viewport(); + ERR_FAIL_COND(!viewport); + viewport->connect("size_changed", callable_mp(this, &Label3D::_font_changed)); + } break; + case NOTIFICATION_EXIT_TREE: { + Viewport *viewport = get_viewport(); + ERR_FAIL_COND(!viewport); + viewport->disconnect("size_changed", callable_mp(this, &Label3D::_font_changed)); } break; case NOTIFICATION_TRANSLATION_CHANGED: { String new_text = tr(text); @@ -422,7 +442,7 @@ void Label3D::_shape() { TS->shaped_text_set_spacing(text_rid, TextServer::SpacingType(i), font->get_spacing(TextServer::SpacingType(i))); } - Array stt; + TypedArray<Vector2i> stt; if (st_parser == TextServer::STRUCTURED_TEXT_CUSTOM) { GDVIRTUAL_CALL(_structured_text_parser, st_args, text, stt); } else { @@ -452,10 +472,10 @@ void Label3D::_shape() { } lines_rid.clear(); - uint16_t autowrap_flags = TextServer::BREAK_MANDATORY; + BitField<TextServer::LineBreakFlag> autowrap_flags = TextServer::BREAK_MANDATORY; switch (autowrap_mode) { case TextServer::AUTOWRAP_WORD_SMART: - autowrap_flags = TextServer::BREAK_WORD_BOUND_ADAPTIVE | TextServer::BREAK_MANDATORY; + autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_ADAPTIVE | TextServer::BREAK_MANDATORY; break; case TextServer::AUTOWRAP_WORD: autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_MANDATORY; @@ -466,8 +486,9 @@ void Label3D::_shape() { case TextServer::AUTOWRAP_OFF: break; } - PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(text_rid, width, 0, autowrap_flags); + autowrap_flags = autowrap_flags | TextServer::BREAK_TRIM_EDGE_SPACES; + PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(text_rid, width, 0, autowrap_flags); float max_line_w = 0.0; for (int i = 0; i < line_breaks.size(); i = i + 2) { RID line = TS->shaped_text_substr(text_rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]); @@ -715,13 +736,13 @@ Ref<Font> Label3D::_get_font_or_default() const { } // Check the project-defined Theme resource. - if (Theme::get_project_default().is_valid()) { + if (ThemeDB::get_singleton()->get_project_theme().is_valid()) { List<StringName> theme_types; - Theme::get_project_default()->get_type_dependencies(get_class_name(), StringName(), &theme_types); + ThemeDB::get_singleton()->get_project_theme()->get_type_dependencies(get_class_name(), StringName(), &theme_types); for (const StringName &E : theme_types) { - if (Theme::get_project_default()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { - Ref<Font> f = Theme::get_project_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); + if (ThemeDB::get_singleton()->get_project_theme()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { + Ref<Font> f = ThemeDB::get_singleton()->get_project_theme()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); if (f.is_valid()) { theme_font = f; theme_font->connect(CoreStringNames::get_singleton()->changed, Callable(const_cast<Label3D *>(this), "_font_changed")); @@ -734,11 +755,11 @@ Ref<Font> Label3D::_get_font_or_default() const { // Lastly, fall back on the items defined in the default Theme, if they exist. { List<StringName> theme_types; - Theme::get_default()->get_type_dependencies(get_class_name(), StringName(), &theme_types); + ThemeDB::get_singleton()->get_default_theme()->get_type_dependencies(get_class_name(), StringName(), &theme_types); for (const StringName &E : theme_types) { - if (Theme::get_default()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { - Ref<Font> f = Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); + if (ThemeDB::get_singleton()->get_default_theme()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { + Ref<Font> f = ThemeDB::get_singleton()->get_default_theme()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); if (f.is_valid()) { theme_font = f; theme_font->connect(CoreStringNames::get_singleton()->changed, Callable(const_cast<Label3D *>(this), "_font_changed")); @@ -749,7 +770,7 @@ Ref<Font> Label3D::_get_font_or_default() const { } // If they don't exist, use any type to return the default/empty value. - Ref<Font> f = Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", StringName()); + Ref<Font> f = ThemeDB::get_singleton()->get_default_theme()->get_theme_item(Theme::DATA_TYPE_FONT, "font", StringName()); if (f.is_valid()) { theme_font = f; theme_font->connect(CoreStringNames::get_singleton()->changed, Callable(const_cast<Label3D *>(this), "_font_changed")); @@ -889,6 +910,7 @@ void Label3D::set_alpha_cut_mode(AlphaCutMode p_mode) { if (alpha_cut != p_mode) { alpha_cut = p_mode; _queue_update(); + notify_property_list_changed(); } } @@ -927,7 +949,12 @@ Label3D::Label3D() { mesh = RenderingServer::get_singleton()->mesh_create(); + // Disable shadow casting by default to improve performance and avoid unintended visual artifacts. set_cast_shadows_setting(SHADOW_CASTING_SETTING_OFF); + + // Label3D can't contribute to GI in any way, so disable it to improve performance. + set_gi_mode(GI_MODE_DISABLED); + set_base(mesh); } diff --git a/scene/3d/label_3d.h b/scene/3d/label_3d.h index 4498e89517..3c9a758e6e 100644 --- a/scene/3d/label_3d.h +++ b/scene/3d/label_3d.h @@ -55,7 +55,7 @@ public: }; private: - real_t pixel_size = 0.01; + real_t pixel_size = 0.005; bool flags[FLAG_MAX] = {}; AlphaCutMode alpha_cut = ALPHA_CUT_DISABLED; float alpha_scissor_threshold = 0.5; @@ -109,7 +109,7 @@ private: TextServer::AutowrapMode autowrap_mode = TextServer::AUTOWRAP_OFF; float width = 500.0; - int font_size = 16; + int font_size = 32; Ref<Font> font_override; mutable Ref<Font> theme_font; Color modulate = Color(1, 1, 1, 1); @@ -117,7 +117,7 @@ private: int outline_render_priority = -1; int render_priority = 0; - int outline_size = 0; + int outline_size = 12; Color outline_modulate = Color(0, 0, 0, 1); float line_spacing = 0.f; @@ -149,7 +149,7 @@ protected: static void _bind_methods(); - void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; void _im_update(); void _font_changed(); diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp index 6c999d85e2..23fd091be6 100644 --- a/scene/3d/light_3d.cpp +++ b/scene/3d/light_3d.cpp @@ -28,6 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#include "core/config/project_settings.h" + #include "light_3d.h" void Light3D::set_param(Param p_param, real_t p_value) { @@ -122,7 +124,14 @@ uint32_t Light3D::get_cull_mask() const { void Light3D::set_color(const Color &p_color) { color = p_color; - RS::get_singleton()->light_set_color(light, p_color); + + if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + Color combined = color.srgb_to_linear(); + combined *= correlated_color.srgb_to_linear(); + RS::get_singleton()->light_set_color(light, combined.linear_to_srgb()); + } else { + RS::get_singleton()->light_set_color(light, color); + } // The gizmo color depends on the light color, so update it. update_gizmos(); } @@ -149,7 +158,7 @@ AABB Light3D::get_aabb() const { } else if (type == RenderingServer::LIGHT_SPOT) { real_t len = param[PARAM_RANGE]; - real_t size = Math::tan(Math::deg2rad(param[PARAM_SPOT_ANGLE])) * len; + real_t size = Math::tan(Math::deg_to_rad(param[PARAM_SPOT_ANGLE])) * len; return AABB(Vector3(-size, -size, -len), Vector3(size * 2, size * 2, len)); } @@ -181,6 +190,56 @@ void Light3D::owner_changed_notify() { _update_visibility(); } +// Temperature expressed in Kelvins. Valid range 1000 - 15000 +// First converts to CIE 1960 then to sRGB +// As explained in the Filament documentation: https://google.github.io/filament/Filament.md.html#lighting/directlighting/lightsparameterization +Color _color_from_temperature(float p_temperature) { + float T2 = p_temperature * p_temperature; + float u = (0.860117757f + 1.54118254e-4f * p_temperature + 1.28641212e-7f * T2) / + (1.0f + 8.42420235e-4f * p_temperature + 7.08145163e-7f * T2); + float v = (0.317398726f + 4.22806245e-5f * p_temperature + 4.20481691e-8f * T2) / + (1.0f - 2.89741816e-5f * p_temperature + 1.61456053e-7f * T2); + + // Convert to xyY space. + float d = 1.0f / (2.0f * u - 8.0f * v + 4.0f); + float x = 3.0f * u * d; + float y = 2.0f * v * d; + + // Convert to XYZ space + const float a = 1.0 / MAX(y, 1e-5f); + Vector3 xyz = Vector3(x * a, 1.0, (1.0f - x - y) * a); + + // Convert from XYZ to sRGB(linear) + Vector3 linear = Vector3(3.2404542f * xyz.x - 1.5371385f * xyz.y - 0.4985314f * xyz.z, + -0.9692660f * xyz.x + 1.8760108f * xyz.y + 0.0415560f * xyz.z, + 0.0556434f * xyz.x - 0.2040259f * xyz.y + 1.0572252f * xyz.z); + linear /= MAX(1e-5f, linear[linear.max_axis_index()]); + // Normalize, clamp, and convert to sRGB. + return Color(linear.x, linear.y, linear.z).clamp().linear_to_srgb(); +} + +void Light3D::set_temperature(const float p_temperature) { + temperature = p_temperature; + if (!GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + return; + } + correlated_color = _color_from_temperature(temperature); + + Color combined = color.srgb_to_linear() * correlated_color.srgb_to_linear(); + + RS::get_singleton()->light_set_color(light, combined.linear_to_srgb()); + // The gizmo color depends on the light color, so update it. + update_gizmos(); +} + +Color Light3D::get_correlated_color() const { + return correlated_color; +} + +float Light3D::get_temperature() const { + return temperature; +} + void Light3D::_update_visibility() { if (!is_inside_tree()) { return; @@ -223,21 +282,25 @@ bool Light3D::is_editor_only() const { return editor_only; } -void Light3D::_validate_property(PropertyInfo &property) const { - if (!shadow && (property.name == "shadow_bias" || property.name == "shadow_normal_bias" || property.name == "shadow_reverse_cull_face" || property.name == "shadow_transmittance_bias" || property.name == "shadow_fog_fade" || property.name == "shadow_blur" || property.name == "distance_fade_shadow")) { - property.usage = PROPERTY_USAGE_NO_EDITOR; +void Light3D::_validate_property(PropertyInfo &p_property) const { + if (!shadow && (p_property.name == "shadow_bias" || p_property.name == "shadow_normal_bias" || p_property.name == "shadow_reverse_cull_face" || p_property.name == "shadow_transmittance_bias" || p_property.name == "shadow_opacity" || p_property.name == "shadow_blur" || p_property.name == "distance_fade_shadow")) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } - if (get_light_type() != RS::LIGHT_DIRECTIONAL && property.name == "light_angular_distance") { - // Angular distance is only used in DirectionalLight3D. - property.usage = PROPERTY_USAGE_NONE; + if (get_light_type() != RS::LIGHT_DIRECTIONAL && (p_property.name == "light_angular_distance" || p_property.name == "light_intensity_lux")) { + // Angular distance and Light Intensity Lux are only used in DirectionalLight3D. + p_property.usage = PROPERTY_USAGE_NONE; + } else if (get_light_type() == RS::LIGHT_DIRECTIONAL && p_property.name == "light_intensity_lumens") { + p_property.usage = PROPERTY_USAGE_NONE; } - if (!distance_fade_enabled && (property.name == "distance_fade_begin" || property.name == "distance_fade_shadow" || property.name == "distance_fade_length")) { - property.usage = PROPERTY_USAGE_NO_EDITOR; + if (!GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units") && (p_property.name == "light_intensity_lumens" || p_property.name == "light_intensity_lux" || p_property.name == "light_temperature")) { + p_property.usage = PROPERTY_USAGE_NONE; } - VisualInstance3D::_validate_property(property); + if (!distance_fade_enabled && (p_property.name == "distance_fade_begin" || p_property.name == "distance_fade_shadow" || p_property.name == "distance_fade_length")) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } } void Light3D::_bind_methods() { @@ -280,10 +343,18 @@ void Light3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_projector", "projector"), &Light3D::set_projector); ClassDB::bind_method(D_METHOD("get_projector"), &Light3D::get_projector); + ClassDB::bind_method(D_METHOD("set_temperature", "temperature"), &Light3D::set_temperature); + ClassDB::bind_method(D_METHOD("get_temperature"), &Light3D::get_temperature); + ClassDB::bind_method(D_METHOD("get_correlated_color"), &Light3D::get_correlated_color); + ADD_GROUP("Light", "light_"); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_intensity_lumens", PROPERTY_HINT_RANGE, "0,100000.0,0.01,or_greater,suffix:lm"), "set_param", "get_param", PARAM_INTENSITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_intensity_lux", PROPERTY_HINT_RANGE, "0,150000.0,0.01,or_greater,suffix:lx"), "set_param", "get_param", PARAM_INTENSITY); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "light_temperature", PROPERTY_HINT_RANGE, "1000,15000.0,1.0,suffix:k"), "set_temperature", "get_temperature"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "light_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_color", "get_color"); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_energy", PROPERTY_HINT_RANGE, "0,16,0.001,or_greater"), "set_param", "get_param", PARAM_ENERGY); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_indirect_energy", PROPERTY_HINT_RANGE, "0,16,0.001,or_greater"), "set_param", "get_param", PARAM_INDIRECT_ENERGY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_volumetric_fog_energy", PROPERTY_HINT_RANGE, "0,16,0.001,or_greater"), "set_param", "get_param", PARAM_VOLUMETRIC_FOG_ENERGY); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "light_projector", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_projector", "get_projector"); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_size", PROPERTY_HINT_RANGE, "0,1,0.001,or_greater,suffix:m"), "set_param", "get_param", PARAM_SIZE); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_angular_distance", PROPERTY_HINT_RANGE, "0,90,0.01,degrees"), "set_param", "get_param", PARAM_SIZE); @@ -291,24 +362,30 @@ void Light3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_specular", PROPERTY_HINT_RANGE, "0,16,0.001,or_greater"), "set_param", "get_param", PARAM_SPECULAR); ADD_PROPERTY(PropertyInfo(Variant::INT, "light_bake_mode", PROPERTY_HINT_ENUM, "Disabled,Static (VoxelGI/SDFGI/LightmapGI),Dynamic (VoxelGI/SDFGI only)"), "set_bake_mode", "get_bake_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "light_cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask"); + ADD_GROUP("Shadow", "shadow_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shadow_enabled"), "set_shadow", "has_shadow"); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_bias", PROPERTY_HINT_RANGE, "0,10,0.001"), "set_param", "get_param", PARAM_SHADOW_BIAS); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_normal_bias", PROPERTY_HINT_RANGE, "0,10,0.001"), "set_param", "get_param", PARAM_SHADOW_NORMAL_BIAS); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shadow_reverse_cull_face"), "set_shadow_reverse_cull_face", "get_shadow_reverse_cull_face"); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_transmittance_bias", PROPERTY_HINT_RANGE, "-16,16,0.001"), "set_param", "get_param", PARAM_TRANSMITTANCE_BIAS); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_fog_fade", PROPERTY_HINT_RANGE, "0.001,10,0.001"), "set_param", "get_param", PARAM_SHADOW_VOLUMETRIC_FOG_FADE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_opacity", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_SHADOW_OPACITY); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_blur", PROPERTY_HINT_RANGE, "0,10,0.001"), "set_param", "get_param", PARAM_SHADOW_BLUR); + + ADD_GROUP("Distance Fade", "distance_fade_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "distance_fade_enabled"), "set_enable_distance_fade", "is_distance_fade_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_begin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), "set_distance_fade_begin", "get_distance_fade_begin"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_shadow", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), "set_distance_fade_shadow", "get_distance_fade_shadow"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_length", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), "set_distance_fade_length", "get_distance_fade_length"); + ADD_GROUP("Editor", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor_only"), "set_editor_only", "is_editor_only"); + ADD_GROUP("", ""); BIND_ENUM_CONSTANT(PARAM_ENERGY); BIND_ENUM_CONSTANT(PARAM_INDIRECT_ENERGY); + BIND_ENUM_CONSTANT(PARAM_VOLUMETRIC_FOG_ENERGY); BIND_ENUM_CONSTANT(PARAM_SPECULAR); BIND_ENUM_CONSTANT(PARAM_RANGE); BIND_ENUM_CONSTANT(PARAM_SIZE); @@ -323,9 +400,10 @@ void Light3D::_bind_methods() { BIND_ENUM_CONSTANT(PARAM_SHADOW_NORMAL_BIAS); BIND_ENUM_CONSTANT(PARAM_SHADOW_BIAS); BIND_ENUM_CONSTANT(PARAM_SHADOW_PANCAKE_SIZE); + BIND_ENUM_CONSTANT(PARAM_SHADOW_OPACITY); BIND_ENUM_CONSTANT(PARAM_SHADOW_BLUR); - BIND_ENUM_CONSTANT(PARAM_SHADOW_VOLUMETRIC_FOG_FADE); BIND_ENUM_CONSTANT(PARAM_TRANSMITTANCE_BIAS); + BIND_ENUM_CONSTANT(PARAM_INTENSITY); BIND_ENUM_CONSTANT(PARAM_MAX); BIND_ENUM_CONSTANT(BAKE_DISABLED); @@ -358,6 +436,7 @@ Light3D::Light3D(RenderingServer::LightType p_type) { set_param(PARAM_ENERGY, 1); set_param(PARAM_INDIRECT_ENERGY, 1); + set_param(PARAM_VOLUMETRIC_FOG_ENERGY, 1); set_param(PARAM_SPECULAR, 0.5); set_param(PARAM_RANGE, 5); set_param(PARAM_SIZE, 0); @@ -370,12 +449,15 @@ Light3D::Light3D(RenderingServer::LightType p_type) { set_param(PARAM_SHADOW_SPLIT_3_OFFSET, 0.5); set_param(PARAM_SHADOW_FADE_START, 0.8); set_param(PARAM_SHADOW_PANCAKE_SIZE, 20.0); + set_param(PARAM_SHADOW_OPACITY, 1.0); set_param(PARAM_SHADOW_BLUR, 1.0); set_param(PARAM_SHADOW_BIAS, 0.03); set_param(PARAM_SHADOW_NORMAL_BIAS, 1.0); set_param(PARAM_TRANSMITTANCE_BIAS, 0.05); - set_param(PARAM_SHADOW_VOLUMETRIC_FOG_FADE, 0.1); set_param(PARAM_SHADOW_FADE_START, 1); + // For OmniLight3D and SpotLight3D, specified in Lumens. + set_param(PARAM_INTENSITY, 1000.0); + set_temperature(6500.0); // Nearly white. set_disable_scale(true); } @@ -421,29 +503,27 @@ DirectionalLight3D::SkyMode DirectionalLight3D::get_sky_mode() const { return sky_mode; } -void DirectionalLight3D::_validate_property(PropertyInfo &property) const { - if (shadow_mode == SHADOW_ORTHOGONAL && (property.name == "directional_shadow_split_1" || property.name == "directional_shadow_blend_splits")) { +void DirectionalLight3D::_validate_property(PropertyInfo &p_property) const { + if (shadow_mode == SHADOW_ORTHOGONAL && (p_property.name == "directional_shadow_split_1" || p_property.name == "directional_shadow_blend_splits")) { // Split 2 and split blending are only used with the PSSM 2 Splits and PSSM 4 Splits shadow modes. - property.usage = PROPERTY_USAGE_NO_EDITOR; + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } - if ((shadow_mode == SHADOW_ORTHOGONAL || shadow_mode == SHADOW_PARALLEL_2_SPLITS) && (property.name == "directional_shadow_split_2" || property.name == "directional_shadow_split_3")) { + if ((shadow_mode == SHADOW_ORTHOGONAL || shadow_mode == SHADOW_PARALLEL_2_SPLITS) && (p_property.name == "directional_shadow_split_2" || p_property.name == "directional_shadow_split_3")) { // Splits 3 and 4 are only used with the PSSM 4 Splits shadow mode. - property.usage = PROPERTY_USAGE_NO_EDITOR; + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } - if (property.name == "light_size" || property.name == "light_projector" || property.name == "light_specular") { + if (p_property.name == "light_size" || p_property.name == "light_projector" || p_property.name == "light_specular") { // Not implemented in DirectionalLight3D (`light_size` is replaced by `light_angular_distance`). - property.usage = PROPERTY_USAGE_NONE; + p_property.usage = PROPERTY_USAGE_NONE; } - if (property.name == "distance_fade_enabled" || property.name == "distance_fade_begin" || property.name == "distance_fade_shadow" || property.name == "distance_fade_length") { + if (p_property.name == "distance_fade_enabled" || p_property.name == "distance_fade_begin" || p_property.name == "distance_fade_shadow" || p_property.name == "distance_fade_length") { // Not relevant for DirectionalLight3D, as the light LOD system only pertains to point lights. // For DirectionalLight3D, `directional_shadow_max_distance` can be used instead. - property.usage = PROPERTY_USAGE_NONE; + p_property.usage = PROPERTY_USAGE_NONE; } - - Light3D::_validate_property(property); } void DirectionalLight3D::_bind_methods() { @@ -483,6 +563,7 @@ DirectionalLight3D::DirectionalLight3D() : set_param(PARAM_SHADOW_FADE_START, 0.8); // Increase the default shadow bias to better suit most scenes. set_param(PARAM_SHADOW_BIAS, 0.1); + set_param(PARAM_INTENSITY, 100000.0); // Specified in Lux, approximate mid-day sun. set_shadow_mode(SHADOW_PARALLEL_4_SPLITS); blend_splits = false; set_sky_mode(SKY_MODE_LIGHT_AND_SKY); @@ -497,8 +578,8 @@ OmniLight3D::ShadowMode OmniLight3D::get_shadow_mode() const { return shadow_mode; } -TypedArray<String> OmniLight3D::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray OmniLight3D::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (!has_shadow() && get_projector().is_valid()) { warnings.push_back(RTR("Projector texture only works with shadows active.")); @@ -527,8 +608,8 @@ OmniLight3D::OmniLight3D() : set_param(PARAM_SHADOW_BIAS, 0.2); } -TypedArray<String> SpotLight3D::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray SpotLight3D::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (has_shadow() && get_param(PARAM_SPOT_ANGLE) >= 90.0) { warnings.push_back(RTR("A SpotLight3D with an angle wider than 90 degrees cannot cast shadows.")); diff --git a/scene/3d/light_3d.h b/scene/3d/light_3d.h index 6ff332df5a..8da45bee79 100644 --- a/scene/3d/light_3d.h +++ b/scene/3d/light_3d.h @@ -40,6 +40,7 @@ public: enum Param { PARAM_ENERGY = RS::LIGHT_PARAM_ENERGY, PARAM_INDIRECT_ENERGY = RS::LIGHT_PARAM_INDIRECT_ENERGY, + PARAM_VOLUMETRIC_FOG_ENERGY = RS::LIGHT_PARAM_VOLUMETRIC_FOG_ENERGY, PARAM_SPECULAR = RS::LIGHT_PARAM_SPECULAR, PARAM_RANGE = RS::LIGHT_PARAM_RANGE, PARAM_SIZE = RS::LIGHT_PARAM_SIZE, @@ -54,9 +55,10 @@ public: PARAM_SHADOW_NORMAL_BIAS = RS::LIGHT_PARAM_SHADOW_NORMAL_BIAS, PARAM_SHADOW_BIAS = RS::LIGHT_PARAM_SHADOW_BIAS, PARAM_SHADOW_PANCAKE_SIZE = RS::LIGHT_PARAM_SHADOW_PANCAKE_SIZE, + PARAM_SHADOW_OPACITY = RS::LIGHT_PARAM_SHADOW_OPACITY, PARAM_SHADOW_BLUR = RS::LIGHT_PARAM_SHADOW_BLUR, - PARAM_SHADOW_VOLUMETRIC_FOG_FADE = RS::LIGHT_PARAM_SHADOW_VOLUMETRIC_FOG_FADE, PARAM_TRANSMITTANCE_BIAS = RS::LIGHT_PARAM_TRANSMITTANCE_BIAS, + PARAM_INTENSITY = RS::LIGHT_PARAM_INTENSITY, PARAM_MAX = RS::LIGHT_PARAM_MAX }; @@ -82,6 +84,8 @@ private: void _update_visibility(); BakeMode bake_mode = BAKE_DYNAMIC; Ref<Texture2D> projector; + Color correlated_color = Color(1.0, 1.0, 1.0); + float temperature = 6500.0; // bind helpers @@ -92,7 +96,7 @@ protected: static void _bind_methods(); void _notification(int p_what); - virtual void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; Light3D(RenderingServer::LightType p_type); @@ -138,6 +142,10 @@ public: void set_projector(const Ref<Texture2D> &p_texture); Ref<Texture2D> get_projector() const; + void set_temperature(const float p_temperature); + float get_temperature() const; + Color get_correlated_color() const; + virtual AABB get_aabb() const override; Light3D(); @@ -170,7 +178,7 @@ private: protected: static void _bind_methods(); - virtual void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; public: void set_shadow_mode(ShadowMode p_mode); @@ -208,7 +216,7 @@ public: void set_shadow_mode(ShadowMode p_mode); ShadowMode get_shadow_mode() const; - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; OmniLight3D(); }; @@ -222,7 +230,7 @@ protected: static void _bind_methods(); public: - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; SpotLight3D() : Light3D(RenderingServer::LIGHT_SPOT) {} diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp index abe942b97a..cbcbac7b83 100644 --- a/scene/3d/lightmap_gi.cpp +++ b/scene/3d/lightmap_gi.cpp @@ -30,10 +30,14 @@ #include "lightmap_gi.h" +#include "core/config/project_settings.h" #include "core/io/config_file.h" #include "core/math/delaunay_3d.h" #include "lightmap_probe.h" #include "scene/3d/mesh_instance_3d.h" +#include "scene/resources/camera_attributes.h" +#include "scene/resources/environment.h" +#include "scene/resources/sky.h" void LightmapGIData::add_user(const NodePath &p_path, const Rect2 &p_uv_scale, int p_slice_index, int32_t p_sub_instance) { User user; @@ -101,6 +105,7 @@ void LightmapGIData::_set_light_textures_data(const Array &p_data) { Vector<Ref<Image>> images; for (int i = 0; i < p_data.size(); i++) { Ref<TextureLayered> texture = p_data[i]; + ERR_FAIL_COND_MSG(texture.is_null(), vformat("Invalid TextureLayered at index %d.", i)); for (int j = 0; j < texture->get_layers(); j++) { images.push_back(texture->get_layer_data(j)); } @@ -146,7 +151,7 @@ Array LightmapGIData::_get_light_textures_data() const { texture_image->create(slice_width, slice_height * texture_slice_count, false, images[0]->get_format()); for (int j = 0; j < texture_slice_count; j++) { - texture_image->blit_rect(images[i * slices_per_texture + j], Rect2(0, 0, slice_width, slice_height), Point2(0, slice_height * j)); + texture_image->blit_rect(images[i * slices_per_texture + j], Rect2i(0, 0, slice_width, slice_height), Point2i(0, slice_height * j)); } String texture_path = texture_count > 1 ? base_name + "_" + itos(i) + ".exr" : base_name + ".exr"; @@ -207,7 +212,7 @@ bool LightmapGIData::is_using_spherical_harmonics() const { return uses_spherical_harmonics; } -void LightmapGIData::set_capture_data(const AABB &p_bounds, bool p_interior, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree) { +void LightmapGIData::set_capture_data(const AABB &p_bounds, bool p_interior, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree, float p_baked_exposure) { if (p_points.size()) { int pc = p_points.size(); ERR_FAIL_COND(pc * 9 != p_point_sh.size()); @@ -221,6 +226,8 @@ void LightmapGIData::set_capture_data(const AABB &p_bounds, bool p_interior, con RS::get_singleton()->lightmap_set_probe_bounds(lightmap, AABB()); RS::get_singleton()->lightmap_set_probe_interior(lightmap, false); } + RS::get_singleton()->lightmap_set_baked_exposure_normalization(lightmap, p_baked_exposure); + baked_exposure = p_baked_exposure; interior = p_interior; bounds = p_bounds; } @@ -249,6 +256,10 @@ bool LightmapGIData::is_interior() const { return interior; } +float LightmapGIData::get_baked_exposure() const { + return baked_exposure; +} + void LightmapGIData::_set_probe_data(const Dictionary &p_data) { ERR_FAIL_COND(!p_data.has("bounds")); ERR_FAIL_COND(!p_data.has("points")); @@ -256,7 +267,8 @@ void LightmapGIData::_set_probe_data(const Dictionary &p_data) { ERR_FAIL_COND(!p_data.has("bsp")); ERR_FAIL_COND(!p_data.has("sh")); ERR_FAIL_COND(!p_data.has("interior")); - set_capture_data(p_data["bounds"], p_data["interior"], p_data["points"], p_data["sh"], p_data["tetrahedra"], p_data["bsp"]); + ERR_FAIL_COND(!p_data.has("baked_exposure")); + set_capture_data(p_data["bounds"], p_data["interior"], p_data["points"], p_data["sh"], p_data["tetrahedra"], p_data["bsp"], p_data["baked_exposure"]); } Dictionary LightmapGIData::_get_probe_data() const { @@ -267,6 +279,7 @@ Dictionary LightmapGIData::_get_probe_data() const { d["bsp"] = get_capture_bsp_tree(); d["sh"] = get_capture_sh(); d["interior"] = is_interior(); + d["baked_exposure"] = get_baked_exposure(); return d; } @@ -291,7 +304,7 @@ void LightmapGIData::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_probe_data", "data"), &LightmapGIData::_set_probe_data); ClassDB::bind_method(D_METHOD("_get_probe_data"), &LightmapGIData::_get_probe_data); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "light_texture", PROPERTY_HINT_RESOURCE_TYPE, "TextureLayered", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK), "set_light_texture", "get_light_texture"); // property usage default but no save + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "light_texture", PROPERTY_HINT_RESOURCE_TYPE, "TextureLayered", PROPERTY_USAGE_EDITOR), "set_light_texture", "get_light_texture"); // property usage default but no save ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "light_textures", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_light_textures_data", "_get_light_textures_data"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uses_spherical_harmonics", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_uses_spherical_harmonics", "is_using_spherical_harmonics"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "user_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_user_data", "_get_user_data"); @@ -753,11 +766,11 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa MeshesFound &mf = meshes_found.write[m_i]; Size2i lightmap_size = mf.mesh->get_lightmap_size_hint() * mf.lightmap_scale; - Vector<RID> overrides; + TypedArray<RID> overrides; overrides.resize(mf.overrides.size()); for (int i = 0; i < mf.overrides.size(); i++) { if (mf.overrides[i].is_valid()) { - overrides.write[i] = mf.overrides[i]->get_rid(); + overrides[i] = mf.overrides[i]->get_rid(); } } TypedArray<Image> images = RS::get_singleton()->bake_render_uv2(mf.mesh->get_rid(), overrides, lightmap_size); @@ -977,15 +990,27 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa Transform3D xf = lights_found[i].xform; Color linear_color = light->get_color().srgb_to_linear(); + float energy = light->get_param(Light3D::PARAM_ENERGY); + if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + energy *= light->get_param(Light3D::PARAM_INTENSITY); + linear_color *= light->get_correlated_color().srgb_to_linear(); + } + if (Object::cast_to<DirectionalLight3D>(light)) { DirectionalLight3D *l = Object::cast_to<DirectionalLight3D>(light); - lightmapper->add_directional_light(light->get_bake_mode() == Light3D::BAKE_STATIC, -xf.basis.get_column(Vector3::AXIS_Z).normalized(), linear_color, l->get_param(Light3D::PARAM_ENERGY), l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR)); + lightmapper->add_directional_light(light->get_bake_mode() == Light3D::BAKE_STATIC, -xf.basis.get_column(Vector3::AXIS_Z).normalized(), linear_color, energy, l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR)); } else if (Object::cast_to<OmniLight3D>(light)) { OmniLight3D *l = Object::cast_to<OmniLight3D>(light); - lightmapper->add_omni_light(light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, linear_color, l->get_param(Light3D::PARAM_ENERGY), l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR)); + if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + energy *= (1.0 / (Math_PI * 4.0)); + } + lightmapper->add_omni_light(light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, linear_color, energy, l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR)); } else if (Object::cast_to<SpotLight3D>(light)) { SpotLight3D *l = Object::cast_to<SpotLight3D>(light); - lightmapper->add_spot_light(light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, -xf.basis.get_column(Vector3::AXIS_Z).normalized(), linear_color, l->get_param(Light3D::PARAM_ENERGY), l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SPOT_ANGLE), l->get_param(Light3D::PARAM_SPOT_ATTENUATION), l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR)); + if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + energy *= (1.0 / Math_PI); + } + lightmapper->add_spot_light(light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, -xf.basis.get_column(Vector3::AXIS_Z).normalized(), linear_color, energy, l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SPOT_ANGLE), l->get_param(Light3D::PARAM_SPOT_ATTENUATION), l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR)); } } for (int i = 0; i < probes_found.size(); i++) { @@ -1040,7 +1065,15 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa } } - Lightmapper::BakeError bake_err = lightmapper->bake(Lightmapper::BakeQuality(bake_quality), use_denoiser, bounces, bias, max_texture_size, directional, Lightmapper::GenerateProbes(gen_probes), environment_image, environment_transform, _lightmap_bake_step_function, &bsud); + float exposure_normalization = 1.0; + if (camera_attributes.is_valid()) { + exposure_normalization = camera_attributes->get_exposure_multiplier(); + if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + exposure_normalization = camera_attributes->calculate_exposure_normalization(); + } + } + + Lightmapper::BakeError bake_err = lightmapper->bake(Lightmapper::BakeQuality(bake_quality), use_denoiser, bounces, bias, max_texture_size, directional, Lightmapper::GenerateProbes(gen_probes), environment_image, environment_transform, _lightmap_bake_step_function, &bsud, exposure_normalization); if (bake_err == Lightmapper::BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES) { return BAKE_ERROR_MESHES_INVALID; @@ -1214,12 +1247,12 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa /* Obtain the colors from the images, they will be re-created as cubemaps on the server, depending on the driver */ - data->set_capture_data(bounds, interior, points, sh, tetrahedrons, bsp_array); + data->set_capture_data(bounds, interior, points, sh, tetrahedrons, bsp_array, exposure_normalization); /* Compute a BSP tree of the simplices, so it's easy to find the exact one */ } data->set_path(p_image_data_path); - Error err = ResourceSaver::save(p_image_data_path, data); + Error err = ResourceSaver::save(data); if (err != OK) { return BAKE_ERROR_CANT_CREATE_IMAGE; @@ -1394,7 +1427,8 @@ float LightmapGI::get_bias() const { } void LightmapGI::set_max_texture_size(int p_size) { - ERR_FAIL_COND(p_size < 2048); + ERR_FAIL_COND_MSG(p_size < 2048, vformat("The LightmapGI maximum texture size supplied (%d) is too small. The minimum allowed value is 2048.", p_size)); + ERR_FAIL_COND_MSG(p_size > 16384, vformat("The LightmapGI maximum texture size supplied (%d) is too large. The maximum allowed value is 16384.", p_size)); max_texture_size = p_size; } @@ -1410,17 +1444,24 @@ LightmapGI::GenerateProbes LightmapGI::get_generate_probes() const { return gen_probes; } -void LightmapGI::_validate_property(PropertyInfo &property) const { - if (property.name == "environment_custom_sky" && environment_mode != ENVIRONMENT_MODE_CUSTOM_SKY) { - property.usage = PROPERTY_USAGE_NONE; +void LightmapGI::set_camera_attributes(const Ref<CameraAttributes> &p_camera_attributes) { + camera_attributes = p_camera_attributes; +} + +Ref<CameraAttributes> LightmapGI::get_camera_attributes() const { + return camera_attributes; +} + +void LightmapGI::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "environment_custom_sky" && environment_mode != ENVIRONMENT_MODE_CUSTOM_SKY) { + p_property.usage = PROPERTY_USAGE_NONE; } - if (property.name == "environment_custom_color" && environment_mode != ENVIRONMENT_MODE_CUSTOM_COLOR) { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name == "environment_custom_color" && environment_mode != ENVIRONMENT_MODE_CUSTOM_COLOR) { + p_property.usage = PROPERTY_USAGE_NONE; } - if (property.name == "environment_custom_energy" && environment_mode != ENVIRONMENT_MODE_CUSTOM_COLOR && environment_mode != ENVIRONMENT_MODE_CUSTOM_SKY) { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name == "environment_custom_energy" && environment_mode != ENVIRONMENT_MODE_CUSTOM_COLOR && environment_mode != ENVIRONMENT_MODE_CUSTOM_SKY) { + p_property.usage = PROPERTY_USAGE_NONE; } - VisualInstance3D::_validate_property(property); } void LightmapGI::_bind_methods() { @@ -1463,6 +1504,9 @@ void LightmapGI::_bind_methods() { ClassDB::bind_method(D_METHOD("set_directional", "directional"), &LightmapGI::set_directional); ClassDB::bind_method(D_METHOD("is_directional"), &LightmapGI::is_directional); + ClassDB::bind_method(D_METHOD("set_camera_attributes", "camera_attributes"), &LightmapGI::set_camera_attributes); + ClassDB::bind_method(D_METHOD("get_camera_attributes"), &LightmapGI::get_camera_attributes); + // ClassDB::bind_method(D_METHOD("bake", "from_node"), &LightmapGI::bake, DEFVAL(Variant())); ADD_GROUP("Tweaks", ""); @@ -1472,12 +1516,13 @@ void LightmapGI::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "interior"), "set_interior", "is_interior"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_denoiser"), "set_use_denoiser", "is_using_denoiser"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bias", PROPERTY_HINT_RANGE, "0.00001,0.1,0.00001,or_greater"), "set_bias", "get_bias"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "max_texture_size"), "set_max_texture_size", "get_max_texture_size"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "max_texture_size", PROPERTY_HINT_RANGE, "2048,16384,1"), "set_max_texture_size", "get_max_texture_size"); ADD_GROUP("Environment", "environment_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "environment_mode", PROPERTY_HINT_ENUM, "Disabled,Scene,Custom Sky,Custom Color"), "set_environment_mode", "get_environment_mode"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "environment_custom_sky", PROPERTY_HINT_RESOURCE_TYPE, "Sky"), "set_environment_custom_sky", "get_environment_custom_sky"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "environment_custom_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_environment_custom_color", "get_environment_custom_color"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "environment_custom_energy", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_environment_custom_energy", "get_environment_custom_energy"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "camera_attributes", PROPERTY_HINT_RESOURCE_TYPE, "CameraAttributesPractical,CameraAttributesPhysical"), "set_camera_attributes", "get_camera_attributes"); ADD_GROUP("Gen Probes", "generate_probes_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "generate_probes_subdiv", PROPERTY_HINT_ENUM, "Disabled,4,8,16,32"), "set_generate_probes", "get_generate_probes"); ADD_GROUP("Data", ""); diff --git a/scene/3d/lightmap_gi.h b/scene/3d/lightmap_gi.h index f7a23c776a..1d282e92c8 100644 --- a/scene/3d/lightmap_gi.h +++ b/scene/3d/lightmap_gi.h @@ -36,6 +36,9 @@ #include "scene/3d/lightmapper.h" #include "scene/3d/visual_instance_3d.h" +class Sky; +class CameraAttributes; + class LightmapGIData : public Resource { GDCLASS(LightmapGIData, Resource); RES_BASE_EXTENSION("lmbake") @@ -47,6 +50,7 @@ class LightmapGIData : public Resource { RID lightmap; AABB bounds; + float baked_exposure = 1.0; struct User { NodePath path; @@ -83,8 +87,9 @@ public: bool is_using_spherical_harmonics() const; bool is_interior() const; + float get_baked_exposure() const; - void set_capture_data(const AABB &p_bounds, bool p_interior, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree); + void set_capture_data(const AABB &p_bounds, bool p_interior, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree, float p_baked_exposure); PackedVector3Array get_capture_points() const; PackedColorArray get_capture_sh() const; PackedInt32Array get_capture_tetrahedra() const; @@ -137,16 +142,17 @@ public: private: BakeQuality bake_quality = BAKE_QUALITY_MEDIUM; bool use_denoiser = true; - int bounces = 1; + int bounces = 3; float bias = 0.0005; int max_texture_size = 16384; bool interior = false; - EnvironmentMode environment_mode = ENVIRONMENT_MODE_DISABLED; + EnvironmentMode environment_mode = ENVIRONMENT_MODE_SCENE; Ref<Sky> environment_custom_sky; - Color environment_custom_color = Color(0.2, 0.7, 1.0); + Color environment_custom_color = Color(1, 1, 1); float environment_custom_energy = 1.0; bool directional = false; - GenerateProbes gen_probes = GENERATE_PROBES_DISABLED; + GenerateProbes gen_probes = GENERATE_PROBES_SUBDIV_8; + Ref<CameraAttributes> camera_attributes; Ref<LightmapGIData> light_data; @@ -216,7 +222,7 @@ private: void _gen_new_positions_from_octree(const GenProbesOctree *p_cell, float p_cell_size, const Vector<Vector3> &probe_positions, LocalVector<Vector3> &new_probe_positions, HashMap<Vector3i, bool> &positions_used, const AABB &p_bounds); protected: - void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; static void _bind_methods(); void _notification(int p_what); @@ -260,6 +266,9 @@ public: void set_generate_probes(GenerateProbes p_generate_probes); GenerateProbes get_generate_probes() const; + void set_camera_attributes(const Ref<CameraAttributes> &p_camera_attributes); + Ref<CameraAttributes> get_camera_attributes() const; + AABB get_aabb() const override; BakeError bake(Node *p_from_node, String p_image_data_path = "", Lightmapper::BakeStepFunc p_bake_step = nullptr, void *p_bake_userdata = nullptr); @@ -271,4 +280,4 @@ VARIANT_ENUM_CAST(LightmapGI::GenerateProbes); VARIANT_ENUM_CAST(LightmapGI::BakeError); VARIANT_ENUM_CAST(LightmapGI::EnvironmentMode); -#endif // BAKED_LIGHTMAP_H +#endif // LIGHTMAP_GI_H diff --git a/scene/3d/lightmapper.h b/scene/3d/lightmapper.h index 9b973fd6bc..5b5c6cf53a 100644 --- a/scene/3d/lightmapper.h +++ b/scene/3d/lightmapper.h @@ -180,7 +180,7 @@ public: virtual void add_omni_light(bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_size, float p_shadow_blur) = 0; virtual void add_spot_light(bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size, float p_shadow_blur) = 0; virtual void add_probe(const Vector3 &p_position) = 0; - virtual BakeError bake(BakeQuality p_quality, bool p_use_denoiser, int p_bounces, float p_bias, int p_max_texture_size, bool p_bake_sh, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function = nullptr, void *p_step_userdata = nullptr) = 0; + virtual BakeError bake(BakeQuality p_quality, bool p_use_denoiser, int p_bounces, float p_bias, int p_max_texture_size, bool p_bake_sh, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function = nullptr, void *p_step_userdata = nullptr, float p_exposure_normalization = 1.0) = 0; virtual int get_bake_texture_count() const = 0; virtual Ref<Image> get_bake_texture(int p_index) const = 0; diff --git a/scene/3d/position_3d.cpp b/scene/3d/marker_3d.cpp index 7dc1b1ace0..3987172561 100644 --- a/scene/3d/position_3d.cpp +++ b/scene/3d/marker_3d.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* position_3d.cpp */ +/* marker_3d.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "position_3d.h" +#include "marker_3d.h" -Position3D::Position3D() { +Marker3D::Marker3D() { } diff --git a/scene/3d/position_3d.h b/scene/3d/marker_3d.h index 5514399e6e..95caa101c5 100644 --- a/scene/3d/position_3d.h +++ b/scene/3d/marker_3d.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* position_3d.h */ +/* marker_3d.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,16 +28,16 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef POSITION_3D_H -#define POSITION_3D_H +#ifndef MARKER_3D_H +#define MARKER_3D_H #include "scene/3d/node_3d.h" -class Position3D : public Node3D { - GDCLASS(Position3D, Node3D); +class Marker3D : public Node3D { + GDCLASS(Marker3D, Node3D); public: - Position3D(); + Marker3D(); }; -#endif // POSITION_3D_H +#endif // MARKER_3D_H diff --git a/scene/3d/mesh_instance_3d.cpp b/scene/3d/mesh_instance_3d.cpp index 31993f898d..b0503c9c02 100644 --- a/scene/3d/mesh_instance_3d.cpp +++ b/scene/3d/mesh_instance_3d.cpp @@ -115,8 +115,8 @@ void MeshInstance3D::set_mesh(const Ref<Mesh> &p_mesh) { if (mesh.is_valid()) { mesh->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &MeshInstance3D::_mesh_changed)); - _mesh_changed(); set_base(mesh->get_rid()); + _mesh_changed(); } else { blend_shape_tracks.clear(); blend_shape_properties.clear(); @@ -380,16 +380,23 @@ void MeshInstance3D::_mesh_changed() { } } + int surface_count = mesh->get_surface_count(); + for (int surface_index = 0; surface_index < surface_count; ++surface_index) { + if (surface_override_materials[surface_index].is_valid()) { + RS::get_singleton()->instance_set_surface_override_material(get_instance(), surface_index, surface_override_materials[surface_index]->get_rid()); + } + } + update_gizmos(); } -void MeshInstance3D::create_debug_tangents() { +MeshInstance3D *MeshInstance3D::create_debug_tangents_node() { Vector<Vector3> lines; Vector<Color> colors; Ref<Mesh> mesh = get_mesh(); if (!mesh.is_valid()) { - return; + return nullptr; } for (int i = 0; i < mesh->get_surface_count(); i++) { @@ -450,15 +457,23 @@ void MeshInstance3D::create_debug_tangents() { MeshInstance3D *mi = memnew(MeshInstance3D); mi->set_mesh(am); mi->set_name("DebugTangents"); - add_child(mi, true); -#ifdef TOOLS_ENABLED + return mi; + } - if (is_inside_tree() && this == get_tree()->get_edited_scene_root()) { - mi->set_owner(this); - } else { - mi->set_owner(get_owner()); - } -#endif + return nullptr; +} + +void MeshInstance3D::create_debug_tangents() { + MeshInstance3D *mi = create_debug_tangents_node(); + if (!mi) { + return; + } + + add_child(mi, true); + if (is_inside_tree() && this == get_tree()->get_edited_scene_root()) { + mi->set_owner(this); + } else { + mi->set_owner(get_owner()); } } @@ -488,8 +503,6 @@ void MeshInstance3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_blend_shape_value", "blend_shape_idx", "value"), &MeshInstance3D::set_blend_shape_value); ClassDB::bind_method(D_METHOD("create_debug_tangents"), &MeshInstance3D::create_debug_tangents); - ClassDB::set_method_flags("MeshInstance3D", "create_debug_tangents", METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh"); ADD_GROUP("Skeleton", ""); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "skin", PROPERTY_HINT_RESOURCE_TYPE, "Skin"), "set_skin", "get_skin"); diff --git a/scene/3d/mesh_instance_3d.h b/scene/3d/mesh_instance_3d.h index dc9c64fa41..caf7d25616 100644 --- a/scene/3d/mesh_instance_3d.h +++ b/scene/3d/mesh_instance_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef MESH_INSTANCE_H -#define MESH_INSTANCE_H +#ifndef MESH_INSTANCE_3D_H +#define MESH_INSTANCE_3D_H #include "core/templates/local_vector.h" #include "scene/3d/visual_instance_3d.h" @@ -90,6 +90,7 @@ public: Node *create_multiple_convex_collisions_node(); void create_multiple_convex_collisions(); + MeshInstance3D *create_debug_tangents_node(); void create_debug_tangents(); virtual AABB get_aabb() const override; @@ -98,4 +99,4 @@ public: ~MeshInstance3D(); }; -#endif +#endif // MESH_INSTANCE_3D_H diff --git a/scene/3d/multimesh_instance_3d.h b/scene/3d/multimesh_instance_3d.h index 111f1e9c09..2fa8dd965f 100644 --- a/scene/3d/multimesh_instance_3d.h +++ b/scene/3d/multimesh_instance_3d.h @@ -53,4 +53,4 @@ public: ~MultiMeshInstance3D(); }; -#endif // MULTIMESH_INSTANCE_H +#endif // MULTIMESH_INSTANCE_3D_H diff --git a/scene/3d/navigation_agent_3d.cpp b/scene/3d/navigation_agent_3d.cpp index 3752713d6a..39068fe83c 100644 --- a/scene/3d/navigation_agent_3d.cpp +++ b/scene/3d/navigation_agent_3d.cpp @@ -53,8 +53,8 @@ void NavigationAgent3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_ignore_y", "ignore"), &NavigationAgent3D::set_ignore_y); ClassDB::bind_method(D_METHOD("get_ignore_y"), &NavigationAgent3D::get_ignore_y); - ClassDB::bind_method(D_METHOD("set_neighbor_dist", "neighbor_dist"), &NavigationAgent3D::set_neighbor_dist); - ClassDB::bind_method(D_METHOD("get_neighbor_dist"), &NavigationAgent3D::get_neighbor_dist); + ClassDB::bind_method(D_METHOD("set_neighbor_distance", "neighbor_distance"), &NavigationAgent3D::set_neighbor_distance); + ClassDB::bind_method(D_METHOD("get_neighbor_distance"), &NavigationAgent3D::get_neighbor_distance); ClassDB::bind_method(D_METHOD("set_max_neighbors", "max_neighbors"), &NavigationAgent3D::set_max_neighbors); ClassDB::bind_method(D_METHOD("get_max_neighbors"), &NavigationAgent3D::get_max_neighbors); @@ -101,7 +101,7 @@ void NavigationAgent3D::_bind_methods() { ADD_GROUP("Avoidance", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "avoidance_enabled"), "set_avoidance_enabled", "get_avoidance_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.1,100,0.01,suffix:m"), "set_radius", "get_radius"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "neighbor_dist", PROPERTY_HINT_RANGE, "0.1,10000,0.01,suffix:m"), "set_neighbor_dist", "get_neighbor_dist"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "neighbor_distance", PROPERTY_HINT_RANGE, "0.1,10000,0.01,suffix:m"), "set_neighbor_distance", "get_neighbor_distance"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_neighbors", PROPERTY_HINT_RANGE, "1,10000,1"), "set_max_neighbors", "get_max_neighbors"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "time_horizon", PROPERTY_HINT_RANGE, "0.01,100,0.01,suffix:s"), "set_time_horizon", "get_time_horizon"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_speed", PROPERTY_HINT_RANGE, "0.1,10000,0.01,suffix:m/s"), "set_max_speed", "get_max_speed"); @@ -179,12 +179,19 @@ void NavigationAgent3D::_notification(int p_what) { NavigationAgent3D::NavigationAgent3D() { agent = NavigationServer3D::get_singleton()->agent_create(); - set_neighbor_dist(50.0); + set_neighbor_distance(50.0); set_max_neighbors(10); set_time_horizon(5.0); set_radius(1.0); set_max_speed(10.0); set_ignore_y(true); + + // Preallocate query and result objects to improve performance. + navigation_query = Ref<NavigationPathQueryParameters3D>(); + navigation_query.instantiate(); + + navigation_result = Ref<NavigationPathQueryResult3D>(); + navigation_result.instantiate(); } NavigationAgent3D::~NavigationAgent3D() { @@ -291,9 +298,9 @@ void NavigationAgent3D::set_ignore_y(bool p_ignore_y) { NavigationServer3D::get_singleton()->agent_set_ignore_y(agent, ignore_y); } -void NavigationAgent3D::set_neighbor_dist(real_t p_dist) { - neighbor_dist = p_dist; - NavigationServer3D::get_singleton()->agent_set_neighbor_dist(agent, neighbor_dist); +void NavigationAgent3D::set_neighbor_distance(real_t p_distance) { + neighbor_distance = p_distance; + NavigationServer3D::get_singleton()->agent_set_neighbor_distance(agent, neighbor_distance); } void NavigationAgent3D::set_max_neighbors(int p_count) { @@ -330,6 +337,8 @@ Vector3 NavigationAgent3D::get_target_location() const { Vector3 NavigationAgent3D::get_next_location() { update_navigation(); + + const Vector<Vector3> &navigation_path = navigation_result->get_path(); if (navigation_path.size() == 0) { ERR_FAIL_COND_V_MSG(agent_parent == nullptr, Vector3(), "The agent has no parent."); return agent_parent->get_global_transform().origin; @@ -338,6 +347,10 @@ Vector3 NavigationAgent3D::get_next_location() { } } +const Vector<Vector3> &NavigationAgent3D::get_nav_path() const { + return navigation_result->get_path(); +} + real_t NavigationAgent3D::distance_to_target() const { ERR_FAIL_COND_V_MSG(agent_parent == nullptr, 0.0, "The agent has no parent."); return agent_parent->get_global_transform().origin.distance_to(target_location); @@ -358,6 +371,8 @@ bool NavigationAgent3D::is_navigation_finished() { Vector3 NavigationAgent3D::get_final_location() { update_navigation(); + + const Vector<Vector3> &navigation_path = navigation_result->get_path(); if (navigation_path.size() == 0) { return Vector3(); } @@ -383,8 +398,8 @@ void NavigationAgent3D::_avoidance_done(Vector3 p_new_velocity) { emit_signal(SNAME("velocity_computed"), p_new_velocity); } -TypedArray<String> NavigationAgent3D::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray NavigationAgent3D::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (!Object::cast_to<Node3D>(get_parent())) { warnings.push_back(RTR("The NavigationAgent3D can be used only under a Node3D inheriting parent node.")); @@ -406,24 +421,26 @@ void NavigationAgent3D::update_navigation() { update_frame_id = Engine::get_singleton()->get_physics_frames(); - Vector3 o = agent_parent->get_global_transform().origin; + Vector3 origin = agent_parent->get_global_transform().origin; bool reload_path = false; if (NavigationServer3D::get_singleton()->agent_is_map_changed(agent)) { reload_path = true; - } else if (navigation_path.size() == 0) { + } else if (navigation_result->get_path().size() == 0) { reload_path = true; } else { // Check if too far from the navigation path if (nav_path_index > 0) { + const Vector<Vector3> &navigation_path = navigation_result->get_path(); + Vector3 segment[2]; segment[0] = navigation_path[nav_path_index - 1]; segment[1] = navigation_path[nav_path_index]; segment[0].y -= navigation_height_offset; segment[1].y -= navigation_height_offset; - Vector3 p = Geometry3D::get_closest_point_to_segment(o, segment); - if (o.distance_to(p) >= path_max_distance) { + Vector3 p = Geometry3D::get_closest_point_to_segment(origin, segment); + if (origin.distance_to(p) >= path_max_distance) { // To faraway, reload path reload_path = true; } @@ -431,24 +448,31 @@ void NavigationAgent3D::update_navigation() { } if (reload_path) { + navigation_query->set_start_position(origin); + navigation_query->set_target_position(target_location); + navigation_query->set_navigation_layers(navigation_layers); + if (map_override.is_valid()) { - navigation_path = NavigationServer3D::get_singleton()->map_get_path(map_override, o, target_location, true, navigation_layers); + navigation_query->set_map(map_override); } else { - navigation_path = NavigationServer3D::get_singleton()->map_get_path(agent_parent->get_world_3d()->get_navigation_map(), o, target_location, true, navigation_layers); + navigation_query->set_map(agent_parent->get_world_3d()->get_navigation_map()); } + + NavigationServer3D::get_singleton()->query_path(navigation_query, navigation_result); navigation_finished = false; nav_path_index = 0; emit_signal(SNAME("path_changed")); } - if (navigation_path.size() == 0) { + if (navigation_result->get_path().size() == 0) { return; } // Check if we can advance the navigation path if (navigation_finished == false) { // Advances to the next far away location. - while (o.distance_to(navigation_path[nav_path_index] - Vector3(0, navigation_height_offset, 0)) < path_desired_distance) { + const Vector<Vector3> &navigation_path = navigation_result->get_path(); + while (origin.distance_to(navigation_path[nav_path_index] - Vector3(0, navigation_height_offset, 0)) < path_desired_distance) { nav_path_index += 1; if (nav_path_index == navigation_path.size()) { _check_distance_to_target(); @@ -462,7 +486,7 @@ void NavigationAgent3D::update_navigation() { } void NavigationAgent3D::_request_repath() { - navigation_path.clear(); + navigation_result->reset(); target_reached = false; navigation_finished = false; update_frame_id = 0; diff --git a/scene/3d/navigation_agent_3d.h b/scene/3d/navigation_agent_3d.h index 0a00d769c3..90ceab0242 100644 --- a/scene/3d/navigation_agent_3d.h +++ b/scene/3d/navigation_agent_3d.h @@ -28,12 +28,14 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef NAVIGATION_AGENT_H -#define NAVIGATION_AGENT_H +#ifndef NAVIGATION_AGENT_3D_H +#define NAVIGATION_AGENT_3D_H #include "scene/main/node.h" class Node3D; +class NavigationPathQueryParameters3D; +class NavigationPathQueryResult3D; class NavigationAgent3D : public Node { GDCLASS(NavigationAgent3D, Node); @@ -52,7 +54,7 @@ class NavigationAgent3D : public Node { real_t radius = 0.0; real_t navigation_height_offset = 0.0; bool ignore_y = false; - real_t neighbor_dist = 0.0; + real_t neighbor_distance = 0.0; int max_neighbors = 0; real_t time_horizon = 0.0; real_t max_speed = 0.0; @@ -60,7 +62,8 @@ class NavigationAgent3D : public Node { real_t path_max_distance = 3.0; Vector3 target_location; - Vector<Vector3> navigation_path; + Ref<NavigationPathQueryParameters3D> navigation_query; + Ref<NavigationPathQueryResult3D> navigation_result; int nav_path_index = 0; bool velocity_submitted = false; Vector3 prev_safe_velocity; @@ -122,9 +125,9 @@ public: return ignore_y; } - void set_neighbor_dist(real_t p_dist); - real_t get_neighbor_dist() const { - return neighbor_dist; + void set_neighbor_distance(real_t p_distance); + real_t get_neighbor_distance() const { + return neighbor_distance; } void set_max_neighbors(int p_count); @@ -150,9 +153,7 @@ public: Vector3 get_next_location(); - Vector<Vector3> get_nav_path() const { - return navigation_path; - } + const Vector<Vector3> &get_nav_path() const; int get_nav_path_index() const { return nav_path_index; @@ -167,7 +168,7 @@ public: void set_velocity(Vector3 p_velocity); void _avoidance_done(Vector3 p_new_velocity); - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; private: void update_navigation(); @@ -175,4 +176,4 @@ private: void _check_distance_to_target(); }; -#endif +#endif // NAVIGATION_AGENT_3D_H diff --git a/scene/3d/navigation_link_3d.cpp b/scene/3d/navigation_link_3d.cpp new file mode 100644 index 0000000000..78fe4754ea --- /dev/null +++ b/scene/3d/navigation_link_3d.cpp @@ -0,0 +1,389 @@ +/*************************************************************************/ +/* navigation_link_3d.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "navigation_link_3d.h" + +#include "mesh_instance_3d.h" +#include "servers/navigation_server_3d.h" + +#ifdef DEBUG_ENABLED +void NavigationLink3D::_update_debug_mesh() { + if (!is_inside_tree()) { + return; + } + + if (Engine::get_singleton()->is_editor_hint()) { + // don't update inside Editor as node 3d gizmo takes care of this + // as collisions and selections for Editor Viewport need to be updated + return; + } + + if (!NavigationServer3D::get_singleton()->get_debug_enabled()) { + if (debug_instance.is_valid()) { + RS::get_singleton()->instance_set_visible(debug_instance, false); + } + return; + } + + if (!debug_instance.is_valid()) { + debug_instance = RenderingServer::get_singleton()->instance_create(); + } + + if (!debug_mesh.is_valid()) { + debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + } + + RID nav_map = get_world_3d()->get_navigation_map(); + real_t search_radius = NavigationServer3D::get_singleton()->map_get_link_connection_radius(nav_map); + Vector3 up_vector = NavigationServer3D::get_singleton()->map_get_up(nav_map); + Vector3::Axis up_axis = up_vector.max_axis_index(); + + debug_mesh->clear_surfaces(); + + Vector<Vector3> lines; + + // Draw line between the points. + lines.push_back(start_location); + lines.push_back(end_location); + + // Draw start location search radius + for (int i = 0; i < 30; i++) { + // Create a circle + const float ra = Math::deg_to_rad((float)(i * 12)); + const float rb = Math::deg_to_rad((float)((i + 1) * 12)); + const Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * search_radius; + const Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * search_radius; + + // Draw axis-aligned circle + switch (up_axis) { + case Vector3::AXIS_X: + lines.append(start_location + Vector3(0, a.x, a.y)); + lines.append(start_location + Vector3(0, b.x, b.y)); + break; + case Vector3::AXIS_Y: + lines.append(start_location + Vector3(a.x, 0, a.y)); + lines.append(start_location + Vector3(b.x, 0, b.y)); + break; + case Vector3::AXIS_Z: + lines.append(start_location + Vector3(a.x, a.y, 0)); + lines.append(start_location + Vector3(b.x, b.y, 0)); + break; + } + } + + // Draw end location search radius + for (int i = 0; i < 30; i++) { + // Create a circle + const float ra = Math::deg_to_rad((float)(i * 12)); + const float rb = Math::deg_to_rad((float)((i + 1) * 12)); + const Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * search_radius; + const Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * search_radius; + + // Draw axis-aligned circle + switch (up_axis) { + case Vector3::AXIS_X: + lines.append(end_location + Vector3(0, a.x, a.y)); + lines.append(end_location + Vector3(0, b.x, b.y)); + break; + case Vector3::AXIS_Y: + lines.append(end_location + Vector3(a.x, 0, a.y)); + lines.append(end_location + Vector3(b.x, 0, b.y)); + break; + case Vector3::AXIS_Z: + lines.append(end_location + Vector3(a.x, a.y, 0)); + lines.append(end_location + Vector3(b.x, b.y, 0)); + break; + } + } + + Array mesh_array; + mesh_array.resize(Mesh::ARRAY_MAX); + mesh_array[Mesh::ARRAY_VERTEX] = lines; + + debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, mesh_array); + + RS::get_singleton()->instance_set_base(debug_instance, debug_mesh->get_rid()); + RS::get_singleton()->instance_set_scenario(debug_instance, get_world_3d()->get_scenario()); + RS::get_singleton()->instance_set_visible(debug_instance, is_visible_in_tree()); + + Ref<StandardMaterial3D> link_material = NavigationServer3D::get_singleton_mut()->get_debug_navigation_link_connections_material(); + Ref<StandardMaterial3D> disabled_link_material = NavigationServer3D::get_singleton_mut()->get_debug_navigation_link_connections_disabled_material(); + + if (enabled) { + RS::get_singleton()->instance_set_surface_override_material(debug_instance, 0, link_material->get_rid()); + } else { + RS::get_singleton()->instance_set_surface_override_material(debug_instance, 0, disabled_link_material->get_rid()); + } +} +#endif // DEBUG_ENABLED + +void NavigationLink3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &NavigationLink3D::set_enabled); + ClassDB::bind_method(D_METHOD("is_enabled"), &NavigationLink3D::is_enabled); + + ClassDB::bind_method(D_METHOD("set_bidirectional", "bidirectional"), &NavigationLink3D::set_bidirectional); + ClassDB::bind_method(D_METHOD("is_bidirectional"), &NavigationLink3D::is_bidirectional); + + ClassDB::bind_method(D_METHOD("set_navigation_layers", "navigation_layers"), &NavigationLink3D::set_navigation_layers); + ClassDB::bind_method(D_METHOD("get_navigation_layers"), &NavigationLink3D::get_navigation_layers); + + ClassDB::bind_method(D_METHOD("set_navigation_layer_value", "layer_number", "value"), &NavigationLink3D::set_navigation_layer_value); + ClassDB::bind_method(D_METHOD("get_navigation_layer_value", "layer_number"), &NavigationLink3D::get_navigation_layer_value); + + ClassDB::bind_method(D_METHOD("set_start_location", "location"), &NavigationLink3D::set_start_location); + ClassDB::bind_method(D_METHOD("get_start_location"), &NavigationLink3D::get_start_location); + + ClassDB::bind_method(D_METHOD("set_end_location", "location"), &NavigationLink3D::set_end_location); + ClassDB::bind_method(D_METHOD("get_end_location"), &NavigationLink3D::get_end_location); + + ClassDB::bind_method(D_METHOD("set_enter_cost", "enter_cost"), &NavigationLink3D::set_enter_cost); + ClassDB::bind_method(D_METHOD("get_enter_cost"), &NavigationLink3D::get_enter_cost); + + ClassDB::bind_method(D_METHOD("set_travel_cost", "travel_cost"), &NavigationLink3D::set_travel_cost); + ClassDB::bind_method(D_METHOD("get_travel_cost"), &NavigationLink3D::get_travel_cost); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bidirectional"), "set_bidirectional", "is_bidirectional"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_3D_NAVIGATION), "set_navigation_layers", "get_navigation_layers"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "start_location"), "set_start_location", "get_start_location"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "end_location"), "set_end_location", "get_end_location"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "enter_cost"), "set_enter_cost", "get_enter_cost"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "travel_cost"), "set_travel_cost", "get_travel_cost"); +} + +void NavigationLink3D::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + if (enabled) { + NavigationServer3D::get_singleton()->link_set_map(link, get_world_3d()->get_navigation_map()); + + // Update global positions for the link. + Transform3D gt = get_global_transform(); + NavigationServer3D::get_singleton()->link_set_start_location(link, gt.xform(start_location)); + NavigationServer3D::get_singleton()->link_set_end_location(link, gt.xform(end_location)); + } + +#ifdef DEBUG_ENABLED + _update_debug_mesh(); +#endif // DEBUG_ENABLED + } break; + case NOTIFICATION_TRANSFORM_CHANGED: { + // Update global positions for the link. + Transform3D gt = get_global_transform(); + NavigationServer3D::get_singleton()->link_set_start_location(link, gt.xform(start_location)); + NavigationServer3D::get_singleton()->link_set_end_location(link, gt.xform(end_location)); + +#ifdef DEBUG_ENABLED + if (is_inside_tree() && debug_instance.is_valid()) { + RS::get_singleton()->instance_set_transform(debug_instance, get_global_transform()); + } +#endif // DEBUG_ENABLED + } break; + case NOTIFICATION_EXIT_TREE: { + NavigationServer3D::get_singleton()->link_set_map(link, RID()); + +#ifdef DEBUG_ENABLED + if (debug_instance.is_valid()) { + RS::get_singleton()->instance_set_scenario(debug_instance, RID()); + RS::get_singleton()->instance_set_visible(debug_instance, false); + } +#endif // DEBUG_ENABLED + } break; + } +} + +NavigationLink3D::NavigationLink3D() { + link = NavigationServer3D::get_singleton()->link_create(); + set_notify_transform(true); +} + +NavigationLink3D::~NavigationLink3D() { + NavigationServer3D::get_singleton()->free(link); + link = RID(); + +#ifdef DEBUG_ENABLED + if (debug_instance.is_valid()) { + RenderingServer::get_singleton()->free(debug_instance); + } + if (debug_mesh.is_valid()) { + RenderingServer::get_singleton()->free(debug_mesh->get_rid()); + } +#endif // DEBUG_ENABLED +} + +void NavigationLink3D::set_enabled(bool p_enabled) { + if (enabled == p_enabled) { + return; + } + + enabled = p_enabled; + + if (!is_inside_tree()) { + return; + } + + if (enabled) { + NavigationServer3D::get_singleton()->link_set_map(link, get_world_3d()->get_navigation_map()); + } else { + NavigationServer3D::get_singleton()->link_set_map(link, RID()); + } + +#ifdef DEBUG_ENABLED + if (debug_instance.is_valid() && debug_mesh.is_valid()) { + if (enabled) { + Ref<StandardMaterial3D> link_material = NavigationServer3D::get_singleton_mut()->get_debug_navigation_link_connections_material(); + RS::get_singleton()->instance_set_surface_override_material(debug_instance, 0, link_material->get_rid()); + } else { + Ref<StandardMaterial3D> disabled_link_material = NavigationServer3D::get_singleton_mut()->get_debug_navigation_link_connections_disabled_material(); + RS::get_singleton()->instance_set_surface_override_material(debug_instance, 0, disabled_link_material->get_rid()); + } + } +#endif // DEBUG_ENABLED + + update_gizmos(); +} + +void NavigationLink3D::set_bidirectional(bool p_bidirectional) { + if (bidirectional == p_bidirectional) { + return; + } + + bidirectional = p_bidirectional; + + NavigationServer3D::get_singleton()->link_set_bidirectional(link, bidirectional); +} + +void NavigationLink3D::set_navigation_layers(uint32_t p_navigation_layers) { + if (navigation_layers == p_navigation_layers) { + return; + } + + navigation_layers = p_navigation_layers; + + NavigationServer3D::get_singleton()->link_set_navigation_layers(link, navigation_layers); +} + +void NavigationLink3D::set_navigation_layer_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Navigation layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Navigation layer number must be between 1 and 32 inclusive."); + + uint32_t _navigation_layers = get_navigation_layers(); + + if (p_value) { + _navigation_layers |= 1 << (p_layer_number - 1); + } else { + _navigation_layers &= ~(1 << (p_layer_number - 1)); + } + + set_navigation_layers(_navigation_layers); +} + +bool NavigationLink3D::get_navigation_layer_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Navigation layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Navigation layer number must be between 1 and 32 inclusive."); + + return get_navigation_layers() & (1 << (p_layer_number - 1)); +} + +void NavigationLink3D::set_start_location(Vector3 p_location) { + if (start_location.is_equal_approx(p_location)) { + return; + } + + start_location = p_location; + + if (!is_inside_tree()) { + return; + } + + Transform3D gt = get_global_transform(); + NavigationServer3D::get_singleton()->link_set_start_location(link, gt.xform(start_location)); + +#ifdef DEBUG_ENABLED + _update_debug_mesh(); +#endif // DEBUG_ENABLED + + update_gizmos(); + update_configuration_warnings(); +} + +void NavigationLink3D::set_end_location(Vector3 p_location) { + if (end_location.is_equal_approx(p_location)) { + return; + } + + end_location = p_location; + + if (!is_inside_tree()) { + return; + } + + Transform3D gt = get_global_transform(); + NavigationServer3D::get_singleton()->link_set_end_location(link, gt.xform(end_location)); + +#ifdef DEBUG_ENABLED + _update_debug_mesh(); +#endif // DEBUG_ENABLED + + update_gizmos(); + update_configuration_warnings(); +} + +void NavigationLink3D::set_enter_cost(real_t p_enter_cost) { + ERR_FAIL_COND_MSG(p_enter_cost < 0.0, "The enter_cost must be positive."); + if (Math::is_equal_approx(enter_cost, p_enter_cost)) { + return; + } + + enter_cost = p_enter_cost; + + NavigationServer3D::get_singleton()->link_set_enter_cost(link, enter_cost); +} + +void NavigationLink3D::set_travel_cost(real_t p_travel_cost) { + ERR_FAIL_COND_MSG(p_travel_cost < 0.0, "The travel_cost must be positive."); + if (Math::is_equal_approx(travel_cost, p_travel_cost)) { + return; + } + + travel_cost = p_travel_cost; + + NavigationServer3D::get_singleton()->link_set_travel_cost(link, travel_cost); +} + +PackedStringArray NavigationLink3D::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); + + if (start_location.is_equal_approx(end_location)) { + warnings.push_back(RTR("NavigationLink3D start location should be different than the end location to be useful.")); + } + + return warnings; +} diff --git a/scene/resources/scene_replication_config.h b/scene/3d/navigation_link_3d.h index ab3658d2a7..1fc03546fa 100644 --- a/scene/resources/scene_replication_config.h +++ b/scene/3d/navigation_link_3d.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* scene_replication_config.h */ +/* navigation_link_3d.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,64 +28,63 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SCENE_REPLICATION_CONFIG_H -#define SCENE_REPLICATION_CONFIG_H +#ifndef NAVIGATION_LINK_3D_H +#define NAVIGATION_LINK_3D_H -#include "core/io/resource.h" +#include "scene/3d/node_3d.h" -#include "core/variant/typed_array.h" +class NavigationLink3D : public Node3D { + GDCLASS(NavigationLink3D, Node3D); -class SceneReplicationConfig : public Resource { - GDCLASS(SceneReplicationConfig, Resource); - OBJ_SAVE_TYPE(SceneReplicationConfig); - RES_BASE_EXTENSION("repl"); + bool enabled = true; + RID link = RID(); + bool bidirectional = true; + uint32_t navigation_layers = 1; + Vector3 end_location = Vector3(); + Vector3 start_location = Vector3(); + real_t enter_cost = 0.0; + real_t travel_cost = 1.0; -private: - struct ReplicationProperty { - NodePath name; - bool spawn = true; - bool sync = true; +#ifdef DEBUG_ENABLED + RID debug_instance; + Ref<ArrayMesh> debug_mesh; - bool operator==(const ReplicationProperty &p_to) { - return name == p_to.name; - } + void _update_debug_mesh(); +#endif // DEBUG_ENABLED - ReplicationProperty() {} +protected: + static void _bind_methods(); + void _notification(int p_what); - ReplicationProperty(const NodePath &p_name) { - name = p_name; - } - }; +public: + NavigationLink3D(); + ~NavigationLink3D(); - List<ReplicationProperty> properties; - List<NodePath> spawn_props; - List<NodePath> sync_props; + void set_enabled(bool p_enabled); + bool is_enabled() const { return enabled; } -protected: - static void _bind_methods(); + void set_bidirectional(bool p_bidirectional); + bool is_bidirectional() const { return bidirectional; } - bool _set(const StringName &p_name, const Variant &p_value); - bool _get(const StringName &p_name, Variant &r_ret) const; - void _get_property_list(List<PropertyInfo> *p_list) const; + void set_navigation_layers(uint32_t p_navigation_layers); + uint32_t get_navigation_layers() const { return navigation_layers; } -public: - TypedArray<NodePath> get_properties() const; + void set_navigation_layer_value(int p_layer_number, bool p_value); + bool get_navigation_layer_value(int p_layer_number) const; - void add_property(const NodePath &p_path, int p_index = -1); - void remove_property(const NodePath &p_path); - bool has_property(const NodePath &p_path) const; + void set_start_location(Vector3 p_location); + Vector3 get_start_location() const { return start_location; } - int property_get_index(const NodePath &p_path) const; - bool property_get_spawn(const NodePath &p_path); - void property_set_spawn(const NodePath &p_path, bool p_enabled); + void set_end_location(Vector3 p_location); + Vector3 get_end_location() const { return end_location; } - bool property_get_sync(const NodePath &p_path); - void property_set_sync(const NodePath &p_path, bool p_enabled); + void set_enter_cost(real_t p_enter_cost); + real_t get_enter_cost() const { return enter_cost; } - const List<NodePath> &get_spawn_properties() { return spawn_props; } - const List<NodePath> &get_sync_properties() { return sync_props; } + void set_travel_cost(real_t p_travel_cost); + real_t get_travel_cost() const { return travel_cost; } - SceneReplicationConfig() {} + PackedStringArray get_configuration_warnings() const override; }; -#endif // SCENE_REPLICATION_CONFIG_H +#endif // NAVIGATION_LINK_3D_H diff --git a/scene/3d/navigation_obstacle_3d.cpp b/scene/3d/navigation_obstacle_3d.cpp index c6eda1f9cd..07d8cd9289 100644 --- a/scene/3d/navigation_obstacle_3d.cpp +++ b/scene/3d/navigation_obstacle_3d.cpp @@ -37,6 +37,9 @@ void NavigationObstacle3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_rid"), &NavigationObstacle3D::get_rid); + ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &NavigationObstacle3D::set_navigation_map); + ClassDB::bind_method(D_METHOD("get_navigation_map"), &NavigationObstacle3D::get_navigation_map); + ClassDB::bind_method(D_METHOD("set_estimate_radius", "estimate_radius"), &NavigationObstacle3D::set_estimate_radius); ClassDB::bind_method(D_METHOD("is_radius_estimated"), &NavigationObstacle3D::is_radius_estimated); ClassDB::bind_method(D_METHOD("set_radius", "radius"), &NavigationObstacle3D::set_radius); @@ -56,28 +59,26 @@ void NavigationObstacle3D::_validate_property(PropertyInfo &p_property) const { void NavigationObstacle3D::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: { - parent_node3d = Object::cast_to<Node3D>(get_parent()); - reevaluate_agent_radius(); - if (parent_node3d != nullptr) { - // place agent on navigation map first or else the RVO agent callback creation fails silently later - NavigationServer3D::get_singleton()->agent_set_map(get_rid(), parent_node3d->get_world_3d()->get_navigation_map()); - } + case NOTIFICATION_POST_ENTER_TREE: { + set_agent_parent(get_parent()); set_physics_process_internal(true); } break; case NOTIFICATION_EXIT_TREE: { - parent_node3d = nullptr; + set_agent_parent(nullptr); set_physics_process_internal(false); } break; case NOTIFICATION_PARENTED: { - parent_node3d = Object::cast_to<Node3D>(get_parent()); - reevaluate_agent_radius(); + if (is_inside_tree() && (get_parent() != parent_node3d)) { + set_agent_parent(get_parent()); + set_physics_process_internal(true); + } } break; case NOTIFICATION_UNPARENTED: { - parent_node3d = nullptr; + set_agent_parent(nullptr); + set_physics_process_internal(false); } break; case NOTIFICATION_PAUSED: { @@ -125,15 +126,15 @@ NavigationObstacle3D::~NavigationObstacle3D() { agent = RID(); // Pointless } -TypedArray<String> NavigationObstacle3D::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray NavigationObstacle3D::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (!Object::cast_to<Node3D>(get_parent())) { warnings.push_back(RTR("The NavigationObstacle3D only serves to provide collision avoidance to a Node3D inheriting parent object.")); } if (Object::cast_to<StaticBody3D>(get_parent())) { - warnings.push_back(RTR("The NavigationObstacle3D is intended for constantly moving bodies like CharacterBody3D or RigidDynamicBody3D as it creates only an RVO avoidance radius and does not follow scene geometry exactly." + warnings.push_back(RTR("The NavigationObstacle3D is intended for constantly moving bodies like CharacterBody3D or RigidBody3D as it creates only an RVO avoidance radius and does not follow scene geometry exactly." "\nNot constantly moving or complete static objects should be (re)baked to a NavigationMesh so agents can not only avoid them but also move along those objects outline at high detail")); } @@ -141,7 +142,7 @@ TypedArray<String> NavigationObstacle3D::get_configuration_warnings() const { } void NavigationObstacle3D::initialize_agent() { - NavigationServer3D::get_singleton()->agent_set_neighbor_dist(agent, 0.0); + NavigationServer3D::get_singleton()->agent_set_neighbor_distance(agent, 0.0); NavigationServer3D::get_singleton()->agent_set_max_neighbors(agent, 0); NavigationServer3D::get_singleton()->agent_set_time_horizon(agent, 0.0); NavigationServer3D::get_singleton()->agent_set_max_speed(agent, 0.0); @@ -189,6 +190,35 @@ real_t NavigationObstacle3D::estimate_agent_radius() const { return 1.0; // Never a 0 radius } +void NavigationObstacle3D::set_agent_parent(Node *p_agent_parent) { + if (Object::cast_to<Node3D>(p_agent_parent) != nullptr) { + parent_node3d = Object::cast_to<Node3D>(p_agent_parent); + if (map_override.is_valid()) { + NavigationServer3D::get_singleton()->agent_set_map(get_rid(), map_override); + } else { + NavigationServer3D::get_singleton()->agent_set_map(get_rid(), parent_node3d->get_world_3d()->get_navigation_map()); + } + reevaluate_agent_radius(); + } else { + parent_node3d = nullptr; + NavigationServer3D::get_singleton()->agent_set_map(get_rid(), RID()); + } +} + +void NavigationObstacle3D::set_navigation_map(RID p_navigation_map) { + map_override = p_navigation_map; + NavigationServer3D::get_singleton()->agent_set_map(agent, map_override); +} + +RID NavigationObstacle3D::get_navigation_map() const { + if (map_override.is_valid()) { + return map_override; + } else if (parent_node3d != nullptr) { + return parent_node3d->get_world_3d()->get_navigation_map(); + } + return RID(); +} + void NavigationObstacle3D::set_estimate_radius(bool p_estimate_radius) { estimate_radius = p_estimate_radius; notify_property_list_changed(); diff --git a/scene/3d/navigation_obstacle_3d.h b/scene/3d/navigation_obstacle_3d.h index 0ddde64c0e..f242d2f99e 100644 --- a/scene/3d/navigation_obstacle_3d.h +++ b/scene/3d/navigation_obstacle_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef NAVIGATION_OBSTACLE_H -#define NAVIGATION_OBSTACLE_H +#ifndef NAVIGATION_OBSTACLE_3D_H +#define NAVIGATION_OBSTACLE_3D_H #include "scene/3d/node_3d.h" @@ -37,15 +37,17 @@ class NavigationObstacle3D : public Node { GDCLASS(NavigationObstacle3D, Node); Node3D *parent_node3d = nullptr; + RID agent; RID map_before_pause; + RID map_override; bool estimate_radius = true; real_t radius = 1.0; protected: static void _bind_methods(); - void _validate_property(PropertyInfo &p_property) const override; + void _validate_property(PropertyInfo &p_property) const; void _notification(int p_what); public: @@ -56,6 +58,11 @@ public: return agent; } + void set_agent_parent(Node *p_agent_parent); + + void set_navigation_map(RID p_navigation_map); + RID get_navigation_map() const; + void set_estimate_radius(bool p_estimate_radius); bool is_radius_estimated() const { return estimate_radius; @@ -65,7 +72,7 @@ public: return radius; } - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; private: void initialize_agent(); @@ -73,4 +80,4 @@ private: real_t estimate_agent_radius() const; }; -#endif +#endif // NAVIGATION_OBSTACLE_3D_H diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp index 2a8149c6f6..06182d921c 100644 --- a/scene/3d/navigation_region_3d.cpp +++ b/scene/3d/navigation_region_3d.cpp @@ -37,6 +37,7 @@ void NavigationRegion3D::set_enabled(bool p_enabled) { if (enabled == p_enabled) { return; } + enabled = p_enabled; if (!is_inside_tree()) { @@ -49,14 +50,29 @@ void NavigationRegion3D::set_enabled(bool p_enabled) { NavigationServer3D::get_singleton()->region_set_map(region, get_world_3d()->get_navigation_map()); } - if (debug_view) { - MeshInstance3D *dm = Object::cast_to<MeshInstance3D>(debug_view); - if (is_enabled()) { - dm->set_material_override(get_tree()->get_debug_navigation_material()); +#ifdef DEBUG_ENABLED + if (debug_instance.is_valid()) { + if (!is_enabled()) { + if (debug_mesh.is_valid()) { + if (debug_mesh->get_surface_count() > 0) { + RS::get_singleton()->instance_set_surface_override_material(debug_instance, 0, NavigationServer3D::get_singleton_mut()->get_debug_navigation_geometry_face_disabled_material()->get_rid()); + } + if (debug_mesh->get_surface_count() > 1) { + RS::get_singleton()->instance_set_surface_override_material(debug_instance, 1, NavigationServer3D::get_singleton_mut()->get_debug_navigation_geometry_edge_disabled_material()->get_rid()); + } + } } else { - dm->set_material_override(get_tree()->get_debug_navigation_disabled_material()); + if (debug_mesh.is_valid()) { + if (debug_mesh->get_surface_count() > 0) { + RS::get_singleton()->instance_set_surface_override_material(debug_instance, 0, RID()); + } + if (debug_mesh->get_surface_count() > 1) { + RS::get_singleton()->instance_set_surface_override_material(debug_instance, 1, RID()); + } + } } } +#endif // DEBUG_ENABLED update_gizmos(); } @@ -66,35 +82,50 @@ bool NavigationRegion3D::is_enabled() const { } void NavigationRegion3D::set_navigation_layers(uint32_t p_navigation_layers) { - NavigationServer3D::get_singleton()->region_set_navigation_layers(region, p_navigation_layers); + if (navigation_layers == p_navigation_layers) { + return; + } + + navigation_layers = p_navigation_layers; + + NavigationServer3D::get_singleton()->region_set_navigation_layers(region, navigation_layers); } uint32_t NavigationRegion3D::get_navigation_layers() const { - return NavigationServer3D::get_singleton()->region_get_navigation_layers(region); + return navigation_layers; } void NavigationRegion3D::set_navigation_layer_value(int p_layer_number, bool p_value) { ERR_FAIL_COND_MSG(p_layer_number < 1, "Navigation layer number must be between 1 and 32 inclusive."); ERR_FAIL_COND_MSG(p_layer_number > 32, "Navigation layer number must be between 1 and 32 inclusive."); + uint32_t _navigation_layers = get_navigation_layers(); + if (p_value) { _navigation_layers |= 1 << (p_layer_number - 1); } else { _navigation_layers &= ~(1 << (p_layer_number - 1)); } + set_navigation_layers(_navigation_layers); } bool NavigationRegion3D::get_navigation_layer_value(int p_layer_number) const { ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Navigation layer number must be between 1 and 32 inclusive."); ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Navigation layer number must be between 1 and 32 inclusive."); + return get_navigation_layers() & (1 << (p_layer_number - 1)); } void NavigationRegion3D::set_enter_cost(real_t p_enter_cost) { ERR_FAIL_COND_MSG(p_enter_cost < 0.0, "The enter_cost must be positive."); - enter_cost = MAX(p_enter_cost, 0.0); - NavigationServer3D::get_singleton()->region_set_enter_cost(region, p_enter_cost); + if (Math::is_equal_approx(enter_cost, p_enter_cost)) { + return; + } + + enter_cost = p_enter_cost; + + NavigationServer3D::get_singleton()->region_set_enter_cost(region, enter_cost); } real_t NavigationRegion3D::get_enter_cost() const { @@ -103,8 +134,13 @@ real_t NavigationRegion3D::get_enter_cost() const { void NavigationRegion3D::set_travel_cost(real_t p_travel_cost) { ERR_FAIL_COND_MSG(p_travel_cost < 0.0, "The travel_cost must be positive."); - travel_cost = MAX(p_travel_cost, 0.0); - NavigationServer3D::get_singleton()->region_set_enter_cost(region, travel_cost); + if (Math::is_equal_approx(travel_cost, p_travel_cost)) { + return; + } + + travel_cost = p_travel_cost; + + NavigationServer3D::get_singleton()->region_set_travel_cost(region, travel_cost); } real_t NavigationRegion3D::get_travel_cost() const { @@ -115,8 +151,6 @@ RID NavigationRegion3D::get_region_rid() const { return region; } -///////////////////////////// - void NavigationRegion3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { @@ -124,30 +158,36 @@ void NavigationRegion3D::_notification(int p_what) { NavigationServer3D::get_singleton()->region_set_map(region, get_world_3d()->get_navigation_map()); } - if (navmesh.is_valid() && get_tree()->is_debugging_navigation_hint()) { - MeshInstance3D *dm = memnew(MeshInstance3D); - dm->set_mesh(navmesh->get_debug_mesh()); - if (is_enabled()) { - dm->set_material_override(get_tree()->get_debug_navigation_material()); - } else { - dm->set_material_override(get_tree()->get_debug_navigation_disabled_material()); - } - add_child(dm); - debug_view = dm; +#ifdef DEBUG_ENABLED + if (NavigationServer3D::get_singleton()->get_debug_enabled()) { + _update_debug_mesh(); } +#endif // DEBUG_ENABLED + } break; case NOTIFICATION_TRANSFORM_CHANGED: { NavigationServer3D::get_singleton()->region_set_transform(region, get_global_transform()); + +#ifdef DEBUG_ENABLED + if (is_inside_tree() && debug_instance.is_valid()) { + RS::get_singleton()->instance_set_transform(debug_instance, get_global_transform()); + } +#endif // DEBUG_ENABLED + } break; case NOTIFICATION_EXIT_TREE: { NavigationServer3D::get_singleton()->region_set_map(region, RID()); - if (debug_view) { - debug_view->queue_delete(); - debug_view = nullptr; +#ifdef DEBUG_ENABLED + if (debug_instance.is_valid()) { + RS::get_singleton()->instance_set_visible(debug_instance, false); } + if (debug_edge_connections_instance.is_valid()) { + RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, false); + } +#endif // DEBUG_ENABLED } break; } } @@ -169,20 +209,21 @@ void NavigationRegion3D::set_navigation_mesh(const Ref<NavigationMesh> &p_navmes NavigationServer3D::get_singleton()->region_set_navmesh(region, p_navmesh); - if (debug_view == nullptr && is_inside_tree() && navmesh.is_valid() && get_tree()->is_debugging_navigation_hint()) { - MeshInstance3D *dm = memnew(MeshInstance3D); - dm->set_mesh(navmesh->get_debug_mesh()); - if (is_enabled()) { - dm->set_material_override(get_tree()->get_debug_navigation_material()); +#ifdef DEBUG_ENABLED + if (is_inside_tree() && NavigationServer3D::get_singleton()->get_debug_enabled()) { + if (navmesh.is_valid()) { + _update_debug_mesh(); + _update_debug_edge_connections_mesh(); } else { - dm->set_material_override(get_tree()->get_debug_navigation_disabled_material()); + if (debug_instance.is_valid()) { + RS::get_singleton()->instance_set_visible(debug_instance, false); + } + if (debug_edge_connections_instance.is_valid()) { + RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, false); + } } - add_child(dm); - debug_view = dm; - } - if (debug_view && navmesh.is_valid()) { - Object::cast_to<MeshInstance3D>(debug_view)->set_mesh(navmesh->get_debug_mesh()); } +#endif // DEBUG_ENABLED emit_signal(SNAME("navigation_mesh_changed")); @@ -220,12 +261,7 @@ void NavigationRegion3D::bake_navigation_mesh(bool p_on_thread) { BakeThreadsArgs *args = memnew(BakeThreadsArgs); args->nav_region = this; - if (p_on_thread && !OS::get_singleton()->can_use_threads()) { - WARN_PRINT("NavigationMesh bake 'on_thread' will be disabled as the current OS does not support multiple threads." - "\nAs a fallback the navigation mesh will bake on the main thread which can cause framerate issues."); - } - - if (p_on_thread && OS::get_singleton()->can_use_threads()) { + if (p_on_thread) { bake_thread.start(_bake_navigation_mesh, args); } else { _bake_navigation_mesh(args); @@ -238,8 +274,8 @@ void NavigationRegion3D::_bake_finished(Ref<NavigationMesh> p_nav_mesh) { emit_signal(SNAME("bake_finished")); } -TypedArray<String> NavigationRegion3D::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray NavigationRegion3D::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (is_visible_in_tree() && is_inside_tree()) { if (!navmesh.is_valid()) { @@ -287,13 +323,31 @@ void NavigationRegion3D::_bind_methods() { void NavigationRegion3D::_navigation_changed() { update_gizmos(); update_configuration_warnings(); + +#ifdef DEBUG_ENABLED + _update_debug_edge_connections_mesh(); +#endif // DEBUG_ENABLED } +#ifdef DEBUG_ENABLED +void NavigationRegion3D::_navigation_map_changed(RID p_map) { + if (is_inside_tree() && p_map == get_world_3d()->get_navigation_map()) { + _update_debug_edge_connections_mesh(); + } +} +#endif // DEBUG_ENABLED + NavigationRegion3D::NavigationRegion3D() { set_notify_transform(true); region = NavigationServer3D::get_singleton()->region_create(); NavigationServer3D::get_singleton()->region_set_enter_cost(region, get_enter_cost()); NavigationServer3D::get_singleton()->region_set_travel_cost(region, get_travel_cost()); + +#ifdef DEBUG_ENABLED + NavigationServer3D::get_singleton_mut()->connect("map_changed", callable_mp(this, &NavigationRegion3D::_navigation_map_changed)); + NavigationServer3D::get_singleton_mut()->connect("navigation_debug_changed", callable_mp(this, &NavigationRegion3D::_update_debug_mesh)); + NavigationServer3D::get_singleton_mut()->connect("navigation_debug_changed", callable_mp(this, &NavigationRegion3D::_update_debug_edge_connections_mesh)); +#endif // DEBUG_ENABLED } NavigationRegion3D::~NavigationRegion3D() { @@ -301,4 +355,255 @@ NavigationRegion3D::~NavigationRegion3D() { navmesh->disconnect("changed", callable_mp(this, &NavigationRegion3D::_navigation_changed)); } NavigationServer3D::get_singleton()->free(region); + +#ifdef DEBUG_ENABLED + NavigationServer3D::get_singleton_mut()->disconnect("map_changed", callable_mp(this, &NavigationRegion3D::_navigation_map_changed)); + NavigationServer3D::get_singleton_mut()->disconnect("navigation_debug_changed", callable_mp(this, &NavigationRegion3D::_update_debug_mesh)); + NavigationServer3D::get_singleton_mut()->disconnect("navigation_debug_changed", callable_mp(this, &NavigationRegion3D::_update_debug_edge_connections_mesh)); + if (debug_instance.is_valid()) { + RenderingServer::get_singleton()->free(debug_instance); + } + if (debug_mesh.is_valid()) { + RenderingServer::get_singleton()->free(debug_mesh->get_rid()); + } + if (debug_edge_connections_instance.is_valid()) { + RenderingServer::get_singleton()->free(debug_edge_connections_instance); + } + if (debug_edge_connections_mesh.is_valid()) { + RenderingServer::get_singleton()->free(debug_edge_connections_mesh->get_rid()); + } +#endif // DEBUG_ENABLED +} + +#ifdef DEBUG_ENABLED +void NavigationRegion3D::_update_debug_mesh() { + if (Engine::get_singleton()->is_editor_hint()) { + // don't update inside Editor as node 3d gizmo takes care of this + // as collisions and selections for Editor Viewport need to be updated + return; + } + + if (!NavigationServer3D::get_singleton()->get_debug_enabled()) { + if (debug_instance.is_valid()) { + RS::get_singleton()->instance_set_visible(debug_instance, false); + } + return; + } + + if (!navmesh.is_valid()) { + if (debug_instance.is_valid()) { + RS::get_singleton()->instance_set_visible(debug_instance, false); + } + return; + } + + if (!debug_instance.is_valid()) { + debug_instance = RenderingServer::get_singleton()->instance_create(); + } + + if (!debug_mesh.is_valid()) { + debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + } + + debug_mesh->clear_surfaces(); + + bool enabled_geometry_face_random_color = NavigationServer3D::get_singleton()->get_debug_navigation_enable_geometry_face_random_color(); + bool enabled_edge_lines = NavigationServer3D::get_singleton()->get_debug_navigation_enable_edge_lines(); + + Vector<Vector3> vertices = navmesh->get_vertices(); + if (vertices.size() == 0) { + return; + } + + int polygon_count = navmesh->get_polygon_count(); + if (polygon_count == 0) { + return; + } + + Vector<Vector3> face_vertex_array; + face_vertex_array.resize(polygon_count * 3); + + Vector<Color> face_color_array; + if (enabled_geometry_face_random_color) { + face_color_array.resize(polygon_count * 3); + } + + Vector<Vector3> line_vertex_array; + if (enabled_edge_lines) { + line_vertex_array.resize(polygon_count * 6); + } + + Color debug_navigation_geometry_face_color = NavigationServer3D::get_singleton()->get_debug_navigation_geometry_face_color(); + + Ref<StandardMaterial3D> face_material = NavigationServer3D::get_singleton_mut()->get_debug_navigation_geometry_face_material(); + Ref<StandardMaterial3D> line_material = NavigationServer3D::get_singleton_mut()->get_debug_navigation_geometry_edge_material(); + + RandomPCG rand; + Color polygon_color = debug_navigation_geometry_face_color; + + for (int i = 0; i < polygon_count; i++) { + if (enabled_geometry_face_random_color) { + // Generate the polygon color, slightly randomly modified from the settings one. + polygon_color.set_hsv(debug_navigation_geometry_face_color.get_h() + rand.random(-1.0, 1.0) * 0.1, debug_navigation_geometry_face_color.get_s(), debug_navigation_geometry_face_color.get_v() + rand.random(-1.0, 1.0) * 0.2); + polygon_color.a = debug_navigation_geometry_face_color.a; + } + + Vector<int> polygon = navmesh->get_polygon(i); + + face_vertex_array.push_back(vertices[polygon[0]]); + face_vertex_array.push_back(vertices[polygon[1]]); + face_vertex_array.push_back(vertices[polygon[2]]); + if (enabled_geometry_face_random_color) { + face_color_array.push_back(polygon_color); + face_color_array.push_back(polygon_color); + face_color_array.push_back(polygon_color); + } + + if (enabled_edge_lines) { + line_vertex_array.push_back(vertices[polygon[0]]); + line_vertex_array.push_back(vertices[polygon[1]]); + line_vertex_array.push_back(vertices[polygon[1]]); + line_vertex_array.push_back(vertices[polygon[2]]); + line_vertex_array.push_back(vertices[polygon[2]]); + line_vertex_array.push_back(vertices[polygon[0]]); + } + } + + Array face_mesh_array; + face_mesh_array.resize(Mesh::ARRAY_MAX); + face_mesh_array[Mesh::ARRAY_VERTEX] = face_vertex_array; + if (enabled_geometry_face_random_color) { + face_mesh_array[Mesh::ARRAY_COLOR] = face_color_array; + } + debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, face_mesh_array); + debug_mesh->surface_set_material(0, face_material); + + if (enabled_edge_lines) { + Array line_mesh_array; + line_mesh_array.resize(Mesh::ARRAY_MAX); + line_mesh_array[Mesh::ARRAY_VERTEX] = line_vertex_array; + debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, line_mesh_array); + debug_mesh->surface_set_material(1, line_material); + } + + RS::get_singleton()->instance_set_base(debug_instance, debug_mesh->get_rid()); + if (is_inside_tree()) { + RS::get_singleton()->instance_set_scenario(debug_instance, get_world_3d()->get_scenario()); + RS::get_singleton()->instance_set_visible(debug_instance, is_visible_in_tree()); + } + if (!is_enabled()) { + if (debug_mesh.is_valid()) { + if (debug_mesh->get_surface_count() > 0) { + RS::get_singleton()->instance_set_surface_override_material(debug_instance, 0, NavigationServer3D::get_singleton_mut()->get_debug_navigation_geometry_face_disabled_material()->get_rid()); + } + if (debug_mesh->get_surface_count() > 1) { + RS::get_singleton()->instance_set_surface_override_material(debug_instance, 1, NavigationServer3D::get_singleton_mut()->get_debug_navigation_geometry_edge_disabled_material()->get_rid()); + } + } + } else { + if (debug_mesh.is_valid()) { + if (debug_mesh->get_surface_count() > 0) { + RS::get_singleton()->instance_set_surface_override_material(debug_instance, 0, RID()); + } + if (debug_mesh->get_surface_count() > 1) { + RS::get_singleton()->instance_set_surface_override_material(debug_instance, 1, RID()); + } + } + } +} +#endif // DEBUG_ENABLED + +#ifdef DEBUG_ENABLED +void NavigationRegion3D::_update_debug_edge_connections_mesh() { + if (!NavigationServer3D::get_singleton()->get_debug_enabled()) { + if (debug_edge_connections_instance.is_valid()) { + RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, false); + } + return; + } + + if (!is_inside_tree()) { + return; + } + + if (!navmesh.is_valid()) { + if (debug_edge_connections_instance.is_valid()) { + RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, false); + } + return; + } + + if (!debug_edge_connections_instance.is_valid()) { + debug_edge_connections_instance = RenderingServer::get_singleton()->instance_create(); + } + + if (!debug_edge_connections_mesh.is_valid()) { + debug_edge_connections_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + } + + debug_edge_connections_mesh->clear_surfaces(); + + float edge_connection_margin = NavigationServer3D::get_singleton()->map_get_edge_connection_margin(get_world_3d()->get_navigation_map()); + float half_edge_connection_margin = edge_connection_margin * 0.5; + int connections_count = NavigationServer3D::get_singleton()->region_get_connections_count(region); + + if (connections_count == 0) { + RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, false); + return; + } + + Vector<Vector3> vertex_array; + vertex_array.resize(connections_count * 6); + + for (int i = 0; i < connections_count; i++) { + Vector3 connection_pathway_start = NavigationServer3D::get_singleton()->region_get_connection_pathway_start(region, i); + Vector3 connection_pathway_end = NavigationServer3D::get_singleton()->region_get_connection_pathway_end(region, i); + + Vector3 direction_start_end = connection_pathway_start.direction_to(connection_pathway_end); + Vector3 direction_end_start = connection_pathway_end.direction_to(connection_pathway_start); + + Vector3 start_right_dir = direction_start_end.cross(Vector3(0, 1, 0)); + Vector3 start_left_dir = -start_right_dir; + + Vector3 end_right_dir = direction_end_start.cross(Vector3(0, 1, 0)); + Vector3 end_left_dir = -end_right_dir; + + Vector3 left_start_pos = connection_pathway_start + (start_left_dir * half_edge_connection_margin); + Vector3 right_start_pos = connection_pathway_start + (start_right_dir * half_edge_connection_margin); + Vector3 left_end_pos = connection_pathway_end + (end_right_dir * half_edge_connection_margin); + Vector3 right_end_pos = connection_pathway_end + (end_left_dir * half_edge_connection_margin); + + vertex_array.push_back(right_end_pos); + vertex_array.push_back(left_start_pos); + vertex_array.push_back(right_start_pos); + + vertex_array.push_back(left_end_pos); + vertex_array.push_back(right_end_pos); + vertex_array.push_back(right_start_pos); + } + + if (vertex_array.size() == 0) { + return; + } + + Ref<StandardMaterial3D> edge_connections_material = NavigationServer3D::get_singleton_mut()->get_debug_navigation_edge_connections_material(); + + Array mesh_array; + mesh_array.resize(Mesh::ARRAY_MAX); + mesh_array[Mesh::ARRAY_VERTEX] = vertex_array; + + debug_edge_connections_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, mesh_array); + debug_edge_connections_mesh->surface_set_material(0, edge_connections_material); + + RS::get_singleton()->instance_set_base(debug_edge_connections_instance, debug_edge_connections_mesh->get_rid()); + RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, is_visible_in_tree()); + if (is_inside_tree()) { + RS::get_singleton()->instance_set_scenario(debug_edge_connections_instance, get_world_3d()->get_scenario()); + } + + bool enable_edge_connections = NavigationServer3D::get_singleton()->get_debug_navigation_enable_edge_connections(); + if (!enable_edge_connections) { + RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, false); + } } +#endif // DEBUG_ENABLED diff --git a/scene/3d/navigation_region_3d.h b/scene/3d/navigation_region_3d.h index aaaf5dd3b8..660538d314 100644 --- a/scene/3d/navigation_region_3d.h +++ b/scene/3d/navigation_region_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef NAVIGATION_REGION_H -#define NAVIGATION_REGION_H +#ifndef NAVIGATION_REGION_3D_H +#define NAVIGATION_REGION_3D_H #include "scene/3d/node_3d.h" #include "scene/resources/navigation_mesh.h" @@ -39,16 +39,27 @@ class NavigationRegion3D : public Node3D { bool enabled = true; RID region; - Ref<NavigationMesh> navmesh; - + uint32_t navigation_layers = 1; real_t enter_cost = 0.0; real_t travel_cost = 1.0; + Ref<NavigationMesh> navmesh; - Node *debug_view = nullptr; Thread bake_thread; void _navigation_changed(); +#ifdef DEBUG_ENABLED + RID debug_instance; + RID debug_edge_connections_instance; + Ref<ArrayMesh> debug_mesh; + Ref<ArrayMesh> debug_edge_connections_mesh; + +private: + void _update_debug_mesh(); + void _update_debug_edge_connections_mesh(); + void _navigation_map_changed(RID p_map); +#endif // DEBUG_ENABLED + protected: void _notification(int p_what); static void _bind_methods(); @@ -79,10 +90,10 @@ public: void bake_navigation_mesh(bool p_on_thread); void _bake_finished(Ref<NavigationMesh> p_nav_mesh); - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; NavigationRegion3D(); ~NavigationRegion3D(); }; -#endif // NAVIGATION_REGION_H +#endif // NAVIGATION_REGION_3D_H diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp index 04b1081516..12d2e66b41 100644 --- a/scene/3d/node_3d.cpp +++ b/scene/3d/node_3d.cpp @@ -561,8 +561,8 @@ void Node3D::clear_gizmos() { #endif } -Array Node3D::get_gizmos_bind() const { - Array ret; +TypedArray<Node3DGizmo> Node3D::get_gizmos_bind() const { + TypedArray<Node3DGizmo> ret; #ifdef TOOLS_ENABLED for (int i = 0; i < data.gizmos.size(); i++) { @@ -733,7 +733,7 @@ void Node3D::rotate_z(real_t p_angle) { void Node3D::translate(const Vector3 &p_offset) { Transform3D t = get_transform(); - t.translate(p_offset); + t.translate_local(p_offset); set_transform(t); } @@ -741,7 +741,7 @@ void Node3D::translate_object_local(const Vector3 &p_offset) { Transform3D t = get_transform(); Transform3D s; - s.translate(p_offset); + s.translate_local(p_offset); set_transform(t * s); } @@ -786,14 +786,15 @@ void Node3D::set_identity() { } void Node3D::look_at(const Vector3 &p_target, const Vector3 &p_up) { + ERR_FAIL_COND_MSG(!is_inside_tree(), "Node not inside tree. Use look_at_from_position() instead."); Vector3 origin = get_global_transform().origin; look_at_from_position(origin, p_target, p_up); } void Node3D::look_at_from_position(const Vector3 &p_pos, const Vector3 &p_target, const Vector3 &p_up) { ERR_FAIL_COND_MSG(p_pos.is_equal_approx(p_target), "Node origin and target are in the same position, look_at() failed."); - ERR_FAIL_COND_MSG(p_up.is_equal_approx(Vector3()), "The up vector can't be zero, look_at() failed."); - ERR_FAIL_COND_MSG(p_up.cross(p_target - p_pos).is_equal_approx(Vector3()), "Up vector and direction between node origin and target are aligned, look_at() failed."); + ERR_FAIL_COND_MSG(p_up.is_zero_approx(), "The up vector can't be zero, look_at() failed."); + ERR_FAIL_COND_MSG(p_up.cross(p_target - p_pos).is_zero_approx(), "Up vector and direction between node origin and target are aligned, look_at() failed."); Transform3D lookat = Transform3D(Basis::looking_at(p_target - p_pos, p_up), p_pos); Vector3 original_scale = get_scale(); @@ -879,25 +880,25 @@ NodePath Node3D::get_visibility_parent() const { return visibility_parent_path; } -void Node3D::_validate_property(PropertyInfo &property) const { - if (data.rotation_edit_mode != ROTATION_EDIT_MODE_BASIS && property.name == "basis") { - property.usage = 0; +void Node3D::_validate_property(PropertyInfo &p_property) const { + if (data.rotation_edit_mode != ROTATION_EDIT_MODE_BASIS && p_property.name == "basis") { + p_property.usage = 0; } - if (data.rotation_edit_mode == ROTATION_EDIT_MODE_BASIS && property.name == "scale") { - property.usage = 0; + if (data.rotation_edit_mode == ROTATION_EDIT_MODE_BASIS && p_property.name == "scale") { + p_property.usage = 0; } - if (data.rotation_edit_mode != ROTATION_EDIT_MODE_QUATERNION && property.name == "quaternion") { - property.usage = 0; + if (data.rotation_edit_mode != ROTATION_EDIT_MODE_QUATERNION && p_property.name == "quaternion") { + p_property.usage = 0; } - if (data.rotation_edit_mode != ROTATION_EDIT_MODE_EULER && property.name == "rotation") { - property.usage = 0; + if (data.rotation_edit_mode != ROTATION_EDIT_MODE_EULER && p_property.name == "rotation") { + p_property.usage = 0; } - if (data.rotation_edit_mode != ROTATION_EDIT_MODE_EULER && property.name == "rotation_order") { - property.usage = 0; + if (data.rotation_edit_mode != ROTATION_EDIT_MODE_EULER && p_property.name == "rotation_order") { + p_property.usage = 0; } } -bool Node3D::property_can_revert(const String &p_name) { +bool Node3D::_property_can_revert(const StringName &p_name) const { if (p_name == "basis") { return true; } else if (p_name == "scale") { @@ -912,47 +913,48 @@ bool Node3D::property_can_revert(const String &p_name) { return false; } -Variant Node3D::property_get_revert(const String &p_name) { - Variant r_ret; +bool Node3D::_property_get_revert(const StringName &p_name, Variant &r_property) const { bool valid = false; if (p_name == "basis") { Variant variant = PropertyUtils::get_property_default_value(this, "transform", &valid); if (valid && variant.get_type() == Variant::Type::TRANSFORM3D) { - r_ret = Transform3D(variant).get_basis(); + r_property = Transform3D(variant).get_basis(); } else { - r_ret = Basis(); + r_property = Basis(); } } else if (p_name == "scale") { Variant variant = PropertyUtils::get_property_default_value(this, "transform", &valid); if (valid && variant.get_type() == Variant::Type::TRANSFORM3D) { - r_ret = Transform3D(variant).get_basis().get_scale(); + r_property = Transform3D(variant).get_basis().get_scale(); } else { - return Vector3(1.0, 1.0, 1.0); + r_property = Vector3(1.0, 1.0, 1.0); } } else if (p_name == "quaternion") { Variant variant = PropertyUtils::get_property_default_value(this, "transform", &valid); if (valid && variant.get_type() == Variant::Type::TRANSFORM3D) { - r_ret = Quaternion(Transform3D(variant).get_basis().get_rotation_quaternion()); + r_property = Quaternion(Transform3D(variant).get_basis().get_rotation_quaternion()); } else { - return Quaternion(); + r_property = Quaternion(); } } else if (p_name == "rotation") { Variant variant = PropertyUtils::get_property_default_value(this, "transform", &valid); if (valid && variant.get_type() == Variant::Type::TRANSFORM3D) { - r_ret = Transform3D(variant).get_basis().get_euler_normalized(data.euler_rotation_order); + r_property = Transform3D(variant).get_basis().get_euler_normalized(data.euler_rotation_order); } else { - return Vector3(); + r_property = Vector3(); } } else if (p_name == "position") { Variant variant = PropertyUtils::get_property_default_value(this, "transform", &valid); if (valid) { - r_ret = Transform3D(variant).get_origin(); + r_property = Transform3D(variant).get_origin(); } else { - return Vector3(); + r_property = Vector3(); } + } else { + return false; } - return r_ret; + return true; } void Node3D::_bind_methods() { @@ -1032,13 +1034,11 @@ void Node3D::_bind_methods() { ClassDB::bind_method(D_METHOD("to_local", "global_point"), &Node3D::to_local); ClassDB::bind_method(D_METHOD("to_global", "local_point"), &Node3D::to_global); - ClassDB::bind_method(D_METHOD("property_can_revert", "name"), &Node3D::property_can_revert); - ClassDB::bind_method(D_METHOD("property_get_revert", "name"), &Node3D::property_get_revert); - BIND_CONSTANT(NOTIFICATION_TRANSFORM_CHANGED); BIND_CONSTANT(NOTIFICATION_ENTER_WORLD); BIND_CONSTANT(NOTIFICATION_EXIT_WORLD); BIND_CONSTANT(NOTIFICATION_VISIBILITY_CHANGED); + BIND_CONSTANT(NOTIFICATION_LOCAL_TRANSFORM_CHANGED); BIND_ENUM_CONSTANT(ROTATION_EDIT_MODE_EULER); BIND_ENUM_CONSTANT(ROTATION_EDIT_MODE_QUATERNION); @@ -1054,9 +1054,9 @@ void Node3D::_bind_methods() { ADD_GROUP("Transform", ""); ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "transform", PROPERTY_HINT_NONE, "suffix:m", PROPERTY_USAGE_NO_EDITOR), "set_transform", "get_transform"); ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "global_transform", PROPERTY_HINT_NONE, "suffix:m", PROPERTY_USAGE_NONE), "set_global_transform", "get_global_transform"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "position", PROPERTY_HINT_RANGE, "-99999,99999,0.001,or_greater,or_lesser,no_slider,suffix:m", PROPERTY_USAGE_EDITOR), "set_position", "get_position"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_lesser,or_greater,radians", PROPERTY_USAGE_EDITOR), "set_rotation", "get_rotation"); - ADD_PROPERTY(PropertyInfo(Variant::QUATERNION, "quaternion", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_quaternion", "get_quaternion"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "position", PROPERTY_HINT_RANGE, "-99999,99999,0.001,or_greater,or_less,no_slider,suffix:m", PROPERTY_USAGE_EDITOR), "set_position", "get_position"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_less,or_greater,radians", PROPERTY_USAGE_EDITOR), "set_rotation", "get_rotation"); + ADD_PROPERTY(PropertyInfo(Variant::QUATERNION, "quaternion", PROPERTY_HINT_HIDE_QUATERNION_EDIT, "", PROPERTY_USAGE_EDITOR), "set_quaternion", "get_quaternion"); ADD_PROPERTY(PropertyInfo(Variant::BASIS, "basis", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_basis", "get_basis"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "scale", PROPERTY_HINT_LINK, "", PROPERTY_USAGE_EDITOR), "set_scale", "get_scale"); ADD_PROPERTY(PropertyInfo(Variant::INT, "rotation_edit_mode", PROPERTY_HINT_ENUM, "Euler,Quaternion,Basis"), "set_rotation_edit_mode", "get_rotation_edit_mode"); diff --git a/scene/3d/node_3d.h b/scene/3d/node_3d.h index b1e129798d..90c7bc89ef 100644 --- a/scene/3d/node_3d.h +++ b/scene/3d/node_3d.h @@ -155,10 +155,10 @@ protected: void _notification(int p_what); static void _bind_methods(); - virtual void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; - bool property_can_revert(const String &p_name); - Variant property_get_revert(const String &p_name); + bool _property_can_revert(const StringName &p_name) const; + bool _property_get_revert(const StringName &p_name, Variant &r_property) const; public: enum { @@ -216,7 +216,7 @@ public: void set_subgizmo_selection(Ref<Node3DGizmo> p_gizmo, int p_id, Transform3D p_transform = Transform3D()); void clear_subgizmo_selection(); Vector<Ref<Node3DGizmo>> get_gizmos() const; - Array get_gizmos_bind() const; + TypedArray<Node3DGizmo> get_gizmos_bind() const; void add_gizmo(Ref<Node3DGizmo> p_gizmo); void remove_gizmo(Ref<Node3DGizmo> p_gizmo); void clear_gizmos(); diff --git a/scene/3d/occluder_instance_3d.cpp b/scene/3d/occluder_instance_3d.cpp index 66d0a8c4e2..4e1ed5654a 100644 --- a/scene/3d/occluder_instance_3d.cpp +++ b/scene/3d/occluder_instance_3d.cpp @@ -186,21 +186,21 @@ ArrayOccluder3D::~ArrayOccluder3D() { ///////////////////////////////////////////////// -void QuadOccluder3D::set_size(const Vector2 &p_size) { +void QuadOccluder3D::set_size(const Size2 &p_size) { if (size == p_size) { return; } - size = p_size.max(Vector2()); + size = p_size.max(Size2()); _update(); } -Vector2 QuadOccluder3D::get_size() const { +Size2 QuadOccluder3D::get_size() const { return size; } void QuadOccluder3D::_update_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) { - Vector2 _size = Vector2(size.x / 2.0f, size.y / 2.0f); + Size2 _size = Size2(size.x / 2.0f, size.y / 2.0f); r_vertices = { Vector3(-_size.x, -_size.y, 0), @@ -670,7 +670,7 @@ OccluderInstance3D::BakeError OccluderInstance3D::bake_scene(Node *p_from_node, occ->set_arrays(vertices, indices); - Error err = ResourceSaver::save(p_occluder_path, occ); + Error err = ResourceSaver::save(occ, p_occluder_path); if (err != OK) { return BAKE_ERROR_CANT_SAVE; @@ -682,8 +682,8 @@ OccluderInstance3D::BakeError OccluderInstance3D::bake_scene(Node *p_from_node, return BAKE_ERROR_OK; } -TypedArray<String> OccluderInstance3D::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray OccluderInstance3D::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (!bool(GLOBAL_GET("rendering/occlusion_culling/use_occlusion_culling"))) { warnings.push_back(RTR("Occlusion culling is disabled in the Project Settings, which means occlusion culling won't be performed in the root viewport.\nTo resolve this, open the Project Settings and enable Rendering > Occlusion Culling > Use Occlusion Culling.")); diff --git a/scene/3d/occluder_instance_3d.h b/scene/3d/occluder_instance_3d.h index ed6610074e..f507fee024 100644 --- a/scene/3d/occluder_instance_3d.h +++ b/scene/3d/occluder_instance_3d.h @@ -88,15 +88,15 @@ class QuadOccluder3D : public Occluder3D { GDCLASS(QuadOccluder3D, Occluder3D); private: - Vector2 size = Vector2(1.0f, 1.0f); + Size2 size = Vector2(1.0f, 1.0f); protected: virtual void _update_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) override; static void _bind_methods(); public: - Vector2 get_size() const; - void set_size(const Vector2 &p_size); + Size2 get_size() const; + void set_size(const Size2 &p_size); QuadOccluder3D(); ~QuadOccluder3D(); @@ -181,7 +181,7 @@ protected: static void _bind_methods(); public: - virtual TypedArray<String> get_configuration_warnings() const override; + virtual PackedStringArray get_configuration_warnings() const override; enum BakeError { BAKE_ERROR_OK, @@ -211,4 +211,4 @@ public: ~OccluderInstance3D(); }; -#endif +#endif // OCCLUDER_INSTANCE_3D_H diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp index f53e783fbd..123a044b84 100644 --- a/scene/3d/path_3d.cpp +++ b/scene/3d/path_3d.cpp @@ -183,8 +183,8 @@ void PathFollow3D::_update_transform(bool p_update_xyz_rot) { return; } real_t bi = c->get_bake_interval(); - real_t o_next = offset + bi; - real_t o_prev = offset - bi; + real_t o_next = progress + bi; + real_t o_prev = progress - bi; if (loop) { o_next = Math::fposmod(o_next, bl); @@ -198,17 +198,17 @@ void PathFollow3D::_update_transform(bool p_update_xyz_rot) { } } - Vector3 pos = c->interpolate_baked(offset, cubic); + Vector3 pos = c->sample_baked(progress, cubic); Transform3D t = get_transform(); // Vector3 pos_offset = Vector3(h_offset, v_offset, 0); not used in all cases // will be replaced by "Vector3(h_offset, v_offset, 0)" where it was formerly used if (rotation_mode == ROTATION_ORIENTED) { - Vector3 forward = c->interpolate_baked(o_next, cubic) - pos; + Vector3 forward = c->sample_baked(o_next, cubic) - pos; // Try with the previous position if (forward.length_squared() < CMP_EPSILON2) { - forward = pos - c->interpolate_baked(o_prev, cubic); + forward = pos - c->sample_baked(o_prev, cubic); } if (forward.length_squared() < CMP_EPSILON2) { @@ -217,10 +217,10 @@ void PathFollow3D::_update_transform(bool p_update_xyz_rot) { forward.normalize(); } - Vector3 up = c->interpolate_baked_up_vector(offset, true); + Vector3 up = c->sample_baked_up_vector(progress, true); - if (o_next < offset) { - Vector3 up1 = c->interpolate_baked_up_vector(o_next, true); + if (o_next < progress) { + Vector3 up1 = c->sample_baked_up_vector(o_next, true); Vector3 axis = up.cross(up1); if (axis.length_squared() < CMP_EPSILON2) { @@ -247,12 +247,12 @@ void PathFollow3D::_update_transform(bool p_update_xyz_rot) { // for a discussion about why not Frenet frame. t.origin = pos; - if (p_update_xyz_rot && prev_offset != offset) { // Only update rotation if some parameter has changed - i.e. not on addition to scene tree. + if (p_update_xyz_rot && prev_offset != progress) { // Only update rotation if some parameter has changed - i.e. not on addition to scene tree. real_t sample_distance = bi * 0.01; - Vector3 t_prev_pos_a = c->interpolate_baked(prev_offset - sample_distance, cubic); - Vector3 t_prev_pos_b = c->interpolate_baked(prev_offset + sample_distance, cubic); - Vector3 t_cur_pos_a = c->interpolate_baked(offset - sample_distance, cubic); - Vector3 t_cur_pos_b = c->interpolate_baked(offset + sample_distance, cubic); + Vector3 t_prev_pos_a = c->sample_baked(prev_offset - sample_distance, cubic); + Vector3 t_prev_pos_b = c->sample_baked(prev_offset + sample_distance, cubic); + Vector3 t_cur_pos_a = c->sample_baked(progress - sample_distance, cubic); + Vector3 t_cur_pos_b = c->sample_baked(progress + sample_distance, cubic); Vector3 t_prev = (t_prev_pos_a - t_prev_pos_b).normalized(); Vector3 t_cur = (t_cur_pos_a - t_cur_pos_b).normalized(); @@ -277,7 +277,7 @@ void PathFollow3D::_update_transform(bool p_update_xyz_rot) { } // do the additional tilting - real_t tilt_angle = c->interpolate_baked_tilt(offset); + real_t tilt_angle = c->sample_baked_tilt(progress); Vector3 tilt_axis = t_cur; // not sure what tilt is supposed to do, is this correct?? if (likely(!Math::is_zero_approx(Math::abs(tilt_angle)))) { @@ -296,7 +296,7 @@ void PathFollow3D::_update_transform(bool p_update_xyz_rot) { } } - t.translate(Vector3(h_offset, v_offset, 0)); + t.translate_local(Vector3(h_offset, v_offset, 0)); } else { t.origin = pos + Vector3(h_offset, v_offset, 0); } @@ -330,20 +330,19 @@ bool PathFollow3D::get_cubic_interpolation() const { return cubic; } -void PathFollow3D::_validate_property(PropertyInfo &property) const { - if (property.name == "offset") { +void PathFollow3D::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "offset") { real_t max = 10000; if (path && path->get_curve().is_valid()) { max = path->get_curve()->get_baked_length(); } - property.hint_string = "0," + rtos(max) + ",0.01,or_lesser,or_greater"; + p_property.hint_string = "0," + rtos(max) + ",0.01,or_less,or_greater"; } - Node3D::_validate_property(property); } -TypedArray<String> PathFollow3D::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray PathFollow3D::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (is_visible_in_tree() && is_inside_tree()) { if (!Object::cast_to<Path3D>(get_parent())) { @@ -360,8 +359,8 @@ TypedArray<String> PathFollow3D::get_configuration_warnings() const { } void PathFollow3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_offset", "offset"), &PathFollow3D::set_offset); - ClassDB::bind_method(D_METHOD("get_offset"), &PathFollow3D::get_offset); + ClassDB::bind_method(D_METHOD("set_progress", "progress"), &PathFollow3D::set_progress); + ClassDB::bind_method(D_METHOD("get_progress"), &PathFollow3D::get_progress); ClassDB::bind_method(D_METHOD("set_h_offset", "h_offset"), &PathFollow3D::set_h_offset); ClassDB::bind_method(D_METHOD("get_h_offset"), &PathFollow3D::get_h_offset); @@ -369,8 +368,8 @@ void PathFollow3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_v_offset", "v_offset"), &PathFollow3D::set_v_offset); ClassDB::bind_method(D_METHOD("get_v_offset"), &PathFollow3D::get_v_offset); - ClassDB::bind_method(D_METHOD("set_unit_offset", "unit_offset"), &PathFollow3D::set_unit_offset); - ClassDB::bind_method(D_METHOD("get_unit_offset"), &PathFollow3D::get_unit_offset); + ClassDB::bind_method(D_METHOD("set_progress_ratio", "ratio"), &PathFollow3D::set_progress_ratio); + ClassDB::bind_method(D_METHOD("get_progress_ratio"), &PathFollow3D::get_progress_ratio); ClassDB::bind_method(D_METHOD("set_rotation_mode", "rotation_mode"), &PathFollow3D::set_rotation_mode); ClassDB::bind_method(D_METHOD("get_rotation_mode"), &PathFollow3D::get_rotation_mode); @@ -381,8 +380,8 @@ void PathFollow3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_loop", "loop"), &PathFollow3D::set_loop); ClassDB::bind_method(D_METHOD("has_loop"), &PathFollow3D::has_loop); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "offset", PROPERTY_HINT_RANGE, "0,10000,0.01,or_lesser,or_greater,suffix:m"), "set_offset", "get_offset"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "unit_offset", PROPERTY_HINT_RANGE, "0,1,0.0001,or_lesser,or_greater", PROPERTY_USAGE_EDITOR), "set_unit_offset", "get_unit_offset"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "progress", PROPERTY_HINT_RANGE, "0,10000,0.01,or_less,or_greater,suffix:m"), "set_progress", "get_progress"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "progress_ratio", PROPERTY_HINT_RANGE, "0,1,0.0001,or_less,or_greater", PROPERTY_USAGE_EDITOR), "set_progress_ratio", "get_progress_ratio"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "h_offset", PROPERTY_HINT_NONE, "suffix:m"), "set_h_offset", "get_h_offset"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "v_offset", PROPERTY_HINT_NONE, "suffix:m"), "set_v_offset", "get_v_offset"); ADD_PROPERTY(PropertyInfo(Variant::INT, "rotation_mode", PROPERTY_HINT_ENUM, "None,Y,XY,XYZ,Oriented"), "set_rotation_mode", "get_rotation_mode"); @@ -396,21 +395,22 @@ void PathFollow3D::_bind_methods() { BIND_ENUM_CONSTANT(ROTATION_ORIENTED); } -void PathFollow3D::set_offset(real_t p_offset) { - prev_offset = offset; - offset = p_offset; +void PathFollow3D::set_progress(real_t p_progress) { + ERR_FAIL_COND(!isfinite(p_progress)); + prev_offset = progress; + progress = p_progress; if (path) { if (path->get_curve().is_valid()) { real_t path_length = path->get_curve()->get_baked_length(); if (loop && path_length) { - offset = Math::fposmod(offset, path_length); - if (!Math::is_zero_approx(p_offset) && Math::is_zero_approx(offset)) { - offset = path_length; + progress = Math::fposmod(progress, path_length); + if (!Math::is_zero_approx(p_progress) && Math::is_zero_approx(progress)) { + progress = path_length; } } else { - offset = CLAMP(offset, 0, path_length); + progress = CLAMP(progress, 0, path_length); } } @@ -440,19 +440,19 @@ real_t PathFollow3D::get_v_offset() const { return v_offset; } -real_t PathFollow3D::get_offset() const { - return offset; +real_t PathFollow3D::get_progress() const { + return progress; } -void PathFollow3D::set_unit_offset(real_t p_unit_offset) { +void PathFollow3D::set_progress_ratio(real_t p_ratio) { if (path && path->get_curve().is_valid() && path->get_curve()->get_baked_length()) { - set_offset(p_unit_offset * path->get_curve()->get_baked_length()); + set_progress(p_ratio * path->get_curve()->get_baked_length()); } } -real_t PathFollow3D::get_unit_offset() const { +real_t PathFollow3D::get_progress_ratio() const { if (path && path->get_curve().is_valid() && path->get_curve()->get_baked_length()) { - return get_offset() / path->get_curve()->get_baked_length(); + return get_progress() / path->get_curve()->get_baked_length(); } else { return 0; } diff --git a/scene/3d/path_3d.h b/scene/3d/path_3d.h index 7c7284534e..b161b12185 100644 --- a/scene/3d/path_3d.h +++ b/scene/3d/path_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef PATH_H -#define PATH_H +#ifndef PATH_3D_H +#define PATH_3D_H #include "scene/3d/node_3d.h" #include "scene/resources/curve.h" @@ -75,7 +75,7 @@ public: private: Path3D *path = nullptr; real_t prev_offset = 0.0; // Offset during the last _update_transform. - real_t offset = 0.0; + real_t progress = 0.0; real_t h_offset = 0.0; real_t v_offset = 0.0; bool cubic = true; @@ -85,14 +85,14 @@ private: void _update_transform(bool p_update_xyz_rot = true); protected: - virtual void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; void _notification(int p_what); static void _bind_methods(); public: - void set_offset(real_t p_offset); - real_t get_offset() const; + void set_progress(real_t p_progress); + real_t get_progress() const; void set_h_offset(real_t p_h_offset); real_t get_h_offset() const; @@ -100,8 +100,8 @@ public: void set_v_offset(real_t p_v_offset); real_t get_v_offset() const; - void set_unit_offset(real_t p_unit_offset); - real_t get_unit_offset() const; + void set_progress_ratio(real_t p_ratio); + real_t get_progress_ratio() const; void set_loop(bool p_loop); bool has_loop() const; @@ -112,11 +112,11 @@ public: void set_cubic_interpolation(bool p_enable); bool get_cubic_interpolation() const; - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; PathFollow3D() {} }; VARIANT_ENUM_CAST(PathFollow3D::RotationMode); -#endif // PATH_H +#endif // PATH_3D_H diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp index 30f7a025fa..594e94644c 100644 --- a/scene/3d/physics_body_3d.cpp +++ b/scene/3d/physics_body_3d.cpp @@ -34,8 +34,8 @@ #include "scene/scene_string_names.h" void PhysicsBody3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("move_and_collide", "distance", "test_only", "safe_margin", "max_collisions"), &PhysicsBody3D::_move, DEFVAL(false), DEFVAL(0.001), DEFVAL(1)); - ClassDB::bind_method(D_METHOD("test_move", "from", "distance", "collision", "safe_margin", "max_collisions"), &PhysicsBody3D::test_move, DEFVAL(Variant()), DEFVAL(0.001), DEFVAL(1)); + ClassDB::bind_method(D_METHOD("move_and_collide", "distance", "test_only", "safe_margin", "recovery_as_collision", "max_collisions"), &PhysicsBody3D::_move, DEFVAL(false), DEFVAL(0.001), DEFVAL(false), DEFVAL(1)); + ClassDB::bind_method(D_METHOD("test_move", "from", "distance", "collision", "safe_margin", "recovery_as_collision", "max_collisions"), &PhysicsBody3D::test_move, DEFVAL(Variant()), DEFVAL(0.001), DEFVAL(false), DEFVAL(1)); ClassDB::bind_method(D_METHOD("set_axis_lock", "axis", "lock"), &PhysicsBody3D::set_axis_lock); ClassDB::bind_method(D_METHOD("get_axis_lock", "axis"), &PhysicsBody3D::get_axis_lock); @@ -91,16 +91,16 @@ void PhysicsBody3D::remove_collision_exception_with(Node *p_node) { PhysicsServer3D::get_singleton()->body_remove_collision_exception(get_rid(), collision_object->get_rid()); } -Ref<KinematicCollision3D> PhysicsBody3D::_move(const Vector3 &p_distance, bool p_test_only, real_t p_margin, int p_max_collisions) { +Ref<KinematicCollision3D> PhysicsBody3D::_move(const Vector3 &p_distance, bool p_test_only, real_t p_margin, bool p_recovery_as_collision, int p_max_collisions) { PhysicsServer3D::MotionParameters parameters(get_global_transform(), p_distance, p_margin); parameters.max_collisions = p_max_collisions; - parameters.recovery_as_collision = false; // Don't report collisions generated only from recovery. + parameters.recovery_as_collision = p_recovery_as_collision; PhysicsServer3D::MotionResult result; if (move_and_collide(parameters, result, p_test_only)) { // Create a new instance when the cached reference is invalid or still in use in script. - if (motion_cache.is_null() || motion_cache->reference_get_count() > 1) { + if (motion_cache.is_null() || motion_cache->get_reference_count() > 1) { motion_cache.instantiate(); motion_cache->owner = this; } @@ -169,7 +169,7 @@ bool PhysicsBody3D::move_and_collide(const PhysicsServer3D::MotionParameters &p_ return colliding; } -bool PhysicsBody3D::test_move(const Transform3D &p_from, const Vector3 &p_distance, const Ref<KinematicCollision3D> &r_collision, real_t p_margin, int p_max_collisions) { +bool PhysicsBody3D::test_move(const Transform3D &p_from, const Vector3 &p_distance, const Ref<KinematicCollision3D> &r_collision, real_t p_margin, bool p_recovery_as_collision, int p_max_collisions) { ERR_FAIL_COND_V(!is_inside_tree(), false); PhysicsServer3D::MotionResult *r = nullptr; @@ -182,7 +182,7 @@ bool PhysicsBody3D::test_move(const Transform3D &p_from, const Vector3 &p_distan } PhysicsServer3D::MotionParameters parameters(p_from, p_distance, p_margin); - parameters.recovery_as_collision = false; // Don't report collisions generated only from recovery. + parameters.recovery_as_collision = p_recovery_as_collision; return PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), parameters, r); } @@ -317,11 +317,6 @@ void AnimatableBody3D::_update_kinematic_motion() { } } -void AnimatableBody3D::_body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state) { - AnimatableBody3D *body = (AnimatableBody3D *)p_instance; - body->_body_state_changed(p_state); -} - void AnimatableBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) { linear_velocity = p_state->get_linear_velocity(); angular_velocity = p_state->get_angular_velocity(); @@ -373,10 +368,10 @@ void AnimatableBody3D::_bind_methods() { AnimatableBody3D::AnimatableBody3D() : StaticBody3D(PhysicsServer3D::BODY_MODE_KINEMATIC) { - PhysicsServer3D::get_singleton()->body_set_state_sync_callback(get_rid(), this, _body_state_changed_callback); + PhysicsServer3D::get_singleton()->body_set_state_sync_callback(get_rid(), callable_mp(this, &AnimatableBody3D::_body_state_changed)); } -void RigidDynamicBody3D::_body_enter_tree(ObjectID p_id) { +void RigidBody3D::_body_enter_tree(ObjectID p_id) { Object *obj = ObjectDB::get_instance(p_id); Node *node = Object::cast_to<Node>(obj); ERR_FAIL_COND(!node); @@ -399,7 +394,7 @@ void RigidDynamicBody3D::_body_enter_tree(ObjectID p_id) { contact_monitor->locked = false; } -void RigidDynamicBody3D::_body_exit_tree(ObjectID p_id) { +void RigidBody3D::_body_exit_tree(ObjectID p_id) { Object *obj = ObjectDB::get_instance(p_id); Node *node = Object::cast_to<Node>(obj); ERR_FAIL_COND(!node); @@ -420,7 +415,7 @@ void RigidDynamicBody3D::_body_exit_tree(ObjectID p_id) { contact_monitor->locked = false; } -void RigidDynamicBody3D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, int p_body_shape, int p_local_shape) { +void RigidBody3D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, int p_body_shape, int p_local_shape) { bool body_in = p_status == 1; ObjectID objid = p_instance; @@ -439,8 +434,8 @@ void RigidDynamicBody3D::_body_inout(int p_status, const RID &p_body, ObjectID p //E->value.rc=0; E->value.in_tree = node && node->is_inside_tree(); if (node) { - node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidDynamicBody3D::_body_enter_tree), make_binds(objid)); - node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidDynamicBody3D::_body_exit_tree), make_binds(objid)); + node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidBody3D::_body_enter_tree).bind(objid)); + node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidBody3D::_body_exit_tree).bind(objid)); if (E->value.in_tree) { emit_signal(SceneStringNames::get_singleton()->body_entered, node); } @@ -466,8 +461,8 @@ void RigidDynamicBody3D::_body_inout(int p_status, const RID &p_body, ObjectID p if (E->value.shapes.is_empty()) { if (node) { - node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidDynamicBody3D::_body_enter_tree)); - node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidDynamicBody3D::_body_exit_tree)); + node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidBody3D::_body_enter_tree)); + node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidBody3D::_body_exit_tree)); if (in_tree) { emit_signal(SceneStringNames::get_singleton()->body_exited, node); } @@ -481,19 +476,14 @@ void RigidDynamicBody3D::_body_inout(int p_status, const RID &p_body, ObjectID p } } -struct _RigidDynamicBodyInOut { +struct _RigidBodyInOut { RID rid; ObjectID id; int shape = 0; int local_shape = 0; }; -void RigidDynamicBody3D::_body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state) { - RigidDynamicBody3D *body = (RigidDynamicBody3D *)p_instance; - body->_body_state_changed(p_state); -} - -void RigidDynamicBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) { +void RigidBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) { set_ignore_transform_notification(true); set_global_transform(p_state->get_transform()); @@ -524,9 +514,9 @@ void RigidDynamicBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) } } - _RigidDynamicBodyInOut *toadd = (_RigidDynamicBodyInOut *)alloca(p_state->get_contact_count() * sizeof(_RigidDynamicBodyInOut)); - int toadd_count = 0; //state->get_contact_count(); - RigidDynamicBody3D_RemoveAction *toremove = (RigidDynamicBody3D_RemoveAction *)alloca(rc * sizeof(RigidDynamicBody3D_RemoveAction)); + _RigidBodyInOut *toadd = (_RigidBodyInOut *)alloca(p_state->get_contact_count() * sizeof(_RigidBodyInOut)); + int toadd_count = 0; + RigidBody3D_RemoveAction *toremove = (RigidBody3D_RemoveAction *)alloca(rc * sizeof(RigidBody3D_RemoveAction)); int toremove_count = 0; //put the ones to add @@ -537,8 +527,6 @@ void RigidDynamicBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) int local_shape = p_state->get_contact_local_shape(i); int shape = p_state->get_contact_collider_shape(i); - //bool found=false; - HashMap<ObjectID, BodyState>::Iterator E = contact_monitor->body_map.find(obj); if (!E) { toadd[toadd_count].rid = rid; @@ -592,7 +580,7 @@ void RigidDynamicBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) } } -void RigidDynamicBody3D::_notification(int p_what) { +void RigidBody3D::_notification(int p_what) { #ifdef TOOLS_ENABLED switch (p_what) { case NOTIFICATION_ENTER_TREE: { @@ -610,7 +598,7 @@ void RigidDynamicBody3D::_notification(int p_what) { #endif } -void RigidDynamicBody3D::_apply_body_mode() { +void RigidBody3D::_apply_body_mode() { if (freeze) { switch (freeze_mode) { case FREEZE_MODE_STATIC: { @@ -621,13 +609,13 @@ void RigidDynamicBody3D::_apply_body_mode() { } break; } } else if (lock_rotation) { - set_body_mode(PhysicsServer3D::BODY_MODE_DYNAMIC_LINEAR); + set_body_mode(PhysicsServer3D::BODY_MODE_RIGID_LINEAR); } else { - set_body_mode(PhysicsServer3D::BODY_MODE_DYNAMIC); + set_body_mode(PhysicsServer3D::BODY_MODE_RIGID); } } -void RigidDynamicBody3D::set_lock_rotation_enabled(bool p_lock_rotation) { +void RigidBody3D::set_lock_rotation_enabled(bool p_lock_rotation) { if (p_lock_rotation == lock_rotation) { return; } @@ -636,11 +624,11 @@ void RigidDynamicBody3D::set_lock_rotation_enabled(bool p_lock_rotation) { _apply_body_mode(); } -bool RigidDynamicBody3D::is_lock_rotation_enabled() const { +bool RigidBody3D::is_lock_rotation_enabled() const { return lock_rotation; } -void RigidDynamicBody3D::set_freeze_enabled(bool p_freeze) { +void RigidBody3D::set_freeze_enabled(bool p_freeze) { if (p_freeze == freeze) { return; } @@ -649,11 +637,11 @@ void RigidDynamicBody3D::set_freeze_enabled(bool p_freeze) { _apply_body_mode(); } -bool RigidDynamicBody3D::is_freeze_enabled() const { +bool RigidBody3D::is_freeze_enabled() const { return freeze; } -void RigidDynamicBody3D::set_freeze_mode(FreezeMode p_freeze_mode) { +void RigidBody3D::set_freeze_mode(FreezeMode p_freeze_mode) { if (p_freeze_mode == freeze_mode) { return; } @@ -662,21 +650,21 @@ void RigidDynamicBody3D::set_freeze_mode(FreezeMode p_freeze_mode) { _apply_body_mode(); } -RigidDynamicBody3D::FreezeMode RigidDynamicBody3D::get_freeze_mode() const { +RigidBody3D::FreezeMode RigidBody3D::get_freeze_mode() const { return freeze_mode; } -void RigidDynamicBody3D::set_mass(real_t p_mass) { +void RigidBody3D::set_mass(real_t p_mass) { ERR_FAIL_COND(p_mass <= 0); mass = p_mass; PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_MASS, mass); } -real_t RigidDynamicBody3D::get_mass() const { +real_t RigidBody3D::get_mass() const { return mass; } -void RigidDynamicBody3D::set_inertia(const Vector3 &p_inertia) { +void RigidBody3D::set_inertia(const Vector3 &p_inertia) { ERR_FAIL_COND(p_inertia.x < 0); ERR_FAIL_COND(p_inertia.y < 0); ERR_FAIL_COND(p_inertia.z < 0); @@ -685,11 +673,11 @@ void RigidDynamicBody3D::set_inertia(const Vector3 &p_inertia) { PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_INERTIA, inertia); } -const Vector3 &RigidDynamicBody3D::get_inertia() const { +const Vector3 &RigidBody3D::get_inertia() const { return inertia; } -void RigidDynamicBody3D::set_center_of_mass_mode(CenterOfMassMode p_mode) { +void RigidBody3D::set_center_of_mass_mode(CenterOfMassMode p_mode) { if (center_of_mass_mode == p_mode) { return; } @@ -711,11 +699,11 @@ void RigidDynamicBody3D::set_center_of_mass_mode(CenterOfMassMode p_mode) { } } -RigidDynamicBody3D::CenterOfMassMode RigidDynamicBody3D::get_center_of_mass_mode() const { +RigidBody3D::CenterOfMassMode RigidBody3D::get_center_of_mass_mode() const { return center_of_mass_mode; } -void RigidDynamicBody3D::set_center_of_mass(const Vector3 &p_center_of_mass) { +void RigidBody3D::set_center_of_mass(const Vector3 &p_center_of_mass) { if (center_of_mass == p_center_of_mass) { return; } @@ -726,106 +714,106 @@ void RigidDynamicBody3D::set_center_of_mass(const Vector3 &p_center_of_mass) { PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_CENTER_OF_MASS, center_of_mass); } -const Vector3 &RigidDynamicBody3D::get_center_of_mass() const { +const Vector3 &RigidBody3D::get_center_of_mass() const { return center_of_mass; } -void RigidDynamicBody3D::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) { +void RigidBody3D::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) { if (physics_material_override.is_valid()) { - if (physics_material_override->is_connected(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidDynamicBody3D::_reload_physics_characteristics))) { - physics_material_override->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidDynamicBody3D::_reload_physics_characteristics)); + if (physics_material_override->is_connected(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidBody3D::_reload_physics_characteristics))) { + physics_material_override->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidBody3D::_reload_physics_characteristics)); } } physics_material_override = p_physics_material_override; if (physics_material_override.is_valid()) { - physics_material_override->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidDynamicBody3D::_reload_physics_characteristics)); + physics_material_override->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidBody3D::_reload_physics_characteristics)); } _reload_physics_characteristics(); } -Ref<PhysicsMaterial> RigidDynamicBody3D::get_physics_material_override() const { +Ref<PhysicsMaterial> RigidBody3D::get_physics_material_override() const { return physics_material_override; } -void RigidDynamicBody3D::set_gravity_scale(real_t p_gravity_scale) { +void RigidBody3D::set_gravity_scale(real_t p_gravity_scale) { gravity_scale = p_gravity_scale; PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_GRAVITY_SCALE, gravity_scale); } -real_t RigidDynamicBody3D::get_gravity_scale() const { +real_t RigidBody3D::get_gravity_scale() const { return gravity_scale; } -void RigidDynamicBody3D::set_linear_damp_mode(DampMode p_mode) { +void RigidBody3D::set_linear_damp_mode(DampMode p_mode) { linear_damp_mode = p_mode; PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_LINEAR_DAMP_MODE, linear_damp_mode); } -RigidDynamicBody3D::DampMode RigidDynamicBody3D::get_linear_damp_mode() const { +RigidBody3D::DampMode RigidBody3D::get_linear_damp_mode() const { return linear_damp_mode; } -void RigidDynamicBody3D::set_angular_damp_mode(DampMode p_mode) { +void RigidBody3D::set_angular_damp_mode(DampMode p_mode) { angular_damp_mode = p_mode; PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_ANGULAR_DAMP_MODE, angular_damp_mode); } -RigidDynamicBody3D::DampMode RigidDynamicBody3D::get_angular_damp_mode() const { +RigidBody3D::DampMode RigidBody3D::get_angular_damp_mode() const { return angular_damp_mode; } -void RigidDynamicBody3D::set_linear_damp(real_t p_linear_damp) { +void RigidBody3D::set_linear_damp(real_t p_linear_damp) { ERR_FAIL_COND(p_linear_damp < 0.0); linear_damp = p_linear_damp; PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_LINEAR_DAMP, linear_damp); } -real_t RigidDynamicBody3D::get_linear_damp() const { +real_t RigidBody3D::get_linear_damp() const { return linear_damp; } -void RigidDynamicBody3D::set_angular_damp(real_t p_angular_damp) { +void RigidBody3D::set_angular_damp(real_t p_angular_damp) { ERR_FAIL_COND(p_angular_damp < 0.0); angular_damp = p_angular_damp; PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_ANGULAR_DAMP, angular_damp); } -real_t RigidDynamicBody3D::get_angular_damp() const { +real_t RigidBody3D::get_angular_damp() const { return angular_damp; } -void RigidDynamicBody3D::set_axis_velocity(const Vector3 &p_axis) { +void RigidBody3D::set_axis_velocity(const Vector3 &p_axis) { Vector3 axis = p_axis.normalized(); linear_velocity -= axis * axis.dot(linear_velocity); linear_velocity += p_axis; PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_LINEAR_VELOCITY, linear_velocity); } -void RigidDynamicBody3D::set_linear_velocity(const Vector3 &p_velocity) { +void RigidBody3D::set_linear_velocity(const Vector3 &p_velocity) { linear_velocity = p_velocity; PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_LINEAR_VELOCITY, linear_velocity); } -Vector3 RigidDynamicBody3D::get_linear_velocity() const { +Vector3 RigidBody3D::get_linear_velocity() const { return linear_velocity; } -void RigidDynamicBody3D::set_angular_velocity(const Vector3 &p_velocity) { +void RigidBody3D::set_angular_velocity(const Vector3 &p_velocity) { angular_velocity = p_velocity; PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_ANGULAR_VELOCITY, angular_velocity); } -Vector3 RigidDynamicBody3D::get_angular_velocity() const { +Vector3 RigidBody3D::get_angular_velocity() const { return angular_velocity; } -Basis RigidDynamicBody3D::get_inverse_inertia_tensor() const { +Basis RigidBody3D::get_inverse_inertia_tensor() const { return inverse_inertia_tensor; } -void RigidDynamicBody3D::set_use_custom_integrator(bool p_enable) { +void RigidBody3D::set_use_custom_integrator(bool p_enable) { if (custom_integrator == p_enable) { return; } @@ -834,102 +822,108 @@ void RigidDynamicBody3D::set_use_custom_integrator(bool p_enable) { PhysicsServer3D::get_singleton()->body_set_omit_force_integration(get_rid(), p_enable); } -bool RigidDynamicBody3D::is_using_custom_integrator() { +bool RigidBody3D::is_using_custom_integrator() { return custom_integrator; } -void RigidDynamicBody3D::set_sleeping(bool p_sleeping) { +void RigidBody3D::set_sleeping(bool p_sleeping) { sleeping = p_sleeping; PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_SLEEPING, sleeping); } -void RigidDynamicBody3D::set_can_sleep(bool p_active) { +void RigidBody3D::set_can_sleep(bool p_active) { can_sleep = p_active; PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_CAN_SLEEP, p_active); } -bool RigidDynamicBody3D::is_able_to_sleep() const { +bool RigidBody3D::is_able_to_sleep() const { return can_sleep; } -bool RigidDynamicBody3D::is_sleeping() const { +bool RigidBody3D::is_sleeping() const { return sleeping; } -void RigidDynamicBody3D::set_max_contacts_reported(int p_amount) { +void RigidBody3D::set_max_contacts_reported(int p_amount) { max_contacts_reported = p_amount; PhysicsServer3D::get_singleton()->body_set_max_contacts_reported(get_rid(), p_amount); } -int RigidDynamicBody3D::get_max_contacts_reported() const { +int RigidBody3D::get_max_contacts_reported() const { return max_contacts_reported; } -void RigidDynamicBody3D::apply_central_impulse(const Vector3 &p_impulse) { +int RigidBody3D::get_contact_count() const { + PhysicsDirectBodyState3D *bs = PhysicsServer3D::get_singleton()->body_get_direct_state(get_rid()); + ERR_FAIL_NULL_V(bs, 0); + return bs->get_contact_count(); +} + +void RigidBody3D::apply_central_impulse(const Vector3 &p_impulse) { PhysicsServer3D::get_singleton()->body_apply_central_impulse(get_rid(), p_impulse); } -void RigidDynamicBody3D::apply_impulse(const Vector3 &p_impulse, const Vector3 &p_position) { +void RigidBody3D::apply_impulse(const Vector3 &p_impulse, const Vector3 &p_position) { PhysicsServer3D *singleton = PhysicsServer3D::get_singleton(); singleton->body_apply_impulse(get_rid(), p_impulse, p_position); } -void RigidDynamicBody3D::apply_torque_impulse(const Vector3 &p_impulse) { +void RigidBody3D::apply_torque_impulse(const Vector3 &p_impulse) { PhysicsServer3D::get_singleton()->body_apply_torque_impulse(get_rid(), p_impulse); } -void RigidDynamicBody3D::apply_central_force(const Vector3 &p_force) { +void RigidBody3D::apply_central_force(const Vector3 &p_force) { PhysicsServer3D::get_singleton()->body_apply_central_force(get_rid(), p_force); } -void RigidDynamicBody3D::apply_force(const Vector3 &p_force, const Vector3 &p_position) { +void RigidBody3D::apply_force(const Vector3 &p_force, const Vector3 &p_position) { PhysicsServer3D *singleton = PhysicsServer3D::get_singleton(); singleton->body_apply_force(get_rid(), p_force, p_position); } -void RigidDynamicBody3D::apply_torque(const Vector3 &p_torque) { +void RigidBody3D::apply_torque(const Vector3 &p_torque) { PhysicsServer3D::get_singleton()->body_apply_torque(get_rid(), p_torque); } -void RigidDynamicBody3D::add_constant_central_force(const Vector3 &p_force) { +void RigidBody3D::add_constant_central_force(const Vector3 &p_force) { PhysicsServer3D::get_singleton()->body_add_constant_central_force(get_rid(), p_force); } -void RigidDynamicBody3D::add_constant_force(const Vector3 &p_force, const Vector3 &p_position) { +void RigidBody3D::add_constant_force(const Vector3 &p_force, const Vector3 &p_position) { PhysicsServer3D *singleton = PhysicsServer3D::get_singleton(); singleton->body_add_constant_force(get_rid(), p_force, p_position); } -void RigidDynamicBody3D::add_constant_torque(const Vector3 &p_torque) { +void RigidBody3D::add_constant_torque(const Vector3 &p_torque) { PhysicsServer3D::get_singleton()->body_add_constant_torque(get_rid(), p_torque); } -void RigidDynamicBody3D::set_constant_force(const Vector3 &p_force) { +void RigidBody3D::set_constant_force(const Vector3 &p_force) { PhysicsServer3D::get_singleton()->body_set_constant_force(get_rid(), p_force); } -Vector3 RigidDynamicBody3D::get_constant_force() const { +Vector3 RigidBody3D::get_constant_force() const { return PhysicsServer3D::get_singleton()->body_get_constant_force(get_rid()); } -void RigidDynamicBody3D::set_constant_torque(const Vector3 &p_torque) { +void RigidBody3D::set_constant_torque(const Vector3 &p_torque) { PhysicsServer3D::get_singleton()->body_set_constant_torque(get_rid(), p_torque); } -Vector3 RigidDynamicBody3D::get_constant_torque() const { +Vector3 RigidBody3D::get_constant_torque() const { return PhysicsServer3D::get_singleton()->body_get_constant_torque(get_rid()); } -void RigidDynamicBody3D::set_use_continuous_collision_detection(bool p_enable) { +void RigidBody3D::set_use_continuous_collision_detection(bool p_enable) { ccd = p_enable; PhysicsServer3D::get_singleton()->body_set_enable_continuous_collision_detection(get_rid(), p_enable); } -bool RigidDynamicBody3D::is_using_continuous_collision_detection() const { +bool RigidBody3D::is_using_continuous_collision_detection() const { return ccd; } -void RigidDynamicBody3D::set_contact_monitor(bool p_enabled) { +void RigidBody3D::set_contact_monitor(bool p_enabled) { if (p_enabled == is_contact_monitor_enabled()) { return; } @@ -943,8 +937,8 @@ void RigidDynamicBody3D::set_contact_monitor(bool p_enabled) { Node *node = Object::cast_to<Node>(obj); if (node) { - node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidDynamicBody3D::_body_enter_tree)); - node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidDynamicBody3D::_body_exit_tree)); + node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidBody3D::_body_enter_tree)); + node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidBody3D::_body_exit_tree)); } } @@ -956,14 +950,14 @@ void RigidDynamicBody3D::set_contact_monitor(bool p_enabled) { } } -bool RigidDynamicBody3D::is_contact_monitor_enabled() const { +bool RigidBody3D::is_contact_monitor_enabled() const { return contact_monitor != nullptr; } -Array RigidDynamicBody3D::get_colliding_bodies() const { - ERR_FAIL_COND_V(!contact_monitor, Array()); +TypedArray<Node3D> RigidBody3D::get_colliding_bodies() const { + ERR_FAIL_COND_V(!contact_monitor, TypedArray<Node3D>()); - Array ret; + TypedArray<Node3D> ret; ret.resize(contact_monitor->body_map.size()); int idx = 0; for (const KeyValue<ObjectID, BodyState> &E : contact_monitor->body_map) { @@ -978,118 +972,119 @@ Array RigidDynamicBody3D::get_colliding_bodies() const { return ret; } -TypedArray<String> RigidDynamicBody3D::get_configuration_warnings() const { +PackedStringArray RigidBody3D::get_configuration_warnings() const { Transform3D t = get_transform(); - TypedArray<String> warnings = Node::get_configuration_warnings(); + PackedStringArray warnings = Node::get_configuration_warnings(); if (ABS(t.basis.get_column(0).length() - 1.0) > 0.05 || ABS(t.basis.get_column(1).length() - 1.0) > 0.05 || ABS(t.basis.get_column(2).length() - 1.0) > 0.05) { - warnings.push_back(RTR("Size changes to RigidDynamicBody will be overridden by the physics engine when running.\nChange the size in children collision shapes instead.")); + warnings.push_back(RTR("Size changes to RigidBody will be overridden by the physics engine when running.\nChange the size in children collision shapes instead.")); } return warnings; } -void RigidDynamicBody3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_mass", "mass"), &RigidDynamicBody3D::set_mass); - ClassDB::bind_method(D_METHOD("get_mass"), &RigidDynamicBody3D::get_mass); +void RigidBody3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_mass", "mass"), &RigidBody3D::set_mass); + ClassDB::bind_method(D_METHOD("get_mass"), &RigidBody3D::get_mass); - ClassDB::bind_method(D_METHOD("set_inertia", "inertia"), &RigidDynamicBody3D::set_inertia); - ClassDB::bind_method(D_METHOD("get_inertia"), &RigidDynamicBody3D::get_inertia); + ClassDB::bind_method(D_METHOD("set_inertia", "inertia"), &RigidBody3D::set_inertia); + ClassDB::bind_method(D_METHOD("get_inertia"), &RigidBody3D::get_inertia); - ClassDB::bind_method(D_METHOD("set_center_of_mass_mode", "mode"), &RigidDynamicBody3D::set_center_of_mass_mode); - ClassDB::bind_method(D_METHOD("get_center_of_mass_mode"), &RigidDynamicBody3D::get_center_of_mass_mode); + ClassDB::bind_method(D_METHOD("set_center_of_mass_mode", "mode"), &RigidBody3D::set_center_of_mass_mode); + ClassDB::bind_method(D_METHOD("get_center_of_mass_mode"), &RigidBody3D::get_center_of_mass_mode); - ClassDB::bind_method(D_METHOD("set_center_of_mass", "center_of_mass"), &RigidDynamicBody3D::set_center_of_mass); - ClassDB::bind_method(D_METHOD("get_center_of_mass"), &RigidDynamicBody3D::get_center_of_mass); + ClassDB::bind_method(D_METHOD("set_center_of_mass", "center_of_mass"), &RigidBody3D::set_center_of_mass); + ClassDB::bind_method(D_METHOD("get_center_of_mass"), &RigidBody3D::get_center_of_mass); - ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &RigidDynamicBody3D::set_physics_material_override); - ClassDB::bind_method(D_METHOD("get_physics_material_override"), &RigidDynamicBody3D::get_physics_material_override); + ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &RigidBody3D::set_physics_material_override); + ClassDB::bind_method(D_METHOD("get_physics_material_override"), &RigidBody3D::get_physics_material_override); - ClassDB::bind_method(D_METHOD("set_linear_velocity", "linear_velocity"), &RigidDynamicBody3D::set_linear_velocity); - ClassDB::bind_method(D_METHOD("get_linear_velocity"), &RigidDynamicBody3D::get_linear_velocity); + ClassDB::bind_method(D_METHOD("set_linear_velocity", "linear_velocity"), &RigidBody3D::set_linear_velocity); + ClassDB::bind_method(D_METHOD("get_linear_velocity"), &RigidBody3D::get_linear_velocity); - ClassDB::bind_method(D_METHOD("set_angular_velocity", "angular_velocity"), &RigidDynamicBody3D::set_angular_velocity); - ClassDB::bind_method(D_METHOD("get_angular_velocity"), &RigidDynamicBody3D::get_angular_velocity); + ClassDB::bind_method(D_METHOD("set_angular_velocity", "angular_velocity"), &RigidBody3D::set_angular_velocity); + ClassDB::bind_method(D_METHOD("get_angular_velocity"), &RigidBody3D::get_angular_velocity); - ClassDB::bind_method(D_METHOD("get_inverse_inertia_tensor"), &RigidDynamicBody3D::get_inverse_inertia_tensor); + ClassDB::bind_method(D_METHOD("get_inverse_inertia_tensor"), &RigidBody3D::get_inverse_inertia_tensor); - ClassDB::bind_method(D_METHOD("set_gravity_scale", "gravity_scale"), &RigidDynamicBody3D::set_gravity_scale); - ClassDB::bind_method(D_METHOD("get_gravity_scale"), &RigidDynamicBody3D::get_gravity_scale); + ClassDB::bind_method(D_METHOD("set_gravity_scale", "gravity_scale"), &RigidBody3D::set_gravity_scale); + ClassDB::bind_method(D_METHOD("get_gravity_scale"), &RigidBody3D::get_gravity_scale); - ClassDB::bind_method(D_METHOD("set_linear_damp_mode", "linear_damp_mode"), &RigidDynamicBody3D::set_linear_damp_mode); - ClassDB::bind_method(D_METHOD("get_linear_damp_mode"), &RigidDynamicBody3D::get_linear_damp_mode); + ClassDB::bind_method(D_METHOD("set_linear_damp_mode", "linear_damp_mode"), &RigidBody3D::set_linear_damp_mode); + ClassDB::bind_method(D_METHOD("get_linear_damp_mode"), &RigidBody3D::get_linear_damp_mode); - ClassDB::bind_method(D_METHOD("set_angular_damp_mode", "angular_damp_mode"), &RigidDynamicBody3D::set_angular_damp_mode); - ClassDB::bind_method(D_METHOD("get_angular_damp_mode"), &RigidDynamicBody3D::get_angular_damp_mode); + ClassDB::bind_method(D_METHOD("set_angular_damp_mode", "angular_damp_mode"), &RigidBody3D::set_angular_damp_mode); + ClassDB::bind_method(D_METHOD("get_angular_damp_mode"), &RigidBody3D::get_angular_damp_mode); - ClassDB::bind_method(D_METHOD("set_linear_damp", "linear_damp"), &RigidDynamicBody3D::set_linear_damp); - ClassDB::bind_method(D_METHOD("get_linear_damp"), &RigidDynamicBody3D::get_linear_damp); + ClassDB::bind_method(D_METHOD("set_linear_damp", "linear_damp"), &RigidBody3D::set_linear_damp); + ClassDB::bind_method(D_METHOD("get_linear_damp"), &RigidBody3D::get_linear_damp); - ClassDB::bind_method(D_METHOD("set_angular_damp", "angular_damp"), &RigidDynamicBody3D::set_angular_damp); - ClassDB::bind_method(D_METHOD("get_angular_damp"), &RigidDynamicBody3D::get_angular_damp); + ClassDB::bind_method(D_METHOD("set_angular_damp", "angular_damp"), &RigidBody3D::set_angular_damp); + ClassDB::bind_method(D_METHOD("get_angular_damp"), &RigidBody3D::get_angular_damp); - ClassDB::bind_method(D_METHOD("set_max_contacts_reported", "amount"), &RigidDynamicBody3D::set_max_contacts_reported); - ClassDB::bind_method(D_METHOD("get_max_contacts_reported"), &RigidDynamicBody3D::get_max_contacts_reported); + ClassDB::bind_method(D_METHOD("set_max_contacts_reported", "amount"), &RigidBody3D::set_max_contacts_reported); + ClassDB::bind_method(D_METHOD("get_max_contacts_reported"), &RigidBody3D::get_max_contacts_reported); + ClassDB::bind_method(D_METHOD("get_contact_count"), &RigidBody3D::get_contact_count); - ClassDB::bind_method(D_METHOD("set_use_custom_integrator", "enable"), &RigidDynamicBody3D::set_use_custom_integrator); - ClassDB::bind_method(D_METHOD("is_using_custom_integrator"), &RigidDynamicBody3D::is_using_custom_integrator); + ClassDB::bind_method(D_METHOD("set_use_custom_integrator", "enable"), &RigidBody3D::set_use_custom_integrator); + ClassDB::bind_method(D_METHOD("is_using_custom_integrator"), &RigidBody3D::is_using_custom_integrator); - ClassDB::bind_method(D_METHOD("set_contact_monitor", "enabled"), &RigidDynamicBody3D::set_contact_monitor); - ClassDB::bind_method(D_METHOD("is_contact_monitor_enabled"), &RigidDynamicBody3D::is_contact_monitor_enabled); + ClassDB::bind_method(D_METHOD("set_contact_monitor", "enabled"), &RigidBody3D::set_contact_monitor); + ClassDB::bind_method(D_METHOD("is_contact_monitor_enabled"), &RigidBody3D::is_contact_monitor_enabled); - ClassDB::bind_method(D_METHOD("set_use_continuous_collision_detection", "enable"), &RigidDynamicBody3D::set_use_continuous_collision_detection); - ClassDB::bind_method(D_METHOD("is_using_continuous_collision_detection"), &RigidDynamicBody3D::is_using_continuous_collision_detection); + ClassDB::bind_method(D_METHOD("set_use_continuous_collision_detection", "enable"), &RigidBody3D::set_use_continuous_collision_detection); + ClassDB::bind_method(D_METHOD("is_using_continuous_collision_detection"), &RigidBody3D::is_using_continuous_collision_detection); - ClassDB::bind_method(D_METHOD("set_axis_velocity", "axis_velocity"), &RigidDynamicBody3D::set_axis_velocity); + ClassDB::bind_method(D_METHOD("set_axis_velocity", "axis_velocity"), &RigidBody3D::set_axis_velocity); - ClassDB::bind_method(D_METHOD("apply_central_impulse", "impulse"), &RigidDynamicBody3D::apply_central_impulse); - ClassDB::bind_method(D_METHOD("apply_impulse", "impulse", "position"), &RigidDynamicBody3D::apply_impulse, Vector3()); - ClassDB::bind_method(D_METHOD("apply_torque_impulse", "impulse"), &RigidDynamicBody3D::apply_torque_impulse); + ClassDB::bind_method(D_METHOD("apply_central_impulse", "impulse"), &RigidBody3D::apply_central_impulse); + ClassDB::bind_method(D_METHOD("apply_impulse", "impulse", "position"), &RigidBody3D::apply_impulse, Vector3()); + ClassDB::bind_method(D_METHOD("apply_torque_impulse", "impulse"), &RigidBody3D::apply_torque_impulse); - ClassDB::bind_method(D_METHOD("apply_central_force", "force"), &RigidDynamicBody3D::apply_central_force); - ClassDB::bind_method(D_METHOD("apply_force", "force", "position"), &RigidDynamicBody3D::apply_force, Vector3()); - ClassDB::bind_method(D_METHOD("apply_torque", "torque"), &RigidDynamicBody3D::apply_torque); + ClassDB::bind_method(D_METHOD("apply_central_force", "force"), &RigidBody3D::apply_central_force); + ClassDB::bind_method(D_METHOD("apply_force", "force", "position"), &RigidBody3D::apply_force, Vector3()); + ClassDB::bind_method(D_METHOD("apply_torque", "torque"), &RigidBody3D::apply_torque); - ClassDB::bind_method(D_METHOD("add_constant_central_force", "force"), &RigidDynamicBody3D::add_constant_central_force); - ClassDB::bind_method(D_METHOD("add_constant_force", "force", "position"), &RigidDynamicBody3D::add_constant_force, Vector3()); - ClassDB::bind_method(D_METHOD("add_constant_torque", "torque"), &RigidDynamicBody3D::add_constant_torque); + ClassDB::bind_method(D_METHOD("add_constant_central_force", "force"), &RigidBody3D::add_constant_central_force); + ClassDB::bind_method(D_METHOD("add_constant_force", "force", "position"), &RigidBody3D::add_constant_force, Vector3()); + ClassDB::bind_method(D_METHOD("add_constant_torque", "torque"), &RigidBody3D::add_constant_torque); - ClassDB::bind_method(D_METHOD("set_constant_force", "force"), &RigidDynamicBody3D::set_constant_force); - ClassDB::bind_method(D_METHOD("get_constant_force"), &RigidDynamicBody3D::get_constant_force); + ClassDB::bind_method(D_METHOD("set_constant_force", "force"), &RigidBody3D::set_constant_force); + ClassDB::bind_method(D_METHOD("get_constant_force"), &RigidBody3D::get_constant_force); - ClassDB::bind_method(D_METHOD("set_constant_torque", "torque"), &RigidDynamicBody3D::set_constant_torque); - ClassDB::bind_method(D_METHOD("get_constant_torque"), &RigidDynamicBody3D::get_constant_torque); + ClassDB::bind_method(D_METHOD("set_constant_torque", "torque"), &RigidBody3D::set_constant_torque); + ClassDB::bind_method(D_METHOD("get_constant_torque"), &RigidBody3D::get_constant_torque); - ClassDB::bind_method(D_METHOD("set_sleeping", "sleeping"), &RigidDynamicBody3D::set_sleeping); - ClassDB::bind_method(D_METHOD("is_sleeping"), &RigidDynamicBody3D::is_sleeping); + ClassDB::bind_method(D_METHOD("set_sleeping", "sleeping"), &RigidBody3D::set_sleeping); + ClassDB::bind_method(D_METHOD("is_sleeping"), &RigidBody3D::is_sleeping); - ClassDB::bind_method(D_METHOD("set_can_sleep", "able_to_sleep"), &RigidDynamicBody3D::set_can_sleep); - ClassDB::bind_method(D_METHOD("is_able_to_sleep"), &RigidDynamicBody3D::is_able_to_sleep); + ClassDB::bind_method(D_METHOD("set_can_sleep", "able_to_sleep"), &RigidBody3D::set_can_sleep); + ClassDB::bind_method(D_METHOD("is_able_to_sleep"), &RigidBody3D::is_able_to_sleep); - ClassDB::bind_method(D_METHOD("set_lock_rotation_enabled", "lock_rotation"), &RigidDynamicBody3D::set_lock_rotation_enabled); - ClassDB::bind_method(D_METHOD("is_lock_rotation_enabled"), &RigidDynamicBody3D::is_lock_rotation_enabled); + ClassDB::bind_method(D_METHOD("set_lock_rotation_enabled", "lock_rotation"), &RigidBody3D::set_lock_rotation_enabled); + ClassDB::bind_method(D_METHOD("is_lock_rotation_enabled"), &RigidBody3D::is_lock_rotation_enabled); - ClassDB::bind_method(D_METHOD("set_freeze_enabled", "freeze_mode"), &RigidDynamicBody3D::set_freeze_enabled); - ClassDB::bind_method(D_METHOD("is_freeze_enabled"), &RigidDynamicBody3D::is_freeze_enabled); + ClassDB::bind_method(D_METHOD("set_freeze_enabled", "freeze_mode"), &RigidBody3D::set_freeze_enabled); + ClassDB::bind_method(D_METHOD("is_freeze_enabled"), &RigidBody3D::is_freeze_enabled); - ClassDB::bind_method(D_METHOD("set_freeze_mode", "freeze_mode"), &RigidDynamicBody3D::set_freeze_mode); - ClassDB::bind_method(D_METHOD("get_freeze_mode"), &RigidDynamicBody3D::get_freeze_mode); + ClassDB::bind_method(D_METHOD("set_freeze_mode", "freeze_mode"), &RigidBody3D::set_freeze_mode); + ClassDB::bind_method(D_METHOD("get_freeze_mode"), &RigidBody3D::get_freeze_mode); - ClassDB::bind_method(D_METHOD("get_colliding_bodies"), &RigidDynamicBody3D::get_colliding_bodies); + ClassDB::bind_method(D_METHOD("get_colliding_bodies"), &RigidBody3D::get_colliding_bodies); GDVIRTUAL_BIND(_integrate_forces, "state"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass", PROPERTY_HINT_RANGE, "0.01,1000,0.01,or_greater,exp,suffix:kg"), "set_mass", "get_mass"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "inertia", PROPERTY_HINT_RANGE, U"0,1000,0.01,or_greater,exp,suffix:kg\u22C5m\u00B2"), "set_inertia", "get_inertia"); ADD_PROPERTY(PropertyInfo(Variant::INT, "center_of_mass_mode", PROPERTY_HINT_ENUM, "Auto,Custom", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_center_of_mass_mode", "get_center_of_mass_mode"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "center_of_mass", PROPERTY_HINT_RANGE, "-10,10,0.01,or_lesser,or_greater,suffix:m"), "set_center_of_mass", "get_center_of_mass"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "center_of_mass", PROPERTY_HINT_RANGE, "-10,10,0.01,or_less,or_greater,suffix:m"), "set_center_of_mass", "get_center_of_mass"); ADD_LINKED_PROPERTY("center_of_mass_mode", "center_of_mass"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_scale", PROPERTY_HINT_RANGE, "-128,128,0.01"), "set_gravity_scale", "get_gravity_scale"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "custom_integrator"), "set_use_custom_integrator", "is_using_custom_integrator"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "continuous_cd"), "set_use_continuous_collision_detection", "is_using_continuous_collision_detection"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "contacts_reported", PROPERTY_HINT_RANGE, "0,64,1,or_greater"), "set_max_contacts_reported", "get_max_contacts_reported"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "max_contacts_reported", PROPERTY_HINT_RANGE, "0,64,1,or_greater"), "set_max_contacts_reported", "get_max_contacts_reported"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "contact_monitor"), "set_contact_monitor", "is_contact_monitor_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sleeping"), "set_sleeping", "is_sleeping"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "can_sleep"), "set_can_sleep", "is_able_to_sleep"); @@ -1124,27 +1119,26 @@ void RigidDynamicBody3D::_bind_methods() { BIND_ENUM_CONSTANT(DAMP_MODE_REPLACE); } -void RigidDynamicBody3D::_validate_property(PropertyInfo &property) const { +void RigidBody3D::_validate_property(PropertyInfo &p_property) const { if (center_of_mass_mode != CENTER_OF_MASS_MODE_CUSTOM) { - if (property.name == "center_of_mass") { - property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + if (p_property.name == "center_of_mass") { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } } - PhysicsBody3D::_validate_property(property); } -RigidDynamicBody3D::RigidDynamicBody3D() : - PhysicsBody3D(PhysicsServer3D::BODY_MODE_DYNAMIC) { - PhysicsServer3D::get_singleton()->body_set_state_sync_callback(get_rid(), this, _body_state_changed_callback); +RigidBody3D::RigidBody3D() : + PhysicsBody3D(PhysicsServer3D::BODY_MODE_RIGID) { + PhysicsServer3D::get_singleton()->body_set_state_sync_callback(get_rid(), callable_mp(this, &RigidBody3D::_body_state_changed)); } -RigidDynamicBody3D::~RigidDynamicBody3D() { +RigidBody3D::~RigidBody3D() { if (contact_monitor) { memdelete(contact_monitor); } } -void RigidDynamicBody3D::_reload_physics_characteristics() { +void RigidBody3D::_reload_physics_characteristics() { if (physics_material_override.is_null()) { PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_BOUNCE, 0); PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_FRICTION, 1); @@ -1177,9 +1171,9 @@ bool CharacterBody3D::move_and_slide() { if ((collision_state.floor || collision_state.wall) && platform_rid.is_valid()) { bool excluded = false; if (collision_state.floor) { - excluded = (moving_platform_floor_layers & platform_layer) == 0; + excluded = (platform_floor_layers & platform_layer) == 0; } else if (collision_state.wall) { - excluded = (moving_platform_wall_layers & platform_layer) == 0; + excluded = (platform_wall_layers & platform_layer) == 0; } if (!excluded) { //this approach makes sure there is less delay between the actual body velocity and the one we saved @@ -1204,9 +1198,8 @@ bool CharacterBody3D::move_and_slide() { last_motion = Vector3(); - if (!current_platform_velocity.is_equal_approx(Vector3())) { + if (!current_platform_velocity.is_zero_approx()) { PhysicsServer3D::MotionParameters parameters(get_global_transform(), current_platform_velocity * delta, margin); - parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. parameters.exclude_bodies.insert(platform_rid); if (platform_object_id.is_valid()) { @@ -1231,10 +1224,10 @@ bool CharacterBody3D::move_and_slide() { // Compute real velocity. real_velocity = get_position_delta() / delta; - if (moving_platform_apply_velocity_on_leave != PLATFORM_VEL_ON_LEAVE_NEVER) { + if (platform_on_leave != PLATFORM_ON_LEAVE_DO_NOTHING) { // Add last platform velocity when just left a moving platform. if (!collision_state.floor && !collision_state.wall) { - if (moving_platform_apply_velocity_on_leave == PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY && current_platform_velocity.dot(up_direction) < 0) { + if (platform_on_leave == PLATFORM_ON_LEAVE_ADD_UPWARD_VELOCITY && current_platform_velocity.dot(up_direction) < 0) { current_platform_velocity = current_platform_velocity.slide(up_direction); } velocity += current_platform_velocity; @@ -1270,8 +1263,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo for (int iteration = 0; iteration < max_slides; ++iteration) { PhysicsServer3D::MotionParameters parameters(get_global_transform(), motion, margin); - parameters.max_collisions = 4; - parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. + parameters.max_collisions = 6; // There can be 4 collisions between 2 walls + 2 more for the floor. PhysicsServer3D::MotionResult result; bool collided = move_and_collide(parameters, result, false, !sliding_enabled); @@ -1311,7 +1303,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo break; } - if (result.remainder.is_equal_approx(Vector3())) { + if (result.remainder.is_zero_approx()) { motion = Vector3(); break; } @@ -1424,7 +1416,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo const PhysicsServer3D::MotionCollision &collision = result.collisions[0]; Vector3 slide_motion = result.remainder.slide(collision.normal); - if (collision_state.floor && !collision_state.wall && !motion_slide_up.is_equal_approx(Vector3())) { + if (collision_state.floor && !collision_state.wall && !motion_slide_up.is_zero_approx()) { // Slide using the intersection between the motion plane and the floor plane, // in order to keep the direction intact. real_t motion_length = slide_motion.length(); @@ -1465,7 +1457,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo total_travel += result.travel; // Apply Constant Speed. - if (p_was_on_floor && floor_constant_speed && can_apply_constant_speed && collision_state.floor && !motion.is_equal_approx(Vector3())) { + if (p_was_on_floor && floor_constant_speed && can_apply_constant_speed && collision_state.floor && !motion.is_zero_approx()) { Vector3 travel_slide_up = total_travel.slide(up_direction); motion = motion.normalized() * MAX(0, (motion_slide_up.length() - travel_slide_up.length())); } @@ -1488,7 +1480,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo collided = true; } - if (!collided || motion.is_equal_approx(Vector3())) { + if (!collided || motion.is_zero_approx()) { break; } @@ -1516,7 +1508,6 @@ void CharacterBody3D::_move_and_slide_floating(double p_delta) { bool first_slide = true; for (int iteration = 0; iteration < max_slides; ++iteration) { PhysicsServer3D::MotionParameters parameters(get_global_transform(), motion, margin); - parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. PhysicsServer3D::MotionResult result; bool collided = move_and_collide(parameters, result, false, false); @@ -1529,7 +1520,7 @@ void CharacterBody3D::_move_and_slide_floating(double p_delta) { CollisionState result_state; _set_collision_direction(result, result_state); - if (result.remainder.is_equal_approx(Vector3())) { + if (result.remainder.is_zero_approx()) { motion = Vector3(); break; } @@ -1553,7 +1544,7 @@ void CharacterBody3D::_move_and_slide_floating(double p_delta) { } } - if (!collided || motion.is_equal_approx(Vector3())) { + if (!collided || motion.is_zero_approx()) { break; } @@ -1571,7 +1562,7 @@ void CharacterBody3D::_snap_on_floor(bool p_was_on_floor, bool p_vel_dir_facing_ PhysicsServer3D::MotionParameters parameters(get_global_transform(), -up_direction * length, margin); parameters.max_collisions = 4; - parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. + parameters.recovery_as_collision = true; // Report margin recovery as collision to improve floor detection. parameters.collide_separation_ray = true; PhysicsServer3D::MotionResult result; @@ -1607,7 +1598,7 @@ bool CharacterBody3D::_on_floor_if_snapped(bool p_was_on_floor, bool p_vel_dir_f PhysicsServer3D::MotionParameters parameters(get_global_transform(), -up_direction * length, margin); parameters.max_collisions = 4; - parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. + parameters.recovery_as_collision = true; // Report margin recovery as collision to improve floor detection. parameters.collide_separation_ray = true; PhysicsServer3D::MotionResult result; @@ -1806,7 +1797,7 @@ Ref<KinematicCollision3D> CharacterBody3D::_get_slide_collision(int p_bounce) { } // Create a new instance when the cached reference is invalid or still in use in script. - if (slide_colliders[p_bounce].is_null() || slide_colliders[p_bounce]->reference_get_count() > 1) { + if (slide_colliders[p_bounce].is_null() || slide_colliders[p_bounce]->get_reference_count() > 1) { slide_colliders.write[p_bounce].instantiate(); slide_colliders.write[p_bounce]->owner = this; } @@ -1854,20 +1845,20 @@ void CharacterBody3D::set_slide_on_ceiling_enabled(bool p_enabled) { slide_on_ceiling = p_enabled; } -uint32_t CharacterBody3D::get_moving_platform_floor_layers() const { - return moving_platform_floor_layers; +uint32_t CharacterBody3D::get_platform_floor_layers() const { + return platform_floor_layers; } -void CharacterBody3D::set_moving_platform_floor_layers(uint32_t p_exclude_layers) { - moving_platform_floor_layers = p_exclude_layers; +void CharacterBody3D::set_platform_floor_layers(uint32_t p_exclude_layers) { + platform_floor_layers = p_exclude_layers; } -uint32_t CharacterBody3D::get_moving_platform_wall_layers() const { - return moving_platform_wall_layers; +uint32_t CharacterBody3D::get_platform_wall_layers() const { + return platform_wall_layers; } -void CharacterBody3D::set_moving_platform_wall_layers(uint32_t p_exclude_layers) { - moving_platform_wall_layers = p_exclude_layers; +void CharacterBody3D::set_platform_wall_layers(uint32_t p_exclude_layers) { + platform_wall_layers = p_exclude_layers; } void CharacterBody3D::set_motion_mode(MotionMode p_mode) { @@ -1878,12 +1869,12 @@ CharacterBody3D::MotionMode CharacterBody3D::get_motion_mode() const { return motion_mode; } -void CharacterBody3D::set_moving_platform_apply_velocity_on_leave(MovingPlatformApplyVelocityOnLeave p_on_leave_apply_velocity) { - moving_platform_apply_velocity_on_leave = p_on_leave_apply_velocity; +void CharacterBody3D::set_platform_on_leave(PlatformOnLeave p_on_leave_apply_velocity) { + platform_on_leave = p_on_leave_apply_velocity; } -CharacterBody3D::MovingPlatformApplyVelocityOnLeave CharacterBody3D::get_moving_platform_apply_velocity_on_leave() const { - return moving_platform_apply_velocity_on_leave; +CharacterBody3D::PlatformOnLeave CharacterBody3D::get_platform_on_leave() const { + return platform_on_leave; } int CharacterBody3D::get_max_slides() const { @@ -1948,7 +1939,7 @@ void CharacterBody3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &CharacterBody3D::set_velocity); ClassDB::bind_method(D_METHOD("get_velocity"), &CharacterBody3D::get_velocity); - ClassDB::bind_method(D_METHOD("set_safe_margin", "pixels"), &CharacterBody3D::set_safe_margin); + ClassDB::bind_method(D_METHOD("set_safe_margin", "margin"), &CharacterBody3D::set_safe_margin); ClassDB::bind_method(D_METHOD("get_safe_margin"), &CharacterBody3D::get_safe_margin); ClassDB::bind_method(D_METHOD("is_floor_stop_on_slope_enabled"), &CharacterBody3D::is_floor_stop_on_slope_enabled); ClassDB::bind_method(D_METHOD("set_floor_stop_on_slope_enabled", "enabled"), &CharacterBody3D::set_floor_stop_on_slope_enabled); @@ -1959,10 +1950,10 @@ void CharacterBody3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_slide_on_ceiling_enabled", "enabled"), &CharacterBody3D::set_slide_on_ceiling_enabled); ClassDB::bind_method(D_METHOD("is_slide_on_ceiling_enabled"), &CharacterBody3D::is_slide_on_ceiling_enabled); - ClassDB::bind_method(D_METHOD("set_moving_platform_floor_layers", "exclude_layer"), &CharacterBody3D::set_moving_platform_floor_layers); - ClassDB::bind_method(D_METHOD("get_moving_platform_floor_layers"), &CharacterBody3D::get_moving_platform_floor_layers); - ClassDB::bind_method(D_METHOD("set_moving_platform_wall_layers", "exclude_layer"), &CharacterBody3D::set_moving_platform_wall_layers); - ClassDB::bind_method(D_METHOD("get_moving_platform_wall_layers"), &CharacterBody3D::get_moving_platform_wall_layers); + ClassDB::bind_method(D_METHOD("set_platform_floor_layers", "exclude_layer"), &CharacterBody3D::set_platform_floor_layers); + ClassDB::bind_method(D_METHOD("get_platform_floor_layers"), &CharacterBody3D::get_platform_floor_layers); + ClassDB::bind_method(D_METHOD("set_platform_wall_layers", "exclude_layer"), &CharacterBody3D::set_platform_wall_layers); + ClassDB::bind_method(D_METHOD("get_platform_wall_layers"), &CharacterBody3D::get_platform_wall_layers); ClassDB::bind_method(D_METHOD("get_max_slides"), &CharacterBody3D::get_max_slides); ClassDB::bind_method(D_METHOD("set_max_slides", "max_slides"), &CharacterBody3D::set_max_slides); @@ -1976,8 +1967,8 @@ void CharacterBody3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_up_direction", "up_direction"), &CharacterBody3D::set_up_direction); ClassDB::bind_method(D_METHOD("set_motion_mode", "mode"), &CharacterBody3D::set_motion_mode); ClassDB::bind_method(D_METHOD("get_motion_mode"), &CharacterBody3D::get_motion_mode); - ClassDB::bind_method(D_METHOD("set_moving_platform_apply_velocity_on_leave", "on_leave_apply_velocity"), &CharacterBody3D::set_moving_platform_apply_velocity_on_leave); - ClassDB::bind_method(D_METHOD("get_moving_platform_apply_velocity_on_leave"), &CharacterBody3D::get_moving_platform_apply_velocity_on_leave); + ClassDB::bind_method(D_METHOD("set_platform_on_leave", "on_leave_apply_velocity"), &CharacterBody3D::set_platform_on_leave); + ClassDB::bind_method(D_METHOD("get_platform_on_leave"), &CharacterBody3D::get_platform_on_leave); ClassDB::bind_method(D_METHOD("is_on_floor"), &CharacterBody3D::is_on_floor); ClassDB::bind_method(D_METHOD("is_on_floor_only"), &CharacterBody3D::is_on_floor_only); @@ -2002,33 +1993,36 @@ void CharacterBody3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "velocity", PROPERTY_HINT_NONE, "suffix:m/s", PROPERTY_USAGE_NO_EDITOR), "set_velocity", "get_velocity"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_max_slides", "get_max_slides"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wall_min_slide_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians", PROPERTY_USAGE_DEFAULT), "set_wall_min_slide_angle", "get_wall_min_slide_angle"); + ADD_GROUP("Floor", "floor_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_stop_on_slope"), "set_floor_stop_on_slope_enabled", "is_floor_stop_on_slope_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_constant_speed"), "set_floor_constant_speed_enabled", "is_floor_constant_speed_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_block_on_wall"), "set_floor_block_on_wall_enabled", "is_floor_block_on_wall_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_max_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians"), "set_floor_max_angle", "get_floor_max_angle"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_snap_length", PROPERTY_HINT_RANGE, "0,1,0.01,or_greater,suffix:m"), "set_floor_snap_length", "get_floor_snap_length"); - ADD_GROUP("Moving Platform", "moving_platform"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_apply_velocity_on_leave", PROPERTY_HINT_ENUM, "Always,Upward Only,Never", PROPERTY_USAGE_DEFAULT), "set_moving_platform_apply_velocity_on_leave", "get_moving_platform_apply_velocity_on_leave"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_floor_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_moving_platform_floor_layers", "get_moving_platform_floor_layers"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_wall_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_moving_platform_wall_layers", "get_moving_platform_wall_layers"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001,suffix:m"), "set_safe_margin", "get_safe_margin"); + + ADD_GROUP("Moving Platform", "platform_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "platform_on_leave", PROPERTY_HINT_ENUM, "Add Velocity,Add Upward Velocity,Do Nothing", PROPERTY_USAGE_DEFAULT), "set_platform_on_leave", "get_platform_on_leave"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "platform_floor_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_platform_floor_layers", "get_platform_floor_layers"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "platform_wall_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_platform_wall_layers", "get_platform_wall_layers"); + + ADD_GROUP("Collision", ""); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001,suffix:m"), "set_safe_margin", "get_safe_margin"); BIND_ENUM_CONSTANT(MOTION_MODE_GROUNDED); BIND_ENUM_CONSTANT(MOTION_MODE_FLOATING); - BIND_ENUM_CONSTANT(PLATFORM_VEL_ON_LEAVE_ALWAYS); - BIND_ENUM_CONSTANT(PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY); - BIND_ENUM_CONSTANT(PLATFORM_VEL_ON_LEAVE_NEVER); + BIND_ENUM_CONSTANT(PLATFORM_ON_LEAVE_ADD_VELOCITY); + BIND_ENUM_CONSTANT(PLATFORM_ON_LEAVE_ADD_UPWARD_VELOCITY); + BIND_ENUM_CONSTANT(PLATFORM_ON_LEAVE_DO_NOTHING); } -void CharacterBody3D::_validate_property(PropertyInfo &property) const { +void CharacterBody3D::_validate_property(PropertyInfo &p_property) const { if (motion_mode == MOTION_MODE_FLOATING) { - if (property.name.begins_with("floor_") || property.name == "up_direction" || property.name == "slide_on_ceiling") { - property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + if (p_property.name.begins_with("floor_") || p_property.name == "up_direction" || p_property.name == "slide_on_ceiling") { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } } - PhysicsBody3D::_validate_property(property); } CharacterBody3D::CharacterBody3D() : @@ -2057,6 +2051,10 @@ int KinematicCollision3D::get_collision_count() const { return result.collision_count; } +real_t KinematicCollision3D::get_depth() const { + return result.collision_depth; +} + Vector3 KinematicCollision3D::get_position(int p_collision_index) const { ERR_FAIL_INDEX_V(p_collision_index, result.collision_count, Vector3()); return result.collisions[p_collision_index].position; @@ -2127,6 +2125,7 @@ Vector3 KinematicCollision3D::get_collider_velocity(int p_collision_index) const void KinematicCollision3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_travel"), &KinematicCollision3D::get_travel); ClassDB::bind_method(D_METHOD("get_remainder"), &KinematicCollision3D::get_remainder); + ClassDB::bind_method(D_METHOD("get_depth"), &KinematicCollision3D::get_depth); ClassDB::bind_method(D_METHOD("get_collision_count"), &KinematicCollision3D::get_collision_count); ClassDB::bind_method(D_METHOD("get_position", "collision_index"), &KinematicCollision3D::get_position, DEFVAL(0)); ClassDB::bind_method(D_METHOD("get_normal", "collision_index"), &KinematicCollision3D::get_normal, DEFVAL(0)); @@ -2272,13 +2271,13 @@ bool PhysicalBone3D::ConeJointData::_set(const StringName &p_name, const Variant } if ("joint_constraints/swing_span" == p_name) { - swing_span = Math::deg2rad(real_t(p_value)); + swing_span = Math::deg_to_rad(real_t(p_value)); if (j.is_valid()) { PhysicsServer3D::get_singleton()->cone_twist_joint_set_param(j, PhysicsServer3D::CONE_TWIST_JOINT_SWING_SPAN, swing_span); } } else if ("joint_constraints/twist_span" == p_name) { - twist_span = Math::deg2rad(real_t(p_value)); + twist_span = Math::deg_to_rad(real_t(p_value)); if (j.is_valid()) { PhysicsServer3D::get_singleton()->cone_twist_joint_set_param(j, PhysicsServer3D::CONE_TWIST_JOINT_TWIST_SPAN, twist_span); } @@ -2314,9 +2313,9 @@ bool PhysicalBone3D::ConeJointData::_get(const StringName &p_name, Variant &r_re } if ("joint_constraints/swing_span" == p_name) { - r_ret = Math::rad2deg(swing_span); + r_ret = Math::rad_to_deg(swing_span); } else if ("joint_constraints/twist_span" == p_name) { - r_ret = Math::rad2deg(twist_span); + r_ret = Math::rad_to_deg(twist_span); } else if ("joint_constraints/bias" == p_name) { r_ret = bias; } else if ("joint_constraints/softness" == p_name) { @@ -2334,7 +2333,7 @@ void PhysicalBone3D::ConeJointData::_get_property_list(List<PropertyInfo> *p_lis JointData::_get_property_list(p_list); p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/swing_span"), PROPERTY_HINT_RANGE, "-180,180,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/twist_span"), PROPERTY_HINT_RANGE, "-40000,40000,0.1,or_lesser,or_greater")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/twist_span"), PROPERTY_HINT_RANGE, "-40000,40000,0.1,or_less,or_greater")); p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/bias"), PROPERTY_HINT_RANGE, "0.01,16.0,0.01")); p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/softness"), PROPERTY_HINT_RANGE, "0.01,16.0,0.01")); p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/relaxation"), PROPERTY_HINT_RANGE, "0.01,16.0,0.01")); @@ -2352,13 +2351,13 @@ bool PhysicalBone3D::HingeJointData::_set(const StringName &p_name, const Varian } } else if ("joint_constraints/angular_limit_upper" == p_name) { - angular_limit_upper = Math::deg2rad(real_t(p_value)); + angular_limit_upper = Math::deg_to_rad(real_t(p_value)); if (j.is_valid()) { PhysicsServer3D::get_singleton()->hinge_joint_set_param(j, PhysicsServer3D::HINGE_JOINT_LIMIT_UPPER, angular_limit_upper); } } else if ("joint_constraints/angular_limit_lower" == p_name) { - angular_limit_lower = Math::deg2rad(real_t(p_value)); + angular_limit_lower = Math::deg_to_rad(real_t(p_value)); if (j.is_valid()) { PhysicsServer3D::get_singleton()->hinge_joint_set_param(j, PhysicsServer3D::HINGE_JOINT_LIMIT_LOWER, angular_limit_lower); } @@ -2396,9 +2395,9 @@ bool PhysicalBone3D::HingeJointData::_get(const StringName &p_name, Variant &r_r if ("joint_constraints/angular_limit_enabled" == p_name) { r_ret = angular_limit_enabled; } else if ("joint_constraints/angular_limit_upper" == p_name) { - r_ret = Math::rad2deg(angular_limit_upper); + r_ret = Math::rad_to_deg(angular_limit_upper); } else if ("joint_constraints/angular_limit_lower" == p_name) { - r_ret = Math::rad2deg(angular_limit_lower); + r_ret = Math::rad_to_deg(angular_limit_lower); } else if ("joint_constraints/angular_limit_bias" == p_name) { r_ret = angular_limit_bias; } else if ("joint_constraints/angular_limit_softness" == p_name) { @@ -2459,13 +2458,13 @@ bool PhysicalBone3D::SliderJointData::_set(const StringName &p_name, const Varia } } else if ("joint_constraints/angular_limit_upper" == p_name) { - angular_limit_upper = Math::deg2rad(real_t(p_value)); + angular_limit_upper = Math::deg_to_rad(real_t(p_value)); if (j.is_valid()) { PhysicsServer3D::get_singleton()->slider_joint_set_param(j, PhysicsServer3D::SLIDER_JOINT_ANGULAR_LIMIT_UPPER, angular_limit_upper); } } else if ("joint_constraints/angular_limit_lower" == p_name) { - angular_limit_lower = Math::deg2rad(real_t(p_value)); + angular_limit_lower = Math::deg_to_rad(real_t(p_value)); if (j.is_valid()) { PhysicsServer3D::get_singleton()->slider_joint_set_param(j, PhysicsServer3D::SLIDER_JOINT_ANGULAR_LIMIT_LOWER, angular_limit_lower); } @@ -2511,9 +2510,9 @@ bool PhysicalBone3D::SliderJointData::_get(const StringName &p_name, Variant &r_ } else if ("joint_constraints/linear_limit_damping" == p_name) { r_ret = linear_limit_damping; } else if ("joint_constraints/angular_limit_upper" == p_name) { - r_ret = Math::rad2deg(angular_limit_upper); + r_ret = Math::rad_to_deg(angular_limit_upper); } else if ("joint_constraints/angular_limit_lower" == p_name) { - r_ret = Math::rad2deg(angular_limit_lower); + r_ret = Math::rad_to_deg(angular_limit_lower); } else if ("joint_constraints/angular_limit_softness" == p_name) { r_ret = angular_limit_softness; } else if ("joint_constraints/angular_limit_restitution" == p_name) { @@ -2637,13 +2636,13 @@ bool PhysicalBone3D::SixDOFJointData::_set(const StringName &p_name, const Varia } } else if ("angular_limit_upper" == var_name) { - axis_data[axis].angular_limit_upper = Math::deg2rad(real_t(p_value)); + axis_data[axis].angular_limit_upper = Math::deg_to_rad(real_t(p_value)); if (j.is_valid()) { PhysicsServer3D::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer3D::G6DOF_JOINT_ANGULAR_UPPER_LIMIT, axis_data[axis].angular_limit_upper); } } else if ("angular_limit_lower" == var_name) { - axis_data[axis].angular_limit_lower = Math::deg2rad(real_t(p_value)); + axis_data[axis].angular_limit_lower = Math::deg_to_rad(real_t(p_value)); if (j.is_valid()) { PhysicsServer3D::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer3D::G6DOF_JOINT_ANGULAR_LOWER_LIMIT, axis_data[axis].angular_limit_lower); } @@ -2753,9 +2752,9 @@ bool PhysicalBone3D::SixDOFJointData::_get(const StringName &p_name, Variant &r_ } else if ("angular_limit_enabled" == var_name) { r_ret = axis_data[axis].angular_limit_enabled; } else if ("angular_limit_upper" == var_name) { - r_ret = Math::rad2deg(axis_data[axis].angular_limit_upper); + r_ret = Math::rad_to_deg(axis_data[axis].angular_limit_upper); } else if ("angular_limit_lower" == var_name) { - r_ret = Math::rad2deg(axis_data[axis].angular_limit_lower); + r_ret = Math::rad_to_deg(axis_data[axis].angular_limit_lower); } else if ("angular_limit_softness" == var_name) { r_ret = axis_data[axis].angular_limit_softness; } else if ("angular_restitution" == var_name) { @@ -2891,11 +2890,6 @@ void PhysicalBone3D::_notification(int p_what) { } } -void PhysicalBone3D::_body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state) { - PhysicalBone3D *bone = (PhysicalBone3D *)p_instance; - bone->_body_state_changed(p_state); -} - void PhysicalBone3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) { if (!simulate_physics || !_internal_simulate_physics) { return; @@ -2985,7 +2979,7 @@ void PhysicalBone3D::_bind_methods() { ADD_GROUP("Joint", "joint_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "joint_type", PROPERTY_HINT_ENUM, "None,PinJoint,ConeJoint,HingeJoint,SliderJoint,6DOFJoint"), "set_joint_type", "get_joint_type"); ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "joint_offset", PROPERTY_HINT_NONE, "suffix:m"), "set_joint_offset", "get_joint_offset"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "joint_rotation", PROPERTY_HINT_RANGE, "-360,360,0.01,or_lesser,or_greater,radians"), "set_joint_rotation", "get_joint_rotation"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "joint_rotation", PROPERTY_HINT_RANGE, "-360,360,0.01,or_less,or_greater,radians"), "set_joint_rotation", "get_joint_rotation"); ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "body_offset", PROPERTY_HINT_NONE, "suffix:m"), "set_body_offset", "get_body_offset"); @@ -3409,10 +3403,11 @@ void PhysicalBone3D::_start_physics_simulation() { return; } reset_to_rest_position(); - set_body_mode(PhysicsServer3D::BODY_MODE_DYNAMIC); + set_body_mode(PhysicsServer3D::BODY_MODE_RIGID); PhysicsServer3D::get_singleton()->body_set_collision_layer(get_rid(), get_collision_layer()); PhysicsServer3D::get_singleton()->body_set_collision_mask(get_rid(), get_collision_mask()); - PhysicsServer3D::get_singleton()->body_set_state_sync_callback(get_rid(), this, _body_state_changed_callback); + PhysicsServer3D::get_singleton()->body_set_collision_priority(get_rid(), get_collision_priority()); + PhysicsServer3D::get_singleton()->body_set_state_sync_callback(get_rid(), callable_mp(this, &PhysicalBone3D::_body_state_changed)); set_as_top_level(true); _internal_simulate_physics = true; } @@ -3425,13 +3420,15 @@ void PhysicalBone3D::_stop_physics_simulation() { set_body_mode(PhysicsServer3D::BODY_MODE_KINEMATIC); PhysicsServer3D::get_singleton()->body_set_collision_layer(get_rid(), get_collision_layer()); PhysicsServer3D::get_singleton()->body_set_collision_mask(get_rid(), get_collision_mask()); + PhysicsServer3D::get_singleton()->body_set_collision_priority(get_rid(), get_collision_priority()); } else { set_body_mode(PhysicsServer3D::BODY_MODE_STATIC); PhysicsServer3D::get_singleton()->body_set_collision_layer(get_rid(), 0); PhysicsServer3D::get_singleton()->body_set_collision_mask(get_rid(), 0); + PhysicsServer3D::get_singleton()->body_set_collision_priority(get_rid(), 1.0); } if (_internal_simulate_physics) { - PhysicsServer3D::get_singleton()->body_set_state_sync_callback(get_rid(), nullptr, nullptr); + PhysicsServer3D::get_singleton()->body_set_state_sync_callback(get_rid(), Callable()); parent_skeleton->set_bone_global_pose_override(bone_id, Transform3D(), 0.0, false); set_as_top_level(false); _internal_simulate_physics = false; diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h index 22dcb218bc..4b874b91d9 100644 --- a/scene/3d/physics_body_3d.h +++ b/scene/3d/physics_body_3d.h @@ -50,11 +50,11 @@ protected: uint16_t locked_axis = 0; - Ref<KinematicCollision3D> _move(const Vector3 &p_distance, bool p_test_only = false, real_t p_margin = 0.001, int p_max_collisions = 1); + Ref<KinematicCollision3D> _move(const Vector3 &p_distance, bool p_test_only = false, real_t p_margin = 0.001, bool p_recovery_as_collision = false, int p_max_collisions = 1); public: bool move_and_collide(const PhysicsServer3D::MotionParameters &p_parameters, PhysicsServer3D::MotionResult &r_result, bool p_test_only = false, bool p_cancel_sliding = true); - bool test_move(const Transform3D &p_from, const Vector3 &p_distance, const Ref<KinematicCollision3D> &r_collision = Ref<KinematicCollision3D>(), real_t p_margin = 0.001, int p_max_collisions = 1); + bool test_move(const Transform3D &p_from, const Vector3 &p_distance, const Ref<KinematicCollision3D> &r_collision = Ref<KinematicCollision3D>(), real_t p_margin = 0.001, bool p_recovery_as_collision = false, int p_max_collisions = 1); void set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool p_lock); bool get_axis_lock(PhysicsServer3D::BodyAxis p_axis) const; @@ -129,8 +129,8 @@ private: bool is_sync_to_physics_enabled() const; }; -class RigidDynamicBody3D : public PhysicsBody3D { - GDCLASS(RigidDynamicBody3D, PhysicsBody3D); +class RigidBody3D : public PhysicsBody3D { + GDCLASS(RigidBody3D, PhysicsBody3D); public: enum FreezeMode { @@ -198,7 +198,7 @@ private: tagged = false; } }; - struct RigidDynamicBody3D_RemoveAction { + struct RigidBody3D_RemoveAction { RID rid; ObjectID body_id; ShapePair pair; @@ -226,7 +226,7 @@ protected: void _notification(int p_what); static void _bind_methods(); - virtual void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; GDVIRTUAL1(_integrate_forces, PhysicsDirectBodyState3D *) @@ -300,11 +300,12 @@ public: void set_max_contacts_reported(int p_amount); int get_max_contacts_reported() const; + int get_contact_count() const; void set_use_continuous_collision_detection(bool p_enable); bool is_using_continuous_collision_detection() const; - Array get_colliding_bodies() const; + TypedArray<Node3D> get_colliding_bodies() const; void apply_central_impulse(const Vector3 &p_impulse); void apply_impulse(const Vector3 &p_impulse, const Vector3 &p_position = Vector3()); @@ -324,18 +325,18 @@ public: void set_constant_torque(const Vector3 &p_torque); Vector3 get_constant_torque() const; - virtual TypedArray<String> get_configuration_warnings() const override; + virtual PackedStringArray get_configuration_warnings() const override; - RigidDynamicBody3D(); - ~RigidDynamicBody3D(); + RigidBody3D(); + ~RigidBody3D(); private: void _reload_physics_characteristics(); }; -VARIANT_ENUM_CAST(RigidDynamicBody3D::FreezeMode); -VARIANT_ENUM_CAST(RigidDynamicBody3D::CenterOfMassMode); -VARIANT_ENUM_CAST(RigidDynamicBody3D::DampMode); +VARIANT_ENUM_CAST(RigidBody3D::FreezeMode); +VARIANT_ENUM_CAST(RigidBody3D::CenterOfMassMode); +VARIANT_ENUM_CAST(RigidBody3D::DampMode); class KinematicCollision3D; @@ -347,10 +348,10 @@ public: MOTION_MODE_GROUNDED, MOTION_MODE_FLOATING, }; - enum MovingPlatformApplyVelocityOnLeave { - PLATFORM_VEL_ON_LEAVE_ALWAYS, - PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY, - PLATFORM_VEL_ON_LEAVE_NEVER, + enum PlatformOnLeave { + PLATFORM_ON_LEAVE_ADD_VELOCITY, + PLATFORM_ON_LEAVE_ADD_UPWARD_VELOCITY, + PLATFORM_ON_LEAVE_DO_NOTHING, }; bool move_and_slide(); @@ -382,7 +383,7 @@ public: private: real_t margin = 0.001; MotionMode motion_mode = MOTION_MODE_GROUNDED; - MovingPlatformApplyVelocityOnLeave moving_platform_apply_velocity_on_leave = PLATFORM_VEL_ON_LEAVE_ALWAYS; + PlatformOnLeave platform_on_leave = PLATFORM_ON_LEAVE_ADD_VELOCITY; union CollisionState { uint32_t state = 0; struct { @@ -410,11 +411,11 @@ private: int platform_layer = 0; RID platform_rid; ObjectID platform_object_id; - uint32_t moving_platform_floor_layers = UINT32_MAX; - uint32_t moving_platform_wall_layers = 0; + uint32_t platform_floor_layers = UINT32_MAX; + uint32_t platform_wall_layers = 0; real_t floor_snap_length = 0.1; - real_t floor_max_angle = Math::deg2rad((real_t)45.0); - real_t wall_min_slide_angle = Math::deg2rad((real_t)15.0); + real_t floor_max_angle = Math::deg_to_rad((real_t)45.0); + real_t wall_min_slide_angle = Math::deg_to_rad((real_t)15.0); Vector3 up_direction = Vector3(0.0, 1.0, 0.0); Vector3 velocity; Vector3 floor_normal; @@ -456,17 +457,17 @@ private: real_t get_wall_min_slide_angle() const; void set_wall_min_slide_angle(real_t p_radians); - uint32_t get_moving_platform_floor_layers() const; - void set_moving_platform_floor_layers(const uint32_t p_exclude_layer); + uint32_t get_platform_floor_layers() const; + void set_platform_floor_layers(const uint32_t p_exclude_layer); - uint32_t get_moving_platform_wall_layers() const; - void set_moving_platform_wall_layers(const uint32_t p_exclude_layer); + uint32_t get_platform_wall_layers() const; + void set_platform_wall_layers(const uint32_t p_exclude_layer); void set_motion_mode(MotionMode p_mode); MotionMode get_motion_mode() const; - void set_moving_platform_apply_velocity_on_leave(MovingPlatformApplyVelocityOnLeave p_on_leave_velocity); - MovingPlatformApplyVelocityOnLeave get_moving_platform_apply_velocity_on_leave() const; + void set_platform_on_leave(PlatformOnLeave p_on_leave_velocity); + PlatformOnLeave get_platform_on_leave() const; void _move_and_slide_floating(double p_delta); void _move_and_slide_grounded(double p_delta, bool p_was_on_floor); @@ -483,11 +484,11 @@ private: protected: void _notification(int p_what); static void _bind_methods(); - virtual void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; }; VARIANT_ENUM_CAST(CharacterBody3D::MotionMode); -VARIANT_ENUM_CAST(CharacterBody3D::MovingPlatformApplyVelocityOnLeave); +VARIANT_ENUM_CAST(CharacterBody3D::PlatformOnLeave); class KinematicCollision3D : public RefCounted { GDCLASS(KinematicCollision3D, RefCounted); @@ -504,6 +505,7 @@ public: Vector3 get_travel() const; Vector3 get_remainder() const; int get_collision_count() const; + real_t get_depth() const; Vector3 get_position(int p_collision_index = 0) const; Vector3 get_normal(int p_collision_index = 0) const; real_t get_angle(int p_collision_index = 0, const Vector3 &p_up_direction = Vector3(0.0, 1.0, 0.0)) const; @@ -784,4 +786,4 @@ private: VARIANT_ENUM_CAST(PhysicalBone3D::JointType); VARIANT_ENUM_CAST(PhysicalBone3D::DampMode); -#endif // PHYSICS_BODY__H +#endif // PHYSICS_BODY_3D_H diff --git a/scene/3d/ray_cast_3d.cpp b/scene/3d/ray_cast_3d.cpp index 2db5ab2d4e..a45ef52452 100644 --- a/scene/3d/ray_cast_3d.cpp +++ b/scene/3d/ray_cast_3d.cpp @@ -88,6 +88,10 @@ Object *RayCast3D::get_collider() const { return ObjectDB::get_instance(against); } +RID RayCast3D::get_collider_rid() const { + return against_rid; +} + int RayCast3D::get_collider_shape() const { return against_shape; } @@ -224,12 +228,14 @@ void RayCast3D::_update_raycast_state() { if (dss->intersect_ray(ray_params, rr)) { collided = true; against = rr.collider_id; + against_rid = rr.rid; collision_point = rr.position; collision_normal = rr.normal; against_shape = rr.shape; } else { collided = false; against = ObjectID(); + against_rid = RID(); against_shape = 0; } } @@ -302,6 +308,7 @@ void RayCast3D::_bind_methods() { ClassDB::bind_method(D_METHOD("force_raycast_update"), &RayCast3D::force_raycast_update); ClassDB::bind_method(D_METHOD("get_collider"), &RayCast3D::get_collider); + ClassDB::bind_method(D_METHOD("get_collider_rid"), &RayCast3D::get_collider_rid); ClassDB::bind_method(D_METHOD("get_collider_shape"), &RayCast3D::get_collider_shape); ClassDB::bind_method(D_METHOD("get_collision_point"), &RayCast3D::get_collision_point); ClassDB::bind_method(D_METHOD("get_collision_normal"), &RayCast3D::get_collision_normal); diff --git a/scene/3d/ray_cast_3d.h b/scene/3d/ray_cast_3d.h index c69c910efb..eb5c3ee90a 100644 --- a/scene/3d/ray_cast_3d.h +++ b/scene/3d/ray_cast_3d.h @@ -41,6 +41,7 @@ class RayCast3D : public Node3D { bool enabled = true; bool collided = false; ObjectID against; + RID against_rid; int against_shape = 0; Vector3 collision_point; Vector3 collision_normal; @@ -113,6 +114,7 @@ public: void force_raycast_update(); bool is_colliding() const; Object *get_collider() const; + RID get_collider_rid() const; int get_collider_shape() const; Vector3 get_collision_point() const; Vector3 get_collision_normal() const; @@ -126,4 +128,4 @@ public: RayCast3D(); }; -#endif // RAY_CAST_H +#endif // RAY_CAST_3D_H diff --git a/scene/3d/reflection_probe.cpp b/scene/3d/reflection_probe.cpp index 0a9d6cbbeb..bc3cc31963 100644 --- a/scene/3d/reflection_probe.cpp +++ b/scene/3d/reflection_probe.cpp @@ -178,13 +178,12 @@ AABB ReflectionProbe::get_aabb() const { return aabb; } -void ReflectionProbe::_validate_property(PropertyInfo &property) const { - if (property.name == "interior/ambient_color" || property.name == "interior/ambient_color_energy") { +void ReflectionProbe::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "ambient_color" || p_property.name == "ambient_color_energy") { if (ambient_mode != AMBIENT_COLOR) { - property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } } - VisualInstance3D::_validate_property(property); } void ReflectionProbe::_bind_methods() { diff --git a/scene/3d/reflection_probe.h b/scene/3d/reflection_probe.h index 424976d895..5a5a3fe0bb 100644 --- a/scene/3d/reflection_probe.h +++ b/scene/3d/reflection_probe.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef REFLECTIONPROBE_H -#define REFLECTIONPROBE_H +#ifndef REFLECTION_PROBE_H +#define REFLECTION_PROBE_H #include "scene/3d/visual_instance_3d.h" @@ -67,7 +67,7 @@ private: protected: static void _bind_methods(); - void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; public: void set_intensity(float p_intensity); @@ -121,4 +121,4 @@ public: VARIANT_ENUM_CAST(ReflectionProbe::AmbientMode); VARIANT_ENUM_CAST(ReflectionProbe::UpdateMode); -#endif // REFLECTIONPROBE_H +#endif // REFLECTION_PROBE_H diff --git a/scene/3d/remote_transform_3d.cpp b/scene/3d/remote_transform_3d.cpp index 9979052385..ff05e88241 100644 --- a/scene/3d/remote_transform_3d.cpp +++ b/scene/3d/remote_transform_3d.cpp @@ -178,8 +178,8 @@ void RemoteTransform3D::force_update_cache() { _update_cache(); } -TypedArray<String> RemoteTransform3D::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray RemoteTransform3D::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (!has_node(remote_node) || !Object::cast_to<Node3D>(get_node(remote_node))) { warnings.push_back(RTR("The \"Remote Path\" property must point to a valid Node3D or Node3D-derived node to work.")); diff --git a/scene/3d/remote_transform_3d.h b/scene/3d/remote_transform_3d.h index 03bb253578..cc83661f26 100644 --- a/scene/3d/remote_transform_3d.h +++ b/scene/3d/remote_transform_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef REMOTETRANSFORM_H -#define REMOTETRANSFORM_H +#ifndef REMOTE_TRANSFORM_3D_H +#define REMOTE_TRANSFORM_3D_H #include "scene/3d/node_3d.h" @@ -70,9 +70,9 @@ public: void force_update_cache(); - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; RemoteTransform3D(); }; -#endif // REMOTETRANSFORM_H +#endif // REMOTE_TRANSFORM_3D_H diff --git a/scene/3d/shape_cast_3d.cpp b/scene/3d/shape_cast_3d.cpp new file mode 100644 index 0000000000..e7d1a8ec7d --- /dev/null +++ b/scene/3d/shape_cast_3d.cpp @@ -0,0 +1,634 @@ +/*************************************************************************/ +/* shape_cast_3d.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "shape_cast_3d.h" + +#include "collision_object_3d.h" +#include "mesh_instance_3d.h" +#include "scene/resources/concave_polygon_shape_3d.h" + +void ShapeCast3D::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + if (Engine::get_singleton()->is_editor_hint()) { + _update_debug_shape_vertices(); + } + if (enabled && !Engine::get_singleton()->is_editor_hint()) { + set_physics_process_internal(true); + } else { + set_physics_process_internal(false); + } + + if (get_tree()->is_debugging_collisions_hint()) { + _update_debug_shape(); + } + + if (Object::cast_to<CollisionObject3D>(get_parent())) { + if (exclude_parent_body) { + exclude.insert(Object::cast_to<CollisionObject3D>(get_parent())->get_rid()); + } else { + exclude.erase(Object::cast_to<CollisionObject3D>(get_parent())->get_rid()); + } + } + } break; + + case NOTIFICATION_EXIT_TREE: { + if (enabled) { + set_physics_process_internal(false); + } + + if (debug_shape) { + _clear_debug_shape(); + } + } break; + + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { + if (!enabled) { + break; + } + + bool prev_collision_state = collided; + _update_shapecast_state(); + if (get_tree()->is_debugging_collisions_hint()) { + if (prev_collision_state != collided) { + _update_debug_shape_material(true); + } + if (collided) { + _update_debug_shape(); + } + if (prev_collision_state == collided && !collided) { + _update_debug_shape(); + } + } + } break; + } +} + +void ShapeCast3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("resource_changed", "resource"), &ShapeCast3D::resource_changed); + + ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &ShapeCast3D::set_enabled); + ClassDB::bind_method(D_METHOD("is_enabled"), &ShapeCast3D::is_enabled); + + ClassDB::bind_method(D_METHOD("set_shape", "shape"), &ShapeCast3D::set_shape); + ClassDB::bind_method(D_METHOD("get_shape"), &ShapeCast3D::get_shape); + + ClassDB::bind_method(D_METHOD("set_target_position", "local_point"), &ShapeCast3D::set_target_position); + ClassDB::bind_method(D_METHOD("get_target_position"), &ShapeCast3D::get_target_position); + + ClassDB::bind_method(D_METHOD("set_margin", "margin"), &ShapeCast3D::set_margin); + ClassDB::bind_method(D_METHOD("get_margin"), &ShapeCast3D::get_margin); + + ClassDB::bind_method(D_METHOD("set_max_results", "max_results"), &ShapeCast3D::set_max_results); + ClassDB::bind_method(D_METHOD("get_max_results"), &ShapeCast3D::get_max_results); + + ClassDB::bind_method(D_METHOD("is_colliding"), &ShapeCast3D::is_colliding); + ClassDB::bind_method(D_METHOD("get_collision_count"), &ShapeCast3D::get_collision_count); + + ClassDB::bind_method(D_METHOD("force_shapecast_update"), &ShapeCast3D::force_shapecast_update); + + ClassDB::bind_method(D_METHOD("get_collider", "index"), &ShapeCast3D::get_collider); + ClassDB::bind_method(D_METHOD("get_collider_shape", "index"), &ShapeCast3D::get_collider_shape); + ClassDB::bind_method(D_METHOD("get_collision_point", "index"), &ShapeCast3D::get_collision_point); + ClassDB::bind_method(D_METHOD("get_collision_normal", "index"), &ShapeCast3D::get_collision_normal); + + ClassDB::bind_method(D_METHOD("get_closest_collision_safe_fraction"), &ShapeCast3D::get_closest_collision_safe_fraction); + ClassDB::bind_method(D_METHOD("get_closest_collision_unsafe_fraction"), &ShapeCast3D::get_closest_collision_unsafe_fraction); + + ClassDB::bind_method(D_METHOD("add_exception_rid", "rid"), &ShapeCast3D::add_exception_rid); + ClassDB::bind_method(D_METHOD("add_exception", "node"), &ShapeCast3D::add_exception); + + ClassDB::bind_method(D_METHOD("remove_exception_rid", "rid"), &ShapeCast3D::remove_exception_rid); + ClassDB::bind_method(D_METHOD("remove_exception", "node"), &ShapeCast3D::remove_exception); + + ClassDB::bind_method(D_METHOD("clear_exceptions"), &ShapeCast3D::clear_exceptions); + + ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &ShapeCast3D::set_collision_mask); + ClassDB::bind_method(D_METHOD("get_collision_mask"), &ShapeCast3D::get_collision_mask); + + ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &ShapeCast3D::set_collision_mask_value); + ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &ShapeCast3D::get_collision_mask_value); + + ClassDB::bind_method(D_METHOD("set_exclude_parent_body", "mask"), &ShapeCast3D::set_exclude_parent_body); + ClassDB::bind_method(D_METHOD("get_exclude_parent_body"), &ShapeCast3D::get_exclude_parent_body); + + ClassDB::bind_method(D_METHOD("set_collide_with_areas", "enable"), &ShapeCast3D::set_collide_with_areas); + ClassDB::bind_method(D_METHOD("is_collide_with_areas_enabled"), &ShapeCast3D::is_collide_with_areas_enabled); + + ClassDB::bind_method(D_METHOD("set_collide_with_bodies", "enable"), &ShapeCast3D::set_collide_with_bodies); + ClassDB::bind_method(D_METHOD("is_collide_with_bodies_enabled"), &ShapeCast3D::is_collide_with_bodies_enabled); + + ClassDB::bind_method(D_METHOD("_get_collision_result"), &ShapeCast3D::_get_collision_result); + + ClassDB::bind_method(D_METHOD("set_debug_shape_custom_color", "debug_shape_custom_color"), &ShapeCast3D::set_debug_shape_custom_color); + ClassDB::bind_method(D_METHOD("get_debug_shape_custom_color"), &ShapeCast3D::get_debug_shape_custom_color); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape3D"), "set_shape", "get_shape"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "exclude_parent"), "set_exclude_parent_body", "get_exclude_parent_body"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "target_position", PROPERTY_HINT_NONE, "suffix:m"), "set_target_position", "get_target_position"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin", PROPERTY_HINT_RANGE, "0,100,0.01,suffix:m"), "set_margin", "get_margin"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "max_results"), "set_max_results", "get_max_results"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "collision_result", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "", "_get_collision_result"); + + ADD_GROUP("Collide With", "collide_with"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_areas", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collide_with_areas", "is_collide_with_areas_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_bodies", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collide_with_bodies", "is_collide_with_bodies_enabled"); + + ADD_GROUP("Debug Shape", "debug_shape"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "debug_shape_custom_color"), "set_debug_shape_custom_color", "get_debug_shape_custom_color"); +} + +PackedStringArray ShapeCast3D::get_configuration_warnings() const { + PackedStringArray warnings = Node3D::get_configuration_warnings(); + + if (shape.is_null()) { + warnings.push_back(RTR("This node cannot interact with other objects unless a Shape3D is assigned.")); + } + if (shape.is_valid() && Object::cast_to<ConcavePolygonShape3D>(*shape)) { + warnings.push_back(RTR("ShapeCast3D does not support ConcavePolygonShape3Ds. Collisions will not be reported.")); + } + return warnings; +} + +void ShapeCast3D::set_enabled(bool p_enabled) { + enabled = p_enabled; + update_gizmos(); + + if (is_inside_tree() && !Engine::get_singleton()->is_editor_hint()) { + set_physics_process_internal(p_enabled); + } + if (!p_enabled) { + collided = false; + } + + if (is_inside_tree() && get_tree()->is_debugging_collisions_hint()) { + if (p_enabled) { + _update_debug_shape(); + } else { + _clear_debug_shape(); + } + } +} + +bool ShapeCast3D::is_enabled() const { + return enabled; +} + +void ShapeCast3D::set_target_position(const Vector3 &p_point) { + target_position = p_point; + if (is_inside_tree() && get_tree()->is_debugging_collisions_hint()) { + _update_debug_shape(); + } + update_gizmos(); + + if (Engine::get_singleton()->is_editor_hint()) { + if (is_inside_tree()) { + _update_debug_shape_vertices(); + } + } else if (debug_shape) { + _update_debug_shape(); + } +} + +Vector3 ShapeCast3D::get_target_position() const { + return target_position; +} + +void ShapeCast3D::set_margin(real_t p_margin) { + margin = p_margin; +} + +real_t ShapeCast3D::get_margin() const { + return margin; +} + +void ShapeCast3D::set_max_results(int p_max_results) { + max_results = p_max_results; +} + +int ShapeCast3D::get_max_results() const { + return max_results; +} + +void ShapeCast3D::set_collision_mask(uint32_t p_mask) { + collision_mask = p_mask; +} + +uint32_t ShapeCast3D::get_collision_mask() const { + return collision_mask; +} + +void ShapeCast3D::set_collision_mask_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); + uint32_t mask = get_collision_mask(); + if (p_value) { + mask |= 1 << (p_layer_number - 1); + } else { + mask &= ~(1 << (p_layer_number - 1)); + } + set_collision_mask(mask); +} + +bool ShapeCast3D::get_collision_mask_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_mask() & (1 << (p_layer_number - 1)); +} + +int ShapeCast3D::get_collision_count() const { + return result.size(); +} + +bool ShapeCast3D::is_colliding() const { + return collided; +} + +Object *ShapeCast3D::get_collider(int p_idx) const { + ERR_FAIL_INDEX_V_MSG(p_idx, result.size(), nullptr, "No collider found."); + + if (result[p_idx].collider_id.is_null()) { + return nullptr; + } + return ObjectDB::get_instance(result[p_idx].collider_id); +} + +int ShapeCast3D::get_collider_shape(int p_idx) const { + ERR_FAIL_INDEX_V_MSG(p_idx, result.size(), -1, "No collider shape found."); + return result[p_idx].shape; +} + +Vector3 ShapeCast3D::get_collision_point(int p_idx) const { + ERR_FAIL_INDEX_V_MSG(p_idx, result.size(), Vector3(), "No collision point found."); + return result[p_idx].point; +} + +Vector3 ShapeCast3D::get_collision_normal(int p_idx) const { + ERR_FAIL_INDEX_V_MSG(p_idx, result.size(), Vector3(), "No collision normal found."); + return result[p_idx].normal; +} + +real_t ShapeCast3D::get_closest_collision_safe_fraction() const { + return collision_safe_fraction; +} + +real_t ShapeCast3D::get_closest_collision_unsafe_fraction() const { + return collision_unsafe_fraction; +} + +void ShapeCast3D::resource_changed(Ref<Resource> p_res) { + if (is_inside_tree() && get_tree()->is_debugging_collisions_hint()) { + _update_debug_shape(); + } + update_gizmos(); +} + +void ShapeCast3D::set_shape(const Ref<Shape3D> &p_shape) { + if (p_shape == shape) { + return; + } + if (!shape.is_null()) { + shape->unregister_owner(this); + } + shape = p_shape; + if (!shape.is_null()) { + shape->register_owner(this); + } + if (p_shape.is_valid()) { + shape_rid = shape->get_rid(); + } + + if (is_inside_tree() && get_tree()->is_debugging_collisions_hint()) { + _update_debug_shape(); + } + + update_gizmos(); + update_configuration_warnings(); +} + +Ref<Shape3D> ShapeCast3D::get_shape() const { + return shape; +} + +void ShapeCast3D::set_exclude_parent_body(bool p_exclude_parent_body) { + if (exclude_parent_body == p_exclude_parent_body) { + return; + } + exclude_parent_body = p_exclude_parent_body; + + if (!is_inside_tree()) { + return; + } + if (Object::cast_to<CollisionObject3D>(get_parent())) { + if (exclude_parent_body) { + exclude.insert(Object::cast_to<CollisionObject3D>(get_parent())->get_rid()); + } else { + exclude.erase(Object::cast_to<CollisionObject3D>(get_parent())->get_rid()); + } + } +} + +bool ShapeCast3D::get_exclude_parent_body() const { + return exclude_parent_body; +} + +void ShapeCast3D::_update_shapecast_state() { + result.clear(); + + ERR_FAIL_COND_MSG(shape.is_null(), "Null reference to shape. ShapeCast3D requires a Shape3D to sweep for collisions."); + + Ref<World3D> w3d = get_world_3d(); + ERR_FAIL_COND(w3d.is_null()); + + PhysicsDirectSpaceState3D *dss = PhysicsServer3D::get_singleton()->space_get_direct_state(w3d->get_space()); + ERR_FAIL_COND(!dss); + + Transform3D gt = get_global_transform(); + + PhysicsDirectSpaceState3D::ShapeParameters params; + params.shape_rid = shape_rid; + params.transform = gt; + params.motion = gt.basis.xform(target_position); + params.margin = margin; + params.exclude = exclude; + params.collision_mask = collision_mask; + params.collide_with_bodies = collide_with_bodies; + params.collide_with_areas = collide_with_areas; + + collision_safe_fraction = 0.0; + collision_unsafe_fraction = 0.0; + + if (target_position != Vector3()) { + dss->cast_motion(params, collision_safe_fraction, collision_unsafe_fraction); + if (collision_unsafe_fraction < 1.0) { + // Move shape transform to the point of impact, + // so we can collect contact info at that point. + gt.set_origin(gt.get_origin() + params.motion * (collision_unsafe_fraction + CMP_EPSILON)); + params.transform = gt; + } + } + // Regardless of whether the shape is stuck or it's moved along + // the motion vector, we'll only consider static collisions from now on. + params.motion = Vector3(); + + bool intersected = true; + while (intersected && result.size() < max_results) { + PhysicsDirectSpaceState3D::ShapeRestInfo info; + intersected = dss->rest_info(params, &info); + if (intersected) { + result.push_back(info); + params.exclude.insert(info.rid); + } + } + collided = !result.is_empty(); +} + +void ShapeCast3D::force_shapecast_update() { + _update_shapecast_state(); +} + +void ShapeCast3D::add_exception_rid(const RID &p_rid) { + exclude.insert(p_rid); +} + +void ShapeCast3D::add_exception(const Object *p_object) { + ERR_FAIL_NULL(p_object); + const CollisionObject3D *co = Object::cast_to<CollisionObject3D>(p_object); + if (!co) { + return; + } + add_exception_rid(co->get_rid()); +} + +void ShapeCast3D::remove_exception_rid(const RID &p_rid) { + exclude.erase(p_rid); +} + +void ShapeCast3D::remove_exception(const Object *p_object) { + ERR_FAIL_NULL(p_object); + const CollisionObject3D *co = Object::cast_to<CollisionObject3D>(p_object); + if (!co) { + return; + } + remove_exception_rid(co->get_rid()); +} + +void ShapeCast3D::clear_exceptions() { + exclude.clear(); +} + +void ShapeCast3D::set_collide_with_areas(bool p_clip) { + collide_with_areas = p_clip; +} + +bool ShapeCast3D::is_collide_with_areas_enabled() const { + return collide_with_areas; +} + +void ShapeCast3D::set_collide_with_bodies(bool p_clip) { + collide_with_bodies = p_clip; +} + +bool ShapeCast3D::is_collide_with_bodies_enabled() const { + return collide_with_bodies; +} + +Array ShapeCast3D::_get_collision_result() const { + Array ret; + + for (int i = 0; i < result.size(); ++i) { + const PhysicsDirectSpaceState3D::ShapeRestInfo &sri = result[i]; + + Dictionary col; + col["point"] = sri.point; + col["normal"] = sri.normal; + col["rid"] = sri.rid; + col["collider"] = ObjectDB::get_instance(sri.collider_id); + col["collider_id"] = sri.collider_id; + col["shape"] = sri.shape; + col["linear_velocity"] = sri.linear_velocity; + + ret.push_back(col); + } + return ret; +} + +void ShapeCast3D::_update_debug_shape_vertices() { + debug_shape_vertices.clear(); + debug_line_vertices.clear(); + + if (!shape.is_null()) { + debug_shape_vertices.append_array(shape->get_debug_mesh_lines()); + for (int i = 0; i < debug_shape_vertices.size(); i++) { + debug_shape_vertices.set(i, debug_shape_vertices[i] + Vector3(target_position * get_closest_collision_safe_fraction())); + } + } + + if (target_position == Vector3()) { + return; + } + + debug_line_vertices.push_back(Vector3()); + debug_line_vertices.push_back(target_position); +} + +const Vector<Vector3> &ShapeCast3D::get_debug_shape_vertices() const { + return debug_shape_vertices; +} + +const Vector<Vector3> &ShapeCast3D::get_debug_line_vertices() const { + return debug_line_vertices; +} + +void ShapeCast3D::set_debug_shape_custom_color(const Color &p_color) { + debug_shape_custom_color = p_color; + if (debug_material.is_valid()) { + _update_debug_shape_material(); + } +} + +Ref<StandardMaterial3D> ShapeCast3D::get_debug_material() { + _update_debug_shape_material(); + return debug_material; +} + +const Color &ShapeCast3D::get_debug_shape_custom_color() const { + return debug_shape_custom_color; +} + +void ShapeCast3D::_create_debug_shape() { + _update_debug_shape_material(); + + Ref<ArrayMesh> mesh = memnew(ArrayMesh); + + MeshInstance3D *mi = memnew(MeshInstance3D); + mi->set_mesh(mesh); + + add_child(mi); + debug_shape = mi; +} + +void ShapeCast3D::_update_debug_shape_material(bool p_check_collision) { + if (!debug_material.is_valid()) { + Ref<StandardMaterial3D> material = memnew(StandardMaterial3D); + debug_material = material; + + material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); + // Use double-sided rendering so that the RayCast can be seen if the camera is inside. + material->set_cull_mode(BaseMaterial3D::CULL_DISABLED); + material->set_transparency(BaseMaterial3D::TRANSPARENCY_ALPHA); + } + + Color color = debug_shape_custom_color; + if (color == Color(0.0, 0.0, 0.0)) { + // Use the default debug shape color defined in the Project Settings. + color = get_tree()->get_debug_collisions_color(); + } + + if (p_check_collision && collided) { + if ((color.get_h() < 0.055 || color.get_h() > 0.945) && color.get_s() > 0.5 && color.get_v() > 0.5) { + // If base color is already quite reddish, highlight collision with green color + color = Color(0.0, 1.0, 0.0, color.a); + } else { + // Else, highlight collision with red color + color = Color(1.0, 0, 0, color.a); + } + } + + Ref<StandardMaterial3D> material = static_cast<Ref<StandardMaterial3D>>(debug_material); + material->set_albedo(color); +} + +void ShapeCast3D::_update_debug_shape() { + if (!enabled) { + return; + } + + if (!debug_shape) { + _create_debug_shape(); + } + + _update_debug_shape_vertices(); + + if (Engine::get_singleton()->is_editor_hint()) { + return; + } + + MeshInstance3D *mi = static_cast<MeshInstance3D *>(debug_shape); + Ref<ArrayMesh> mesh = mi->get_mesh(); + if (!mesh.is_valid()) { + return; + } + + mesh->clear_surfaces(); + + Array a; + a.resize(Mesh::ARRAY_MAX); + + uint32_t flags = 0; + int surface_count = 0; + + if (!debug_shape_vertices.is_empty()) { + a[Mesh::ARRAY_VERTEX] = debug_shape_vertices; + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, a, Array(), Dictionary(), flags); + mesh->surface_set_material(surface_count, debug_material); + ++surface_count; + } + + if (!debug_line_vertices.is_empty()) { + a[Mesh::ARRAY_VERTEX] = debug_line_vertices; + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, a, Array(), Dictionary(), flags); + mesh->surface_set_material(surface_count, debug_material); + ++surface_count; + } +} + +void ShapeCast3D::_clear_debug_shape() { + if (!debug_shape) { + return; + } + + MeshInstance3D *mi = static_cast<MeshInstance3D *>(debug_shape); + if (mi->is_inside_tree()) { + mi->queue_delete(); + } else { + memdelete(mi); + } + + debug_shape = nullptr; +} + +ShapeCast3D::~ShapeCast3D() { + if (!shape.is_null()) { + shape->unregister_owner(this); + } +} diff --git a/scene/3d/shape_cast_3d.h b/scene/3d/shape_cast_3d.h new file mode 100644 index 0000000000..2526d8d32c --- /dev/null +++ b/scene/3d/shape_cast_3d.h @@ -0,0 +1,142 @@ +/*************************************************************************/ +/* shape_cast_3d.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SHAPE_CAST_3D_H +#define SHAPE_CAST_3D_H + +#include "scene/3d/node_3d.h" +#include "scene/resources/shape_3d.h" + +class ShapeCast3D : public Node3D { + GDCLASS(ShapeCast3D, Node3D); + + bool enabled = true; + void resource_changed(Ref<Resource> p_res); + + Ref<Shape3D> shape; + RID shape_rid; + Vector3 target_position = Vector3(0, -1, 0); + + HashSet<RID> exclude; + real_t margin = 0.0; + uint32_t collision_mask = 1; + bool exclude_parent_body = true; + bool collide_with_areas = false; + bool collide_with_bodies = true; + + Node *debug_shape = nullptr; + Ref<Material> debug_material; + Color debug_shape_custom_color = Color(0.0, 0.0, 0.0); + Vector<Vector3> debug_shape_vertices; + Vector<Vector3> debug_line_vertices; + + void _create_debug_shape(); + void _update_debug_shape(); + void _update_debug_shape_material(bool p_check_collision = false); + void _update_debug_shape_vertices(); + void _clear_debug_shape(); + + // Result + int max_results = 32; + Vector<PhysicsDirectSpaceState3D::ShapeRestInfo> result; + bool collided = false; + real_t collision_safe_fraction = 1.0; + real_t collision_unsafe_fraction = 1.0; + + Array _get_collision_result() const; + + ~ShapeCast3D(); + +protected: + void _notification(int p_what); + void _update_shapecast_state(); + static void _bind_methods(); + +public: + void set_collide_with_areas(bool p_clip); + bool is_collide_with_areas_enabled() const; + + void set_collide_with_bodies(bool p_clip); + bool is_collide_with_bodies_enabled() const; + + void set_enabled(bool p_enabled); + bool is_enabled() const; + + void set_shape(const Ref<Shape3D> &p_shape); + Ref<Shape3D> get_shape() const; + + void set_target_position(const Vector3 &p_point); + Vector3 get_target_position() const; + + void set_margin(real_t p_margin); + real_t get_margin() const; + + void set_max_results(int p_max_results); + int get_max_results() const; + + void set_collision_mask(uint32_t p_mask); + uint32_t get_collision_mask() const; + + void set_collision_mask_value(int p_layer_number, bool p_value); + bool get_collision_mask_value(int p_layer_number) const; + + void set_exclude_parent_body(bool p_exclude_parent_body); + bool get_exclude_parent_body() const; + + const Color &get_debug_shape_custom_color() const; + void set_debug_shape_custom_color(const Color &p_color); + + const Vector<Vector3> &get_debug_shape_vertices() const; + const Vector<Vector3> &get_debug_line_vertices() const; + + Ref<StandardMaterial3D> get_debug_material(); + + int get_collision_count() const; + Object *get_collider(int p_idx) const; + int get_collider_shape(int p_idx) const; + Vector3 get_collision_point(int p_idx) const; + Vector3 get_collision_normal(int p_idx) const; + + real_t get_closest_collision_safe_fraction() const; + real_t get_closest_collision_unsafe_fraction() const; + + void force_shapecast_update(); + bool is_colliding() const; + + void add_exception_rid(const RID &p_rid); + void add_exception(const Object *p_object); + void remove_exception_rid(const RID &p_rid); + void remove_exception(const Object *p_object); + void clear_exceptions(); + + virtual PackedStringArray get_configuration_warnings() const override; +}; + +#endif // SHAPE_CAST_3D_H diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp index fbd5f31dd5..85b2c5154b 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -45,7 +45,6 @@ void SkinReference::_skin_changed() { } void SkinReference::_bind_methods() { - ClassDB::bind_method(D_METHOD("_skin_changed"), &SkinReference::_skin_changed); ClassDB::bind_method(D_METHOD("get_skeleton"), &SkinReference::get_skeleton); ClassDB::bind_method(D_METHOD("get_skin"), &SkinReference::get_skin); } @@ -176,39 +175,37 @@ void Skeleton3D::_get_property_list(List<PropertyInfo> *p_list) const { } } -void Skeleton3D::_validate_property(PropertyInfo &property) const { - PackedStringArray split = property.name.split("/"); +void Skeleton3D::_validate_property(PropertyInfo &p_property) const { + PackedStringArray split = p_property.name.split("/"); if (split.size() == 3 && split[0] == "bones") { if (split[2] == "rest") { - property.usage |= PROPERTY_USAGE_READ_ONLY; + p_property.usage |= PROPERTY_USAGE_READ_ONLY; } if (is_show_rest_only()) { if (split[2] == "enabled") { - property.usage |= PROPERTY_USAGE_READ_ONLY; + p_property.usage |= PROPERTY_USAGE_READ_ONLY; } if (split[2] == "position") { - property.usage |= PROPERTY_USAGE_READ_ONLY; + p_property.usage |= PROPERTY_USAGE_READ_ONLY; } if (split[2] == "rotation") { - property.usage |= PROPERTY_USAGE_READ_ONLY; + p_property.usage |= PROPERTY_USAGE_READ_ONLY; } if (split[2] == "scale") { - property.usage |= PROPERTY_USAGE_READ_ONLY; + p_property.usage |= PROPERTY_USAGE_READ_ONLY; } } else if (!is_bone_enabled(split[1].to_int())) { if (split[2] == "position") { - property.usage |= PROPERTY_USAGE_READ_ONLY; + p_property.usage |= PROPERTY_USAGE_READ_ONLY; } if (split[2] == "rotation") { - property.usage |= PROPERTY_USAGE_READ_ONLY; + p_property.usage |= PROPERTY_USAGE_READ_ONLY; } if (split[2] == "scale") { - property.usage |= PROPERTY_USAGE_READ_ONLY; + p_property.usage |= PROPERTY_USAGE_READ_ONLY; } } } - - Node3D::_validate_property(property); } void Skeleton3D::_update_process_order() { @@ -317,9 +314,7 @@ void Skeleton3D::_notification(int p_what) { rs->skeleton_bone_set_transform(skeleton, i, bonesptr[bone_index].pose_global * skin->get_bind_pose(i)); } } -#ifdef TOOLS_ENABLED emit_signal(SceneStringNames::get_singleton()->pose_updated); -#endif // TOOLS_ENABLED } break; #ifndef _3D_DISABLED @@ -493,6 +488,19 @@ int Skeleton3D::get_bone_axis_forward_enum(int p_bone) { return bones[p_bone].rest_bone_forward_axis; } +void Skeleton3D::set_motion_scale(float p_motion_scale) { + if (p_motion_scale <= 0) { + motion_scale = 1; + ERR_FAIL_MSG("Motion scale must be larger than 0."); + } + motion_scale = p_motion_scale; +} + +float Skeleton3D::get_motion_scale() const { + ERR_FAIL_COND_V(motion_scale <= 0, 1); + return motion_scale; +} + // Skeleton creation api void Skeleton3D::add_bone(const String &p_name) { @@ -592,53 +600,25 @@ void Skeleton3D::unparent_bone_and_rest(int p_bone) { int Skeleton3D::get_bone_parent(int p_bone) const { const int bone_size = bones.size(); ERR_FAIL_INDEX_V(p_bone, bone_size, -1); - + if (process_order_dirty) { + const_cast<Skeleton3D *>(this)->_update_process_order(); + } return bones[p_bone].parent; } -Vector<int> Skeleton3D::get_bone_children(int p_bone) { +Vector<int> Skeleton3D::get_bone_children(int p_bone) const { const int bone_size = bones.size(); ERR_FAIL_INDEX_V(p_bone, bone_size, Vector<int>()); + if (process_order_dirty) { + const_cast<Skeleton3D *>(this)->_update_process_order(); + } return bones[p_bone].child_bones; } -void Skeleton3D::set_bone_children(int p_bone, Vector<int> p_children) { - const int bone_size = bones.size(); - ERR_FAIL_INDEX(p_bone, bone_size); - bones.write[p_bone].child_bones = p_children; - - process_order_dirty = true; - rest_dirty = true; - _make_dirty(); -} - -void Skeleton3D::add_bone_child(int p_bone, int p_child) { - const int bone_size = bones.size(); - ERR_FAIL_INDEX(p_bone, bone_size); - bones.write[p_bone].child_bones.push_back(p_child); - - process_order_dirty = true; - rest_dirty = true; - _make_dirty(); -} - -void Skeleton3D::remove_bone_child(int p_bone, int p_child) { - const int bone_size = bones.size(); - ERR_FAIL_INDEX(p_bone, bone_size); - - int child_idx = bones[p_bone].child_bones.find(p_child); - if (child_idx >= 0) { - bones.write[p_bone].child_bones.remove_at(child_idx); - } else { - WARN_PRINT("Cannot remove child bone: Child bone not found."); +Vector<int> Skeleton3D::get_parentless_bones() const { + if (process_order_dirty) { + const_cast<Skeleton3D *>(this)->_update_process_order(); } - - process_order_dirty = true; - rest_dirty = true; - _make_dirty(); -} - -Vector<int> Skeleton3D::get_parentless_bones() { return parentless_bones; } @@ -748,6 +728,20 @@ Vector3 Skeleton3D::get_bone_pose_scale(int p_bone) const { return bones[p_bone].pose_scale; } +void Skeleton3D::reset_bone_pose(int p_bone) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); + set_bone_pose_position(p_bone, bones[p_bone].rest.origin); + set_bone_pose_rotation(p_bone, bones[p_bone].rest.basis.get_rotation_quaternion()); + set_bone_pose_scale(p_bone, bones[p_bone].rest.basis.get_scale()); +} + +void Skeleton3D::reset_bone_poses() { + for (int i = 0; i < bones.size(); i++) { + reset_bone_pose(i); + } +} + Transform3D Skeleton3D::get_bone_pose(int p_bone) const { const int bone_size = bones.size(); ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); @@ -765,8 +759,6 @@ void Skeleton3D::_make_dirty() { } void Skeleton3D::localize_rests() { - _update_process_order(); - Vector<int> bones_to_process = get_parentless_bones(); while (bones_to_process.size() > 0) { int current_bone_idx = bones_to_process[0]; @@ -958,7 +950,6 @@ Ref<Skin> Skeleton3D::create_skin_from_rest_transforms() { skin.instantiate(); skin->set_bind_count(bones.size()); - _update_process_order(); // Just in case. // Pose changed, rebuild cache of inverses. const Bone *bonesptr = bones.ptr(); @@ -1015,7 +1006,7 @@ Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) { skin_bindings.insert(skin_ref.operator->()); - skin_ref->skin->connect("changed", Callable(skin_ref.operator->(), "_skin_changed")); + skin_ref->skin->connect("changed", callable_mp(skin_ref.operator->(), &SkinReference::_skin_changed)); _make_dirty(); // Skin needs to be updated, so update skeleton. @@ -1215,9 +1206,6 @@ void Skeleton3D::_bind_methods() { ClassDB::bind_method(D_METHOD("unparent_bone_and_rest", "bone_idx"), &Skeleton3D::unparent_bone_and_rest); ClassDB::bind_method(D_METHOD("get_bone_children", "bone_idx"), &Skeleton3D::get_bone_children); - ClassDB::bind_method(D_METHOD("set_bone_children", "bone_idx", "bone_children"), &Skeleton3D::set_bone_children); - ClassDB::bind_method(D_METHOD("add_bone_child", "bone_idx", "child_bone_idx"), &Skeleton3D::add_bone_child); - ClassDB::bind_method(D_METHOD("remove_bone_child", "bone_idx", "child_bone_idx"), &Skeleton3D::remove_bone_child); ClassDB::bind_method(D_METHOD("get_parentless_bones"), &Skeleton3D::get_parentless_bones); @@ -1241,6 +1229,9 @@ void Skeleton3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_bone_pose_rotation", "bone_idx"), &Skeleton3D::get_bone_pose_rotation); ClassDB::bind_method(D_METHOD("get_bone_pose_scale", "bone_idx"), &Skeleton3D::get_bone_pose_scale); + ClassDB::bind_method(D_METHOD("reset_bone_pose", "bone_idx"), &Skeleton3D::reset_bone_pose); + ClassDB::bind_method(D_METHOD("reset_bone_poses"), &Skeleton3D::reset_bone_poses); + ClassDB::bind_method(D_METHOD("is_bone_enabled", "bone_idx"), &Skeleton3D::is_bone_enabled); ClassDB::bind_method(D_METHOD("set_bone_enabled", "bone_idx", "enabled"), &Skeleton3D::set_bone_enabled, DEFVAL(true)); @@ -1257,6 +1248,9 @@ void Skeleton3D::_bind_methods() { ClassDB::bind_method(D_METHOD("force_update_all_bone_transforms"), &Skeleton3D::force_update_all_bone_transforms); ClassDB::bind_method(D_METHOD("force_update_bone_child_transform", "bone_idx"), &Skeleton3D::force_update_bone_children_transforms); + ClassDB::bind_method(D_METHOD("set_motion_scale", "motion_scale"), &Skeleton3D::set_motion_scale); + ClassDB::bind_method(D_METHOD("get_motion_scale"), &Skeleton3D::get_motion_scale); + // Helper functions ClassDB::bind_method(D_METHOD("global_pose_to_world_transform", "global_pose"), &Skeleton3D::global_pose_to_world_transform); ClassDB::bind_method(D_METHOD("world_transform_to_global_pose", "world_transform"), &Skeleton3D::world_transform_to_global_pose); @@ -1280,15 +1274,13 @@ void Skeleton3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_modification_stack"), &Skeleton3D::get_modification_stack); ClassDB::bind_method(D_METHOD("execute_modifications", "delta", "execution_mode"), &Skeleton3D::execute_modifications); -#ifndef _3D_DISABLED + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "motion_scale", PROPERTY_HINT_RANGE, "0.001,10,0.001,or_greater"), "set_motion_scale", "get_motion_scale"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_rest_only"), "set_show_rest_only", "is_show_rest_only"); +#ifndef _3D_DISABLED ADD_PROPERTY(PropertyInfo(Variant::BOOL, "animate_physical_bones"), "set_animate_physical_bones", "get_animate_physical_bones"); #endif // _3D_DISABLED -#ifdef TOOLS_ENABLED ADD_SIGNAL(MethodInfo("pose_updated")); -#endif // TOOLS_ENABLED - ADD_SIGNAL(MethodInfo("bone_pose_changed", PropertyInfo(Variant::INT, "bone_idx"))); ADD_SIGNAL(MethodInfo("bone_enabled_changed", PropertyInfo(Variant::INT, "bone_idx"))); ADD_SIGNAL(MethodInfo("show_rest_only_changed")); diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h index cb4c82d232..c2e9cfcced 100644 --- a/scene/3d/skeleton_3d.h +++ b/scene/3d/skeleton_3d.h @@ -51,12 +51,14 @@ class SkinReference : public RefCounted { uint64_t skeleton_version = 0; Vector<uint32_t> skin_bone_indices; uint32_t *skin_bone_indices_ptrs = nullptr; - void _skin_changed(); protected: static void _bind_methods(); public: + // Public for use with callable_mp. + void _skin_changed(); + RID get_skeleton() const; Ref<Skin> get_skin() const; ~SkinReference(); @@ -146,6 +148,7 @@ private: bool rest_dirty = false; bool show_rest_only = false; + float motion_scale = 1.0; uint64_t version = 1; @@ -155,7 +158,7 @@ protected: bool _get(const StringName &p_path, Variant &r_ret) const; bool _set(const StringName &p_path, const Variant &p_value); void _get_property_list(List<PropertyInfo> *p_list) const; - virtual void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; void _notification(int p_what); static void _bind_methods(); @@ -190,11 +193,8 @@ public: void unparent_bone_and_rest(int p_bone); - Vector<int> get_bone_children(int p_bone); - void set_bone_children(int p_bone, Vector<int> p_children); - void add_bone_child(int p_bone, int p_child); - void remove_bone_child(int p_bone, int p_child); - Vector<int> get_parentless_bones(); + Vector<int> get_bone_children(int p_bone) const; + Vector<int> get_parentless_bones() const; int get_bone_count() const; @@ -211,6 +211,9 @@ public: bool is_show_rest_only() const; void clear_bones(); + void set_motion_scale(float p_motion_scale); + float get_motion_scale() const; + // posing api void set_bone_pose_position(int p_bone, const Vector3 &p_position); @@ -223,6 +226,9 @@ public: Quaternion get_bone_pose_rotation(int p_bone) const; Vector3 get_bone_pose_scale(int p_bone) const; + void reset_bone_pose(int p_bone); + void reset_bone_poses(); + void clear_bones_global_pose_override(); Transform3D get_bone_global_pose_override(int p_bone) const; void set_bone_global_pose_override(int p_bone, const Transform3D &p_pose, real_t p_amount, bool p_persistent = false); @@ -288,4 +294,4 @@ public: ~Skeleton3D(); }; -#endif +#endif // SKELETON_3D_H diff --git a/scene/3d/skeleton_ik_3d.cpp b/scene/3d/skeleton_ik_3d.cpp index 2182c5ba11..f0534c8099 100644 --- a/scene/3d/skeleton_ik_3d.cpp +++ b/scene/3d/skeleton_ik_3d.cpp @@ -329,8 +329,8 @@ void FabrikInverseKinematic::_update_chain(const Skeleton3D *p_sk, ChainItem *p_ } } -void SkeletonIK3D::_validate_property(PropertyInfo &property) const { - if (property.name == "root_bone" || property.name == "tip_bone") { +void SkeletonIK3D::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "root_bone" || p_property.name == "tip_bone") { if (skeleton) { String names("--,"); for (int i = 0; i < skeleton->get_bone_count(); i++) { @@ -340,15 +340,13 @@ void SkeletonIK3D::_validate_property(PropertyInfo &property) const { names += skeleton->get_bone_name(i); } - property.hint = PROPERTY_HINT_ENUM; - property.hint_string = names; + p_property.hint = PROPERTY_HINT_ENUM; + p_property.hint_string = names; } else { - property.hint = PROPERTY_HINT_NONE; - property.hint_string = ""; + p_property.hint = PROPERTY_HINT_NONE; + p_property.hint_string = ""; } } - - Node::_validate_property(property); } void SkeletonIK3D::_bind_methods() { diff --git a/scene/3d/skeleton_ik_3d.h b/scene/3d/skeleton_ik_3d.h index 0f656187de..097df2c400 100644 --- a/scene/3d/skeleton_ik_3d.h +++ b/scene/3d/skeleton_ik_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SKELETON_IK_H -#define SKELETON_IK_H +#ifndef SKELETON_IK_3D_H +#define SKELETON_IK_3D_H #ifndef _3D_DISABLED @@ -137,7 +137,7 @@ class SkeletonIK3D : public Node { FabrikInverseKinematic::Task *task = nullptr; protected: - virtual void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; static void _bind_methods(); virtual void _notification(int p_what); @@ -192,4 +192,4 @@ private: #endif // _3D_DISABLED -#endif // SKELETON_IK_H +#endif // SKELETON_IK_3D_H diff --git a/scene/3d/soft_dynamic_body_3d.cpp b/scene/3d/soft_body_3d.cpp index d68e7fd527..2466b71aea 100644 --- a/scene/3d/soft_dynamic_body_3d.cpp +++ b/scene/3d/soft_body_3d.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* soft_dynamic_body_3d.cpp */ +/* soft_body_3d.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,13 +28,13 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "soft_dynamic_body_3d.h" +#include "soft_body_3d.h" #include "scene/3d/physics_body_3d.h" -SoftDynamicBodyRenderingServerHandler::SoftDynamicBodyRenderingServerHandler() {} +SoftBodyRenderingServerHandler::SoftBodyRenderingServerHandler() {} -void SoftDynamicBodyRenderingServerHandler::prepare(RID p_mesh, int p_surface) { +void SoftBodyRenderingServerHandler::prepare(RID p_mesh, int p_surface) { clear(); ERR_FAIL_COND(!p_mesh.is_valid()); @@ -56,7 +56,7 @@ void SoftDynamicBodyRenderingServerHandler::prepare(RID p_mesh, int p_surface) { offset_normal = surface_offsets[RS::ARRAY_NORMAL]; } -void SoftDynamicBodyRenderingServerHandler::clear() { +void SoftBodyRenderingServerHandler::clear() { buffer.resize(0); stride = 0; offset_vertices = 0; @@ -66,48 +66,57 @@ void SoftDynamicBodyRenderingServerHandler::clear() { mesh = RID(); } -void SoftDynamicBodyRenderingServerHandler::open() { +void SoftBodyRenderingServerHandler::open() { write_buffer = buffer.ptrw(); } -void SoftDynamicBodyRenderingServerHandler::close() { +void SoftBodyRenderingServerHandler::close() { write_buffer = nullptr; } -void SoftDynamicBodyRenderingServerHandler::commit_changes() { +void SoftBodyRenderingServerHandler::commit_changes() { RS::get_singleton()->mesh_surface_update_vertex_region(mesh, surface, 0, buffer); } -void SoftDynamicBodyRenderingServerHandler::set_vertex(int p_vertex_id, const void *p_vector3) { +void SoftBodyRenderingServerHandler::set_vertex(int p_vertex_id, const void *p_vector3) { memcpy(&write_buffer[p_vertex_id * stride + offset_vertices], p_vector3, sizeof(float) * 3); } -void SoftDynamicBodyRenderingServerHandler::set_normal(int p_vertex_id, const void *p_vector3) { - memcpy(&write_buffer[p_vertex_id * stride + offset_normal], p_vector3, sizeof(float) * 3); +void SoftBodyRenderingServerHandler::set_normal(int p_vertex_id, const void *p_vector3) { + // Store normal vector in A2B10G10R10 format. + Vector3 n; + memcpy(&n, p_vector3, sizeof(Vector3)); + n *= Vector3(0.5, 0.5, 0.5); + n += Vector3(0.5, 0.5, 0.5); + Vector2 res = n.octahedron_encode(); + uint32_t value = 0; + value |= (uint16_t)CLAMP(res.x * 65535, 0, 65535); + value |= (uint16_t)CLAMP(res.y * 65535, 0, 65535) << 16; + memcpy(&write_buffer[p_vertex_id * stride + offset_normal], &value, sizeof(uint32_t)); } -void SoftDynamicBodyRenderingServerHandler::set_aabb(const AABB &p_aabb) { +void SoftBodyRenderingServerHandler::set_aabb(const AABB &p_aabb) { RS::get_singleton()->mesh_set_custom_aabb(mesh, p_aabb); } -SoftDynamicBody3D::PinnedPoint::PinnedPoint() { +SoftBody3D::PinnedPoint::PinnedPoint() { } -SoftDynamicBody3D::PinnedPoint::PinnedPoint(const PinnedPoint &obj_tocopy) { +SoftBody3D::PinnedPoint::PinnedPoint(const PinnedPoint &obj_tocopy) { point_index = obj_tocopy.point_index; spatial_attachment_path = obj_tocopy.spatial_attachment_path; spatial_attachment = obj_tocopy.spatial_attachment; offset = obj_tocopy.offset; } -void SoftDynamicBody3D::PinnedPoint::operator=(const PinnedPoint &obj) { +void SoftBody3D::PinnedPoint::operator=(const PinnedPoint &obj) { point_index = obj.point_index; spatial_attachment_path = obj.spatial_attachment_path; spatial_attachment = obj.spatial_attachment; offset = obj.offset; } -void SoftDynamicBody3D::_update_pickable() { +void SoftBody3D::_update_pickable() { if (!is_inside_tree()) { return; } @@ -115,7 +124,7 @@ void SoftDynamicBody3D::_update_pickable() { PhysicsServer3D::get_singleton()->soft_body_set_ray_pickable(physics_rid, pickable); } -bool SoftDynamicBody3D::_set(const StringName &p_name, const Variant &p_value) { +bool SoftBody3D::_set(const StringName &p_name, const Variant &p_value) { String name = p_name; String which = name.get_slicec('/', 0); @@ -132,7 +141,7 @@ bool SoftDynamicBody3D::_set(const StringName &p_name, const Variant &p_value) { return false; } -bool SoftDynamicBody3D::_get(const StringName &p_name, Variant &r_ret) const { +bool SoftBody3D::_get(const StringName &p_name, Variant &r_ret) const { String name = p_name; String which = name.get_slicec('/', 0); @@ -159,7 +168,7 @@ bool SoftDynamicBody3D::_get(const StringName &p_name, Variant &r_ret) const { return false; } -void SoftDynamicBody3D::_get_property_list(List<PropertyInfo> *p_list) const { +void SoftBody3D::_get_property_list(List<PropertyInfo> *p_list) const { const int pinned_points_indices_size = pinned_points.size(); p_list->push_back(PropertyInfo(Variant::PACKED_INT32_ARRAY, PNAME("pinned_points"))); @@ -172,7 +181,7 @@ void SoftDynamicBody3D::_get_property_list(List<PropertyInfo> *p_list) const { } } -bool SoftDynamicBody3D::_set_property_pinned_points_indices(const Array &p_indices) { +bool SoftBody3D::_set_property_pinned_points_indices(const Array &p_indices) { const int p_indices_size = p_indices.size(); { // Remove the pined points on physics server that will be removed by resize @@ -201,7 +210,7 @@ bool SoftDynamicBody3D::_set_property_pinned_points_indices(const Array &p_indic return true; } -bool SoftDynamicBody3D::_set_property_pinned_points_attachment(int p_item, const String &p_what, const Variant &p_value) { +bool SoftBody3D::_set_property_pinned_points_attachment(int p_item, const String &p_what, const Variant &p_value) { if (pinned_points.size() <= p_item) { return false; } @@ -220,7 +229,7 @@ bool SoftDynamicBody3D::_set_property_pinned_points_attachment(int p_item, const return true; } -bool SoftDynamicBody3D::_get_property_pinned_points(int p_item, const String &p_what, Variant &r_ret) const { +bool SoftBody3D::_get_property_pinned_points(int p_item, const String &p_what, Variant &r_ret) const { if (pinned_points.size() <= p_item) { return false; } @@ -239,7 +248,7 @@ bool SoftDynamicBody3D::_get_property_pinned_points(int p_item, const String &p_ return true; } -void SoftDynamicBody3D::_notification(int p_what) { +void SoftBody3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_WORLD: { if (Engine::get_singleton()->is_editor_hint()) { @@ -304,56 +313,56 @@ void SoftDynamicBody3D::_notification(int p_what) { } } -void SoftDynamicBody3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("get_physics_rid"), &SoftDynamicBody3D::get_physics_rid); +void SoftBody3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_physics_rid"), &SoftBody3D::get_physics_rid); - ClassDB::bind_method(D_METHOD("set_collision_mask", "collision_mask"), &SoftDynamicBody3D::set_collision_mask); - ClassDB::bind_method(D_METHOD("get_collision_mask"), &SoftDynamicBody3D::get_collision_mask); + ClassDB::bind_method(D_METHOD("set_collision_mask", "collision_mask"), &SoftBody3D::set_collision_mask); + ClassDB::bind_method(D_METHOD("get_collision_mask"), &SoftBody3D::get_collision_mask); - ClassDB::bind_method(D_METHOD("set_collision_layer", "collision_layer"), &SoftDynamicBody3D::set_collision_layer); - ClassDB::bind_method(D_METHOD("get_collision_layer"), &SoftDynamicBody3D::get_collision_layer); + ClassDB::bind_method(D_METHOD("set_collision_layer", "collision_layer"), &SoftBody3D::set_collision_layer); + ClassDB::bind_method(D_METHOD("get_collision_layer"), &SoftBody3D::get_collision_layer); - ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &SoftDynamicBody3D::set_collision_mask_value); - ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &SoftDynamicBody3D::get_collision_mask_value); + ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &SoftBody3D::set_collision_mask_value); + ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &SoftBody3D::get_collision_mask_value); - ClassDB::bind_method(D_METHOD("set_collision_layer_value", "layer_number", "value"), &SoftDynamicBody3D::set_collision_layer_value); - ClassDB::bind_method(D_METHOD("get_collision_layer_value", "layer_number"), &SoftDynamicBody3D::get_collision_layer_value); + ClassDB::bind_method(D_METHOD("set_collision_layer_value", "layer_number", "value"), &SoftBody3D::set_collision_layer_value); + ClassDB::bind_method(D_METHOD("get_collision_layer_value", "layer_number"), &SoftBody3D::get_collision_layer_value); - ClassDB::bind_method(D_METHOD("set_parent_collision_ignore", "parent_collision_ignore"), &SoftDynamicBody3D::set_parent_collision_ignore); - ClassDB::bind_method(D_METHOD("get_parent_collision_ignore"), &SoftDynamicBody3D::get_parent_collision_ignore); + ClassDB::bind_method(D_METHOD("set_parent_collision_ignore", "parent_collision_ignore"), &SoftBody3D::set_parent_collision_ignore); + ClassDB::bind_method(D_METHOD("get_parent_collision_ignore"), &SoftBody3D::get_parent_collision_ignore); - ClassDB::bind_method(D_METHOD("set_disable_mode", "mode"), &SoftDynamicBody3D::set_disable_mode); - ClassDB::bind_method(D_METHOD("get_disable_mode"), &SoftDynamicBody3D::get_disable_mode); + ClassDB::bind_method(D_METHOD("set_disable_mode", "mode"), &SoftBody3D::set_disable_mode); + ClassDB::bind_method(D_METHOD("get_disable_mode"), &SoftBody3D::get_disable_mode); - ClassDB::bind_method(D_METHOD("get_collision_exceptions"), &SoftDynamicBody3D::get_collision_exceptions); - ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body"), &SoftDynamicBody3D::add_collision_exception_with); - ClassDB::bind_method(D_METHOD("remove_collision_exception_with", "body"), &SoftDynamicBody3D::remove_collision_exception_with); + ClassDB::bind_method(D_METHOD("get_collision_exceptions"), &SoftBody3D::get_collision_exceptions); + ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body"), &SoftBody3D::add_collision_exception_with); + ClassDB::bind_method(D_METHOD("remove_collision_exception_with", "body"), &SoftBody3D::remove_collision_exception_with); - ClassDB::bind_method(D_METHOD("set_simulation_precision", "simulation_precision"), &SoftDynamicBody3D::set_simulation_precision); - ClassDB::bind_method(D_METHOD("get_simulation_precision"), &SoftDynamicBody3D::get_simulation_precision); + ClassDB::bind_method(D_METHOD("set_simulation_precision", "simulation_precision"), &SoftBody3D::set_simulation_precision); + ClassDB::bind_method(D_METHOD("get_simulation_precision"), &SoftBody3D::get_simulation_precision); - ClassDB::bind_method(D_METHOD("set_total_mass", "mass"), &SoftDynamicBody3D::set_total_mass); - ClassDB::bind_method(D_METHOD("get_total_mass"), &SoftDynamicBody3D::get_total_mass); + ClassDB::bind_method(D_METHOD("set_total_mass", "mass"), &SoftBody3D::set_total_mass); + ClassDB::bind_method(D_METHOD("get_total_mass"), &SoftBody3D::get_total_mass); - ClassDB::bind_method(D_METHOD("set_linear_stiffness", "linear_stiffness"), &SoftDynamicBody3D::set_linear_stiffness); - ClassDB::bind_method(D_METHOD("get_linear_stiffness"), &SoftDynamicBody3D::get_linear_stiffness); + ClassDB::bind_method(D_METHOD("set_linear_stiffness", "linear_stiffness"), &SoftBody3D::set_linear_stiffness); + ClassDB::bind_method(D_METHOD("get_linear_stiffness"), &SoftBody3D::get_linear_stiffness); - ClassDB::bind_method(D_METHOD("set_pressure_coefficient", "pressure_coefficient"), &SoftDynamicBody3D::set_pressure_coefficient); - ClassDB::bind_method(D_METHOD("get_pressure_coefficient"), &SoftDynamicBody3D::get_pressure_coefficient); + ClassDB::bind_method(D_METHOD("set_pressure_coefficient", "pressure_coefficient"), &SoftBody3D::set_pressure_coefficient); + ClassDB::bind_method(D_METHOD("get_pressure_coefficient"), &SoftBody3D::get_pressure_coefficient); - ClassDB::bind_method(D_METHOD("set_damping_coefficient", "damping_coefficient"), &SoftDynamicBody3D::set_damping_coefficient); - ClassDB::bind_method(D_METHOD("get_damping_coefficient"), &SoftDynamicBody3D::get_damping_coefficient); + ClassDB::bind_method(D_METHOD("set_damping_coefficient", "damping_coefficient"), &SoftBody3D::set_damping_coefficient); + ClassDB::bind_method(D_METHOD("get_damping_coefficient"), &SoftBody3D::get_damping_coefficient); - ClassDB::bind_method(D_METHOD("set_drag_coefficient", "drag_coefficient"), &SoftDynamicBody3D::set_drag_coefficient); - ClassDB::bind_method(D_METHOD("get_drag_coefficient"), &SoftDynamicBody3D::get_drag_coefficient); + ClassDB::bind_method(D_METHOD("set_drag_coefficient", "drag_coefficient"), &SoftBody3D::set_drag_coefficient); + ClassDB::bind_method(D_METHOD("get_drag_coefficient"), &SoftBody3D::get_drag_coefficient); - ClassDB::bind_method(D_METHOD("get_point_transform", "point_index"), &SoftDynamicBody3D::get_point_transform); + ClassDB::bind_method(D_METHOD("get_point_transform", "point_index"), &SoftBody3D::get_point_transform); - ClassDB::bind_method(D_METHOD("set_point_pinned", "point_index", "pinned", "attachment_path"), &SoftDynamicBody3D::pin_point, DEFVAL(NodePath())); - ClassDB::bind_method(D_METHOD("is_point_pinned", "point_index"), &SoftDynamicBody3D::is_point_pinned); + ClassDB::bind_method(D_METHOD("set_point_pinned", "point_index", "pinned", "attachment_path"), &SoftBody3D::pin_point, DEFVAL(NodePath())); + ClassDB::bind_method(D_METHOD("is_point_pinned", "point_index"), &SoftBody3D::is_point_pinned); - ClassDB::bind_method(D_METHOD("set_ray_pickable", "ray_pickable"), &SoftDynamicBody3D::set_ray_pickable); - ClassDB::bind_method(D_METHOD("is_ray_pickable"), &SoftDynamicBody3D::is_ray_pickable); + ClassDB::bind_method(D_METHOD("set_ray_pickable", "ray_pickable"), &SoftBody3D::set_ray_pickable); + ClassDB::bind_method(D_METHOD("is_ray_pickable"), &SoftBody3D::is_ray_pickable); ADD_GROUP("Collision", "collision_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer", "get_collision_layer"); @@ -375,8 +384,8 @@ void SoftDynamicBody3D::_bind_methods() { BIND_ENUM_CONSTANT(DISABLE_MODE_KEEP_ACTIVE); } -TypedArray<String> SoftDynamicBody3D::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray SoftBody3D::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (mesh.is_null()) { warnings.push_back(RTR("This body will be ignored until you set a mesh.")); @@ -384,13 +393,13 @@ TypedArray<String> SoftDynamicBody3D::get_configuration_warnings() const { Transform3D t = get_transform(); if ((ABS(t.basis.get_column(0).length() - 1.0) > 0.05 || ABS(t.basis.get_column(1).length() - 1.0) > 0.05 || ABS(t.basis.get_column(2).length() - 1.0) > 0.05)) { - warnings.push_back(RTR("Size changes to SoftDynamicBody3D will be overridden by the physics engine when running.\nChange the size in children collision shapes instead.")); + warnings.push_back(RTR("Size changes to SoftBody3D will be overridden by the physics engine when running.\nChange the size in children collision shapes instead.")); } return warnings; } -void SoftDynamicBody3D::_update_physics_server() { +void SoftBody3D::_update_physics_server() { if (!simulation_started) { return; } @@ -406,7 +415,7 @@ void SoftDynamicBody3D::_update_physics_server() { } } -void SoftDynamicBody3D::_draw_soft_mesh() { +void SoftBody3D::_draw_soft_mesh() { if (mesh.is_null()) { return; } @@ -436,7 +445,7 @@ void SoftDynamicBody3D::_draw_soft_mesh() { rendering_server_handler->commit_changes(); } -void SoftDynamicBody3D::_prepare_physics_server() { +void SoftBody3D::_prepare_physics_server() { #ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint()) { if (mesh.is_valid()) { @@ -456,22 +465,22 @@ void SoftDynamicBody3D::_prepare_physics_server() { mesh_rid = mesh->get_rid(); } PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, mesh_rid); - RS::get_singleton()->connect("frame_pre_draw", callable_mp(this, &SoftDynamicBody3D::_draw_soft_mesh)); + RS::get_singleton()->connect("frame_pre_draw", callable_mp(this, &SoftBody3D::_draw_soft_mesh)); } else { PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, RID()); - if (RS::get_singleton()->is_connected("frame_pre_draw", callable_mp(this, &SoftDynamicBody3D::_draw_soft_mesh))) { - RS::get_singleton()->disconnect("frame_pre_draw", callable_mp(this, &SoftDynamicBody3D::_draw_soft_mesh)); + if (RS::get_singleton()->is_connected("frame_pre_draw", callable_mp(this, &SoftBody3D::_draw_soft_mesh))) { + RS::get_singleton()->disconnect("frame_pre_draw", callable_mp(this, &SoftBody3D::_draw_soft_mesh)); } } } -void SoftDynamicBody3D::_become_mesh_owner() { +void SoftBody3D::_become_mesh_owner() { Vector<Ref<Material>> copy_materials; copy_materials.append_array(surface_override_materials); ERR_FAIL_COND(!mesh->get_surface_count()); - // Get current mesh array and create new mesh array with necessary flag for SoftDynamicBody + // Get current mesh array and create new mesh array with necessary flag for SoftBody Array surface_arrays = mesh->surface_get_arrays(0); Array surface_blend_arrays = mesh->surface_get_blend_shape_arrays(0); Dictionary surface_lods = mesh->surface_get_lods(0); @@ -493,25 +502,25 @@ void SoftDynamicBody3D::_become_mesh_owner() { owned_mesh = soft_mesh->get_rid(); } -void SoftDynamicBody3D::set_collision_mask(uint32_t p_mask) { +void SoftBody3D::set_collision_mask(uint32_t p_mask) { collision_mask = p_mask; PhysicsServer3D::get_singleton()->soft_body_set_collision_mask(physics_rid, p_mask); } -uint32_t SoftDynamicBody3D::get_collision_mask() const { +uint32_t SoftBody3D::get_collision_mask() const { return collision_mask; } -void SoftDynamicBody3D::set_collision_layer(uint32_t p_layer) { +void SoftBody3D::set_collision_layer(uint32_t p_layer) { collision_layer = p_layer; PhysicsServer3D::get_singleton()->soft_body_set_collision_layer(physics_rid, p_layer); } -uint32_t SoftDynamicBody3D::get_collision_layer() const { +uint32_t SoftBody3D::get_collision_layer() const { return collision_layer; } -void SoftDynamicBody3D::set_collision_layer_value(int p_layer_number, bool p_value) { +void SoftBody3D::set_collision_layer_value(int p_layer_number, bool p_value) { ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); uint32_t collision_layer = get_collision_layer(); @@ -523,13 +532,13 @@ void SoftDynamicBody3D::set_collision_layer_value(int p_layer_number, bool p_val set_collision_layer(collision_layer); } -bool SoftDynamicBody3D::get_collision_layer_value(int p_layer_number) const { +bool SoftBody3D::get_collision_layer_value(int p_layer_number) const { ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); return get_collision_layer() & (1 << (p_layer_number - 1)); } -void SoftDynamicBody3D::set_collision_mask_value(int p_layer_number, bool p_value) { +void SoftBody3D::set_collision_mask_value(int p_layer_number, bool p_value) { ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); uint32_t mask = get_collision_mask(); @@ -541,13 +550,13 @@ void SoftDynamicBody3D::set_collision_mask_value(int p_layer_number, bool p_valu set_collision_mask(mask); } -bool SoftDynamicBody3D::get_collision_mask_value(int p_layer_number) const { +bool SoftBody3D::get_collision_mask_value(int p_layer_number) const { ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); return get_collision_mask() & (1 << (p_layer_number - 1)); } -void SoftDynamicBody3D::set_disable_mode(DisableMode p_mode) { +void SoftBody3D::set_disable_mode(DisableMode p_mode) { if (disable_mode == p_mode) { return; } @@ -559,33 +568,33 @@ void SoftDynamicBody3D::set_disable_mode(DisableMode p_mode) { } } -SoftDynamicBody3D::DisableMode SoftDynamicBody3D::get_disable_mode() const { +SoftBody3D::DisableMode SoftBody3D::get_disable_mode() const { return disable_mode; } -void SoftDynamicBody3D::set_parent_collision_ignore(const NodePath &p_parent_collision_ignore) { +void SoftBody3D::set_parent_collision_ignore(const NodePath &p_parent_collision_ignore) { parent_collision_ignore = p_parent_collision_ignore; } -const NodePath &SoftDynamicBody3D::get_parent_collision_ignore() const { +const NodePath &SoftBody3D::get_parent_collision_ignore() const { return parent_collision_ignore; } -void SoftDynamicBody3D::set_pinned_points_indices(Vector<SoftDynamicBody3D::PinnedPoint> p_pinned_points_indices) { +void SoftBody3D::set_pinned_points_indices(Vector<SoftBody3D::PinnedPoint> p_pinned_points_indices) { pinned_points = p_pinned_points_indices; for (int i = pinned_points.size() - 1; 0 <= i; --i) { pin_point(p_pinned_points_indices[i].point_index, true); } } -Vector<SoftDynamicBody3D::PinnedPoint> SoftDynamicBody3D::get_pinned_points_indices() { +Vector<SoftBody3D::PinnedPoint> SoftBody3D::get_pinned_points_indices() { return pinned_points; } -Array SoftDynamicBody3D::get_collision_exceptions() { +TypedArray<PhysicsBody3D> SoftBody3D::get_collision_exceptions() { List<RID> exceptions; PhysicsServer3D::get_singleton()->soft_body_get_collision_exceptions(physics_rid, &exceptions); - Array ret; + TypedArray<PhysicsBody3D> ret; for (const RID &body : exceptions) { ObjectID instance_id = PhysicsServer3D::get_singleton()->body_get_object_instance_id(body); Object *obj = ObjectDB::get_instance(instance_id); @@ -595,77 +604,77 @@ Array SoftDynamicBody3D::get_collision_exceptions() { return ret; } -void SoftDynamicBody3D::add_collision_exception_with(Node *p_node) { +void SoftBody3D::add_collision_exception_with(Node *p_node) { ERR_FAIL_NULL(p_node); CollisionObject3D *collision_object = Object::cast_to<CollisionObject3D>(p_node); ERR_FAIL_COND_MSG(!collision_object, "Collision exception only works between two CollisionObject3Ds."); PhysicsServer3D::get_singleton()->soft_body_add_collision_exception(physics_rid, collision_object->get_rid()); } -void SoftDynamicBody3D::remove_collision_exception_with(Node *p_node) { +void SoftBody3D::remove_collision_exception_with(Node *p_node) { ERR_FAIL_NULL(p_node); CollisionObject3D *collision_object = Object::cast_to<CollisionObject3D>(p_node); ERR_FAIL_COND_MSG(!collision_object, "Collision exception only works between two CollisionObject3Ds."); PhysicsServer3D::get_singleton()->soft_body_remove_collision_exception(physics_rid, collision_object->get_rid()); } -int SoftDynamicBody3D::get_simulation_precision() { +int SoftBody3D::get_simulation_precision() { return PhysicsServer3D::get_singleton()->soft_body_get_simulation_precision(physics_rid); } -void SoftDynamicBody3D::set_simulation_precision(int p_simulation_precision) { +void SoftBody3D::set_simulation_precision(int p_simulation_precision) { PhysicsServer3D::get_singleton()->soft_body_set_simulation_precision(physics_rid, p_simulation_precision); } -real_t SoftDynamicBody3D::get_total_mass() { +real_t SoftBody3D::get_total_mass() { return PhysicsServer3D::get_singleton()->soft_body_get_total_mass(physics_rid); } -void SoftDynamicBody3D::set_total_mass(real_t p_total_mass) { +void SoftBody3D::set_total_mass(real_t p_total_mass) { PhysicsServer3D::get_singleton()->soft_body_set_total_mass(physics_rid, p_total_mass); } -void SoftDynamicBody3D::set_linear_stiffness(real_t p_linear_stiffness) { +void SoftBody3D::set_linear_stiffness(real_t p_linear_stiffness) { PhysicsServer3D::get_singleton()->soft_body_set_linear_stiffness(physics_rid, p_linear_stiffness); } -real_t SoftDynamicBody3D::get_linear_stiffness() { +real_t SoftBody3D::get_linear_stiffness() { return PhysicsServer3D::get_singleton()->soft_body_get_linear_stiffness(physics_rid); } -real_t SoftDynamicBody3D::get_pressure_coefficient() { +real_t SoftBody3D::get_pressure_coefficient() { return PhysicsServer3D::get_singleton()->soft_body_get_pressure_coefficient(physics_rid); } -void SoftDynamicBody3D::set_pressure_coefficient(real_t p_pressure_coefficient) { +void SoftBody3D::set_pressure_coefficient(real_t p_pressure_coefficient) { PhysicsServer3D::get_singleton()->soft_body_set_pressure_coefficient(physics_rid, p_pressure_coefficient); } -real_t SoftDynamicBody3D::get_damping_coefficient() { +real_t SoftBody3D::get_damping_coefficient() { return PhysicsServer3D::get_singleton()->soft_body_get_damping_coefficient(physics_rid); } -void SoftDynamicBody3D::set_damping_coefficient(real_t p_damping_coefficient) { +void SoftBody3D::set_damping_coefficient(real_t p_damping_coefficient) { PhysicsServer3D::get_singleton()->soft_body_set_damping_coefficient(physics_rid, p_damping_coefficient); } -real_t SoftDynamicBody3D::get_drag_coefficient() { +real_t SoftBody3D::get_drag_coefficient() { return PhysicsServer3D::get_singleton()->soft_body_get_drag_coefficient(physics_rid); } -void SoftDynamicBody3D::set_drag_coefficient(real_t p_drag_coefficient) { +void SoftBody3D::set_drag_coefficient(real_t p_drag_coefficient) { PhysicsServer3D::get_singleton()->soft_body_set_drag_coefficient(physics_rid, p_drag_coefficient); } -Vector3 SoftDynamicBody3D::get_point_transform(int p_point_index) { +Vector3 SoftBody3D::get_point_transform(int p_point_index) { return PhysicsServer3D::get_singleton()->soft_body_get_point_global_position(physics_rid, p_point_index); } -void SoftDynamicBody3D::pin_point_toggle(int p_point_index) { +void SoftBody3D::pin_point_toggle(int p_point_index) { pin_point(p_point_index, !(-1 != _has_pinned_point(p_point_index))); } -void SoftDynamicBody3D::pin_point(int p_point_index, bool pin, const NodePath &p_spatial_attachment_path) { +void SoftBody3D::pin_point(int p_point_index, bool pin, const NodePath &p_spatial_attachment_path) { _pin_point_on_physics_server(p_point_index, pin); if (pin) { _add_pinned_point(p_point_index, p_spatial_attachment_path); @@ -674,35 +683,35 @@ void SoftDynamicBody3D::pin_point(int p_point_index, bool pin, const NodePath &p } } -bool SoftDynamicBody3D::is_point_pinned(int p_point_index) const { +bool SoftBody3D::is_point_pinned(int p_point_index) const { return -1 != _has_pinned_point(p_point_index); } -void SoftDynamicBody3D::set_ray_pickable(bool p_ray_pickable) { +void SoftBody3D::set_ray_pickable(bool p_ray_pickable) { ray_pickable = p_ray_pickable; _update_pickable(); } -bool SoftDynamicBody3D::is_ray_pickable() const { +bool SoftBody3D::is_ray_pickable() const { return ray_pickable; } -SoftDynamicBody3D::SoftDynamicBody3D() : +SoftBody3D::SoftBody3D() : physics_rid(PhysicsServer3D::get_singleton()->soft_body_create()) { - rendering_server_handler = memnew(SoftDynamicBodyRenderingServerHandler); + rendering_server_handler = memnew(SoftBodyRenderingServerHandler); PhysicsServer3D::get_singleton()->body_attach_object_instance_id(physics_rid, get_instance_id()); } -SoftDynamicBody3D::~SoftDynamicBody3D() { +SoftBody3D::~SoftBody3D() { memdelete(rendering_server_handler); PhysicsServer3D::get_singleton()->free(physics_rid); } -void SoftDynamicBody3D::_make_cache_dirty() { +void SoftBody3D::_make_cache_dirty() { pinned_points_cache_dirty = true; } -void SoftDynamicBody3D::_update_cache_pin_points_datas() { +void SoftBody3D::_update_cache_pin_points_datas() { if (!pinned_points_cache_dirty) { return; } @@ -715,17 +724,17 @@ void SoftDynamicBody3D::_update_cache_pin_points_datas() { w[i].spatial_attachment = Object::cast_to<Node3D>(get_node(w[i].spatial_attachment_path)); } if (!w[i].spatial_attachment) { - ERR_PRINT("Node3D node not defined in the pinned point, this is undefined behavior for SoftDynamicBody3D!"); + ERR_PRINT("Node3D node not defined in the pinned point, this is undefined behavior for SoftBody3D!"); } } } -void SoftDynamicBody3D::_pin_point_on_physics_server(int p_point_index, bool pin) { +void SoftBody3D::_pin_point_on_physics_server(int p_point_index, bool pin) { PhysicsServer3D::get_singleton()->soft_body_pin_point(physics_rid, p_point_index, pin); } -void SoftDynamicBody3D::_add_pinned_point(int p_point_index, const NodePath &p_spatial_attachment_path) { - SoftDynamicBody3D::PinnedPoint *pinned_point; +void SoftBody3D::_add_pinned_point(int p_point_index, const NodePath &p_spatial_attachment_path) { + SoftBody3D::PinnedPoint *pinned_point; if (-1 == _get_pinned_point(p_point_index, pinned_point)) { // Create new PinnedPoint pp; @@ -750,7 +759,7 @@ void SoftDynamicBody3D::_add_pinned_point(int p_point_index, const NodePath &p_s } } -void SoftDynamicBody3D::_reset_points_offsets() { +void SoftBody3D::_reset_points_offsets() { if (!Engine::get_singleton()->is_editor_hint()) { return; } @@ -772,25 +781,25 @@ void SoftDynamicBody3D::_reset_points_offsets() { } } -void SoftDynamicBody3D::_remove_pinned_point(int p_point_index) { +void SoftBody3D::_remove_pinned_point(int p_point_index) { const int id(_has_pinned_point(p_point_index)); if (-1 != id) { pinned_points.remove_at(id); } } -int SoftDynamicBody3D::_get_pinned_point(int p_point_index, SoftDynamicBody3D::PinnedPoint *&r_point) const { +int SoftBody3D::_get_pinned_point(int p_point_index, SoftBody3D::PinnedPoint *&r_point) const { const int id = _has_pinned_point(p_point_index); if (-1 == id) { r_point = nullptr; return -1; } else { - r_point = const_cast<SoftDynamicBody3D::PinnedPoint *>(&pinned_points.ptr()[id]); + r_point = const_cast<SoftBody3D::PinnedPoint *>(&pinned_points.ptr()[id]); return id; } } -int SoftDynamicBody3D::_has_pinned_point(int p_point_index) const { +int SoftBody3D::_has_pinned_point(int p_point_index) const { const PinnedPoint *r = pinned_points.ptr(); for (int i = pinned_points.size() - 1; 0 <= i; --i) { if (p_point_index == r[i].point_index) { diff --git a/scene/3d/soft_dynamic_body_3d.h b/scene/3d/soft_body_3d.h index e11e5c73df..9ec1f18396 100644 --- a/scene/3d/soft_dynamic_body_3d.h +++ b/scene/3d/soft_body_3d.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* soft_dynamic_body_3d.h */ +/* soft_body_3d.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,16 +28,17 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SOFT_DYNAMIC_BODY_H -#define SOFT_DYNAMIC_BODY_H +#ifndef SOFT_BODY_3D_H +#define SOFT_BODY_3D_H #include "scene/3d/mesh_instance_3d.h" #include "servers/physics_server_3d.h" -class SoftDynamicBody3D; +class PhysicsBody3D; +class SoftBody3D; -class SoftDynamicBodyRenderingServerHandler : public PhysicsServer3DRenderingServerHandler { - friend class SoftDynamicBody3D; +class SoftBodyRenderingServerHandler : public PhysicsServer3DRenderingServerHandler { + friend class SoftBody3D; RID mesh; int surface = 0; @@ -49,7 +50,7 @@ class SoftDynamicBodyRenderingServerHandler : public PhysicsServer3DRenderingSer uint8_t *write_buffer = nullptr; private: - SoftDynamicBodyRenderingServerHandler(); + SoftBodyRenderingServerHandler(); bool is_ready(RID p_mesh_rid) const { return mesh.is_valid() && mesh == p_mesh_rid; } void prepare(RID p_mesh_rid, int p_surface); void clear(); @@ -63,8 +64,8 @@ public: void set_aabb(const AABB &p_aabb) override; }; -class SoftDynamicBody3D : public MeshInstance3D { - GDCLASS(SoftDynamicBody3D, MeshInstance3D); +class SoftBody3D : public MeshInstance3D { + GDCLASS(SoftBody3D, MeshInstance3D); public: enum DisableMode { @@ -84,7 +85,7 @@ public: }; private: - SoftDynamicBodyRenderingServerHandler *rendering_server_handler = nullptr; + SoftBodyRenderingServerHandler *rendering_server_handler = nullptr; RID physics_rid; @@ -124,7 +125,7 @@ protected: void _notification(int p_what); static void _bind_methods(); - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; public: RID get_physics_rid() const { return physics_rid; } @@ -168,7 +169,7 @@ public: void set_drag_coefficient(real_t p_drag_coefficient); real_t get_drag_coefficient(); - Array get_collision_exceptions(); + TypedArray<PhysicsBody3D> get_collision_exceptions(); void add_collision_exception_with(Node *p_node); void remove_collision_exception_with(Node *p_node); @@ -181,8 +182,8 @@ public: void set_ray_pickable(bool p_ray_pickable); bool is_ray_pickable() const; - SoftDynamicBody3D(); - ~SoftDynamicBody3D(); + SoftBody3D(); + ~SoftBody3D(); private: void _make_cache_dirty(); @@ -197,6 +198,6 @@ private: int _has_pinned_point(int p_point_index) const; }; -VARIANT_ENUM_CAST(SoftDynamicBody3D::DisableMode); +VARIANT_ENUM_CAST(SoftBody3D::DisableMode); -#endif // SOFT_DYNAMIC_BODY_H +#endif // SOFT_BODY_3D_H diff --git a/scene/3d/spring_arm_3d.h b/scene/3d/spring_arm_3d.h index 0b5307acf7..1a6f03abe4 100644 --- a/scene/3d/spring_arm_3d.h +++ b/scene/3d/spring_arm_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SPRING_ARM_H -#define SPRING_ARM_H +#ifndef SPRING_ARM_3D_H +#define SPRING_ARM_3D_H #include "scene/3d/node_3d.h" @@ -68,4 +68,4 @@ private: void process_spring(); }; -#endif +#endif // SPRING_ARM_3D_H diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index cb6354f7a8..4b83bcdfc4 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -30,7 +30,6 @@ #include "sprite_3d.h" -#include "core/core_string_names.h" #include "scene/scene_string_names.h" Color SpriteBase3D::_get_color_accum() { @@ -58,7 +57,7 @@ void SpriteBase3D::_propagate_color_changed() { } color_dirty = true; - _queue_update(); + _queue_redraw(); for (SpriteBase3D *&E : children) { E->_propagate_color_changed(); @@ -88,9 +87,185 @@ void SpriteBase3D::_notification(int p_what) { } } +void SpriteBase3D::draw_texture_rect(Ref<Texture2D> p_texture, Rect2 p_dst_rect, Rect2 p_src_rect) { + ERR_FAIL_COND(p_texture.is_null()); + + Rect2 final_rect; + Rect2 final_src_rect; + if (!p_texture->get_rect_region(p_dst_rect, p_src_rect, final_rect, final_src_rect)) { + return; + } + + if (final_rect.size.x == 0 || final_rect.size.y == 0) { + return; + } + + // 2D: 3D plane (axes match exactly when `axis == Vector3::AXIS_Z`): + // -X+ -X+ + // - + + // Y +--------+ +--------+ +--------+ Y +--------+ + // + | +--+ | | | (2) | | - | 0--1 | + // | |ab| | (1) | +--+ | (3) | 3--2 | | |ab| | + // | |cd| | --> | |ab| | --> | |cd| | <==> | |cd| | + // | +--+ | | |cd| | | |ab| | | 3--2 | + // | | | +--+ | | 0--1 | | | + // +--------+ +--------+ +--------+ +--------+ + + // (1) Y-wise shift `final_rect` within `p_dst_rect` so after inverting Y + // axis distances between top/bottom borders will be preserved (so for + // example AtlasTextures with vertical margins will look the same in 2D/3D). + final_rect.position.y = (p_dst_rect.position.y + p_dst_rect.size.y) - ((final_rect.position.y + final_rect.size.y) - p_dst_rect.position.y); + + Color color = _get_color_accum(); + + real_t pixel_size = get_pixel_size(); + + // (2) Order vertices (0123) bottom-top in 2D / top-bottom in 3D. + Vector2 vertices[4] = { + (final_rect.position + Vector2(0, final_rect.size.y)) * pixel_size, + (final_rect.position + final_rect.size) * pixel_size, + (final_rect.position + Vector2(final_rect.size.x, 0)) * pixel_size, + final_rect.position * pixel_size, + }; + + Vector2 src_tsize = p_texture->get_size(); + + // Properly setup UVs for impostor textures (AtlasTexture). + Ref<AtlasTexture> atlas_tex = p_texture; + if (atlas_tex != nullptr) { + src_tsize[0] = atlas_tex->get_atlas()->get_width(); + src_tsize[1] = atlas_tex->get_atlas()->get_height(); + } + + // (3) Assign UVs (abcd) according to the vertices order (bottom-top in 2D / top-bottom in 3D). + Vector2 uvs[4] = { + final_src_rect.position / src_tsize, + (final_src_rect.position + Vector2(final_src_rect.size.x, 0)) / src_tsize, + (final_src_rect.position + final_src_rect.size) / src_tsize, + (final_src_rect.position + Vector2(0, final_src_rect.size.y)) / src_tsize, + }; + + if (is_flipped_h()) { + SWAP(uvs[0], uvs[1]); + SWAP(uvs[2], uvs[3]); + } + + if (is_flipped_v()) { + SWAP(uvs[0], uvs[3]); + SWAP(uvs[1], uvs[2]); + } + + Vector3 normal; + int axis = get_axis(); + normal[axis] = 1.0; + + Plane tangent; + if (axis == Vector3::AXIS_X) { + tangent = Plane(0, 0, -1, 1); + } else { + tangent = Plane(1, 0, 0, 1); + } + + int x_axis = ((axis + 1) % 3); + int y_axis = ((axis + 2) % 3); + + if (axis != Vector3::AXIS_Z) { + SWAP(x_axis, y_axis); + + for (int i = 0; i < 4; i++) { + //uvs[i] = Vector2(1.0,1.0)-uvs[i]; + //SWAP(vertices[i].x,vertices[i].y); + if (axis == Vector3::AXIS_Y) { + vertices[i].y = -vertices[i].y; + } else if (axis == Vector3::AXIS_X) { + vertices[i].x = -vertices[i].x; + } + } + } + + AABB aabb; + + // Everything except position and UV is compressed. + uint8_t *vertex_write_buffer = vertex_buffer.ptrw(); + uint8_t *attribute_write_buffer = attribute_buffer.ptrw(); + + uint32_t v_normal; + { + Vector3 n = normal * Vector3(0.5, 0.5, 0.5) + Vector3(0.5, 0.5, 0.5); + + Vector2 res = n.octahedron_encode(); + uint32_t value = 0; + value |= (uint16_t)CLAMP(res.x * 65535, 0, 65535); + value |= (uint16_t)CLAMP(res.y * 65535, 0, 65535) << 16; + + v_normal = value; + } + uint32_t v_tangent; + { + Plane t = tangent; + Vector2 res = t.normal.octahedron_tangent_encode(t.d); + uint32_t value = 0; + value |= (uint16_t)CLAMP(res.x * 65535, 0, 65535); + value |= (uint16_t)CLAMP(res.y * 65535, 0, 65535) << 16; + + v_tangent = value; + } + + uint8_t v_color[4] = { + uint8_t(CLAMP(color.r * 255.0, 0.0, 255.0)), + uint8_t(CLAMP(color.g * 255.0, 0.0, 255.0)), + uint8_t(CLAMP(color.b * 255.0, 0.0, 255.0)), + uint8_t(CLAMP(color.a * 255.0, 0.0, 255.0)) + }; + + for (int i = 0; i < 4; i++) { + Vector3 vtx; + vtx[x_axis] = vertices[i][0]; + vtx[y_axis] = vertices[i][1]; + if (i == 0) { + aabb.position = vtx; + aabb.size = Vector3(); + } else { + aabb.expand_to(vtx); + } + + float v_uv[2] = { (float)uvs[i].x, (float)uvs[i].y }; + memcpy(&attribute_write_buffer[i * attrib_stride + mesh_surface_offsets[RS::ARRAY_TEX_UV]], v_uv, 8); + + float v_vertex[3] = { (float)vtx.x, (float)vtx.y, (float)vtx.z }; + + memcpy(&vertex_write_buffer[i * vertex_stride + mesh_surface_offsets[RS::ARRAY_VERTEX]], &v_vertex, sizeof(float) * 3); + memcpy(&vertex_write_buffer[i * vertex_stride + mesh_surface_offsets[RS::ARRAY_NORMAL]], &v_normal, 4); + memcpy(&vertex_write_buffer[i * vertex_stride + mesh_surface_offsets[RS::ARRAY_TANGENT]], &v_tangent, 4); + memcpy(&attribute_write_buffer[i * attrib_stride + mesh_surface_offsets[RS::ARRAY_COLOR]], v_color, 4); + } + + RID mesh = get_mesh(); + RS::get_singleton()->mesh_surface_update_vertex_region(mesh, 0, 0, vertex_buffer); + RS::get_singleton()->mesh_surface_update_attribute_region(mesh, 0, 0, attribute_buffer); + + RS::get_singleton()->mesh_set_custom_aabb(mesh, aabb); + set_aabb(aabb); + + RID shader_rid; + StandardMaterial3D::get_material_for_2d(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS, get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y, false, get_draw_flag(FLAG_DISABLE_DEPTH_TEST), get_draw_flag(FLAG_FIXED_SIZE), get_texture_filter(), &shader_rid); + if (last_shader != shader_rid) { + RS::get_singleton()->material_set_shader(get_material(), shader_rid); + last_shader = shader_rid; + } + if (last_texture != p_texture->get_rid()) { + RS::get_singleton()->material_set_param(get_material(), "texture_albedo", p_texture->get_rid()); + last_texture = p_texture->get_rid(); + } + if (get_alpha_cut_mode() == ALPHA_CUT_DISABLED) { + RS::get_singleton()->material_set_render_priority(get_material(), get_render_priority()); + RS::get_singleton()->mesh_surface_set_material(mesh, 0, get_material()); + } +} + void SpriteBase3D::set_centered(bool p_center) { centered = p_center; - _queue_update(); + _queue_redraw(); } bool SpriteBase3D::is_centered() const { @@ -99,7 +274,7 @@ bool SpriteBase3D::is_centered() const { void SpriteBase3D::set_offset(const Point2 &p_offset) { offset = p_offset; - _queue_update(); + _queue_redraw(); } Point2 SpriteBase3D::get_offset() const { @@ -108,7 +283,7 @@ Point2 SpriteBase3D::get_offset() const { void SpriteBase3D::set_flip_h(bool p_flip) { hflip = p_flip; - _queue_update(); + _queue_redraw(); } bool SpriteBase3D::is_flipped_h() const { @@ -117,7 +292,7 @@ bool SpriteBase3D::is_flipped_h() const { void SpriteBase3D::set_flip_v(bool p_flip) { vflip = p_flip; - _queue_update(); + _queue_redraw(); } bool SpriteBase3D::is_flipped_v() const { @@ -127,7 +302,7 @@ bool SpriteBase3D::is_flipped_v() const { void SpriteBase3D::set_modulate(const Color &p_color) { modulate = p_color; _propagate_color_changed(); - _queue_update(); + _queue_redraw(); } Color SpriteBase3D::get_modulate() const { @@ -137,7 +312,7 @@ Color SpriteBase3D::get_modulate() const { void SpriteBase3D::set_render_priority(int p_priority) { ERR_FAIL_COND(p_priority < RS::MATERIAL_RENDER_PRIORITY_MIN || p_priority > RS::MATERIAL_RENDER_PRIORITY_MAX); render_priority = p_priority; - _queue_update(); + _queue_redraw(); } int SpriteBase3D::get_render_priority() const { @@ -146,7 +321,7 @@ int SpriteBase3D::get_render_priority() const { void SpriteBase3D::set_pixel_size(real_t p_amount) { pixel_size = p_amount; - _queue_update(); + _queue_redraw(); } real_t SpriteBase3D::get_pixel_size() const { @@ -156,7 +331,7 @@ real_t SpriteBase3D::get_pixel_size() const { void SpriteBase3D::set_axis(Vector3::Axis p_axis) { ERR_FAIL_INDEX(p_axis, 3); axis = p_axis; - _queue_update(); + _queue_redraw(); } Vector3::Axis SpriteBase3D::get_axis() const { @@ -171,7 +346,8 @@ void SpriteBase3D::_im_update() { //texture->draw_rect_region(ci,dst_rect,src_rect,modulate); } -void SpriteBase3D::_queue_update() { +void SpriteBase3D::_queue_redraw() { + // The 3D equivalent of CanvasItem.queue_redraw(). if (pending_update) { return; } @@ -250,7 +426,7 @@ Ref<TriangleMesh> SpriteBase3D::generate_triangle_mesh() const { void SpriteBase3D::set_draw_flag(DrawFlags p_flag, bool p_enable) { ERR_FAIL_INDEX(p_flag, FLAG_MAX); flags[p_flag] = p_enable; - _queue_update(); + _queue_redraw(); } bool SpriteBase3D::get_draw_flag(DrawFlags p_flag) const { @@ -261,7 +437,7 @@ bool SpriteBase3D::get_draw_flag(DrawFlags p_flag) const { void SpriteBase3D::set_alpha_cut_mode(AlphaCutMode p_mode) { ERR_FAIL_INDEX(p_mode, 3); alpha_cut = p_mode; - _queue_update(); + _queue_redraw(); } SpriteBase3D::AlphaCutMode SpriteBase3D::get_alpha_cut_mode() const { @@ -269,9 +445,9 @@ SpriteBase3D::AlphaCutMode SpriteBase3D::get_alpha_cut_mode() const { } void SpriteBase3D::set_billboard_mode(StandardMaterial3D::BillboardMode p_mode) { - ERR_FAIL_INDEX(p_mode, 3); + ERR_FAIL_INDEX(p_mode, 3); // Cannot use BILLBOARD_PARTICLES. billboard_mode = p_mode; - _queue_update(); + _queue_redraw(); } StandardMaterial3D::BillboardMode SpriteBase3D::get_billboard_mode() const { @@ -281,7 +457,7 @@ StandardMaterial3D::BillboardMode SpriteBase3D::get_billboard_mode() const { void SpriteBase3D::set_texture_filter(StandardMaterial3D::TextureFilter p_filter) { if (texture_filter != p_filter) { texture_filter = p_filter; - _queue_update(); + _queue_redraw(); } } @@ -329,7 +505,6 @@ void SpriteBase3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_item_rect"), &SpriteBase3D::get_item_rect); ClassDB::bind_method(D_METHOD("generate_triangle_mesh"), &SpriteBase3D::generate_triangle_mesh); - ClassDB::bind_method(D_METHOD("_queue_update"), &SpriteBase3D::_queue_update); ClassDB::bind_method(D_METHOD("_im_update"), &SpriteBase3D::_im_update); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "centered"), "set_centered", "is_centered"); @@ -368,7 +543,7 @@ SpriteBase3D::SpriteBase3D() { } material = RenderingServer::get_singleton()->material_create(); - // Set defaults for material, names need to match up those in StandardMaterial3D + // Set defaults for material, names need to match up those in StandardMaterial3D. RS::get_singleton()->material_set_param(material, "albedo", Color(1, 1, 1, 1)); RS::get_singleton()->material_set_param(material, "specular", 0.5); RS::get_singleton()->material_set_param(material, "metallic", 0.0); @@ -377,7 +552,7 @@ SpriteBase3D::SpriteBase3D() { RS::get_singleton()->material_set_param(material, "uv1_scale", Vector3(1, 1, 1)); RS::get_singleton()->material_set_param(material, "uv2_offset", Vector3(0, 0, 0)); RS::get_singleton()->material_set_param(material, "uv2_scale", Vector3(1, 1, 1)); - RS::get_singleton()->material_set_param(material, "alpha_scissor_threshold", 0.98); + RS::get_singleton()->material_set_param(material, "alpha_scissor_threshold", 0.5); mesh = RenderingServer::get_singleton()->mesh_create(); @@ -394,7 +569,7 @@ SpriteBase3D::SpriteBase3D() { mesh_colors.resize(4); mesh_uvs.resize(4); - // create basic mesh and store format information + // Create basic mesh and store format information. for (int i = 0; i < 4; i++) { mesh_normals.write[i] = Vector3(0.0, 0.0, 0.0); mesh_tangents.write[i * 4 + 0] = 0.0; @@ -448,7 +623,7 @@ void Sprite3D::_draw() { if (get_base() != get_mesh()) { set_base(get_mesh()); } - if (!texture.is_valid()) { + if (texture.is_null()) { set_base(RID()); return; } @@ -465,173 +640,17 @@ void Sprite3D::_draw() { } Size2 frame_size = base_rect.size / Size2(hframes, vframes); - Point2 frame_offset = Point2(frame % hframes, frame / hframes); - frame_offset *= frame_size; + Point2 frame_offset = Point2(frame % hframes, frame / hframes) * frame_size; - Point2 dest_offset = get_offset(); + Point2 dst_offset = get_offset(); if (is_centered()) { - dest_offset -= frame_size / 2; + dst_offset -= frame_size / 2.0f; } Rect2 src_rect(base_rect.position + frame_offset, frame_size); - Rect2 final_dst_rect(dest_offset, frame_size); - Rect2 final_rect; - Rect2 final_src_rect; - if (!texture->get_rect_region(final_dst_rect, src_rect, final_rect, final_src_rect)) { - return; - } - - if (final_rect.size.x == 0 || final_rect.size.y == 0) { - return; - } + Rect2 dst_rect(dst_offset, frame_size); - Color color = _get_color_accum(); - - real_t pixel_size = get_pixel_size(); - - Vector2 vertices[4] = { - - (final_rect.position + Vector2(0, final_rect.size.y)) * pixel_size, - (final_rect.position + final_rect.size) * pixel_size, - (final_rect.position + Vector2(final_rect.size.x, 0)) * pixel_size, - final_rect.position * pixel_size, - - }; - - Vector2 src_tsize = tsize; - - // Properly setup UVs for impostor textures (AtlasTexture). - Ref<AtlasTexture> atlas_tex = texture; - if (atlas_tex != nullptr) { - src_tsize[0] = atlas_tex->get_atlas()->get_width(); - src_tsize[1] = atlas_tex->get_atlas()->get_height(); - } - - Vector2 uvs[4] = { - final_src_rect.position / src_tsize, - (final_src_rect.position + Vector2(final_src_rect.size.x, 0)) / src_tsize, - (final_src_rect.position + final_src_rect.size) / src_tsize, - (final_src_rect.position + Vector2(0, final_src_rect.size.y)) / src_tsize, - }; - - if (is_flipped_h()) { - SWAP(uvs[0], uvs[1]); - SWAP(uvs[2], uvs[3]); - } - - if (is_flipped_v()) { - SWAP(uvs[0], uvs[3]); - SWAP(uvs[1], uvs[2]); - } - - Vector3 normal; - int axis = get_axis(); - normal[axis] = 1.0; - - Plane tangent; - if (axis == Vector3::AXIS_X) { - tangent = Plane(0, 0, -1, 1); - } else { - tangent = Plane(1, 0, 0, 1); - } - - int x_axis = ((axis + 1) % 3); - int y_axis = ((axis + 2) % 3); - - if (axis != Vector3::AXIS_Z) { - SWAP(x_axis, y_axis); - - for (int i = 0; i < 4; i++) { - //uvs[i] = Vector2(1.0,1.0)-uvs[i]; - //SWAP(vertices[i].x,vertices[i].y); - if (axis == Vector3::AXIS_Y) { - vertices[i].y = -vertices[i].y; - } else if (axis == Vector3::AXIS_X) { - vertices[i].x = -vertices[i].x; - } - } - } - - AABB aabb; - - // Everything except position and UV is compressed - uint8_t *vertex_write_buffer = vertex_buffer.ptrw(); - uint8_t *attribute_write_buffer = attribute_buffer.ptrw(); - - uint32_t v_normal; - { - Vector3 n = normal * Vector3(0.5, 0.5, 0.5) + Vector3(0.5, 0.5, 0.5); - - uint32_t value = 0; - value |= CLAMP(int(n.x * 1023.0), 0, 1023); - value |= CLAMP(int(n.y * 1023.0), 0, 1023) << 10; - value |= CLAMP(int(n.z * 1023.0), 0, 1023) << 20; - - v_normal = value; - } - uint32_t v_tangent; - { - Plane t = tangent; - uint32_t value = 0; - value |= CLAMP(int((t.normal.x * 0.5 + 0.5) * 1023.0), 0, 1023); - value |= CLAMP(int((t.normal.y * 0.5 + 0.5) * 1023.0), 0, 1023) << 10; - value |= CLAMP(int((t.normal.z * 0.5 + 0.5) * 1023.0), 0, 1023) << 20; - if (t.d > 0) { - value |= 3UL << 30; - } - v_tangent = value; - } - - uint8_t v_color[4] = { - uint8_t(CLAMP(color.r * 255.0, 0.0, 255.0)), - uint8_t(CLAMP(color.g * 255.0, 0.0, 255.0)), - uint8_t(CLAMP(color.b * 255.0, 0.0, 255.0)), - uint8_t(CLAMP(color.a * 255.0, 0.0, 255.0)) - }; - - for (int i = 0; i < 4; i++) { - Vector3 vtx; - vtx[x_axis] = vertices[i][0]; - vtx[y_axis] = vertices[i][1]; - if (i == 0) { - aabb.position = vtx; - aabb.size = Vector3(); - } else { - aabb.expand_to(vtx); - } - - float v_uv[2] = { (float)uvs[i].x, (float)uvs[i].y }; - memcpy(&attribute_write_buffer[i * attrib_stride + mesh_surface_offsets[RS::ARRAY_TEX_UV]], v_uv, 8); - - float v_vertex[3] = { (float)vtx.x, (float)vtx.y, (float)vtx.z }; - - memcpy(&vertex_write_buffer[i * vertex_stride + mesh_surface_offsets[RS::ARRAY_VERTEX]], &v_vertex, sizeof(float) * 3); - memcpy(&vertex_write_buffer[i * vertex_stride + mesh_surface_offsets[RS::ARRAY_NORMAL]], &v_normal, 4); - memcpy(&vertex_write_buffer[i * vertex_stride + mesh_surface_offsets[RS::ARRAY_TANGENT]], &v_tangent, 4); - memcpy(&attribute_write_buffer[i * attrib_stride + mesh_surface_offsets[RS::ARRAY_COLOR]], v_color, 4); - } - - RID mesh = get_mesh(); - RS::get_singleton()->mesh_surface_update_vertex_region(mesh, 0, 0, vertex_buffer); - RS::get_singleton()->mesh_surface_update_attribute_region(mesh, 0, 0, attribute_buffer); - - RS::get_singleton()->mesh_set_custom_aabb(mesh, aabb); - set_aabb(aabb); - - RID shader_rid; - StandardMaterial3D::get_material_for_2d(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS, get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y, false, get_draw_flag(FLAG_DISABLE_DEPTH_TEST), get_draw_flag(FLAG_FIXED_SIZE), get_texture_filter(), &shader_rid); - if (last_shader != shader_rid) { - RS::get_singleton()->material_set_shader(get_material(), shader_rid); - last_shader = shader_rid; - } - if (last_texture != texture->get_rid()) { - RS::get_singleton()->material_set_param(get_material(), "texture_albedo", texture->get_rid()); - last_texture = texture->get_rid(); - } - if (get_alpha_cut_mode() == ALPHA_CUT_DISABLED) { - RS::get_singleton()->material_set_render_priority(get_material(), get_render_priority()); - RS::get_singleton()->mesh_surface_set_material(mesh, 0, get_material()); - } + draw_texture_rect(texture, dst_rect, src_rect); } void Sprite3D::set_texture(const Ref<Texture2D> &p_texture) { @@ -639,13 +658,14 @@ void Sprite3D::set_texture(const Ref<Texture2D> &p_texture) { return; } if (texture.is_valid()) { - texture->disconnect(CoreStringNames::get_singleton()->changed, Callable(this, "_queue_update")); + texture->disconnect(SceneStringNames::get_singleton()->changed, callable_mp((SpriteBase3D *)this, &Sprite3D::_queue_redraw)); } texture = p_texture; if (texture.is_valid()) { - texture->connect(CoreStringNames::get_singleton()->changed, Callable(this, "_queue_update")); + texture->connect(SceneStringNames::get_singleton()->changed, callable_mp((SpriteBase3D *)this, &Sprite3D::_queue_redraw)); } - _queue_update(); + + _queue_redraw(); emit_signal(SceneStringNames::get_singleton()->texture_changed); } @@ -659,7 +679,7 @@ void Sprite3D::set_region_enabled(bool p_region) { } region = p_region; - _queue_update(); + _queue_redraw(); } bool Sprite3D::is_region_enabled() const { @@ -670,7 +690,7 @@ void Sprite3D::set_region_rect(const Rect2 &p_region_rect) { bool changed = region_rect != p_region_rect; region_rect = p_region_rect; if (region && changed) { - _queue_update(); + _queue_redraw(); } } @@ -683,7 +703,7 @@ void Sprite3D::set_frame(int p_frame) { frame = p_frame; - _queue_update(); + _queue_redraw(); emit_signal(SceneStringNames::get_singleton()->frame_changed); } @@ -706,7 +726,7 @@ Vector2i Sprite3D::get_frame_coords() const { void Sprite3D::set_vframes(int p_amount) { ERR_FAIL_COND(p_amount < 1); vframes = p_amount; - _queue_update(); + _queue_redraw(); notify_property_list_changed(); } @@ -717,7 +737,7 @@ int Sprite3D::get_vframes() const { void Sprite3D::set_hframes(int p_amount) { ERR_FAIL_COND(p_amount < 1); hframes = p_amount; - _queue_update(); + _queue_redraw(); notify_property_list_changed(); } @@ -751,18 +771,16 @@ Rect2 Sprite3D::get_item_rect() const { return Rect2(ofs, s); } -void Sprite3D::_validate_property(PropertyInfo &property) const { - if (property.name == "frame") { - property.hint = PROPERTY_HINT_RANGE; - property.hint_string = "0," + itos(vframes * hframes - 1) + ",1"; - property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS; +void Sprite3D::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "frame") { + p_property.hint = PROPERTY_HINT_RANGE; + p_property.hint_string = "0," + itos(vframes * hframes - 1) + ",1"; + p_property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS; } - if (property.name == "frame_coords") { - property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS; + if (p_property.name == "frame_coords") { + p_property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS; } - - SpriteBase3D::_validate_property(property); } void Sprite3D::_bind_methods() { @@ -811,22 +829,14 @@ void AnimatedSprite3D::_draw() { set_base(get_mesh()); } - if (frames.is_null()) { - return; - } - - if (frame < 0) { - return; - } - - if (!frames->has_animation(animation)) { + if (frames.is_null() || !frames->has_animation(animation)) { return; } Ref<Texture2D> texture = frames->get_frame(animation, frame); - if (!texture.is_valid()) { + if (texture.is_null()) { set_base(RID()); - return; //no texuture no life + return; } Size2 tsize = texture->get_size(); if (tsize.x == 0 || tsize.y == 0) { @@ -843,246 +853,117 @@ void AnimatedSprite3D::_draw() { Rect2 dst_rect(ofs, tsize); - Rect2 final_rect; - Rect2 final_src_rect; - if (!texture->get_rect_region(dst_rect, src_rect, final_rect, final_src_rect)) { - return; - } - - if (final_rect.size.x == 0 || final_rect.size.y == 0) { - return; - } - - Color color = _get_color_accum(); - - real_t pixel_size = get_pixel_size(); - - Vector2 vertices[4] = { - - (final_rect.position + Vector2(0, final_rect.size.y)) * pixel_size, - (final_rect.position + final_rect.size) * pixel_size, - (final_rect.position + Vector2(final_rect.size.x, 0)) * pixel_size, - final_rect.position * pixel_size, - - }; - - Vector2 src_tsize = tsize; - - // Properly setup UVs for impostor textures (AtlasTexture). - Ref<AtlasTexture> atlas_tex = texture; - if (atlas_tex != nullptr) { - src_tsize[0] = atlas_tex->get_atlas()->get_width(); - src_tsize[1] = atlas_tex->get_atlas()->get_height(); - } - - Vector2 uvs[4] = { - final_src_rect.position / src_tsize, - (final_src_rect.position + Vector2(final_src_rect.size.x, 0)) / src_tsize, - (final_src_rect.position + final_src_rect.size) / src_tsize, - (final_src_rect.position + Vector2(0, final_src_rect.size.y)) / src_tsize, - }; - - if (is_flipped_h()) { - SWAP(uvs[0], uvs[1]); - SWAP(uvs[2], uvs[3]); - } - if (is_flipped_v()) { - SWAP(uvs[0], uvs[3]); - SWAP(uvs[1], uvs[2]); - } - - Vector3 normal; - int axis = get_axis(); - normal[axis] = 1.0; - - Plane tangent; - if (axis == Vector3::AXIS_X) { - tangent = Plane(0, 0, -1, -1); - } else { - tangent = Plane(1, 0, 0, -1); - } - - int x_axis = ((axis + 1) % 3); - int y_axis = ((axis + 2) % 3); - - if (axis != Vector3::AXIS_Z) { - SWAP(x_axis, y_axis); - - for (int i = 0; i < 4; i++) { - //uvs[i] = Vector2(1.0,1.0)-uvs[i]; - //SWAP(vertices[i].x,vertices[i].y); - if (axis == Vector3::AXIS_Y) { - vertices[i].y = -vertices[i].y; - } else if (axis == Vector3::AXIS_X) { - vertices[i].x = -vertices[i].x; - } - } - } - - AABB aabb; - - // Everything except position and UV is compressed - uint8_t *vertex_write_buffer = vertex_buffer.ptrw(); - uint8_t *attribute_write_buffer = attribute_buffer.ptrw(); - - uint32_t v_normal; - { - Vector3 n = normal * Vector3(0.5, 0.5, 0.5) + Vector3(0.5, 0.5, 0.5); - - uint32_t value = 0; - value |= CLAMP(int(n.x * 1023.0), 0, 1023); - value |= CLAMP(int(n.y * 1023.0), 0, 1023) << 10; - value |= CLAMP(int(n.z * 1023.0), 0, 1023) << 20; - - v_normal = value; - } - uint32_t v_tangent; - { - Plane t = tangent; - uint32_t value = 0; - value |= CLAMP(int((t.normal.x * 0.5 + 0.5) * 1023.0), 0, 1023); - value |= CLAMP(int((t.normal.y * 0.5 + 0.5) * 1023.0), 0, 1023) << 10; - value |= CLAMP(int((t.normal.z * 0.5 + 0.5) * 1023.0), 0, 1023) << 20; - if (t.d > 0) { - value |= 3UL << 30; - } - v_tangent = value; - } - - uint8_t v_color[4] = { - uint8_t(CLAMP(color.r * 255.0, 0.0, 255.0)), - uint8_t(CLAMP(color.g * 255.0, 0.0, 255.0)), - uint8_t(CLAMP(color.b * 255.0, 0.0, 255.0)), - uint8_t(CLAMP(color.a * 255.0, 0.0, 255.0)) - }; - - for (int i = 0; i < 4; i++) { - Vector3 vtx; - vtx[x_axis] = vertices[i][0]; - vtx[y_axis] = vertices[i][1]; - if (i == 0) { - aabb.position = vtx; - aabb.size = Vector3(); - } else { - aabb.expand_to(vtx); - } - - float v_uv[2] = { (float)uvs[i].x, (float)uvs[i].y }; - memcpy(&attribute_write_buffer[i * attrib_stride + mesh_surface_offsets[RS::ARRAY_TEX_UV]], v_uv, 8); - - float v_vertex[3] = { (float)vtx.x, (float)vtx.y, (float)vtx.z }; - memcpy(&vertex_write_buffer[i * vertex_stride + mesh_surface_offsets[RS::ARRAY_VERTEX]], &v_vertex, sizeof(float) * 3); - memcpy(&vertex_write_buffer[i * vertex_stride + mesh_surface_offsets[RS::ARRAY_NORMAL]], &v_normal, 4); - memcpy(&vertex_write_buffer[i * vertex_stride + mesh_surface_offsets[RS::ARRAY_TANGENT]], &v_tangent, 4); - memcpy(&attribute_write_buffer[i * attrib_stride + mesh_surface_offsets[RS::ARRAY_COLOR]], v_color, 4); - } - - RID mesh = get_mesh(); - RS::get_singleton()->mesh_surface_update_vertex_region(mesh, 0, 0, vertex_buffer); - RS::get_singleton()->mesh_surface_update_attribute_region(mesh, 0, 0, attribute_buffer); - - RS::get_singleton()->mesh_set_custom_aabb(mesh, aabb); - set_aabb(aabb); - - RID shader_rid; - StandardMaterial3D::get_material_for_2d(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS, get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y, false, get_draw_flag(FLAG_DISABLE_DEPTH_TEST), get_draw_flag(FLAG_FIXED_SIZE), get_texture_filter(), &shader_rid); - if (last_shader != shader_rid) { - RS::get_singleton()->material_set_shader(get_material(), shader_rid); - last_shader = shader_rid; - } - if (last_texture != texture->get_rid()) { - RS::get_singleton()->material_set_param(get_material(), "texture_albedo", texture->get_rid()); - last_texture = texture->get_rid(); - } - if (get_alpha_cut_mode() == ALPHA_CUT_DISABLED) { - RS::get_singleton()->material_set_render_priority(get_material(), get_render_priority()); - RS::get_singleton()->mesh_surface_set_material(mesh, 0, get_material()); - } + draw_texture_rect(texture, dst_rect, src_rect); } -void AnimatedSprite3D::_validate_property(PropertyInfo &property) const { +void AnimatedSprite3D::_validate_property(PropertyInfo &p_property) const { if (!frames.is_valid()) { return; } - if (property.name == "animation") { - property.hint = PROPERTY_HINT_ENUM; + + if (p_property.name == "animation") { + p_property.hint = PROPERTY_HINT_ENUM; List<StringName> names; frames->get_animation_list(&names); names.sort_custom<StringName::AlphCompare>(); bool current_found = false; + bool is_first_element = true; - for (List<StringName>::Element *E = names.front(); E; E = E->next()) { - if (E->prev()) { - property.hint_string += ","; + for (const StringName &E : names) { + if (!is_first_element) { + p_property.hint_string += ","; + } else { + is_first_element = false; } - property.hint_string += String(E->get()); - if (animation == E->get()) { + p_property.hint_string += String(E); + if (animation == E) { current_found = true; } } if (!current_found) { - if (property.hint_string.is_empty()) { - property.hint_string = String(animation); + if (p_property.hint_string.is_empty()) { + p_property.hint_string = String(animation); } else { - property.hint_string = String(animation) + "," + property.hint_string; + p_property.hint_string = String(animation) + "," + p_property.hint_string; } } + return; } - if (property.name == "frame") { - property.hint = PROPERTY_HINT_RANGE; + if (p_property.name == "frame") { + if (playing) { + p_property.usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY; + return; + } + + p_property.hint = PROPERTY_HINT_RANGE; if (frames->has_animation(animation) && frames->get_frame_count(animation) > 0) { - property.hint_string = "0," + itos(frames->get_frame_count(animation) - 1) + ",1"; + p_property.hint_string = "0," + itos(frames->get_frame_count(animation) - 1) + ",1"; } else { // Avoid an error, `hint_string` is required for `PROPERTY_HINT_RANGE`. - property.hint_string = "0,0,1"; + p_property.hint_string = "0,0,1"; } - property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS; + p_property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS; } - - SpriteBase3D::_validate_property(property); } void AnimatedSprite3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_INTERNAL_PROCESS: { - if (frames.is_null()) { - return; - } - if (!frames->has_animation(animation)) { + if (frames.is_null() || !frames->has_animation(animation)) { return; } - if (frame < 0) { - return; + + double speed = frames->get_animation_speed(animation) * Math::abs(speed_scale); + if (speed == 0) { + return; // Do nothing. } + int last_frame = frames->get_frame_count(animation) - 1; double remaining = get_process_delta_time(); - while (remaining) { - double speed = frames->get_animation_speed(animation); - if (speed == 0) { - return; // Do nothing. - } - if (timeout <= 0) { - timeout = 1.0 / speed; - - int fc = frames->get_frame_count(animation); - if (frame >= fc - 1) { - if (frames->get_animation_loop(animation)) { - frame = 0; + timeout = _get_frame_duration(); + + if (!playing_backwards) { + // Forward. + if (frame >= last_frame) { + if (frames->get_animation_loop(animation)) { + frame = 0; + emit_signal(SceneStringNames::get_singleton()->animation_finished); + } else { + frame = last_frame; + if (!is_over) { + is_over = true; + emit_signal(SceneStringNames::get_singleton()->animation_finished); + } + } } else { - frame = fc - 1; + frame++; } - emit_signal(SceneStringNames::get_singleton()->animation_finished); } else { - frame++; + // Reversed. + if (frame <= 0) { + if (frames->get_animation_loop(animation)) { + frame = last_frame; + emit_signal(SceneStringNames::get_singleton()->animation_finished); + } else { + frame = 0; + if (!is_over) { + is_over = true; + emit_signal(SceneStringNames::get_singleton()->animation_finished); + } + } + } else { + frame--; + } } - _queue_update(); + _queue_redraw(); + emit_signal(SceneStringNames::get_singleton()->frame_changed); } @@ -1096,14 +977,15 @@ void AnimatedSprite3D::_notification(int p_what) { void AnimatedSprite3D::set_sprite_frames(const Ref<SpriteFrames> &p_frames) { if (frames.is_valid()) { - frames->disconnect("changed", Callable(this, "_res_changed")); + frames->disconnect(SceneStringNames::get_singleton()->changed, callable_mp(this, &AnimatedSprite3D::_res_changed)); } + frames = p_frames; if (frames.is_valid()) { - frames->connect("changed", Callable(this, "_res_changed")); + frames->connect(SceneStringNames::get_singleton()->changed, callable_mp(this, &AnimatedSprite3D::_res_changed)); } - if (!frames.is_valid()) { + if (frames.is_null()) { frame = 0; } else { set_frame(frame); @@ -1111,7 +993,7 @@ void AnimatedSprite3D::set_sprite_frames(const Ref<SpriteFrames> &p_frames) { notify_property_list_changed(); _reset_timeout(); - _queue_update(); + _queue_redraw(); update_configuration_warnings(); } @@ -1120,7 +1002,7 @@ Ref<SpriteFrames> AnimatedSprite3D::get_sprite_frames() const { } void AnimatedSprite3D::set_frame(int p_frame) { - if (!frames.is_valid()) { + if (frames.is_null()) { return; } @@ -1141,7 +1023,8 @@ void AnimatedSprite3D::set_frame(int p_frame) { frame = p_frame; _reset_timeout(); - _queue_update(); + _queue_redraw(); + emit_signal(SceneStringNames::get_singleton()->frame_changed); } @@ -1149,8 +1032,30 @@ int AnimatedSprite3D::get_frame() const { return frame; } +void AnimatedSprite3D::set_speed_scale(double p_speed_scale) { + if (speed_scale == p_speed_scale) { + return; + } + + double elapsed = _get_frame_duration() - timeout; + + speed_scale = p_speed_scale; + playing_backwards = signbit(speed_scale) != backwards; + + // We adapt the timeout so that the animation speed adapts as soon as the speed scale is changed. + _reset_timeout(); + timeout -= elapsed; +} + +double AnimatedSprite3D::get_speed_scale() const { + return speed_scale; +} + Rect2 AnimatedSprite3D::get_item_rect() const { - if (!frames.is_valid() || !frames->has_animation(animation) || frame < 0 || frame >= frames->get_frame_count(animation)) { + if (frames.is_null() || !frames->has_animation(animation)) { + return Rect2(0, 0, 1, 1); + } + if (frame < 0 || frame >= frames->get_frame_count(animation)) { return Rect2(0, 0, 1, 1); } @@ -1177,35 +1082,51 @@ Rect2 AnimatedSprite3D::get_item_rect() const { void AnimatedSprite3D::_res_changed() { set_frame(frame); - _queue_update(); + + _queue_redraw(); } -void AnimatedSprite3D::_set_playing(bool p_playing) { +void AnimatedSprite3D::set_playing(bool p_playing) { if (playing == p_playing) { return; } playing = p_playing; _reset_timeout(); set_process_internal(playing); + notify_property_list_changed(); } -bool AnimatedSprite3D::_is_playing() const { +bool AnimatedSprite3D::is_playing() const { return playing; } -void AnimatedSprite3D::play(const StringName &p_animation) { +void AnimatedSprite3D::play(const StringName &p_animation, bool p_backwards) { + backwards = p_backwards; + playing_backwards = signbit(speed_scale) != backwards; + if (p_animation) { set_animation(p_animation); + if (frames.is_valid() && playing_backwards && get_frame() == 0) { + set_frame(frames->get_frame_count(p_animation) - 1); + } } - _set_playing(true); + + is_over = false; + set_playing(true); } void AnimatedSprite3D::stop() { - _set_playing(false); + set_playing(false); } -bool AnimatedSprite3D::is_playing() const { - return playing; +double AnimatedSprite3D::_get_frame_duration() { + if (frames.is_valid() && frames->has_animation(animation)) { + double speed = frames->get_animation_speed(animation) * Math::abs(speed_scale); + if (speed > 0) { + return 1.0 / speed; + } + } + return 0.0; } void AnimatedSprite3D::_reset_timeout() { @@ -1213,19 +1134,13 @@ void AnimatedSprite3D::_reset_timeout() { return; } - if (frames.is_valid() && frames->has_animation(animation)) { - float speed = frames->get_animation_speed(animation); - if (speed > 0) { - timeout = 1.0 / speed; - } else { - timeout = 0; - } - } else { - timeout = 0; - } + timeout = _get_frame_duration(); + is_over = false; } void AnimatedSprite3D::set_animation(const StringName &p_animation) { + ERR_FAIL_COND_MSG(frames == nullptr, vformat("There is no animation with name '%s'.", p_animation)); + ERR_FAIL_COND_MSG(!frames->get_animation_names().has(p_animation), vformat("There is no animation with name '%s'.", p_animation)); if (animation == p_animation) { return; } @@ -1234,15 +1149,15 @@ void AnimatedSprite3D::set_animation(const StringName &p_animation) { _reset_timeout(); set_frame(0); notify_property_list_changed(); - _queue_update(); + _queue_redraw(); } StringName AnimatedSprite3D::get_animation() const { return animation; } -TypedArray<String> AnimatedSprite3D::get_configuration_warnings() const { - TypedArray<String> warnings = SpriteBase3D::get_configuration_warnings(); +PackedStringArray AnimatedSprite3D::get_configuration_warnings() const { + PackedStringArray warnings = SpriteBase3D::get_configuration_warnings(); if (frames.is_null()) { warnings.push_back(RTR("A SpriteFrames resource must be created or set in the \"Frames\" property in order for AnimatedSprite3D to display frames.")); } @@ -1268,16 +1183,18 @@ void AnimatedSprite3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_animation", "animation"), &AnimatedSprite3D::set_animation); ClassDB::bind_method(D_METHOD("get_animation"), &AnimatedSprite3D::get_animation); - ClassDB::bind_method(D_METHOD("_set_playing", "playing"), &AnimatedSprite3D::_set_playing); - ClassDB::bind_method(D_METHOD("_is_playing"), &AnimatedSprite3D::_is_playing); + ClassDB::bind_method(D_METHOD("set_playing", "playing"), &AnimatedSprite3D::set_playing); + ClassDB::bind_method(D_METHOD("is_playing"), &AnimatedSprite3D::is_playing); - ClassDB::bind_method(D_METHOD("play", "anim"), &AnimatedSprite3D::play, DEFVAL(StringName())); + ClassDB::bind_method(D_METHOD("play", "anim", "backwards"), &AnimatedSprite3D::play, DEFVAL(StringName()), DEFVAL(false)); ClassDB::bind_method(D_METHOD("stop"), &AnimatedSprite3D::stop); - ClassDB::bind_method(D_METHOD("is_playing"), &AnimatedSprite3D::is_playing); ClassDB::bind_method(D_METHOD("set_frame", "frame"), &AnimatedSprite3D::set_frame); ClassDB::bind_method(D_METHOD("get_frame"), &AnimatedSprite3D::get_frame); + ClassDB::bind_method(D_METHOD("set_speed_scale", "speed_scale"), &AnimatedSprite3D::set_speed_scale); + ClassDB::bind_method(D_METHOD("get_speed_scale"), &AnimatedSprite3D::get_speed_scale); + ClassDB::bind_method(D_METHOD("_res_changed"), &AnimatedSprite3D::_res_changed); ADD_SIGNAL(MethodInfo("frame_changed")); @@ -1286,7 +1203,8 @@ void AnimatedSprite3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "frames", PROPERTY_HINT_RESOURCE_TYPE, "SpriteFrames"), "set_sprite_frames", "get_sprite_frames"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "animation"), "set_animation", "get_animation"); ADD_PROPERTY(PropertyInfo(Variant::INT, "frame"), "set_frame", "get_frame"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing"), "_set_playing", "_is_playing"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed_scale"), "set_speed_scale", "get_speed_scale"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing"), "set_playing", "is_playing"); } AnimatedSprite3D::AnimatedSprite3D() { diff --git a/scene/3d/sprite_3d.h b/scene/3d/sprite_3d.h index 6ac85a7bbc..edc48c7b71 100644 --- a/scene/3d/sprite_3d.h +++ b/scene/3d/sprite_3d.h @@ -80,6 +80,9 @@ private: RID mesh; RID material; + RID last_shader; + RID last_texture; + bool flags[FLAG_MAX] = {}; AlphaCutMode alpha_cut = ALPHA_CUT_DISABLED; StandardMaterial3D::BillboardMode billboard_mode = StandardMaterial3D::BILLBOARD_DISABLED; @@ -94,6 +97,7 @@ protected: void _notification(int p_what); static void _bind_methods(); virtual void _draw() = 0; + void draw_texture_rect(Ref<Texture2D> p_texture, Rect2 p_dst_rect, Rect2 p_src_rect); _FORCE_INLINE_ void set_aabb(const AABB &p_aabb) { aabb = p_aabb; } _FORCE_INLINE_ RID &get_mesh() { return mesh; } _FORCE_INLINE_ RID &get_material() { return material; } @@ -106,7 +110,7 @@ protected: uint32_t skin_stride = 0; uint32_t mesh_surface_format = 0; - void _queue_update(); + void _queue_redraw(); public: void set_centered(bool p_center); @@ -167,14 +171,11 @@ class Sprite3D : public SpriteBase3D { int vframes = 1; int hframes = 1; - RID last_shader; - RID last_texture; - protected: virtual void _draw() override; static void _bind_methods(); - virtual void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; public: void set_texture(const Ref<Texture2D> &p_texture); @@ -209,34 +210,36 @@ class AnimatedSprite3D : public SpriteBase3D { Ref<SpriteFrames> frames; bool playing = false; + bool playing_backwards = false; + bool backwards = false; StringName animation = "default"; int frame = 0; + float speed_scale = 1.0f; bool centered = false; + bool is_over = false; double timeout = 0.0; void _res_changed(); + double _get_frame_duration(); void _reset_timeout(); - void _set_playing(bool p_playing); - bool _is_playing() const; - - RID last_shader; - RID last_texture; protected: virtual void _draw() override; static void _bind_methods(); void _notification(int p_what); - virtual void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; public: void set_sprite_frames(const Ref<SpriteFrames> &p_frames); Ref<SpriteFrames> get_sprite_frames() const; - void play(const StringName &p_animation = StringName()); + void play(const StringName &p_animation = StringName(), bool p_backwards = false); void stop(); + + void set_playing(bool p_playing); bool is_playing() const; void set_animation(const StringName &p_animation); @@ -245,9 +248,12 @@ public: void set_frame(int p_frame); int get_frame() const; + void set_speed_scale(double p_speed_scale); + double get_speed_scale() const; + virtual Rect2 get_item_rect() const override; - virtual TypedArray<String> get_configuration_warnings() const override; + virtual PackedStringArray get_configuration_warnings() const override; virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; AnimatedSprite3D(); diff --git a/scene/3d/vehicle_body_3d.cpp b/scene/3d/vehicle_body_3d.cpp index 42ed52c9f2..36b5e61f45 100644 --- a/scene/3d/vehicle_body_3d.cpp +++ b/scene/3d/vehicle_body_3d.cpp @@ -105,8 +105,8 @@ void VehicleWheel3D::_notification(int p_what) { } } -TypedArray<String> VehicleWheel3D::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray VehicleWheel3D::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (!Object::cast_to<VehicleBody3D>(get_parent())) { warnings.push_back(RTR("VehicleWheel3D serves to provide a wheel system to a VehicleBody3D. Please use it as a child of a VehicleBody3D.")); @@ -807,7 +807,7 @@ void VehicleBody3D::_update_friction(PhysicsDirectBodyState3D *s) { } void VehicleBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) { - RigidDynamicBody3D::_body_state_changed(p_state); + RigidBody3D::_body_state_changed(p_state); real_t step = p_state->get_step(); diff --git a/scene/3d/vehicle_body_3d.h b/scene/3d/vehicle_body_3d.h index 0ef8bd7482..a6a49ee88a 100644 --- a/scene/3d/vehicle_body_3d.h +++ b/scene/3d/vehicle_body_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef VEHICLE_BODY_H -#define VEHICLE_BODY_H +#ifndef VEHICLE_BODY_3D_H +#define VEHICLE_BODY_3D_H #include "scene/3d/physics_body_3d.h" @@ -147,13 +147,13 @@ public: void set_steering(real_t p_steering); real_t get_steering() const; - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; VehicleWheel3D(); }; -class VehicleBody3D : public RigidDynamicBody3D { - GDCLASS(VehicleBody3D, RigidDynamicBody3D); +class VehicleBody3D : public RigidBody3D { + GDCLASS(VehicleBody3D, RigidBody3D); real_t engine_force = 0.0; real_t brake = 0.0; @@ -210,4 +210,4 @@ public: VehicleBody3D(); }; -#endif // VEHICLE_BODY_H +#endif // VEHICLE_BODY_3D_H diff --git a/scene/3d/velocity_tracker_3d.h b/scene/3d/velocity_tracker_3d.h index 7fdcacc9c1..d3b92ab766 100644 --- a/scene/3d/velocity_tracker_3d.h +++ b/scene/3d/velocity_tracker_3d.h @@ -28,14 +28,12 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SPATIAL_VELOCITY_TRACKER_H -#define SPATIAL_VELOCITY_TRACKER_H +#ifndef VELOCITY_TRACKER_3D_H +#define VELOCITY_TRACKER_3D_H #include "scene/3d/node_3d.h" class VelocityTracker3D : public RefCounted { - GDCLASS(VelocityTracker3D, RefCounted); - struct PositionHistory { uint64_t frame = 0; Vector3 position; @@ -58,4 +56,4 @@ public: VelocityTracker3D(); }; -#endif // SPATIAL_VELOCITY_TRACKER_H +#endif // VELOCITY_TRACKER_3D_H diff --git a/scene/3d/visible_on_screen_notifier_3d.h b/scene/3d/visible_on_screen_notifier_3d.h index fe17f1e444..60461569f4 100644 --- a/scene/3d/visible_on_screen_notifier_3d.h +++ b/scene/3d/visible_on_screen_notifier_3d.h @@ -96,4 +96,4 @@ public: VARIANT_ENUM_CAST(VisibleOnScreenEnabler3D::EnableMode); -#endif // VISIBILITY_NOTIFIER_H +#endif // VISIBLE_ON_SCREEN_NOTIFIER_3D_H diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp index 69917f6992..e93ad5ecbf 100644 --- a/scene/3d/visual_instance_3d.cpp +++ b/scene/3d/visual_instance_3d.cpp @@ -227,8 +227,8 @@ const StringName *GeometryInstance3D::_instance_uniform_get_remap(const StringNa StringName *r = instance_uniform_property_remap.getptr(p_name); if (!r) { String s = p_name; - if (s.begins_with("shader_params/")) { - StringName name = s.replace("shader_params/", ""); + if (s.begins_with("shader_uniforms/")) { + StringName name = s.replace("shader_uniforms/", ""); instance_uniform_property_remap[p_name] = name; return instance_uniform_property_remap.getptr(p_name); } @@ -242,7 +242,7 @@ const StringName *GeometryInstance3D::_instance_uniform_get_remap(const StringNa bool GeometryInstance3D::_set(const StringName &p_name, const Variant &p_value) { const StringName *r = _instance_uniform_get_remap(p_name); if (r) { - set_shader_instance_uniform(*r, p_value); + set_instance_shader_parameter(*r, p_value); return true; } #ifndef DISABLE_DEPRECATED @@ -262,7 +262,7 @@ bool GeometryInstance3D::_set(const StringName &p_name, const Variant &p_value) bool GeometryInstance3D::_get(const StringName &p_name, Variant &r_ret) const { const StringName *r = _instance_uniform_get_remap(p_name); if (r) { - r_ret = get_shader_instance_uniform(*r); + r_ret = get_instance_shader_parameter(*r); return true; } @@ -284,7 +284,7 @@ void GeometryInstance3D::_get_property_list(List<PropertyInfo> *p_list) const { pi.usage = PROPERTY_USAGE_EDITOR | (has_def_value ? PROPERTY_USAGE_CHECKABLE : PROPERTY_USAGE_NONE); //do not save if not changed } - pi.name = "shader_params/" + pi.name; + pi.name = "shader_uniforms/" + pi.name; p_list->push_back(pi); } } @@ -319,24 +319,24 @@ float GeometryInstance3D::get_lod_bias() const { return lod_bias; } -void GeometryInstance3D::set_shader_instance_uniform(const StringName &p_uniform, const Variant &p_value) { +void GeometryInstance3D::set_instance_shader_parameter(const StringName &p_name, const Variant &p_value) { if (p_value.get_type() == Variant::NIL) { - Variant def_value = RS::get_singleton()->instance_geometry_get_shader_parameter_default_value(get_instance(), p_uniform); - RS::get_singleton()->instance_geometry_set_shader_parameter(get_instance(), p_uniform, def_value); + Variant def_value = RS::get_singleton()->instance_geometry_get_shader_parameter_default_value(get_instance(), p_name); + RS::get_singleton()->instance_geometry_set_shader_parameter(get_instance(), p_name, def_value); instance_uniforms.erase(p_value); } else { - instance_uniforms[p_uniform] = p_value; + instance_uniforms[p_name] = p_value; if (p_value.get_type() == Variant::OBJECT) { RID tex_id = p_value; - RS::get_singleton()->instance_geometry_set_shader_parameter(get_instance(), p_uniform, tex_id); + RS::get_singleton()->instance_geometry_set_shader_parameter(get_instance(), p_name, tex_id); } else { - RS::get_singleton()->instance_geometry_set_shader_parameter(get_instance(), p_uniform, p_value); + RS::get_singleton()->instance_geometry_set_shader_parameter(get_instance(), p_name, p_value); } } } -Variant GeometryInstance3D::get_shader_instance_uniform(const StringName &p_uniform) const { - return RS::get_singleton()->instance_geometry_get_shader_parameter(get_instance(), p_uniform); +Variant GeometryInstance3D::get_instance_shader_parameter(const StringName &p_name) const { + return RS::get_singleton()->instance_geometry_get_shader_parameter(get_instance(), p_name); } void GeometryInstance3D::set_custom_aabb(AABB aabb) { @@ -385,8 +385,8 @@ bool GeometryInstance3D::is_ignoring_occlusion_culling() { return ignore_occlusion_culling; } -TypedArray<String> GeometryInstance3D::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray GeometryInstance3D::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (!Math::is_zero_approx(visibility_range_end) && visibility_range_end <= visibility_range_begin) { warnings.push_back(RTR("The GeometryInstance3D visibility range's End distance is set to a non-zero value, but is lower than the Begin distance.\nThis means the GeometryInstance3D will never be visible.\nTo resolve this, set the End distance to 0 or to a value greater than the Begin distance.")); @@ -434,8 +434,8 @@ void GeometryInstance3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_visibility_range_fade_mode", "mode"), &GeometryInstance3D::set_visibility_range_fade_mode); ClassDB::bind_method(D_METHOD("get_visibility_range_fade_mode"), &GeometryInstance3D::get_visibility_range_fade_mode); - ClassDB::bind_method(D_METHOD("set_shader_instance_uniform", "uniform", "value"), &GeometryInstance3D::set_shader_instance_uniform); - ClassDB::bind_method(D_METHOD("get_shader_instance_uniform", "uniform"), &GeometryInstance3D::get_shader_instance_uniform); + ClassDB::bind_method(D_METHOD("set_instance_shader_parameter", "name", "value"), &GeometryInstance3D::set_instance_shader_parameter); + ClassDB::bind_method(D_METHOD("get_instance_shader_parameter", "name"), &GeometryInstance3D::get_instance_shader_parameter); ClassDB::bind_method(D_METHOD("set_extra_cull_margin", "margin"), &GeometryInstance3D::set_extra_cull_margin); ClassDB::bind_method(D_METHOD("get_extra_cull_margin"), &GeometryInstance3D::get_extra_cull_margin); @@ -461,15 +461,16 @@ void GeometryInstance3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "extra_cull_margin", PROPERTY_HINT_RANGE, "0,16384,0.01,suffix:m"), "set_extra_cull_margin", "get_extra_cull_margin"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lod_bias", PROPERTY_HINT_RANGE, "0.001,128,0.001"), "set_lod_bias", "get_lod_bias"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_occlusion_culling"), "set_ignore_occlusion_culling", "is_ignoring_occlusion_culling"); + ADD_GROUP("Global Illumination", "gi_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_mode", PROPERTY_HINT_ENUM, "Disabled,Static (VoxelGI/SDFGI/LightmapGI),Dynamic (VoxelGI only)"), "set_gi_mode", "get_gi_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_lightmap_scale", PROPERTY_HINT_ENUM, String::utf8("1×,2×,4×,8×")), "set_lightmap_scale", "get_lightmap_scale"); ADD_GROUP("Visibility Range", "visibility_range_"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_begin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,suffix:m"), "set_visibility_range_begin", "get_visibility_range_begin"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_begin_margin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,suffix:m"), "set_visibility_range_begin_margin", "get_visibility_range_begin_margin"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_end", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,suffix:m"), "set_visibility_range_end", "get_visibility_range_end"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_end_margin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,suffix:m"), "set_visibility_range_end_margin", "get_visibility_range_end_margin"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_begin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), "set_visibility_range_begin", "get_visibility_range_begin"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_begin_margin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), "set_visibility_range_begin_margin", "get_visibility_range_begin_margin"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_end", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), "set_visibility_range_end", "get_visibility_range_end"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_end_margin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), "set_visibility_range_end_margin", "get_visibility_range_end_margin"); ADD_PROPERTY(PropertyInfo(Variant::INT, "visibility_range_fade_mode", PROPERTY_HINT_ENUM, "Disabled,Self,Dependencies"), "set_visibility_range_fade_mode", "get_visibility_range_fade_mode"); BIND_ENUM_CONSTANT(SHADOW_CASTING_SETTING_OFF); diff --git a/scene/3d/visual_instance_3d.h b/scene/3d/visual_instance_3d.h index 9e0d9b9a2a..4755545516 100644 --- a/scene/3d/visual_instance_3d.h +++ b/scene/3d/visual_instance_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef VISUAL_INSTANCE_H -#define VISUAL_INSTANCE_H +#ifndef VISUAL_INSTANCE_3D_H +#define VISUAL_INSTANCE_3D_H #include "scene/3d/node_3d.h" @@ -178,15 +178,15 @@ public: void set_lightmap_scale(LightmapScale p_scale); LightmapScale get_lightmap_scale() const; - void set_shader_instance_uniform(const StringName &p_uniform, const Variant &p_value); - Variant get_shader_instance_uniform(const StringName &p_uniform) const; + void set_instance_shader_parameter(const StringName &p_name, const Variant &p_value); + Variant get_instance_shader_parameter(const StringName &p_name) const; void set_custom_aabb(AABB aabb); void set_ignore_occlusion_culling(bool p_enabled); bool is_ignoring_occlusion_culling(); - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; GeometryInstance3D(); virtual ~GeometryInstance3D(); }; @@ -196,4 +196,4 @@ VARIANT_ENUM_CAST(GeometryInstance3D::LightmapScale); VARIANT_ENUM_CAST(GeometryInstance3D::GIMode); VARIANT_ENUM_CAST(GeometryInstance3D::VisibilityRangeFadeMode); -#endif +#endif // VISUAL_INSTANCE_3D_H diff --git a/scene/3d/voxel_gi.cpp b/scene/3d/voxel_gi.cpp index ae231026a7..c2728960ee 100644 --- a/scene/3d/voxel_gi.cpp +++ b/scene/3d/voxel_gi.cpp @@ -30,8 +30,11 @@ #include "voxel_gi.h" +#include "core/config/project_settings.h" +#include "core/core_string_names.h" #include "mesh_instance_3d.h" #include "multimesh_instance_3d.h" +#include "scene/resources/camera_attributes.h" #include "voxelizer.h" void VoxelGIData::_set_data(const Dictionary &p_data) { @@ -281,6 +284,14 @@ Vector3 VoxelGI::get_extents() const { return extents; } +void VoxelGI::set_camera_attributes(const Ref<CameraAttributes> &p_camera_attributes) { + camera_attributes = p_camera_attributes; +} + +Ref<CameraAttributes> VoxelGI::get_camera_attributes() const { + return camera_attributes; +} + void VoxelGI::_find_meshes(Node *p_at_node, List<PlotMesh> &plot_meshes) { MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_at_node); if (mi && mi->get_gi_mode() == GeometryInstance3D::GI_MODE_STATIC && mi->is_visible_in_tree()) { @@ -370,9 +381,17 @@ void VoxelGI::bake(Node *p_from_node, bool p_create_visual_debug) { p_from_node = p_from_node ? p_from_node : get_parent(); ERR_FAIL_NULL(p_from_node); + float exposure_normalization = 1.0; + if (camera_attributes.is_valid()) { + exposure_normalization = camera_attributes->get_exposure_multiplier(); + if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + exposure_normalization = camera_attributes->calculate_exposure_normalization(); + } + } + Voxelizer baker; - baker.begin_bake(subdiv_value[subdiv], AABB(-extents, extents * 2.0)); + baker.begin_bake(subdiv_value[subdiv], AABB(-extents, extents * 2.0), exposure_normalization); List<PlotMesh> mesh_list; @@ -428,6 +447,8 @@ void VoxelGI::bake(Node *p_from_node, bool p_create_visual_debug) { Vector<uint8_t> df = baker.get_sdf_3d_image(); + RS::get_singleton()->voxel_gi_set_baked_exposure_normalization(probe_data->get_rid(), exposure_normalization); + probe_data->allocate(baker.get_to_cell_space_xform(), AABB(-extents, extents * 2.0), baker.get_voxel_gi_octree_size(), baker.get_voxel_gi_octree_cells(), baker.get_voxel_gi_data_cells(), df, baker.get_voxel_gi_level_cell_count()); set_probe_data(probe_data); @@ -451,8 +472,8 @@ AABB VoxelGI::get_aabb() const { return AABB(-extents, extents * 2); } -TypedArray<String> VoxelGI::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray VoxelGI::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (RenderingServer::get_singleton()->is_low_end()) { warnings.push_back(RTR("VoxelGIs are not supported by the OpenGL video driver.\nUse a LightmapGI instead.")); @@ -472,12 +493,16 @@ void VoxelGI::_bind_methods() { ClassDB::bind_method(D_METHOD("set_extents", "extents"), &VoxelGI::set_extents); ClassDB::bind_method(D_METHOD("get_extents"), &VoxelGI::get_extents); + ClassDB::bind_method(D_METHOD("set_camera_attributes", "camera_attributes"), &VoxelGI::set_camera_attributes); + ClassDB::bind_method(D_METHOD("get_camera_attributes"), &VoxelGI::get_camera_attributes); + ClassDB::bind_method(D_METHOD("bake", "from_node", "create_visual_debug"), &VoxelGI::bake, DEFVAL(Variant()), DEFVAL(false)); ClassDB::bind_method(D_METHOD("debug_bake"), &VoxelGI::_debug_bake); ClassDB::set_method_flags(get_class_static(), _scs_create("debug_bake"), METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR); ADD_PROPERTY(PropertyInfo(Variant::INT, "subdiv", PROPERTY_HINT_ENUM, "64,128,256,512"), "set_subdiv", "get_subdiv"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_NONE, "suffix:m"), "set_extents", "get_extents"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "camera_attributes", PROPERTY_HINT_RESOURCE_TYPE, "CameraAttributesPractical,CameraAttributesPhysical"), "set_camera_attributes", "get_camera_attributes"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "data", PROPERTY_HINT_RESOURCE_TYPE, "VoxelGIData", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE), "set_probe_data", "get_probe_data"); BIND_ENUM_CONSTANT(SUBDIV_64); diff --git a/scene/3d/voxel_gi.h b/scene/3d/voxel_gi.h index e1a38dd7a0..fc10091d4f 100644 --- a/scene/3d/voxel_gi.h +++ b/scene/3d/voxel_gi.h @@ -33,6 +33,8 @@ #include "scene/3d/visual_instance_3d.h" +class CameraAttributes; + class VoxelGIData : public Resource { GDCLASS(VoxelGIData, Resource); @@ -49,9 +51,9 @@ class VoxelGIData : public Resource { float energy = 1.0; float bias = 1.5; float normal_bias = 0.0; - float propagation = 0.7; + float propagation = 0.5; bool interior = false; - bool use_two_bounces = false; + bool use_two_bounces = true; protected: static void _bind_methods(); @@ -117,6 +119,7 @@ private: Subdiv subdiv = SUBDIV_128; Vector3 extents = Vector3(10, 10, 10); + Ref<CameraAttributes> camera_attributes; struct PlotMesh { Ref<Material> override_material; @@ -144,13 +147,17 @@ public: void set_extents(const Vector3 &p_extents); Vector3 get_extents() const; + + void set_camera_attributes(const Ref<CameraAttributes> &p_camera_attributes); + Ref<CameraAttributes> get_camera_attributes() const; + Vector3i get_estimated_cell_size() const; void bake(Node *p_from_node = nullptr, bool p_create_visual_debug = false); virtual AABB get_aabb() const override; - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; VoxelGI(); ~VoxelGI(); diff --git a/scene/3d/voxelizer.cpp b/scene/3d/voxelizer.cpp index 42a2a68e2d..6daa9e0aec 100644 --- a/scene/3d/voxelizer.cpp +++ b/scene/3d/voxelizer.cpp @@ -30,6 +30,8 @@ #include "voxelizer.h" +#include "core/config/project_settings.h" + static _FORCE_INLINE_ void get_uv_and_normal(const Vector3 &p_pos, const Vector3 *p_vtx, const Vector2 *p_uv, const Vector3 *p_normal, Vector2 &r_uv, Vector3 &r_normal) { if (p_pos.is_equal_approx(p_vtx[0])) { r_uv = p_uv[0]; @@ -323,8 +325,8 @@ Vector<Color> Voxelizer::_get_bake_texture(Ref<Image> p_image, const Color &p_co } Voxelizer::MaterialCache Voxelizer::_get_material_cache(Ref<Material> p_material) { - //this way of obtaining materials is inaccurate and also does not support some compressed formats very well - Ref<StandardMaterial3D> mat = p_material; + // This way of obtaining materials is inaccurate and also does not support some compressed formats very well. + Ref<BaseMaterial3D> mat = p_material; Ref<Material> material = mat; //hack for now @@ -335,7 +337,7 @@ Voxelizer::MaterialCache Voxelizer::_get_material_cache(Ref<Material> p_material MaterialCache mc; if (mat.is_valid()) { - Ref<Texture2D> albedo_tex = mat->get_texture(StandardMaterial3D::TEXTURE_ALBEDO); + Ref<Texture2D> albedo_tex = mat->get_texture(BaseMaterial3D::TEXTURE_ALBEDO); Ref<Image> img_albedo; if (albedo_tex.is_valid()) { @@ -345,10 +347,13 @@ Voxelizer::MaterialCache Voxelizer::_get_material_cache(Ref<Material> p_material mc.albedo = _get_bake_texture(img_albedo, Color(1, 1, 1), mat->get_albedo()); // no albedo texture, color is additive } - Ref<Texture2D> emission_tex = mat->get_texture(StandardMaterial3D::TEXTURE_EMISSION); + Ref<Texture2D> emission_tex = mat->get_texture(BaseMaterial3D::TEXTURE_EMISSION); Color emission_col = mat->get_emission(); - float emission_energy = mat->get_emission_energy(); + float emission_energy = mat->get_emission_energy_multiplier() * exposure_normalization; + if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + emission_energy *= mat->get_emission_intensity(); + } Ref<Image> img_emission; @@ -356,7 +361,7 @@ Voxelizer::MaterialCache Voxelizer::_get_material_cache(Ref<Material> p_material img_emission = emission_tex->get_image(); } - if (mat->get_emission_operator() == StandardMaterial3D::EMISSION_OP_ADD) { + if (mat->get_emission_operator() == BaseMaterial3D::EMISSION_OP_ADD) { mc.emission = _get_bake_texture(img_emission, Color(1, 1, 1) * emission_energy, emission_col * emission_energy); } else { mc.emission = _get_bake_texture(img_emission, emission_col * emission_energy, Color(0, 0, 0)); @@ -608,10 +613,11 @@ void Voxelizer::_fixup_plot(int p_idx, int p_level) { } } -void Voxelizer::begin_bake(int p_subdiv, const AABB &p_bounds) { +void Voxelizer::begin_bake(int p_subdiv, const AABB &p_bounds, float p_exposure_normalization) { sorted = false; original_bounds = p_bounds; cell_subdiv = p_subdiv; + exposure_normalization = p_exposure_normalization; bake_cells.resize(1); material_cache.clear(); diff --git a/scene/3d/voxelizer.h b/scene/3d/voxelizer.h index 0179795ddc..f5bb9af107 100644 --- a/scene/3d/voxelizer.h +++ b/scene/3d/voxelizer.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef VOXEL_LIGHT_BAKER_H -#define VOXEL_LIGHT_BAKER_H +#ifndef VOXELIZER_H +#define VOXELIZER_H #include "scene/resources/multimesh.h" @@ -87,6 +87,7 @@ private: }; HashMap<Ref<Material>, MaterialCache> material_cache; + float exposure_normalization = 1.0; AABB original_bounds; AABB po2_bounds; int axis_cell_size[3] = {}; @@ -111,7 +112,7 @@ private: void _sort(); public: - void begin_bake(int p_subdiv, const AABB &p_bounds); + void begin_bake(int p_subdiv, const AABB &p_bounds, float p_exposure_normalization); void plot_mesh(const Transform3D &p_xform, Ref<Mesh> &p_mesh, const Vector<Ref<Material>> &p_materials, const Ref<Material> &p_override_material); void end_bake(); @@ -129,4 +130,4 @@ public: Voxelizer(); }; -#endif // VOXEL_LIGHT_BAKER_H +#endif // VOXELIZER_H diff --git a/scene/3d/world_environment.cpp b/scene/3d/world_environment.cpp index fe9d9ae4dd..6cc5e9ef20 100644 --- a/scene/3d/world_environment.cpp +++ b/scene/3d/world_environment.cpp @@ -42,9 +42,9 @@ void WorldEnvironment::_notification(int p_what) { _update_current_environment(); } - if (camera_effects.is_valid()) { - add_to_group("_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id())); - _update_current_camera_effects(); + if (camera_attributes.is_valid()) { + add_to_group("_world_camera_attributes_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id())); + _update_current_camera_attributes(); } } break; @@ -55,9 +55,9 @@ void WorldEnvironment::_notification(int p_what) { _update_current_environment(); } - if (camera_effects.is_valid()) { - remove_from_group("_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id())); - _update_current_camera_effects(); + if (camera_attributes.is_valid()) { + remove_from_group("_world_camera_attributes_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id())); + _update_current_camera_attributes(); } } break; } @@ -74,15 +74,15 @@ void WorldEnvironment::_update_current_environment() { get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, "_world_environment_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warnings"); } -void WorldEnvironment::_update_current_camera_effects() { - WorldEnvironment *first = Object::cast_to<WorldEnvironment>(get_tree()->get_first_node_in_group("_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()))); +void WorldEnvironment::_update_current_camera_attributes() { + WorldEnvironment *first = Object::cast_to<WorldEnvironment>(get_tree()->get_first_node_in_group("_world_camera_attributes_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()))); if (first) { - get_viewport()->find_world_3d()->set_camera_effects(first->camera_effects); + get_viewport()->find_world_3d()->set_camera_attributes(first->camera_attributes); } else { - get_viewport()->find_world_3d()->set_camera_effects(Ref<CameraEffects>()); + get_viewport()->find_world_3d()->set_camera_attributes(Ref<CameraAttributes>()); } - get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, "_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warnings"); + get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, "_world_camera_attributes_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warnings"); } void WorldEnvironment::set_environment(const Ref<Environment> &p_environment) { @@ -110,36 +110,36 @@ Ref<Environment> WorldEnvironment::get_environment() const { return environment; } -void WorldEnvironment::set_camera_effects(const Ref<CameraEffects> &p_camera_effects) { - if (camera_effects == p_camera_effects) { +void WorldEnvironment::set_camera_attributes(const Ref<CameraAttributes> &p_camera_attributes) { + if (camera_attributes == p_camera_attributes) { return; } - if (is_inside_tree() && camera_effects.is_valid() && get_viewport()->find_world_3d()->get_camera_effects() == camera_effects) { - remove_from_group("_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id())); + if (is_inside_tree() && camera_attributes.is_valid() && get_viewport()->find_world_3d()->get_camera_attributes() == camera_attributes) { + remove_from_group("_world_camera_attributes_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id())); } - camera_effects = p_camera_effects; - if (is_inside_tree() && camera_effects.is_valid()) { - add_to_group("_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id())); + camera_attributes = p_camera_attributes; + if (is_inside_tree() && camera_attributes.is_valid()) { + add_to_group("_world_camera_attributes_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id())); } if (is_inside_tree()) { - _update_current_camera_effects(); + _update_current_camera_attributes(); } else { update_configuration_warnings(); } } -Ref<CameraEffects> WorldEnvironment::get_camera_effects() const { - return camera_effects; +Ref<CameraAttributes> WorldEnvironment::get_camera_attributes() const { + return camera_attributes; } -TypedArray<String> WorldEnvironment::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray WorldEnvironment::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); - if (!environment.is_valid() && !camera_effects.is_valid()) { - warnings.push_back(RTR("To have any visible effect, WorldEnvironment requires its \"Environment\" property to contain an Environment, its \"Camera Effects\" property to contain a CameraEffects resource, or both.")); + if (!environment.is_valid() && !camera_attributes.is_valid()) { + warnings.push_back(RTR("To have any visible effect, WorldEnvironment requires its \"Environment\" property to contain an Environment, its \"Camera Attributes\" property to contain a CameraAttributes resource, or both.")); } if (!is_inside_tree()) { @@ -150,7 +150,7 @@ TypedArray<String> WorldEnvironment::get_configuration_warnings() const { warnings.push_back(("Only the first Environment has an effect in a scene (or set of instantiated scenes).")); } - if (camera_effects.is_valid() && get_viewport()->find_world_3d()->get_camera_effects() != camera_effects) { + if (camera_attributes.is_valid() && get_viewport()->find_world_3d()->get_camera_attributes() != camera_attributes) { warnings.push_back(RTR("Only one WorldEnvironment is allowed per scene (or set of instantiated scenes).")); } @@ -162,9 +162,9 @@ void WorldEnvironment::_bind_methods() { ClassDB::bind_method(D_METHOD("get_environment"), &WorldEnvironment::get_environment); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "environment", PROPERTY_HINT_RESOURCE_TYPE, "Environment"), "set_environment", "get_environment"); - ClassDB::bind_method(D_METHOD("set_camera_effects", "env"), &WorldEnvironment::set_camera_effects); - ClassDB::bind_method(D_METHOD("get_camera_effects"), &WorldEnvironment::get_camera_effects); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "camera_effects", PROPERTY_HINT_RESOURCE_TYPE, "CameraEffects"), "set_camera_effects", "get_camera_effects"); + ClassDB::bind_method(D_METHOD("set_camera_attributes", "camera_attributes"), &WorldEnvironment::set_camera_attributes); + ClassDB::bind_method(D_METHOD("get_camera_attributes"), &WorldEnvironment::get_camera_attributes); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "camera_attributes", PROPERTY_HINT_RESOURCE_TYPE, "CameraAttributesPractical,CameraAttributesPhysical"), "set_camera_attributes", "get_camera_attributes"); } WorldEnvironment::WorldEnvironment() { diff --git a/scene/3d/world_environment.h b/scene/3d/world_environment.h index 8dbb57364c..cc46a06b4c 100644 --- a/scene/3d/world_environment.h +++ b/scene/3d/world_environment.h @@ -28,21 +28,21 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SCENARIO_FX_H -#define SCENARIO_FX_H +#ifndef WORLD_ENVIRONMENT_H +#define WORLD_ENVIRONMENT_H #include "scene/main/node.h" -#include "scene/resources/camera_effects.h" +#include "scene/resources/camera_attributes.h" #include "scene/resources/environment.h" class WorldEnvironment : public Node { GDCLASS(WorldEnvironment, Node); Ref<Environment> environment; - Ref<CameraEffects> camera_effects; + Ref<CameraAttributes> camera_attributes; void _update_current_environment(); - void _update_current_camera_effects(); + void _update_current_camera_attributes(); protected: void _notification(int p_what); @@ -52,12 +52,12 @@ public: void set_environment(const Ref<Environment> &p_environment); Ref<Environment> get_environment() const; - void set_camera_effects(const Ref<CameraEffects> &p_camera_effects); - Ref<CameraEffects> get_camera_effects() const; + void set_camera_attributes(const Ref<CameraAttributes> &p_camera_attributes); + Ref<CameraAttributes> get_camera_attributes() const; - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; WorldEnvironment(); }; -#endif +#endif // WORLD_ENVIRONMENT_H diff --git a/scene/3d/xr_nodes.cpp b/scene/3d/xr_nodes.cpp index 1dad6078b4..4401d22f30 100644 --- a/scene/3d/xr_nodes.cpp +++ b/scene/3d/xr_nodes.cpp @@ -88,8 +88,8 @@ void XRCamera3D::_pose_changed(const Ref<XRPose> &p_pose) { } } -TypedArray<String> XRCamera3D::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray XRCamera3D::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (is_visible() && is_inside_tree()) { // must be child node of XROrigin3D! @@ -120,7 +120,7 @@ Vector3 XRCamera3D::project_local_ray_normal(const Point2 &p_pos) const { Vector3 ray; // Just use the first view, if multiple views are supported this function has no good result - CameraMatrix cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far()); + Projection cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far()); Vector2 screen_he = cm.get_viewport_half_extents(); ray = Vector3(((cpos.x / viewport_size.width) * 2.0 - 1.0) * screen_he.x, ((1.0 - (cpos.y / viewport_size.height)) * 2.0 - 1.0) * screen_he.y, -get_near()).normalized(); @@ -143,7 +143,7 @@ Point2 XRCamera3D::unproject_position(const Vector3 &p_pos) const { Size2 viewport_size = get_viewport()->get_visible_rect().size; // Just use the first view, if multiple views are supported this function has no good result - CameraMatrix cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far()); + Projection cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far()); Plane p(get_camera_transform().xform_inv(p_pos), 1.0); @@ -173,7 +173,7 @@ Vector3 XRCamera3D::project_position(const Point2 &p_point, real_t p_z_depth) co Size2 viewport_size = get_viewport()->get_visible_rect().size; // Just use the first view, if multiple views are supported this function has no good result - CameraMatrix cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far()); + Projection cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far()); Vector2 vp_he = cm.get_viewport_half_extents(); @@ -202,7 +202,7 @@ Vector<Plane> XRCamera3D::get_frustum() const { Size2 viewport_size = get_viewport()->get_visible_rect().size; // TODO Just use the first view for now, this is mostly for debugging so we may look into using our combined projection here. - CameraMatrix cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far()); + Projection cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far()); return cm.get_projection_planes(get_camera_transform()); }; @@ -244,27 +244,25 @@ void XRNode3D::_bind_methods() { ClassDB::bind_method(D_METHOD("trigger_haptic_pulse", "action_name", "frequency", "amplitude", "duration_sec", "delay_sec"), &XRNode3D::trigger_haptic_pulse); }; -void XRNode3D::_validate_property(PropertyInfo &property) const { +void XRNode3D::_validate_property(PropertyInfo &p_property) const { XRServer *xr_server = XRServer::get_singleton(); ERR_FAIL_NULL(xr_server); - if (property.name == "tracker") { + if (p_property.name == "tracker") { PackedStringArray names = xr_server->get_suggested_tracker_names(); String hint_string; for (const String &name : names) { hint_string += name + ","; } - property.hint_string = hint_string; - } else if (property.name == "pose") { + p_property.hint_string = hint_string; + } else if (p_property.name == "pose") { PackedStringArray names = xr_server->get_suggested_pose_names(tracker_name); String hint_string; for (const String &name : names) { hint_string += name + ","; } - property.hint_string = hint_string; + p_property.hint_string = hint_string; } - - Node3D::_validate_property(property); } void XRNode3D::set_tracker(const StringName p_tracker_name) { @@ -416,8 +414,8 @@ XRNode3D::~XRNode3D() { xr_server->disconnect("tracker_removed", callable_mp(this, &XRNode3D::_removed_tracker)); } -TypedArray<String> XRNode3D::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray XRNode3D::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (is_visible() && is_inside_tree()) { // must be child node of XROrigin! @@ -584,8 +582,8 @@ Plane XRAnchor3D::get_plane() const { //////////////////////////////////////////////////////////////////////////////////////////////////// -TypedArray<String> XROrigin3D::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray XROrigin3D::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (is_visible() && is_inside_tree()) { if (tracked_camera == nullptr) { diff --git a/scene/3d/xr_nodes.h b/scene/3d/xr_nodes.h index 3079e20dc7..ef846cc3a3 100644 --- a/scene/3d/xr_nodes.h +++ b/scene/3d/xr_nodes.h @@ -55,7 +55,7 @@ protected: void _pose_changed(const Ref<XRPose> &p_pose); public: - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; virtual Vector3 project_local_ray_normal(const Point2 &p_pos) const override; virtual Point2 unproject_position(const Vector3 &p_pos) const override; @@ -93,7 +93,7 @@ protected: void _pose_changed(const Ref<XRPose> &p_pose); public: - virtual void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; void set_tracker(const StringName p_tracker_name); StringName get_tracker() const; @@ -107,7 +107,7 @@ public: Ref<XRPose> get_pose(); - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; XRNode3D(); ~XRNode3D(); @@ -187,7 +187,7 @@ protected: static void _bind_methods(); public: - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; void set_tracked_camera(XRCamera3D *p_tracked_camera); XRCamera3D *get_tracked_camera() const; diff --git a/scene/SCsub b/scene/SCsub index a7b23af598..b4b2d6dd0a 100644 --- a/scene/SCsub +++ b/scene/SCsub @@ -9,7 +9,6 @@ env.add_source_files(env.scene_sources, "*.cpp") # Chain load SCsubs SConscript("main/SCsub") -SConscript("multiplayer/SCsub") SConscript("gui/SCsub") if not env["disable_3d"]: SConscript("3d/SCsub") @@ -18,6 +17,7 @@ SConscript("animation/SCsub") SConscript("audio/SCsub") SConscript("resources/SCsub") SConscript("debugger/SCsub") +SConscript("theme/SCsub") # Build it all as a library lib = env.add_library("scene", env.scene_sources) diff --git a/scene/animation/animation_blend_space_1d.cpp b/scene/animation/animation_blend_space_1d.cpp index 594f98410e..f30aea3bdd 100644 --- a/scene/animation/animation_blend_space_1d.cpp +++ b/scene/animation/animation_blend_space_1d.cpp @@ -42,15 +42,14 @@ Ref<AnimationNode> AnimationNodeBlendSpace1D::get_child_by_name(const StringName return get_blend_point_node(p_name.operator String().to_int()); } -void AnimationNodeBlendSpace1D::_validate_property(PropertyInfo &property) const { - if (property.name.begins_with("blend_point_")) { - String left = property.name.get_slicec('/', 0); +void AnimationNodeBlendSpace1D::_validate_property(PropertyInfo &p_property) const { + if (p_property.name.begins_with("blend_point_")) { + String left = p_property.name.get_slicec('/', 0); int idx = left.get_slicec('_', 2).to_int(); if (idx >= blend_points_used) { - property.usage = PROPERTY_USAGE_NONE; + p_property.usage = PROPERTY_USAGE_NONE; } } - AnimationRootNode::_validate_property(property); } void AnimationNodeBlendSpace1D::_tree_changed() { @@ -78,6 +77,9 @@ void AnimationNodeBlendSpace1D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_value_label", "text"), &AnimationNodeBlendSpace1D::set_value_label); ClassDB::bind_method(D_METHOD("get_value_label"), &AnimationNodeBlendSpace1D::get_value_label); + ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeBlendSpace1D::set_use_sync); + ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeBlendSpace1D::is_using_sync); + ClassDB::bind_method(D_METHOD("_add_blend_point", "index", "node"), &AnimationNodeBlendSpace1D::_add_blend_point); for (int i = 0; i < MAX_BLEND_POINTS; i++) { @@ -89,6 +91,7 @@ void AnimationNodeBlendSpace1D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_max_space", "get_max_space"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "snap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_snap", "get_snap"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "value_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_value_label", "get_value_label"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_use_sync", "is_using_sync"); } void AnimationNodeBlendSpace1D::get_child_nodes(List<ChildNode> *r_child_nodes) { @@ -117,7 +120,7 @@ void AnimationNodeBlendSpace1D::add_blend_point(const Ref<AnimationRootNode> &p_ blend_points[p_at_index].node = p_node; blend_points[p_at_index].position = p_position; - blend_points[p_at_index].node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace1D::_tree_changed), varray(), CONNECT_REFERENCE_COUNTED); + blend_points[p_at_index].node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace1D::_tree_changed), CONNECT_REFERENCE_COUNTED); blend_points_used++; emit_signal(SNAME("tree_changed")); @@ -138,7 +141,7 @@ void AnimationNodeBlendSpace1D::set_blend_point_node(int p_point, const Ref<Anim } blend_points[p_point].node = p_node; - blend_points[p_point].node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace1D::_tree_changed), varray(), CONNECT_REFERENCE_COUNTED); + blend_points[p_point].node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace1D::_tree_changed), CONNECT_REFERENCE_COUNTED); emit_signal(SNAME("tree_changed")); } @@ -211,6 +214,14 @@ String AnimationNodeBlendSpace1D::get_value_label() const { return value_label; } +void AnimationNodeBlendSpace1D::set_use_sync(bool p_sync) { + sync = p_sync; +} + +bool AnimationNodeBlendSpace1D::is_using_sync() const { + return sync; +} + void AnimationNodeBlendSpace1D::_add_blend_point(int p_index, const Ref<AnimationRootNode> &p_node) { if (p_index == blend_points_used) { add_blend_point(p_node, 0); @@ -226,7 +237,7 @@ double AnimationNodeBlendSpace1D::process(double p_time, bool p_seek, bool p_see if (blend_points_used == 1) { // only one point available, just play that animation - return blend_node(blend_points[0].name, blend_points[0].node, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, false); + return blend_node(blend_points[0].name, blend_points[0].node, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, true); } double blend_pos = get_parameter(blend_position); @@ -295,9 +306,12 @@ double AnimationNodeBlendSpace1D::process(double p_time, bool p_seek, bool p_see double max_time_remaining = 0.0; for (int i = 0; i < blend_points_used; i++) { - double remaining = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, weights[i], FILTER_IGNORE, false); - - max_time_remaining = MAX(max_time_remaining, remaining); + if (i == point_lower || i == point_higher) { + double remaining = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, weights[i], FILTER_IGNORE, true); + max_time_remaining = MAX(max_time_remaining, remaining); + } else { + blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, 0, FILTER_IGNORE, sync); + } } return max_time_remaining; diff --git a/scene/animation/animation_blend_space_1d.h b/scene/animation/animation_blend_space_1d.h index b2075c8c93..1876ccebc7 100644 --- a/scene/animation/animation_blend_space_1d.h +++ b/scene/animation/animation_blend_space_1d.h @@ -63,7 +63,9 @@ class AnimationNodeBlendSpace1D : public AnimationRootNode { StringName blend_position = "blend_position"; protected: - virtual void _validate_property(PropertyInfo &property) const override; + bool sync = false; + + void _validate_property(PropertyInfo &p_property) const; static void _bind_methods(); public: @@ -93,6 +95,9 @@ public: void set_value_label(const String &p_label); String get_value_label() const; + void set_use_sync(bool p_sync); + bool is_using_sync() const; + double process(double p_time, bool p_seek, bool p_seek_root) override; String get_caption() const override; diff --git a/scene/animation/animation_blend_space_2d.cpp b/scene/animation/animation_blend_space_2d.cpp index acdce2d7de..2dc61efb94 100644 --- a/scene/animation/animation_blend_space_2d.cpp +++ b/scene/animation/animation_blend_space_2d.cpp @@ -80,7 +80,7 @@ void AnimationNodeBlendSpace2D::add_blend_point(const Ref<AnimationRootNode> &p_ blend_points[p_at_index].node = p_node; blend_points[p_at_index].position = p_position; - blend_points[p_at_index].node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace2D::_tree_changed), varray(), CONNECT_REFERENCE_COUNTED); + blend_points[p_at_index].node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace2D::_tree_changed), CONNECT_REFERENCE_COUNTED); blend_points_used++; _queue_auto_triangles(); @@ -102,7 +102,7 @@ void AnimationNodeBlendSpace2D::set_blend_point_node(int p_point, const Ref<Anim blend_points[p_point].node->disconnect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace2D::_tree_changed)); } blend_points[p_point].node = p_node; - blend_points[p_point].node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace2D::_tree_changed), varray(), CONNECT_REFERENCE_COUNTED); + blend_points[p_point].node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace2D::_tree_changed), CONNECT_REFERENCE_COUNTED); emit_signal(SNAME("tree_changed")); } @@ -502,7 +502,7 @@ double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek, bool p_see for (int j = 0; j < 3; j++) { if (i == triangle_points[j]) { //blend with the given weight - double t = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, blend_weights[j], FILTER_IGNORE, false); + double t = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, blend_weights[j], FILTER_IGNORE, true); if (first || t < mind) { mind = t; first = false; @@ -513,8 +513,7 @@ double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek, bool p_see } if (!found) { - //ignore - blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, 0, FILTER_IGNORE, false); + blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, 0, FILTER_IGNORE, sync); } } } else { @@ -539,16 +538,22 @@ double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek, bool p_see na_n->set_backward(na_c->is_backward()); } //see how much animation remains - from = length_internal - blend_node(blend_points[closest].name, blend_points[closest].node, p_time, false, p_seek_root, 0.0, FILTER_IGNORE, false); + from = length_internal - blend_node(blend_points[closest].name, blend_points[closest].node, p_time, false, p_seek_root, 0.0, FILTER_IGNORE, true); } - mind = blend_node(blend_points[new_closest].name, blend_points[new_closest].node, from, true, p_seek_root, 1.0, FILTER_IGNORE, false); + mind = blend_node(blend_points[new_closest].name, blend_points[new_closest].node, from, true, p_seek_root, 1.0, FILTER_IGNORE, true); length_internal = from + mind; closest = new_closest; } else { - mind = blend_node(blend_points[closest].name, blend_points[closest].node, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, false); + mind = blend_node(blend_points[closest].name, blend_points[closest].node, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, true); + } + + for (int i = 0; i < blend_points_used; i++) { + if (i != closest) { + blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, 0, FILTER_IGNORE, sync); + } } } @@ -561,18 +566,17 @@ String AnimationNodeBlendSpace2D::get_caption() const { return "BlendSpace2D"; } -void AnimationNodeBlendSpace2D::_validate_property(PropertyInfo &property) const { - if (auto_triangles && property.name == "triangles") { - property.usage = PROPERTY_USAGE_NONE; +void AnimationNodeBlendSpace2D::_validate_property(PropertyInfo &p_property) const { + if (auto_triangles && p_property.name == "triangles") { + p_property.usage = PROPERTY_USAGE_NONE; } - if (property.name.begins_with("blend_point_")) { - String left = property.name.get_slicec('/', 0); + if (p_property.name.begins_with("blend_point_")) { + String left = p_property.name.get_slicec('/', 0); int idx = left.get_slicec('_', 2).to_int(); if (idx >= blend_points_used) { - property.usage = PROPERTY_USAGE_NONE; + p_property.usage = PROPERTY_USAGE_NONE; } } - AnimationRootNode::_validate_property(property); } void AnimationNodeBlendSpace2D::set_auto_triangles(bool p_enable) { @@ -604,6 +608,14 @@ AnimationNodeBlendSpace2D::BlendMode AnimationNodeBlendSpace2D::get_blend_mode() return blend_mode; } +void AnimationNodeBlendSpace2D::set_use_sync(bool p_sync) { + sync = p_sync; +} + +bool AnimationNodeBlendSpace2D::is_using_sync() const { + return sync; +} + void AnimationNodeBlendSpace2D::_bind_methods() { ClassDB::bind_method(D_METHOD("add_blend_point", "node", "pos", "at_index"), &AnimationNodeBlendSpace2D::add_blend_point, DEFVAL(-1)); ClassDB::bind_method(D_METHOD("set_blend_point_position", "point", "pos"), &AnimationNodeBlendSpace2D::set_blend_point_position); @@ -644,6 +656,9 @@ void AnimationNodeBlendSpace2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_blend_mode", "mode"), &AnimationNodeBlendSpace2D::set_blend_mode); ClassDB::bind_method(D_METHOD("get_blend_mode"), &AnimationNodeBlendSpace2D::get_blend_mode); + ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeBlendSpace2D::set_use_sync); + ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeBlendSpace2D::is_using_sync); + ClassDB::bind_method(D_METHOD("_update_triangles"), &AnimationNodeBlendSpace2D::_update_triangles); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_triangles", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_auto_triangles", "get_auto_triangles"); @@ -661,6 +676,7 @@ void AnimationNodeBlendSpace2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::STRING, "x_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_x_label", "get_x_label"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "y_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_y_label", "get_y_label"); ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Interpolated,Discrete,Carry", PROPERTY_USAGE_NO_EDITOR), "set_blend_mode", "get_blend_mode"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_use_sync", "is_using_sync"); ADD_SIGNAL(MethodInfo("triangles_updated")); BIND_ENUM_CONSTANT(BLEND_MODE_INTERPOLATED); diff --git a/scene/animation/animation_blend_space_2d.h b/scene/animation/animation_blend_space_2d.h index 01f53ed25a..250189f202 100644 --- a/scene/animation/animation_blend_space_2d.h +++ b/scene/animation/animation_blend_space_2d.h @@ -88,7 +88,9 @@ protected: void _tree_changed(); protected: - virtual void _validate_property(PropertyInfo &property) const override; + bool sync = false; + + void _validate_property(PropertyInfo &p_property) const; static void _bind_methods(); public: @@ -137,6 +139,9 @@ public: void set_blend_mode(BlendMode p_blend_mode); BlendMode get_blend_mode() const; + void set_use_sync(bool p_sync); + bool is_using_sync() const; + virtual Ref<AnimationNode> get_child_by_name(const StringName &p_name) override; AnimationNodeBlendSpace2D(); diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp index 4f6975deb1..c063d8f1bf 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -47,8 +47,8 @@ void AnimationNodeAnimation::get_parameter_list(List<PropertyInfo> *r_list) cons r_list->push_back(PropertyInfo(Variant::FLOAT, time, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); } -void AnimationNodeAnimation::_validate_property(PropertyInfo &property) const { - if (property.name == "animation" && get_editable_animation_list) { +void AnimationNodeAnimation::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "animation" && get_editable_animation_list) { Vector<String> names = get_editable_animation_list(); String anims; for (int i = 0; i < names.size(); i++) { @@ -58,8 +58,8 @@ void AnimationNodeAnimation::_validate_property(PropertyInfo &property) const { anims += String(names[i]); } if (!anims.is_empty()) { - property.hint = PROPERTY_HINT_ENUM; - property.hint_string = anims; + p_property.hint = PROPERTY_HINT_ENUM; + p_property.hint_string = anims; } } } @@ -179,6 +179,26 @@ AnimationNodeAnimation::AnimationNodeAnimation() { //////////////////////////////////////////////////////// +void AnimationNodeSync::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeSync::set_use_sync); + ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeSync::is_using_sync); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync"); +} + +void AnimationNodeSync::set_use_sync(bool p_sync) { + sync = p_sync; +} + +bool AnimationNodeSync::is_using_sync() const { + return sync; +} + +AnimationNodeSync::AnimationNodeSync() { +} + +//////////////////////////////////////////////////////// + void AnimationNodeOneShot::get_parameter_list(List<PropertyInfo> *r_list) const { r_list->push_back(PropertyInfo(Variant::BOOL, active)); r_list->push_back(PropertyInfo(Variant::BOOL, prev_active, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); @@ -197,19 +217,19 @@ Variant AnimationNodeOneShot::get_parameter_default_value(const StringName &p_pa } } -void AnimationNodeOneShot::set_fadein_time(float p_time) { +void AnimationNodeOneShot::set_fadein_time(double p_time) { fade_in = p_time; } -void AnimationNodeOneShot::set_fadeout_time(float p_time) { +void AnimationNodeOneShot::set_fadeout_time(double p_time) { fade_out = p_time; } -float AnimationNodeOneShot::get_fadein_time() const { +double AnimationNodeOneShot::get_fadein_time() const { return fade_in; } -float AnimationNodeOneShot::get_fadeout_time() const { +double AnimationNodeOneShot::get_fadeout_time() const { return fade_out; } @@ -217,11 +237,11 @@ void AnimationNodeOneShot::set_autorestart(bool p_active) { autorestart = p_active; } -void AnimationNodeOneShot::set_autorestart_delay(float p_time) { +void AnimationNodeOneShot::set_autorestart_delay(double p_time) { autorestart_delay = p_time; } -void AnimationNodeOneShot::set_autorestart_random_delay(float p_time) { +void AnimationNodeOneShot::set_autorestart_random_delay(double p_time) { autorestart_random_delay = p_time; } @@ -229,11 +249,11 @@ bool AnimationNodeOneShot::has_autorestart() const { return autorestart; } -float AnimationNodeOneShot::get_autorestart_delay() const { +double AnimationNodeOneShot::get_autorestart_delay() const { return autorestart_delay; } -float AnimationNodeOneShot::get_autorestart_random_delay() const { +double AnimationNodeOneShot::get_autorestart_random_delay() const { return autorestart_random_delay; } @@ -276,7 +296,7 @@ double AnimationNodeOneShot::process(double p_time, bool p_seek, bool p_seek_roo } if (!active) { - return blend_input(0, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, !sync); + return blend_input(0, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, sync); } } @@ -293,7 +313,7 @@ double AnimationNodeOneShot::process(double p_time, bool p_seek, bool p_seek_roo set_parameter(this->prev_active, true); } - float blend; + real_t blend; if (time < fade_in) { if (fade_in > 0) { @@ -313,12 +333,12 @@ double AnimationNodeOneShot::process(double p_time, bool p_seek, bool p_seek_roo double main_rem; if (mix == MIX_MODE_ADD) { - main_rem = blend_input(0, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, !sync); + main_rem = blend_input(0, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, sync); } else { - main_rem = blend_input(0, p_time, p_seek, p_seek_root, 1.0 - blend, FILTER_BLEND, !sync); + main_rem = blend_input(0, p_time, p_seek, p_seek_root, 1.0 - blend, FILTER_BLEND, sync); } - double os_rem = blend_input(1, os_seek ? time : p_time, os_seek, p_seek_root, blend, FILTER_PASS, false); + double os_rem = blend_input(1, os_seek ? time : p_time, os_seek, p_seek_root, blend, FILTER_PASS, true); if (do_start) { remaining = os_rem; @@ -331,7 +351,7 @@ double AnimationNodeOneShot::process(double p_time, bool p_seek, bool p_seek_roo set_parameter(this->active, false); set_parameter(this->prev_active, false); if (autorestart) { - float restart_sec = autorestart_delay + Math::randf() * autorestart_random_delay; + double restart_sec = autorestart_delay + Math::randd() * autorestart_random_delay; set_parameter(this->time_to_restart, restart_sec); } } @@ -343,14 +363,6 @@ double AnimationNodeOneShot::process(double p_time, bool p_seek, bool p_seek_roo return MAX(main_rem, remaining); } -void AnimationNodeOneShot::set_use_sync(bool p_sync) { - sync = p_sync; -} - -bool AnimationNodeOneShot::is_using_sync() const { - return sync; -} - void AnimationNodeOneShot::_bind_methods() { ClassDB::bind_method(D_METHOD("set_fadein_time", "time"), &AnimationNodeOneShot::set_fadein_time); ClassDB::bind_method(D_METHOD("get_fadein_time"), &AnimationNodeOneShot::get_fadein_time); @@ -370,9 +382,6 @@ void AnimationNodeOneShot::_bind_methods() { ClassDB::bind_method(D_METHOD("set_mix_mode", "mode"), &AnimationNodeOneShot::set_mix_mode); ClassDB::bind_method(D_METHOD("get_mix_mode"), &AnimationNodeOneShot::get_mix_mode); - ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeOneShot::set_use_sync); - ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeOneShot::is_using_sync); - ADD_PROPERTY(PropertyInfo(Variant::INT, "mix_mode", PROPERTY_HINT_ENUM, "Blend,Add"), "set_mix_mode", "get_mix_mode"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fadein_time", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater,suffix:s"), "set_fadein_time", "get_fadein_time"); @@ -384,9 +393,6 @@ void AnimationNodeOneShot::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "autorestart_delay", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater,suffix:s"), "set_autorestart_delay", "get_autorestart_delay"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "autorestart_random_delay", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater,suffix:s"), "set_autorestart_random_delay", "get_autorestart_random_delay"); - ADD_GROUP("", ""); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync"); - BIND_ENUM_CONSTANT(MIX_MODE_BLEND); BIND_ENUM_CONSTANT(MIX_MODE_ADD); } @@ -410,31 +416,19 @@ String AnimationNodeAdd2::get_caption() const { return "Add2"; } -void AnimationNodeAdd2::set_use_sync(bool p_sync) { - sync = p_sync; -} - -bool AnimationNodeAdd2::is_using_sync() const { - return sync; -} - bool AnimationNodeAdd2::has_filter() const { return true; } double AnimationNodeAdd2::process(double p_time, bool p_seek, bool p_seek_root) { double amount = get_parameter(add_amount); - double rem0 = blend_input(0, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, !sync); - blend_input(1, p_time, p_seek, p_seek_root, amount, FILTER_PASS, !sync); + double rem0 = blend_input(0, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, sync); + blend_input(1, p_time, p_seek, p_seek_root, amount, FILTER_PASS, sync); return rem0; } void AnimationNodeAdd2::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeAdd2::set_use_sync); - ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeAdd2::is_using_sync); - - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync"); } AnimationNodeAdd2::AnimationNodeAdd2() { @@ -456,32 +450,20 @@ String AnimationNodeAdd3::get_caption() const { return "Add3"; } -void AnimationNodeAdd3::set_use_sync(bool p_sync) { - sync = p_sync; -} - -bool AnimationNodeAdd3::is_using_sync() const { - return sync; -} - bool AnimationNodeAdd3::has_filter() const { return true; } double AnimationNodeAdd3::process(double p_time, bool p_seek, bool p_seek_root) { double amount = get_parameter(add_amount); - blend_input(0, p_time, p_seek, p_seek_root, MAX(0, -amount), FILTER_PASS, !sync); - double rem0 = blend_input(1, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, !sync); - blend_input(2, p_time, p_seek, p_seek_root, MAX(0, amount), FILTER_PASS, !sync); + blend_input(0, p_time, p_seek, p_seek_root, MAX(0, -amount), FILTER_PASS, sync); + double rem0 = blend_input(1, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, sync); + blend_input(2, p_time, p_seek, p_seek_root, MAX(0, amount), FILTER_PASS, sync); return rem0; } void AnimationNodeAdd3::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeAdd3::set_use_sync); - ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeAdd3::is_using_sync); - - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync"); } AnimationNodeAdd3::AnimationNodeAdd3() { @@ -507,29 +489,17 @@ String AnimationNodeBlend2::get_caption() const { double AnimationNodeBlend2::process(double p_time, bool p_seek, bool p_seek_root) { double amount = get_parameter(blend_amount); - double rem0 = blend_input(0, p_time, p_seek, p_seek_root, 1.0 - amount, FILTER_BLEND, !sync); - double rem1 = blend_input(1, p_time, p_seek, p_seek_root, amount, FILTER_PASS, !sync); + double rem0 = blend_input(0, p_time, p_seek, p_seek_root, 1.0 - amount, FILTER_BLEND, sync); + double rem1 = blend_input(1, p_time, p_seek, p_seek_root, amount, FILTER_PASS, sync); return amount > 0.5 ? rem1 : rem0; //hacky but good enough } -void AnimationNodeBlend2::set_use_sync(bool p_sync) { - sync = p_sync; -} - -bool AnimationNodeBlend2::is_using_sync() const { - return sync; -} - bool AnimationNodeBlend2::has_filter() const { return true; } void AnimationNodeBlend2::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeBlend2::set_use_sync); - ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeBlend2::is_using_sync); - - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync"); } AnimationNodeBlend2::AnimationNodeBlend2() { @@ -551,41 +521,28 @@ String AnimationNodeBlend3::get_caption() const { return "Blend3"; } -void AnimationNodeBlend3::set_use_sync(bool p_sync) { - sync = p_sync; -} - -bool AnimationNodeBlend3::is_using_sync() const { - return sync; -} - double AnimationNodeBlend3::process(double p_time, bool p_seek, bool p_seek_root) { double amount = get_parameter(blend_amount); - double rem0 = blend_input(0, p_time, p_seek, p_seek_root, MAX(0, -amount), FILTER_IGNORE, !sync); - double rem1 = blend_input(1, p_time, p_seek, p_seek_root, 1.0 - ABS(amount), FILTER_IGNORE, !sync); - double rem2 = blend_input(2, p_time, p_seek, p_seek_root, MAX(0, amount), FILTER_IGNORE, !sync); + double rem0 = blend_input(0, p_time, p_seek, p_seek_root, MAX(0, -amount), FILTER_IGNORE, sync); + double rem1 = blend_input(1, p_time, p_seek, p_seek_root, 1.0 - ABS(amount), FILTER_IGNORE, sync); + double rem2 = blend_input(2, p_time, p_seek, p_seek_root, MAX(0, amount), FILTER_IGNORE, sync); return amount > 0.5 ? rem2 : (amount < -0.5 ? rem0 : rem1); //hacky but good enough } void AnimationNodeBlend3::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeBlend3::set_use_sync); - ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeBlend3::is_using_sync); - - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync"); } AnimationNodeBlend3::AnimationNodeBlend3() { add_input("-blend"); add_input("in"); add_input("+blend"); - sync = false; } ///////////////////////////////// void AnimationNodeTimeScale::get_parameter_list(List<PropertyInfo> *r_list) const { - r_list->push_back(PropertyInfo(Variant::FLOAT, scale, PROPERTY_HINT_RANGE, "-32,32,0.01,or_lesser,or_greater")); + r_list->push_back(PropertyInfo(Variant::FLOAT, scale, PROPERTY_HINT_RANGE, "-32,32,0.01,or_less,or_greater")); } Variant AnimationNodeTimeScale::get_parameter_default_value(const StringName &p_parameter) const { @@ -599,9 +556,9 @@ String AnimationNodeTimeScale::get_caption() const { double AnimationNodeTimeScale::process(double p_time, bool p_seek, bool p_seek_root) { double scale = get_parameter(this->scale); if (p_seek) { - return blend_input(0, p_time, true, p_seek_root, 1.0, FILTER_IGNORE, false); + return blend_input(0, p_time, true, p_seek_root, 1.0, FILTER_IGNORE, true); } else { - return blend_input(0, p_time * scale, false, p_seek_root, 1.0, FILTER_IGNORE, false); + return blend_input(0, p_time * scale, false, p_seek_root, 1.0, FILTER_IGNORE, true); } } @@ -629,13 +586,13 @@ String AnimationNodeTimeSeek::get_caption() const { double AnimationNodeTimeSeek::process(double p_time, bool p_seek, bool p_seek_root) { double seek_pos = get_parameter(this->seek_pos); if (p_seek) { - return blend_input(0, p_time, true, p_seek_root, 1.0, FILTER_IGNORE, false); + return blend_input(0, p_time, true, p_seek_root, 1.0, FILTER_IGNORE, true); } else if (seek_pos >= 0) { - double ret = blend_input(0, seek_pos, true, true, 1.0, FILTER_IGNORE, false); + double ret = blend_input(0, seek_pos, true, true, 1.0, FILTER_IGNORE, true); set_parameter(this->seek_pos, -1.0); //reset return ret; } else { - return blend_input(0, p_time, false, p_seek_root, 1.0, FILTER_IGNORE, false); + return blend_input(0, p_time, false, p_seek_root, 1.0, FILTER_IGNORE, true); } } @@ -719,12 +676,28 @@ String AnimationNodeTransition::get_input_caption(int p_input) const { return inputs[p_input].name; } -void AnimationNodeTransition::set_cross_fade_time(float p_fade) { - xfade = p_fade; +void AnimationNodeTransition::set_xfade_time(double p_fade) { + xfade_time = p_fade; } -float AnimationNodeTransition::get_cross_fade_time() const { - return xfade; +double AnimationNodeTransition::get_xfade_time() const { + return xfade_time; +} + +void AnimationNodeTransition::set_xfade_curve(const Ref<Curve> &p_curve) { + xfade_curve = p_curve; +} + +Ref<Curve> AnimationNodeTransition::get_xfade_curve() const { + return xfade_curve; +} + +void AnimationNodeTransition::set_from_start(bool p_from_start) { + from_start = p_from_start; +} + +bool AnimationNodeTransition::is_from_start() const { + return from_start; } double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_seek_root) { @@ -742,7 +715,7 @@ double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_seek_ set_parameter(this->prev, prev_current); prev = prev_current; - prev_xfading = xfade; + prev_xfading = xfade_time; time = 0; switched = true; } @@ -753,9 +726,15 @@ double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_seek_ double rem = 0.0; + for (int i = 0; i < enabled_inputs; i++) { + if (i != current && i != prev) { + blend_input(i, p_time, p_seek, p_seek_root, 0, FILTER_IGNORE, sync); + } + } + if (prev < 0) { // process current animation, check for transition - rem = blend_input(current, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, false); + rem = blend_input(current, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, true); if (p_seek) { time = p_time; @@ -763,26 +742,29 @@ double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_seek_ time += p_time; } - if (inputs[current].auto_advance && rem <= xfade) { + if (inputs[current].auto_advance && rem <= xfade_time) { set_parameter(this->current, (current + 1) % enabled_inputs); } } else { // cross-fading from prev to current - float blend = xfade == 0 ? 0 : (prev_xfading / xfade); + real_t blend = xfade_time == 0 ? 0 : (prev_xfading / xfade_time); + if (xfade_curve.is_valid()) { + blend = xfade_curve->sample(blend); + } - if (!p_seek && switched) { //just switched, seek to start of current + if (from_start && !p_seek && switched) { //just switched, seek to start of current - rem = blend_input(current, 0, true, p_seek_root, 1.0 - blend, FILTER_IGNORE, false); + rem = blend_input(current, 0, true, p_seek_root, 1.0 - blend, FILTER_IGNORE, true); } else { - rem = blend_input(current, p_time, p_seek, p_seek_root, 1.0 - blend, FILTER_IGNORE, false); + rem = blend_input(current, p_time, p_seek, p_seek_root, 1.0 - blend, FILTER_IGNORE, true); } - if (p_seek) { // don't seek prev animation - blend_input(prev, 0, false, p_seek_root, blend, FILTER_IGNORE, false); + if (p_seek) { + blend_input(prev, p_time, true, p_seek_root, blend, FILTER_IGNORE, true); time = p_time; } else { - blend_input(prev, p_time, false, p_seek_root, blend, FILTER_IGNORE, false); + blend_input(prev, p_time, false, p_seek_root, blend, FILTER_IGNORE, true); time += p_time; prev_xfading -= p_time; if (prev_xfading < 0) { @@ -797,18 +779,16 @@ double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_seek_ return rem; } -void AnimationNodeTransition::_validate_property(PropertyInfo &property) const { - if (property.name.begins_with("input_")) { - String n = property.name.get_slicec('/', 0).get_slicec('_', 1); +void AnimationNodeTransition::_validate_property(PropertyInfo &p_property) const { + if (p_property.name.begins_with("input_")) { + String n = p_property.name.get_slicec('/', 0).get_slicec('_', 1); if (n != "count") { int idx = n.to_int(); if (idx >= enabled_inputs) { - property.usage = PROPERTY_USAGE_NONE; + p_property.usage = PROPERTY_USAGE_NONE; } } } - - AnimationNode::_validate_property(property); } void AnimationNodeTransition::_bind_methods() { @@ -821,11 +801,19 @@ void AnimationNodeTransition::_bind_methods() { ClassDB::bind_method(D_METHOD("set_input_caption", "input", "caption"), &AnimationNodeTransition::set_input_caption); ClassDB::bind_method(D_METHOD("get_input_caption", "input"), &AnimationNodeTransition::get_input_caption); - ClassDB::bind_method(D_METHOD("set_cross_fade_time", "time"), &AnimationNodeTransition::set_cross_fade_time); - ClassDB::bind_method(D_METHOD("get_cross_fade_time"), &AnimationNodeTransition::get_cross_fade_time); + ClassDB::bind_method(D_METHOD("set_xfade_time", "time"), &AnimationNodeTransition::set_xfade_time); + ClassDB::bind_method(D_METHOD("get_xfade_time"), &AnimationNodeTransition::get_xfade_time); + + ClassDB::bind_method(D_METHOD("set_xfade_curve", "curve"), &AnimationNodeTransition::set_xfade_curve); + ClassDB::bind_method(D_METHOD("get_xfade_curve"), &AnimationNodeTransition::get_xfade_curve); - ADD_PROPERTY(PropertyInfo(Variant::INT, "input_count", PROPERTY_HINT_RANGE, "0,64,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_enabled_inputs", "get_enabled_inputs"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "xfade_time", PROPERTY_HINT_RANGE, "0,120,0.01,suffix:s"), "set_cross_fade_time", "get_cross_fade_time"); + ClassDB::bind_method(D_METHOD("set_from_start", "from_start"), &AnimationNodeTransition::set_from_start); + ClassDB::bind_method(D_METHOD("is_from_start"), &AnimationNodeTransition::is_from_start); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "enabled_inputs", PROPERTY_HINT_RANGE, "0,64,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_enabled_inputs", "get_enabled_inputs"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "xfade_time", PROPERTY_HINT_RANGE, "0,120,0.01,suffix:s"), "set_xfade_time", "get_xfade_time"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "xfade_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_xfade_curve", "get_xfade_curve"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "from_start"), "set_from_start", "is_from_start"); for (int i = 0; i < MAX_INPUTS; i++) { ADD_PROPERTYI(PropertyInfo(Variant::STRING, "input_" + itos(i) + "/name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "set_input_caption", "get_input_caption", i); @@ -846,7 +834,7 @@ String AnimationNodeOutput::get_caption() const { } double AnimationNodeOutput::process(double p_time, bool p_seek, bool p_seek_root) { - return blend_input(0, p_time, p_seek, p_seek_root, 1.0); + return blend_input(0, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, true); } AnimationNodeOutput::AnimationNodeOutput() { @@ -869,8 +857,8 @@ void AnimationNodeBlendTree::add_node(const StringName &p_name, Ref<AnimationNod emit_changed(); emit_signal(SNAME("tree_changed")); - p_node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendTree::_tree_changed), varray(), CONNECT_REFERENCE_COUNTED); - p_node->connect("changed", callable_mp(this, &AnimationNodeBlendTree::_node_changed), varray(p_name), CONNECT_REFERENCE_COUNTED); + p_node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendTree::_tree_changed), CONNECT_REFERENCE_COUNTED); + p_node->connect("changed", callable_mp(this, &AnimationNodeBlendTree::_node_changed).bind(p_name), CONNECT_REFERENCE_COUNTED); } Ref<AnimationNode> AnimationNodeBlendTree::get_node(const StringName &p_name) const { @@ -970,7 +958,7 @@ void AnimationNodeBlendTree::rename_node(const StringName &p_name, const StringN } } //connection must be done with new name - nodes[p_new_name].node->connect("changed", callable_mp(this, &AnimationNodeBlendTree::_node_changed), varray(p_new_name), CONNECT_REFERENCE_COUNTED); + nodes[p_new_name].node->connect("changed", callable_mp(this, &AnimationNodeBlendTree::_node_changed).bind(p_new_name), CONNECT_REFERENCE_COUNTED); emit_signal(SNAME("tree_changed")); } @@ -1060,7 +1048,7 @@ String AnimationNodeBlendTree::get_caption() const { double AnimationNodeBlendTree::process(double p_time, bool p_seek, bool p_seek_root) { Ref<AnimationNodeOutput> output = nodes[SceneStringNames::get_singleton()->output].node; - return _blend_node("output", nodes[SceneStringNames::get_singleton()->output].connections, this, output, p_time, p_seek, p_seek_root, 1.0); + return _blend_node("output", nodes[SceneStringNames::get_singleton()->output].connections, this, output, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, true); } void AnimationNodeBlendTree::get_node_list(List<StringName> *r_list) { @@ -1187,6 +1175,7 @@ void AnimationNodeBlendTree::_tree_changed() { void AnimationNodeBlendTree::_node_changed(const StringName &p_node) { ERR_FAIL_COND(!nodes.has(p_node)); nodes[p_node].connections.resize(nodes[p_node].node->get_input_count()); + emit_signal(SNAME("node_changed"), p_node); } void AnimationNodeBlendTree::_bind_methods() { @@ -1212,6 +1201,8 @@ void AnimationNodeBlendTree::_bind_methods() { BIND_CONSTANT(CONNECTION_ERROR_NO_OUTPUT); BIND_CONSTANT(CONNECTION_ERROR_SAME_NODE); BIND_CONSTANT(CONNECTION_ERROR_CONNECTION_EXISTS); + + ADD_SIGNAL(MethodInfo("node_changed", PropertyInfo(Variant::STRING_NAME, "node_name"))); } void AnimationNodeBlendTree::_initialize_node_tree() { diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h index 0a2305b8d6..1c31718259 100644 --- a/scene/animation/animation_blend_tree.h +++ b/scene/animation/animation_blend_tree.h @@ -67,7 +67,7 @@ public: AnimationNodeAnimation(); protected: - void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; static void _bind_methods(); private: @@ -77,8 +77,23 @@ private: VARIANT_ENUM_CAST(AnimationNodeAnimation::PlayMode) -class AnimationNodeOneShot : public AnimationNode { - GDCLASS(AnimationNodeOneShot, AnimationNode); +class AnimationNodeSync : public AnimationNode { + GDCLASS(AnimationNodeSync, AnimationNode); + +protected: + bool sync = false; + + static void _bind_methods(); + +public: + void set_use_sync(bool p_sync); + bool is_using_sync() const; + + AnimationNodeSync(); +}; + +class AnimationNodeOneShot : public AnimationNodeSync { + GDCLASS(AnimationNodeOneShot, AnimationNodeSync); public: enum MixMode { @@ -87,20 +102,18 @@ public: }; private: - float fade_in = 0.0; - float fade_out = 0.0; + double fade_in = 0.0; + double fade_out = 0.0; bool autorestart = false; - float autorestart_delay = 1.0; - float autorestart_random_delay = 0.0; + double autorestart_delay = 1.0; + double autorestart_random_delay = 0.0; MixMode mix = MIX_MODE_BLEND; - bool sync = false; - /* bool active; bool do_start; - float time; - float remaining;*/ + double time; + double remaining;*/ StringName active = PNAME("active"); StringName prev_active = "prev_active"; @@ -117,26 +130,23 @@ public: virtual String get_caption() const override; - void set_fadein_time(float p_time); - void set_fadeout_time(float p_time); + void set_fadein_time(double p_time); + void set_fadeout_time(double p_time); - float get_fadein_time() const; - float get_fadeout_time() const; + double get_fadein_time() const; + double get_fadeout_time() const; void set_autorestart(bool p_active); - void set_autorestart_delay(float p_time); - void set_autorestart_random_delay(float p_time); + void set_autorestart_delay(double p_time); + void set_autorestart_random_delay(double p_time); bool has_autorestart() const; - float get_autorestart_delay() const; - float get_autorestart_random_delay() const; + double get_autorestart_delay() const; + double get_autorestart_random_delay() const; void set_mix_mode(MixMode p_mix); MixMode get_mix_mode() const; - void set_use_sync(bool p_sync); - bool is_using_sync() const; - virtual bool has_filter() const override; virtual double process(double p_time, bool p_seek, bool p_seek_root) override; @@ -145,11 +155,10 @@ public: VARIANT_ENUM_CAST(AnimationNodeOneShot::MixMode) -class AnimationNodeAdd2 : public AnimationNode { - GDCLASS(AnimationNodeAdd2, AnimationNode); +class AnimationNodeAdd2 : public AnimationNodeSync { + GDCLASS(AnimationNodeAdd2, AnimationNodeSync); StringName add_amount = PNAME("add_amount"); - bool sync = false; protected: static void _bind_methods(); @@ -160,20 +169,16 @@ public: virtual String get_caption() const override; - void set_use_sync(bool p_sync); - bool is_using_sync() const; - virtual bool has_filter() const override; virtual double process(double p_time, bool p_seek, bool p_seek_root) override; AnimationNodeAdd2(); }; -class AnimationNodeAdd3 : public AnimationNode { - GDCLASS(AnimationNodeAdd3, AnimationNode); +class AnimationNodeAdd3 : public AnimationNodeSync { + GDCLASS(AnimationNodeAdd3, AnimationNodeSync); StringName add_amount = PNAME("add_amount"); - bool sync = false; protected: static void _bind_methods(); @@ -184,20 +189,16 @@ public: virtual String get_caption() const override; - void set_use_sync(bool p_sync); - bool is_using_sync() const; - virtual bool has_filter() const override; virtual double process(double p_time, bool p_seek, bool p_seek_root) override; AnimationNodeAdd3(); }; -class AnimationNodeBlend2 : public AnimationNode { - GDCLASS(AnimationNodeBlend2, AnimationNode); +class AnimationNodeBlend2 : public AnimationNodeSync { + GDCLASS(AnimationNodeBlend2, AnimationNodeSync); StringName blend_amount = PNAME("blend_amount"); - bool sync = false; protected: static void _bind_methods(); @@ -209,18 +210,14 @@ public: virtual String get_caption() const override; virtual double process(double p_time, bool p_seek, bool p_seek_root) override; - void set_use_sync(bool p_sync); - bool is_using_sync() const; - virtual bool has_filter() const override; AnimationNodeBlend2(); }; -class AnimationNodeBlend3 : public AnimationNode { - GDCLASS(AnimationNodeBlend3, AnimationNode); +class AnimationNodeBlend3 : public AnimationNodeSync { + GDCLASS(AnimationNodeBlend3, AnimationNodeSync); StringName blend_amount = PNAME("blend_amount"); - bool sync; protected: static void _bind_methods(); @@ -231,9 +228,6 @@ public: virtual String get_caption() const override; - void set_use_sync(bool p_sync); - bool is_using_sync() const; - double process(double p_time, bool p_seek, bool p_seek_root) override; AnimationNodeBlend3(); }; @@ -276,8 +270,8 @@ public: AnimationNodeTimeSeek(); }; -class AnimationNodeTransition : public AnimationNode { - GDCLASS(AnimationNodeTransition, AnimationNode); +class AnimationNodeTransition : public AnimationNodeSync { + GDCLASS(AnimationNodeTransition, AnimationNodeSync); enum { MAX_INPUTS = 32 @@ -291,9 +285,9 @@ class AnimationNodeTransition : public AnimationNode { int enabled_inputs = 0; /* - float prev_xfading; + double prev_xfading; int prev; - float time; + double time; int current; int prev_current; */ @@ -303,13 +297,15 @@ class AnimationNodeTransition : public AnimationNode { StringName current = PNAME("current"); StringName prev_current = "prev_current"; - float xfade = 0.0; + double xfade_time = 0.0; + Ref<Curve> xfade_curve; + bool from_start = true; void _update_inputs(); protected: static void _bind_methods(); - void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; public: virtual void get_parameter_list(List<PropertyInfo> *r_list) const override; @@ -326,8 +322,14 @@ public: void set_input_caption(int p_input, const String &p_name); String get_input_caption(int p_input) const; - void set_cross_fade_time(float p_fade); - float get_cross_fade_time() const; + void set_xfade_time(double p_fade); + double get_xfade_time() const; + + void set_xfade_curve(const Ref<Curve> &p_curve); + Ref<Curve> get_xfade_curve() const; + + void set_from_start(bool p_from_start); + bool is_from_start() const; double process(double p_time, bool p_seek, bool p_seek_root) override; diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp index 2ee7f4fa43..facffb99ee 100644 --- a/scene/animation/animation_node_state_machine.cpp +++ b/scene/animation/animation_node_state_machine.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "animation_node_state_machine.h" +#include "scene/main/window.h" ///////////////////////////////////////////////// @@ -98,12 +99,20 @@ NodePath AnimationNodeStateMachineTransition::get_advance_expression_base_node() void AnimationNodeStateMachineTransition::set_xfade_time(float p_xfade) { ERR_FAIL_COND(p_xfade < 0); - xfade = p_xfade; + xfade_time = p_xfade; emit_changed(); } float AnimationNodeStateMachineTransition::get_xfade_time() const { - return xfade; + return xfade_time; +} + +void AnimationNodeStateMachineTransition::set_xfade_curve(const Ref<Curve> &p_curve) { + xfade_curve = p_curve; +} + +Ref<Curve> AnimationNodeStateMachineTransition::get_xfade_curve() const { + return xfade_curve; } void AnimationNodeStateMachineTransition::set_disabled(bool p_disabled) { @@ -137,6 +146,9 @@ void AnimationNodeStateMachineTransition::_bind_methods() { ClassDB::bind_method(D_METHOD("set_xfade_time", "secs"), &AnimationNodeStateMachineTransition::set_xfade_time); ClassDB::bind_method(D_METHOD("get_xfade_time"), &AnimationNodeStateMachineTransition::get_xfade_time); + ClassDB::bind_method(D_METHOD("set_xfade_curve", "curve"), &AnimationNodeStateMachineTransition::set_xfade_curve); + ClassDB::bind_method(D_METHOD("get_xfade_curve"), &AnimationNodeStateMachineTransition::get_xfade_curve); + ClassDB::bind_method(D_METHOD("set_disabled", "disabled"), &AnimationNodeStateMachineTransition::set_disabled); ClassDB::bind_method(D_METHOD("is_disabled"), &AnimationNodeStateMachineTransition::is_disabled); @@ -150,6 +162,7 @@ void AnimationNodeStateMachineTransition::_bind_methods() { ClassDB::bind_method(D_METHOD("get_advance_expression_base_node"), &AnimationNodeStateMachineTransition::get_advance_expression_base_node); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "xfade_time", PROPERTY_HINT_RANGE, "0,240,0.01,suffix:s"), "set_xfade_time", "get_xfade_time"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "xfade_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_xfade_curve", "get_xfade_curve"); ADD_PROPERTY(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,32,1"), "set_priority", "get_priority"); ADD_GROUP("Switch", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "switch_mode", PROPERTY_HINT_ENUM, "Immediate,Sync,At End"), "set_switch_mode", "get_switch_mode"); @@ -157,7 +170,7 @@ void AnimationNodeStateMachineTransition::_bind_methods() { ADD_GROUP("Advance", "advance_"); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "advance_condition"), "set_advance_condition", "get_advance_condition"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "advance_expression", PROPERTY_HINT_EXPRESSION, ""), "set_advance_expression", "get_advance_expression"); - ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "advance_expression_base_node"), "set_advance_expression_base_node", "get_advance_expression_base_node"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "advance_expression_base_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node"), "set_advance_expression_base_node", "get_advance_expression_base_node"); ADD_GROUP("Disabling", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled"); @@ -332,6 +345,15 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_sta } double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek, bool p_seek_root) { + if (p_time == -1) { + Ref<AnimationNodeStateMachine> anodesm = p_state_machine->states[current].node; + if (anodesm.is_valid()) { + p_state_machine->blend_node(current, p_state_machine->states[current].node, -1, p_seek, p_seek_root, 0, AnimationNode::FILTER_IGNORE, true); + } + playing = false; + return 0; + } + //if not playing and it can restart, then restart if (!playing && start_request == StringName()) { if (!stop_request && p_state_machine->start_node) { @@ -391,11 +413,11 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s bool do_start = (p_seek && p_time == 0) || play_start || current == StringName(); if (do_start) { - if (p_state_machine->start_node != StringName() && p_seek && p_time == 0) { + if (p_state_machine->start_node != StringName() && p_seek && p_time == 0 && current == StringName()) { current = p_state_machine->start_node; } - len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_seek_root, 1.0, AnimationNode::FILTER_IGNORE, false); + len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_seek_root, 1.0, AnimationNode::FILTER_IGNORE, true); pos_current = 0; } @@ -420,10 +442,13 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s } } - float rem = p_state_machine->blend_node(current, p_state_machine->states[current].node, p_time, p_seek, p_seek_root, fade_blend, AnimationNode::FILTER_IGNORE, false); + if (current_curve.is_valid()) { + fade_blend = current_curve->sample(fade_blend); + } + float rem = p_state_machine->blend_node(current, p_state_machine->states[current].node, p_time, p_seek, p_seek_root, fade_blend, AnimationNode::FILTER_IGNORE, true); if (fading_from != StringName()) { - p_state_machine->blend_node(fading_from, p_state_machine->states[fading_from].node, p_time, p_seek, p_seek_root, 1.0 - fade_blend, AnimationNode::FILTER_IGNORE, false); + p_state_machine->blend_node(fading_from, p_state_machine->states[fading_from].node, p_time, p_seek, p_seek_root, 1.0 - fade_blend, AnimationNode::FILTER_IGNORE, true); } //guess playback position @@ -450,6 +475,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s if (p_state_machine->transitions[i].local_from == current && p_state_machine->transitions[i].local_to == path[0]) { next_xfade = p_state_machine->transitions[i].transition->get_xfade_time(); + current_curve = p_state_machine->transitions[i].transition->get_xfade_curve(); switch_mode = p_state_machine->transitions[i].transition->get_switch_mode(); next = path[0]; } @@ -475,7 +501,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s // handles start_node: if previous state machine is pointing to a node inside the current state machine, starts the current machine from start_node to prev_local_to if (p_state_machine->start_node == current && p_state_machine->transitions[i].local_from == current) { if (p_state_machine->prev_state_machine != nullptr) { - Ref<AnimationNodeStateMachinePlayback> prev_playback = p_state_machine->prev_state_machine->get_parameter("playback"); + Ref<AnimationNodeStateMachinePlayback> prev_playback = p_state_machine->prev_state_machine->get_parameter(p_state_machine->playback); if (prev_playback.is_valid()) { StringName prev_local_to = String(prev_playback->current_transition.next).replace_first(String(p_state_machine->state_machine_name) + "/", ""); @@ -504,6 +530,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s tr.to = String(p_state_machine->transitions[auto_advance_to].to).replace_first("../", ""); tr.next = p_state_machine->transitions[auto_advance_to].to; current_transition = tr; + current_curve = p_state_machine->transitions[auto_advance_to].transition->get_xfade_curve(); next_xfade = p_state_machine->transitions[auto_advance_to].transition->get_xfade_time(); switch_mode = p_state_machine->transitions[auto_advance_to].transition->get_switch_mode(); } @@ -513,7 +540,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s AnimationNodeStateMachine *prev_state_machine = p_state_machine->prev_state_machine; if (prev_state_machine != nullptr) { - Ref<AnimationNodeStateMachinePlayback> prev_playback = prev_state_machine->get_parameter("playback"); + Ref<AnimationNodeStateMachinePlayback> prev_playback = prev_state_machine->get_parameter(p_state_machine->playback); if (prev_playback.is_valid()) { if (next_xfade) { @@ -561,7 +588,6 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s } if (goto_next) { //end_loop should be used because fade time may be too small or zero and animation may have looped - if (next_xfade) { //time to fade, baby fading_from = current; @@ -575,14 +601,23 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s if (path.size()) { //if it came from path, remove path path.remove_at(0); } + + { // if the current node is a state machine, update the "playing" variable to false by passing -1 in p_time + Ref<AnimationNodeStateMachine> anodesm = p_state_machine->states[current].node; + if (anodesm.is_valid()) { + p_state_machine->blend_node(current, p_state_machine->states[current].node, -1, p_seek, p_seek_root, 0, AnimationNode::FILTER_IGNORE, true); + } + } + current = next; + if (switch_mode == AnimationNodeStateMachineTransition::SWITCH_MODE_SYNC) { - len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_seek_root, 0, AnimationNode::FILTER_IGNORE, false); + len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_seek_root, 0, AnimationNode::FILTER_IGNORE, true); pos_current = MIN(pos_current, len_current); - p_state_machine->blend_node(current, p_state_machine->states[current].node, pos_current, true, p_seek_root, 0, AnimationNode::FILTER_IGNORE, false); + p_state_machine->blend_node(current, p_state_machine->states[current].node, pos_current, true, p_seek_root, 0, AnimationNode::FILTER_IGNORE, true); } else { - len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_seek_root, 0, AnimationNode::FILTER_IGNORE, false); + len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_seek_root, 0, AnimationNode::FILTER_IGNORE, true); pos_current = 0; } @@ -590,15 +625,16 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s } } - // time left must always be 1 because the end node don't length to compute - if (p_state_machine->end_node != current) { - rem = 1; + if (current != p_state_machine->end_node) { + rem = 1; // the time remaining must always be 1 because there is no way to predict how long it takes for the entire state machine to complete } else { - Ref<AnimationNodeStateMachinePlayback> prev_playback = p_state_machine->prev_state_machine->get_parameter("playback"); + if (p_state_machine->prev_state_machine != nullptr) { + Ref<AnimationNodeStateMachinePlayback> prev_playback = p_state_machine->prev_state_machine->get_parameter(p_state_machine->playback); - if (prev_playback.is_valid()) { - prev_playback->current_transition = current_transition; - prev_playback->force_auto_advance = true; + if (prev_playback.is_valid()) { + prev_playback->current_transition = current_transition; + prev_playback->force_auto_advance = true; + } } } @@ -621,16 +657,18 @@ bool AnimationNodeStateMachinePlayback::_check_advance_condition(const Ref<Anima ERR_FAIL_COND_V(tree_base == nullptr, false); NodePath advance_expression_base_node_path; - if (!transition->advance_expression_base_node.is_empty()) { - advance_expression_base_node_path = transition->advance_expression_base_node; + Node *expression_base = nullptr; + if (!transition->get_advance_expression_base_node().is_empty()) { + advance_expression_base_node_path = transition->get_advance_expression_base_node(); + expression_base = tree_base->get_tree()->get_root()->get_child(0)->get_node_or_null(advance_expression_base_node_path); } else { advance_expression_base_node_path = tree_base->get_advance_expression_base_node(); + expression_base = tree_base->get_node_or_null(advance_expression_base_node_path); } - Node *expression_base = tree_base->get_node_or_null(advance_expression_base_node_path); if (expression_base) { Ref<Expression> exp = transition->expression; - bool ret = exp->execute(Array(), tree_base, false, Engine::get_singleton()->is_editor_hint()); // Avoids allowing the user to crash the system with an expression by only allowing const calls. + bool ret = exp->execute(Array(), expression_base, false, Engine::get_singleton()->is_editor_hint()); // Avoids allowing the user to crash the system with an expression by only allowing const calls. if (!exp->has_execute_failed()) { if (ret) { return true; @@ -673,23 +711,6 @@ void AnimationNodeStateMachine::get_parameter_list(List<PropertyInfo> *r_list) c for (const StringName &E : advance_conditions) { r_list->push_back(PropertyInfo(Variant::BOOL, E)); } - - // for (const KeyValue<StringName, State> &E : states) { - // if (E->node == ansm) { - // for (int i = 0; i < E->node->transitions.size(); i++) { - // StringName ac = E->node->transitions[i].transition->get_advance_condition_name(); - // if (ac != StringName() && advance_conditions.find(ac) == nullptr) { - // advance_conditions.push_back(ac); - // } - // } - - // advance_conditions.sort_custom<StringName::AlphCompare>(); - - // for (const StringName &E : advance_conditions) { - // r_list->push_back(PropertyInfo(Variant::BOOL, E)); - // } - // } - // } } Variant AnimationNodeStateMachine::get_parameter_default_value(const StringName &p_parameter) const { @@ -723,7 +744,7 @@ void AnimationNodeStateMachine::add_node(const StringName &p_name, Ref<Animation emit_changed(); emit_signal(SNAME("tree_changed")); - p_node->connect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed), varray(), CONNECT_REFERENCE_COUNTED); + p_node->connect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed), CONNECT_REFERENCE_COUNTED); } void AnimationNodeStateMachine::replace_node(const StringName &p_name, Ref<AnimationNode> p_node) { @@ -743,7 +764,7 @@ void AnimationNodeStateMachine::replace_node(const StringName &p_name, Ref<Anima emit_changed(); emit_signal(SNAME("tree_changed")); - p_node->connect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed), varray(), CONNECT_REFERENCE_COUNTED); + p_node->connect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed), CONNECT_REFERENCE_COUNTED); } bool AnimationNodeStateMachine::can_edit_node(const StringName &p_name) const { @@ -830,20 +851,12 @@ void AnimationNodeStateMachine::rename_node(const StringName &p_name, const Stri anodesm->state_machine_name = p_new_name; } - for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].local_from == p_name) { - _rename_transition(transitions[i].from, String(transitions[i].from).replace_first(p_name, p_new_name)); - } - - if (transitions[i].local_to == p_name) { - _rename_transition(transitions[i].to, String(transitions[i].to).replace_first(p_name, p_new_name)); - } - } + _rename_transitions(p_name, p_new_name); emit_signal("tree_changed"); } -void AnimationNodeStateMachine::_rename_transition(const StringName &p_name, const StringName &p_new_name) { +void AnimationNodeStateMachine::_rename_transitions(const StringName &p_name, const StringName &p_new_name) { if (updating_transitions) { return; } @@ -854,12 +867,16 @@ void AnimationNodeStateMachine::_rename_transition(const StringName &p_name, con Vector<String> path = String(transitions[i].to).split("/"); if (path.size() > 1) { if (path[0] == "..") { - prev_state_machine->_rename_transition(String(state_machine_name) + "/" + p_name, String(state_machine_name) + "/" + p_new_name); + prev_state_machine->_rename_transitions(String(state_machine_name) + "/" + p_name, String(state_machine_name) + "/" + p_new_name); } else { - ((Ref<AnimationNodeStateMachine>)states[transitions[i].local_to].node)->_rename_transition("../" + p_name, "../" + p_new_name); + ((Ref<AnimationNodeStateMachine>)states[transitions[i].local_to].node)->_rename_transitions("../" + p_name, "../" + p_new_name); } } + if (transitions[i].local_from == p_name) { + transitions.write[i].local_from = p_new_name; + } + transitions.write[i].from = p_new_name; } @@ -867,12 +884,16 @@ void AnimationNodeStateMachine::_rename_transition(const StringName &p_name, con Vector<String> path = String(transitions[i].from).split("/"); if (path.size() > 1) { if (path[0] == "..") { - prev_state_machine->_rename_transition(String(state_machine_name) + "/" + p_name, String(state_machine_name) + "/" + p_new_name); + prev_state_machine->_rename_transitions(String(state_machine_name) + "/" + p_name, String(state_machine_name) + "/" + p_new_name); } else { - ((Ref<AnimationNodeStateMachine>)states[transitions[i].local_from].node)->_rename_transition("../" + p_name, "../" + p_new_name); + ((Ref<AnimationNodeStateMachine>)states[transitions[i].local_from].node)->_rename_transitions("../" + p_name, "../" + p_new_name); } } + if (transitions[i].local_to == p_name) { + transitions.write[i].local_to = p_new_name; + } + transitions.write[i].to = p_new_name; } @@ -883,10 +904,6 @@ void AnimationNodeStateMachine::_rename_transition(const StringName &p_name, con void AnimationNodeStateMachine::get_node_list(List<StringName> *r_nodes) const { List<StringName> nodes; for (const KeyValue<StringName, State> &E : states) { - if (E.key == end_node && prev_state_machine == nullptr) { - continue; - } - nodes.push_back(E.key); } nodes.sort_custom<StringName::AlphCompare>(); @@ -1032,7 +1049,7 @@ void AnimationNodeStateMachine::add_transition(const StringName &p_from, const S tr.local_to = local_to; tr.transition = p_transition; - tr.transition->connect("advance_condition_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed), varray(), CONNECT_REFERENCE_COUNTED); + tr.transition->connect("advance_condition_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed), CONNECT_REFERENCE_COUNTED); transitions.push_back(tr); diff --git a/scene/animation/animation_node_state_machine.h b/scene/animation/animation_node_state_machine.h index d4e58ca3d3..ab78b1afe8 100644 --- a/scene/animation/animation_node_state_machine.h +++ b/scene/animation/animation_node_state_machine.h @@ -49,7 +49,8 @@ private: bool auto_advance = false; StringName advance_condition; StringName advance_condition_name; - float xfade = 0.0; + float xfade_time = 0.0; + Ref<Curve> xfade_curve; bool disabled = false; int priority = 1; String advance_expression; @@ -82,6 +83,9 @@ public: void set_xfade_time(float p_xfade); float get_xfade_time() const; + void set_xfade_curve(const Ref<Curve> &p_curve); + Ref<Curve> get_xfade_curve() const; + void set_disabled(bool p_disabled); bool is_disabled() const; @@ -117,6 +121,7 @@ class AnimationNodeStateMachinePlayback : public Resource { StringName current; Transition current_transition; + Ref<Curve> current_curve; bool force_auto_advance = false; StringName fading_from; @@ -185,7 +190,7 @@ private: void _tree_changed(); void _remove_transition(const Ref<AnimationNodeStateMachineTransition> p_transition); - void _rename_transition(const StringName &p_name, const StringName &p_new_name); + void _rename_transitions(const StringName &p_name, const StringName &p_new_name); bool _can_connect(const StringName &p_name, Vector<AnimationNodeStateMachine *> p_parents = Vector<AnimationNodeStateMachine *>()); StringName _get_shortest_path(const StringName &p_path) const; diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index 2e87dbf9da..54b10d9d57 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -37,6 +37,7 @@ #ifdef TOOLS_ENABLED #include "editor/editor_node.h" +#include "editor/editor_undo_redo_manager.h" #include "scene/2d/skeleton_2d.h" void AnimatedValuesBackup::update_skeletons() { @@ -64,7 +65,7 @@ void AnimatedValuesBackup::restore() const { if (arr.size() == 3) { Object::cast_to<Skeleton3D>(entry->object)->set_bone_pose_position(entry->bone_idx, arr[0]); Object::cast_to<Skeleton3D>(entry->object)->set_bone_pose_rotation(entry->bone_idx, arr[1]); - Object::cast_to<Skeleton3D>(entry->object)->set_bone_pose_scale(entry->bone_idx, arr[0]); + Object::cast_to<Skeleton3D>(entry->object)->set_bone_pose_scale(entry->bone_idx, arr[2]); } } } @@ -155,7 +156,7 @@ bool AnimationPlayer::_get(const StringName &p_name, Variant &r_ret) const { } else if (name == "blend_times") { Vector<BlendKey> keys; - for (const KeyValue<BlendKey, float> &E : blend_times) { + for (const KeyValue<BlendKey, double> &E : blend_times) { keys.ordered_insert(E.key); } @@ -174,8 +175,8 @@ bool AnimationPlayer::_get(const StringName &p_name, Variant &r_ret) const { return true; } -void AnimationPlayer::_validate_property(PropertyInfo &property) const { - if (property.name == "current_animation") { +void AnimationPlayer::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "current_animation") { List<String> names; for (const KeyValue<StringName, AnimationData> &E : animation_set) { @@ -191,10 +192,8 @@ void AnimationPlayer::_validate_property(PropertyInfo &property) const { hint += E->get(); } - property.hint_string = hint; + p_property.hint_string = hint; } - - Node::_validate_property(property); } void AnimationPlayer::_get_property_list(List<PropertyInfo> *p_list) const { @@ -217,7 +216,7 @@ void AnimationPlayer::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::ARRAY, "blend_times", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL)); } -void AnimationPlayer::advance(float p_time) { +void AnimationPlayer::advance(double p_time) { _animation_process(p_time); } @@ -323,10 +322,8 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov #endif // _3D_DISABLED - { - if (!child->is_connected("tree_exiting", callable_mp(this, &AnimationPlayer::_node_removed))) { - child->connect("tree_exiting", callable_mp(this, &AnimationPlayer::_node_removed), make_binds(child), CONNECT_ONESHOT); - } + if (!child->is_connected("tree_exiting", callable_mp(this, &AnimationPlayer::_node_removed))) { + child->connect("tree_exiting", callable_mp(this, &AnimationPlayer::_node_removed).bind(child), CONNECT_ONE_SHOT); } TrackNodeCacheKey key; @@ -370,8 +367,12 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov node_cache->node_3d = nullptr; ERR_CONTINUE(node_cache->bone_idx < 0); } + Transform3D rest = node_cache->skeleton->get_bone_rest(bone_idx); + node_cache->init_loc = rest.origin; + node_cache->init_rot = rest.basis.get_rotation_quaternion(); + node_cache->init_scale = rest.basis.get_scale(); } else { - // no property, just use spatialnode + // Not a skeleton, the node can be accessed with the node_3d member. node_cache->skeleton = nullptr; } } @@ -450,6 +451,23 @@ static void _call_object(Object *p_object, const StringName &p_method, const Vec } } +Variant AnimationPlayer::_post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx) { + switch (p_anim->track_get_type(p_track)) { +#ifndef _3D_DISABLED + case Animation::TYPE_POSITION_3D: { + if (p_object_idx >= 0) { + const Skeleton3D *skel = Object::cast_to<Skeleton3D>(p_object); + return Vector3(p_value) * skel->get_motion_scale(); + } + return p_value; + } break; +#endif // _3D_DISABLED + default: { + } break; + } + return p_value; +} + void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double p_time, double p_delta, float p_interp, bool p_is_current, bool p_seeked, bool p_started, int p_pingponged) { _ensure_node_caches(p_anim); ERR_FAIL_COND(p_anim->node_cache.size() != p_anim->animation->get_track_count()); @@ -494,14 +512,15 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double if (err != OK) { continue; } + loc = _post_process_key_value(a, i, loc, nc->node_3d, nc->bone_idx); if (nc->accum_pass != accum_pass) { ERR_CONTINUE(cache_update_size >= NODE_CACHE_UPDATE_MAX); cache_update[cache_update_size++] = nc; nc->accum_pass = accum_pass; nc->loc_accum = loc; - nc->rot_accum = Quaternion(); - nc->scale_accum = Vector3(); + nc->rot_accum = nc->init_rot; + nc->scale_accum = nc->init_scale; } else { nc->loc_accum = nc->loc_accum.lerp(loc, p_interp); } @@ -521,14 +540,15 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double if (err != OK) { continue; } + rot = _post_process_key_value(a, i, rot, nc->node_3d, nc->bone_idx); if (nc->accum_pass != accum_pass) { ERR_CONTINUE(cache_update_size >= NODE_CACHE_UPDATE_MAX); cache_update[cache_update_size++] = nc; nc->accum_pass = accum_pass; - nc->loc_accum = Vector3(); + nc->loc_accum = nc->init_loc; nc->rot_accum = rot; - nc->scale_accum = Vector3(); + nc->scale_accum = nc->init_scale; } else { nc->rot_accum = nc->rot_accum.slerp(rot, p_interp); } @@ -548,13 +568,14 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double if (err != OK) { continue; } + scale = _post_process_key_value(a, i, scale, nc->node_3d, nc->bone_idx); if (nc->accum_pass != accum_pass) { ERR_CONTINUE(cache_update_size >= NODE_CACHE_UPDATE_MAX); cache_update[cache_update_size++] = nc; nc->accum_pass = accum_pass; - nc->loc_accum = Vector3(); - nc->rot_accum = Quaternion(); + nc->loc_accum = nc->init_loc; + nc->rot_accum = nc->init_rot; nc->scale_accum = scale; } else { nc->scale_accum = nc->scale_accum.lerp(scale, p_interp); @@ -575,6 +596,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double if (err != OK) { continue; } + blend = _post_process_key_value(a, i, blend, nc->node_blend_shape, nc->blend_shape_idx); if (nc->accum_pass != accum_pass) { ERR_CONTINUE(cache_update_size >= NODE_CACHE_UPDATE_MAX); @@ -627,16 +649,15 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double if (p_time < first_key_time) { double c = Math::ease(p_time / first_key_time, transition); Variant first_value = a->track_get_key_value(i, first_key); - Variant interp_value; - Variant::interpolate(pa->capture, first_value, c, interp_value); - + first_value = _post_process_key_value(a, i, first_value, nc->node); + Variant interp_value = Animation::interpolate_variant(pa->capture, first_value, c); if (pa->accum_pass != accum_pass) { ERR_CONTINUE(cache_update_prop_size >= NODE_CACHE_UPDATE_MAX); cache_update_prop[cache_update_prop_size++] = pa; pa->value_accum = interp_value; pa->accum_pass = accum_pass; } else { - Variant::interpolate(pa->value_accum, interp_value, p_interp, pa->value_accum); + pa->value_accum = Animation::interpolate_variant(pa->value_accum, interp_value, p_interp); } continue; //handled @@ -649,6 +670,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double if (value == Variant()) { continue; } + value = _post_process_key_value(a, i, value, nc->node); if (pa->accum_pass != accum_pass) { ERR_CONTINUE(cache_update_prop_size >= NODE_CACHE_UPDATE_MAX); @@ -656,7 +678,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double pa->value_accum = value; pa->accum_pass = accum_pass; } else { - Variant::interpolate(pa->value_accum, value, p_interp, pa->value_accum); + pa->value_accum = Animation::interpolate_variant(pa->value_accum, value, p_interp); } } else if (p_is_current && p_delta != 0) { @@ -665,6 +687,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double for (int &F : indices) { Variant value = a->track_get_key_value(i, F); + value = _post_process_key_value(a, i, value, nc->node); switch (pa->special) { case SP_NONE: { bool valid; @@ -749,6 +772,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double TrackNodeCache::BezierAnim *ba = &E->value; real_t bezier = a->bezier_track_interpolate(i, p_time); + bezier = _post_process_key_value(a, i, bezier, nc->node); if (ba->accum_pass != accum_pass) { ERR_CONTINUE(cache_update_bezier_size >= NODE_CACHE_UPDATE_MAX); cache_update_bezier[cache_update_bezier_size++] = ba; @@ -1126,7 +1150,7 @@ void AnimationPlayer::_animation_update_transforms() { } #endif - static_cast<Node2D *>(pa->object)->set_rotation(Math::deg2rad((double)pa->value_accum)); + static_cast<Node2D *>(pa->object)->set_rotation(Math::deg_to_rad((double)pa->value_accum)); } break; case SP_NODE2D_SCALE: { #ifdef DEBUG_ENABLED @@ -1173,11 +1197,15 @@ void AnimationPlayer::_animation_process(double p_delta) { emit_signal(SceneStringNames::get_singleton()->animation_changed, old, new_name); } } else { - //stop(); playing = false; _set_process(false); if (end_notify) { emit_signal(SceneStringNames::get_singleton()->animation_finished, playback.assigned); + + if (movie_quit_on_finish && OS::get_singleton()->has_feature("movie")) { + print_line(vformat("Movie Maker mode is enabled. Quitting on animation finish as requested by: %s", get_path())); + get_tree()->quit(); + } } } end_reached = false; @@ -1241,6 +1269,8 @@ void AnimationPlayer::_animation_set_cache_update() { // If something was modified or removed, caches need to be cleared clear_caches(); } + + emit_signal(SNAME("animation_list_changed")); } void AnimationPlayer::_animation_added(const StringName &p_name, const StringName &p_library) { @@ -1257,7 +1287,7 @@ void AnimationPlayer::_animation_removed(const StringName &p_name, const StringN // Erase blends if needed List<BlendKey> to_erase; - for (const KeyValue<BlendKey, float> &E : blend_times) { + for (const KeyValue<BlendKey, double> &E : blend_times) { BlendKey bk = E.key; if (bk.from == name || bk.to == name) { to_erase.push_back(bk); @@ -1273,8 +1303,8 @@ void AnimationPlayer::_animation_removed(const StringName &p_name, const StringN void AnimationPlayer::_rename_animation(const StringName &p_from_name, const StringName &p_to_name) { // Rename autoplay or blends if needed. List<BlendKey> to_erase; - HashMap<BlendKey, float, BlendKey> to_insert; - for (const KeyValue<BlendKey, float> &E : blend_times) { + HashMap<BlendKey, double, BlendKey> to_insert; + for (const KeyValue<BlendKey, double> &E : blend_times) { BlendKey bk = E.key; BlendKey new_bk = bk; bool erase = false; @@ -1345,9 +1375,9 @@ Error AnimationPlayer::add_animation_library(const StringName &p_name, const Ref animation_libraries.insert(insert_pos, ald); - ald.library->connect(SNAME("animation_added"), callable_mp(this, &AnimationPlayer::_animation_added), varray(p_name)); - ald.library->connect(SNAME("animation_removed"), callable_mp(this, &AnimationPlayer::_animation_added), varray(p_name)); - ald.library->connect(SNAME("animation_renamed"), callable_mp(this, &AnimationPlayer::_animation_renamed), varray(p_name)); + ald.library->connect(SNAME("animation_added"), callable_mp(this, &AnimationPlayer::_animation_added).bind(p_name)); + ald.library->connect(SNAME("animation_removed"), callable_mp(this, &AnimationPlayer::_animation_added).bind(p_name)); + ald.library->connect(SNAME("animation_renamed"), callable_mp(this, &AnimationPlayer::_animation_renamed).bind(p_name)); _animation_set_cache_update(); @@ -1385,7 +1415,7 @@ void AnimationPlayer::remove_animation_library(const StringName &p_name) { } void AnimationPlayer::_ref_anim(const Ref<Animation> &p_anim) { - Ref<Animation>(p_anim)->connect(SceneStringNames::get_singleton()->tracks_changed, callable_mp(this, &AnimationPlayer::_animation_changed), varray(), CONNECT_REFERENCE_COUNTED); + Ref<Animation>(p_anim)->connect(SceneStringNames::get_singleton()->tracks_changed, callable_mp(this, &AnimationPlayer::_animation_changed), CONNECT_REFERENCE_COUNTED); } void AnimationPlayer::_unref_anim(const Ref<Animation> &p_anim) { @@ -1411,9 +1441,9 @@ void AnimationPlayer::rename_animation_library(const StringName &p_name, const S animation_libraries[i].library->disconnect(SNAME("animation_removed"), callable_mp(this, &AnimationPlayer::_animation_added)); animation_libraries[i].library->disconnect(SNAME("animation_renamed"), callable_mp(this, &AnimationPlayer::_animation_renamed)); - animation_libraries[i].library->connect(SNAME("animation_added"), callable_mp(this, &AnimationPlayer::_animation_added), varray(p_new_name)); - animation_libraries[i].library->connect(SNAME("animation_removed"), callable_mp(this, &AnimationPlayer::_animation_added), varray(p_new_name)); - animation_libraries[i].library->connect(SNAME("animation_renamed"), callable_mp(this, &AnimationPlayer::_animation_renamed), varray(p_new_name)); + animation_libraries[i].library->connect(SNAME("animation_added"), callable_mp(this, &AnimationPlayer::_animation_added).bind(p_new_name)); + animation_libraries[i].library->connect(SNAME("animation_removed"), callable_mp(this, &AnimationPlayer::_animation_added).bind(p_new_name)); + animation_libraries[i].library->connect(SNAME("animation_renamed"), callable_mp(this, &AnimationPlayer::_animation_renamed).bind(p_new_name)); for (const KeyValue<StringName, Ref<Animation>> &K : animation_libraries[i].library->animations) { StringName old_name = p_name == StringName() ? K.key : StringName(String(p_name) + "/" + String(K.key)); @@ -1472,7 +1502,7 @@ bool AnimationPlayer::has_animation(const StringName &p_name) const { } Ref<Animation> AnimationPlayer::get_animation(const StringName &p_name) const { - ERR_FAIL_COND_V_MSG(!animation_set.has(p_name), Ref<Animation>(), vformat("Animation not found: %s.", p_name)); + ERR_FAIL_COND_V_MSG(!animation_set.has(p_name), Ref<Animation>(), vformat("Animation not found: \"%s\".", p_name)); const AnimationData &data = animation_set[p_name]; @@ -1493,7 +1523,7 @@ void AnimationPlayer::get_animation_list(List<StringName> *p_animations) const { } } -void AnimationPlayer::set_blend_time(const StringName &p_animation1, const StringName &p_animation2, float p_time) { +void AnimationPlayer::set_blend_time(const StringName &p_animation1, const StringName &p_animation2, double p_time) { ERR_FAIL_COND_MSG(!animation_set.has(p_animation1), vformat("Animation not found: %s.", p_animation1)); ERR_FAIL_COND_MSG(!animation_set.has(p_animation2), vformat("Animation not found: %s.", p_animation2)); ERR_FAIL_COND_MSG(p_time < 0, "Blend time cannot be smaller than 0."); @@ -1508,7 +1538,7 @@ void AnimationPlayer::set_blend_time(const StringName &p_animation1, const Strin } } -float AnimationPlayer::get_blend_time(const StringName &p_animation1, const StringName &p_animation2) const { +double AnimationPlayer::get_blend_time(const StringName &p_animation1, const StringName &p_animation2) const { BlendKey bk; bk.from = p_animation1; bk.to = p_animation2; @@ -1541,11 +1571,11 @@ void AnimationPlayer::clear_queue() { queued.clear(); } -void AnimationPlayer::play_backwards(const StringName &p_name, float p_custom_blend) { +void AnimationPlayer::play_backwards(const StringName &p_name, double p_custom_blend) { play(p_name, p_custom_blend, -1, true); } -void AnimationPlayer::play(const StringName &p_name, float p_custom_blend, float p_custom_scale, bool p_from_end) { +void AnimationPlayer::play(const StringName &p_name, double p_custom_blend, float p_custom_scale, bool p_from_end) { StringName name = p_name; if (String(name) == "") { @@ -1557,7 +1587,7 @@ void AnimationPlayer::play(const StringName &p_name, float p_custom_blend, float Playback &c = playback; if (c.current.from) { - float blend_time = 0.0; + double blend_time = 0.0; // find if it can blend BlendKey bk; bk.from = c.current.from->name; @@ -1711,7 +1741,7 @@ void AnimationPlayer::seek(double p_time, bool p_update) { } } -void AnimationPlayer::seek_delta(double p_time, float p_delta) { +void AnimationPlayer::seek_delta(double p_time, double p_delta) { if (!playback.current.from) { if (playback.assigned) { ERR_FAIL_COND_MSG(!animation_set.has(playback.assigned), vformat("Animation not found: %s.", playback.assigned)); @@ -1732,12 +1762,12 @@ bool AnimationPlayer::is_valid() const { return (playback.current.from); } -float AnimationPlayer::get_current_animation_position() const { +double AnimationPlayer::get_current_animation_position() const { ERR_FAIL_COND_V_MSG(!playback.current.from, 0, "AnimationPlayer has no current animation"); return playback.current.pos; } -float AnimationPlayer::get_current_animation_length() const { +double AnimationPlayer::get_current_animation_length() const { ERR_FAIL_COND_V_MSG(!playback.current.from, 0, "AnimationPlayer has no current animation"); return playback.current.from->animation->get_length(); } @@ -1864,6 +1894,14 @@ AnimationPlayer::AnimationMethodCallMode AnimationPlayer::get_method_call_mode() return method_call_mode; } +void AnimationPlayer::set_movie_quit_on_finish_enabled(bool p_enabled) { + movie_quit_on_finish = p_enabled; +} + +bool AnimationPlayer::is_movie_quit_on_finish_enabled() const { + return movie_quit_on_finish; +} + void AnimationPlayer::_set_process(bool p_process, bool p_force) { if (processing == p_process && !p_force) { return; @@ -1895,11 +1933,11 @@ StringName AnimationPlayer::animation_get_next(const StringName &p_animation) co return animation_set[p_animation].next; } -void AnimationPlayer::set_default_blend_time(float p_default) { +void AnimationPlayer::set_default_blend_time(double p_default) { default_blend_time = p_default; } -float AnimationPlayer::get_default_blend_time() const { +double AnimationPlayer::get_default_blend_time() const { return default_blend_time; } @@ -2008,7 +2046,7 @@ Ref<AnimatedValuesBackup> AnimationPlayer::apply_reset(bool p_user_initiated) { Ref<AnimatedValuesBackup> new_values = aux_player->backup_animated_values(); old_values->restore(); - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Anim Apply Reset")); ur->add_do_method(new_values.ptr(), "restore"); ur->add_undo_method(old_values.ptr(), "restore"); @@ -2084,6 +2122,9 @@ void AnimationPlayer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_method_call_mode", "mode"), &AnimationPlayer::set_method_call_mode); ClassDB::bind_method(D_METHOD("get_method_call_mode"), &AnimationPlayer::get_method_call_mode); + ClassDB::bind_method(D_METHOD("set_movie_quit_on_finish_enabled", "enabled"), &AnimationPlayer::set_movie_quit_on_finish_enabled); + ClassDB::bind_method(D_METHOD("is_movie_quit_on_finish_enabled"), &AnimationPlayer::is_movie_quit_on_finish_enabled); + ClassDB::bind_method(D_METHOD("get_current_animation_position"), &AnimationPlayer::get_current_animation_position); ClassDB::bind_method(D_METHOD("get_current_animation_length"), &AnimationPlayer::get_current_animation_length); @@ -2105,9 +2146,12 @@ void AnimationPlayer::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "playback_speed", PROPERTY_HINT_RANGE, "-64,64,0.01"), "set_speed_scale", "get_speed_scale"); ADD_PROPERTY(PropertyInfo(Variant::INT, "method_call_mode", PROPERTY_HINT_ENUM, "Deferred,Immediate"), "set_method_call_mode", "get_method_call_mode"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "movie_quit_on_finish"), "set_movie_quit_on_finish_enabled", "is_movie_quit_on_finish_enabled"); + ADD_SIGNAL(MethodInfo("animation_finished", PropertyInfo(Variant::STRING_NAME, "anim_name"))); ADD_SIGNAL(MethodInfo("animation_changed", PropertyInfo(Variant::STRING_NAME, "old_name"), PropertyInfo(Variant::STRING_NAME, "new_name"))); ADD_SIGNAL(MethodInfo("animation_started", PropertyInfo(Variant::STRING_NAME, "anim_name"))); + ADD_SIGNAL(MethodInfo("animation_list_changed")); ADD_SIGNAL(MethodInfo("caches_cleared")); BIND_ENUM_CONSTANT(ANIMATION_PROCESS_PHYSICS); diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h index d3eb37a345..4f32927d25 100644 --- a/scene/animation/animation_player.h +++ b/scene/animation/animation_player.h @@ -109,6 +109,9 @@ private: bool loc_used = false; bool rot_used = false; bool scale_used = false; + Vector3 init_loc = Vector3(0, 0, 0); + Quaternion init_rot = Quaternion(0, 0, 0, 1); + Vector3 init_scale = Vector3(1, 1, 1); Vector3 loc_accum; Quaternion rot_accum; @@ -188,7 +191,7 @@ private: uint64_t accum_pass = 1; float speed_scale = 1.0; - float default_blend_time = 0.0; + double default_blend_time = 0.0; struct AnimationData { String name; @@ -227,7 +230,7 @@ private: } }; - HashMap<BlendKey, float, BlendKey> blend_times; + HashMap<BlendKey, double, BlendKey> blend_times; struct PlaybackData { AnimationData *from = nullptr; @@ -238,8 +241,8 @@ private: struct Blend { PlaybackData data; - float blend_time = 0.0; - float blend_left = 0.0; + double blend_time = 0.0; + double blend_left = 0.0; }; struct Playback { @@ -259,6 +262,7 @@ private: bool reset_on_save = true; AnimationProcessCallback process_callback = ANIMATION_PROCESS_IDLE; AnimationMethodCallMode method_call_mode = ANIMATION_METHOD_CALL_DEFERRED; + bool movie_quit_on_finish = false; bool processing = false; bool active = true; @@ -307,12 +311,14 @@ private: protected: bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; - virtual void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; void _get_property_list(List<PropertyInfo> *p_list) const; void _notification(int p_what); static void _bind_methods(); + virtual Variant _post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx = -1); + public: StringName find_animation(const Ref<Animation> &p_animation) const; StringName find_animation_library(const Ref<Animation> &p_animation) const; @@ -328,17 +334,17 @@ public: void get_animation_list(List<StringName> *p_animations) const; bool has_animation(const StringName &p_name) const; - void set_blend_time(const StringName &p_animation1, const StringName &p_animation2, float p_time); - float get_blend_time(const StringName &p_animation1, const StringName &p_animation2) const; + void set_blend_time(const StringName &p_animation1, const StringName &p_animation2, double p_time); + double get_blend_time(const StringName &p_animation1, const StringName &p_animation2) const; void animation_set_next(const StringName &p_animation, const StringName &p_next); StringName animation_get_next(const StringName &p_animation) const; - void set_default_blend_time(float p_default); - float get_default_blend_time() const; + void set_default_blend_time(double p_default); + double get_default_blend_time() const; - void play(const StringName &p_name = StringName(), float p_custom_blend = -1, float p_custom_scale = 1.0, bool p_from_end = false); - void play_backwards(const StringName &p_name = StringName(), float p_custom_blend = -1); + void play(const StringName &p_name = StringName(), double p_custom_blend = -1, float p_custom_scale = 1.0, bool p_from_end = false); + void play_backwards(const StringName &p_name = StringName(), double p_custom_blend = -1); void queue(const StringName &p_name); Vector<String> get_queue(); void clear_queue(); @@ -368,12 +374,15 @@ public: void set_method_call_mode(AnimationMethodCallMode p_mode); AnimationMethodCallMode get_method_call_mode() const; + void set_movie_quit_on_finish_enabled(bool p_enabled); + bool is_movie_quit_on_finish_enabled() const; + void seek(double p_time, bool p_update = false); - void seek_delta(double p_time, float p_delta); - float get_current_animation_position() const; - float get_current_animation_length() const; + void seek_delta(double p_time, double p_delta); + double get_current_animation_position() const; + double get_current_animation_length() const; - void advance(float p_time); + void advance(double p_time); void set_root(const NodePath &p_root); NodePath get_root() const; @@ -395,4 +404,4 @@ public: VARIANT_ENUM_CAST(AnimationPlayer::AnimationProcessCallback); VARIANT_ENUM_CAST(AnimationPlayer::AnimationMethodCallMode); -#endif +#endif // ANIMATION_PLAYER_H diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index 8c8822ac3f..05a4a2d024 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -150,7 +150,7 @@ void AnimationNode::make_invalid(const String &p_reason) { state->invalid_reasons += String::utf8("• ") + p_reason; } -double AnimationNode::blend_input(int p_input, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter, bool p_optimize) { +double AnimationNode::blend_input(int p_input, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter, bool p_sync) { ERR_FAIL_INDEX_V(p_input, inputs.size(), 0); ERR_FAIL_COND_V(!state, 0); @@ -169,7 +169,7 @@ double AnimationNode::blend_input(int p_input, double p_time, bool p_seek, bool //inputs.write[p_input].last_pass = state->last_pass; real_t activity = 0.0; - double ret = _blend_node(node_name, blend_tree->get_node_connection_array(node_name), nullptr, node, p_time, p_seek, p_seek_root, p_blend, p_filter, p_optimize, &activity); + double ret = _blend_node(node_name, blend_tree->get_node_connection_array(node_name), nullptr, node, p_time, p_seek, p_seek_root, p_blend, p_filter, p_sync, &activity); Vector<AnimationTree::Activity> *activity_ptr = state->tree->input_activity_map.getptr(base_path); @@ -180,11 +180,11 @@ double AnimationNode::blend_input(int p_input, double p_time, bool p_seek, bool return ret; } -double AnimationNode::blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter, bool p_optimize) { - return _blend_node(p_sub_path, Vector<StringName>(), this, p_node, p_time, p_seek, p_seek_root, p_blend, p_filter, p_optimize); +double AnimationNode::blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter, bool p_sync) { + return _blend_node(p_sub_path, Vector<StringName>(), this, p_node, p_time, p_seek, p_seek_root, p_blend, p_filter, p_sync); } -double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter, bool p_optimize, real_t *r_max) { +double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter, bool p_sync, real_t *r_max) { ERR_FAIL_COND_V(!p_node.is_valid(), 0); ERR_FAIL_COND_V(!state, 0); @@ -292,9 +292,11 @@ double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<Stri } // If tracks for blending don't exist for one of the animations, Rest or RESET animation is blended as init animation instead. - // Then, blend weight is 0 means that the init animation blend weight is 1. + // Then blend weight is 0 means that the init animation blend weight is 1. + // In that case, processing only the animation with the lacking track will not process the lacking track, and will not properly apply the Reset value. + // This means that all tracks which the animations in the branch that may be blended have must be processed. // Therefore, the blending process must be executed even if the blend weight is 0. - if (!p_seek && p_optimize && !any_valid) { + if (!p_seek && !p_sync && !any_valid) { return p_node->_pre_process(new_path, new_parent, state, 0, p_seek, p_seek_root, p_connections); } return p_node->_pre_process(new_path, new_parent, state, p_time, p_seek, p_seek_root, p_connections); @@ -397,9 +399,9 @@ void AnimationNode::_set_filters(const Array &p_filters) { } } -void AnimationNode::_validate_property(PropertyInfo &property) const { - if (!has_filter() && (property.name == "filter_enabled" || property.name == "filters")) { - property.usage = PROPERTY_USAGE_NONE; +void AnimationNode::_validate_property(PropertyInfo &p_property) const { + if (!has_filter() && (p_property.name == "filter_enabled" || p_property.name == "filters")) { + p_property.usage = PROPERTY_USAGE_NONE; } } @@ -428,8 +430,8 @@ void AnimationNode::_bind_methods() { ClassDB::bind_method(D_METHOD("_get_filters"), &AnimationNode::_get_filters); ClassDB::bind_method(D_METHOD("blend_animation", "animation", "time", "delta", "seeked", "seek_root", "blend", "pingponged"), &AnimationNode::blend_animation, DEFVAL(0)); - ClassDB::bind_method(D_METHOD("blend_node", "name", "node", "time", "seek", "seek_root", "blend", "filter", "optimize"), &AnimationNode::blend_node, DEFVAL(FILTER_IGNORE), DEFVAL(true)); - ClassDB::bind_method(D_METHOD("blend_input", "input_index", "time", "seek", "seek_root", "blend", "filter", "optimize"), &AnimationNode::blend_input, DEFVAL(FILTER_IGNORE), DEFVAL(true)); + ClassDB::bind_method(D_METHOD("blend_node", "name", "node", "time", "seek", "seek_root", "blend", "filter", "sync"), &AnimationNode::blend_node, DEFVAL(FILTER_IGNORE), DEFVAL(true)); + ClassDB::bind_method(D_METHOD("blend_input", "input_index", "time", "seek", "seek_root", "blend", "filter", "sync"), &AnimationNode::blend_input, DEFVAL(FILTER_IGNORE), DEFVAL(true)); ClassDB::bind_method(D_METHOD("set_parameter", "name", "value"), &AnimationNode::set_parameter); ClassDB::bind_method(D_METHOD("get_parameter", "name"), &AnimationNode::get_parameter); @@ -587,7 +589,7 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { } if (!child->is_connected("tree_exited", callable_mp(this, &AnimationTree::_node_removed))) { - child->connect("tree_exited", callable_mp(this, &AnimationTree::_node_removed), varray(child)); + child->connect("tree_exited", callable_mp(this, &AnimationTree::_node_removed).bind(child)); } switch (track_type) { @@ -600,6 +602,8 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { track_value->object = child; } + track_value->is_using_angle = anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_LINEAR_ANGLE || anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_CUBIC_ANGLE; + track_value->subpath = leftover_path; track_value->object_id = track_value->object->get_instance_id(); @@ -802,6 +806,10 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { default: { } } + } else if (track_cache_type == Animation::TYPE_VALUE) { + // If it has at least one angle interpolation, it also uses angle interpolation for blending. + TrackCacheValue *track_value = memnew(TrackCacheValue); + track_value->is_using_angle |= anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_LINEAR_ANGLE || anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_CUBIC_ANGLE; } track->setup_pass = setup_pass; @@ -839,6 +847,11 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { return true; } +void AnimationTree::_animation_player_changed() { + emit_signal(SNAME("animation_player_changed")); + _clear_caches(); +} + void AnimationTree::_clear_caches() { for (KeyValue<NodePath, TrackCache *> &K : track_cache) { memdelete(K.value); @@ -1052,7 +1065,9 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } + loc[0] = _post_process_key_value(a, i, loc[0], t->object, t->bone_idx); a->position_track_interpolate(i, (double)a->get_length(), &loc[1]); + loc[1] = _post_process_key_value(a, i, loc[1], t->object, t->bone_idx); t->loc += (loc[1] - loc[0]) * blend; prev_time = 0; } @@ -1062,7 +1077,9 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } + loc[0] = _post_process_key_value(a, i, loc[0], t->object, t->bone_idx); a->position_track_interpolate(i, 0, &loc[1]); + loc[1] = _post_process_key_value(a, i, loc[1], t->object, t->bone_idx); t->loc += (loc[1] - loc[0]) * blend; prev_time = (double)a->get_length(); } @@ -1072,8 +1089,10 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } + loc[0] = _post_process_key_value(a, i, loc[0], t->object, t->bone_idx); a->position_track_interpolate(i, time, &loc[1]); + loc[1] = _post_process_key_value(a, i, loc[1], t->object, t->bone_idx); t->loc += (loc[1] - loc[0]) * blend; prev_time = !backward ? 0 : (double)a->get_length(); @@ -1090,6 +1109,7 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } + loc = _post_process_key_value(a, i, loc, t->object, t->bone_idx); t->loc += (loc - t->init_loc) * blend; } @@ -1148,7 +1168,9 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } + rot[0] = _post_process_key_value(a, i, rot[0], t->object, t->bone_idx); a->rotation_track_interpolate(i, (double)a->get_length(), &rot[1]); + rot[1] = _post_process_key_value(a, i, rot[1], t->object, t->bone_idx); t->rot = (t->rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized(); prev_time = 0; } @@ -1158,6 +1180,7 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } + rot[0] = _post_process_key_value(a, i, rot[0], t->object, t->bone_idx); a->rotation_track_interpolate(i, 0, &rot[1]); t->rot = (t->rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized(); prev_time = (double)a->get_length(); @@ -1168,8 +1191,10 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } + rot[0] = _post_process_key_value(a, i, rot[0], t->object, t->bone_idx); a->rotation_track_interpolate(i, time, &rot[1]); + rot[1] = _post_process_key_value(a, i, rot[1], t->object, t->bone_idx); t->rot = (t->rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized(); prev_time = !backward ? 0 : (double)a->get_length(); @@ -1186,6 +1211,7 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } + rot = _post_process_key_value(a, i, rot, t->object, t->bone_idx); t->rot = (t->rot * Quaternion().slerp(t->init_rot.inverse() * rot, blend)).normalized(); } @@ -1244,8 +1270,10 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } + scale[0] = _post_process_key_value(a, i, scale[0], t->object, t->bone_idx); a->scale_track_interpolate(i, (double)a->get_length(), &scale[1]); t->scale += (scale[1] - scale[0]) * blend; + scale[1] = _post_process_key_value(a, i, scale[1], t->object, t->bone_idx); prev_time = 0; } } else { @@ -1254,7 +1282,9 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } + scale[0] = _post_process_key_value(a, i, scale[0], t->object, t->bone_idx); a->scale_track_interpolate(i, 0, &scale[1]); + scale[1] = _post_process_key_value(a, i, scale[1], t->object, t->bone_idx); t->scale += (scale[1] - scale[0]) * blend; prev_time = (double)a->get_length(); } @@ -1264,8 +1294,10 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } + scale[0] = _post_process_key_value(a, i, scale[0], t->object, t->bone_idx); a->scale_track_interpolate(i, time, &scale[1]); + scale[1] = _post_process_key_value(a, i, scale[1], t->object, t->bone_idx); t->scale += (scale[1] - scale[0]) * blend; prev_time = !backward ? 0 : (double)a->get_length(); @@ -1282,6 +1314,7 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } + scale = _post_process_key_value(a, i, scale, t->object, t->bone_idx); t->scale += (scale - t->init_scale) * blend; } @@ -1304,6 +1337,7 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } + value = _post_process_key_value(a, i, value, t->object, t->shape_index); t->value += (value - t->init_value) * blend; #endif // _3D_DISABLED @@ -1315,6 +1349,7 @@ void AnimationTree::_process_graph(double p_delta) { if (update_mode == Animation::UPDATE_CONTINUOUS || update_mode == Animation::UPDATE_CAPTURE) { Variant value = a->value_track_interpolate(i, time); + value = _post_process_key_value(a, i, value, t->object); if (value == Variant()) { continue; @@ -1329,8 +1364,33 @@ void AnimationTree::_process_graph(double p_delta) { t->value = t->init_value; } - Variant::sub(value, t->init_value, value); - Variant::blend(t->value, value, blend, t->value); + // Special case for angle interpolation. + if (t->is_using_angle) { + // For blending consistency, it prevents rotation of more than 180 degrees from init_value. + // This is the same as for Quaternion blends. + float rot_a = t->value; + float rot_b = value; + float rot_init = t->init_value; + rot_a = Math::fposmod(rot_a, (float)Math_TAU); + rot_b = Math::fposmod(rot_b, (float)Math_TAU); + rot_init = Math::fposmod(rot_init, (float)Math_TAU); + if (rot_init < Math_PI) { + rot_a = rot_a > rot_init + Math_PI ? rot_a - Math_TAU : rot_a; + rot_b = rot_b > rot_init + Math_PI ? rot_b - Math_TAU : rot_b; + } else { + rot_a = rot_a < rot_init - Math_PI ? rot_a + Math_TAU : rot_a; + rot_b = rot_b < rot_init - Math_PI ? rot_b + Math_TAU : rot_b; + } + t->value = Math::fposmod(rot_a + (rot_b - rot_init) * (float)blend, (float)Math_TAU); + } else { + if (t->init_value.get_type() == Variant::BOOL) { + value = Animation::subtract_variant(value.operator real_t(), t->init_value.operator real_t()); + t->value = Animation::blend_variant(t->value.operator real_t(), value.operator real_t(), blend); + } else { + value = Animation::subtract_variant(value, t->init_value); + t->value = Animation::blend_variant(t->value, value, blend); + } + } } else { if (blend < CMP_EPSILON) { continue; //nothing to blend @@ -1342,12 +1402,14 @@ void AnimationTree::_process_graph(double p_delta) { continue; } Variant value = a->track_get_key_value(i, idx); + value = _post_process_key_value(a, i, value, t->object); t->object->set_indexed(t->subpath, value); } else { List<int> indices; a->value_track_get_key_indices(i, time, delta, &indices, pingponged); for (int &F : indices) { Variant value = a->track_get_key_value(i, F); + value = _post_process_key_value(a, i, value, t->object); t->object->set_indexed(t->subpath, value); } } @@ -1386,6 +1448,7 @@ void AnimationTree::_process_graph(double p_delta) { TrackCacheBezier *t = static_cast<TrackCacheBezier *>(track); real_t bezier = a->bezier_track_interpolate(i, time); + bezier = _post_process_key_value(a, i, bezier, t->object); if (t->process_pass != process_pass) { t->process_pass = process_pass; @@ -1501,7 +1564,7 @@ void AnimationTree::_process_graph(double p_delta) { } } - real_t db = Math::linear2db(MAX(blend, 0.00001)); + real_t db = Math::linear_to_db(MAX(blend, 0.00001)); if (t->object->has_method(SNAME("set_unit_db"))) { t->object->call(SNAME("set_unit_db"), db); } else { @@ -1645,7 +1708,11 @@ void AnimationTree::_process_graph(double p_delta) { case Animation::TYPE_VALUE: { TrackCacheValue *t = static_cast<TrackCacheValue *>(track); - t->object->set_indexed(t->subpath, t->value); + if (t->init_value.get_type() == Variant::BOOL) { + t->object->set_indexed(t->subpath, t->value.operator real_t() >= 0.5); + } else { + t->object->set_indexed(t->subpath, t->value); + } } break; case Animation::TYPE_BEZIER: { @@ -1661,13 +1728,31 @@ void AnimationTree::_process_graph(double p_delta) { } } -void AnimationTree::advance(real_t p_time) { +Variant AnimationTree::_post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx) { + switch (p_anim->track_get_type(p_track)) { +#ifndef _3D_DISABLED + case Animation::TYPE_POSITION_3D: { + if (p_object_idx >= 0) { + const Skeleton3D *skel = Object::cast_to<Skeleton3D>(p_object); + return Vector3(p_value) * skel->get_motion_scale(); + } + return p_value; + } break; +#endif // _3D_DISABLED + default: { + } break; + } + return p_value; +} + +void AnimationTree::advance(double p_time) { _process_graph(p_time); } void AnimationTree::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { + _setup_animation_player(); if (last_animation_player.is_valid()) { Object *player = ObjectDB::get_instance(last_animation_player); if (player) { @@ -1700,8 +1785,43 @@ void AnimationTree::_notification(int p_what) { } } +void AnimationTree::_setup_animation_player() { + if (!is_inside_tree()) { + return; + } + + AnimationPlayer *new_player = nullptr; + if (!animation_player.is_empty()) { + new_player = Object::cast_to<AnimationPlayer>(get_node(animation_player)); + if (new_player && !new_player->is_connected("animation_list_changed", callable_mp(this, &AnimationTree::_animation_player_changed))) { + new_player->connect("animation_list_changed", callable_mp(this, &AnimationTree::_animation_player_changed)); + } + } + + if (new_player) { + if (!last_animation_player.is_valid()) { + // Animation player set newly. + emit_signal(SNAME("animation_player_changed")); + return; + } else if (last_animation_player == new_player->get_instance_id()) { + // Animation player isn't changed. + return; + } + } else if (!last_animation_player.is_valid()) { + // Animation player is being empty. + return; + } + + AnimationPlayer *old_player = Object::cast_to<AnimationPlayer>(ObjectDB::get_instance(last_animation_player)); + if (old_player && old_player->is_connected("animation_list_changed", callable_mp(this, &AnimationTree::_animation_player_changed))) { + old_player->disconnect("animation_list_changed", callable_mp(this, &AnimationTree::_animation_player_changed)); + } + emit_signal(SNAME("animation_player_changed")); +} + void AnimationTree::set_animation_player(const NodePath &p_player) { animation_player = p_player; + _setup_animation_player(); update_configuration_warnings(); } @@ -1729,8 +1849,8 @@ uint64_t AnimationTree::get_last_process_pass() const { return process_pass; } -TypedArray<String> AnimationTree::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray AnimationTree::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (!root.is_valid()) { warnings.push_back(RTR("No root AnimationNode for the graph is set.")); @@ -1938,6 +2058,8 @@ void AnimationTree::_bind_methods() { BIND_ENUM_CONSTANT(ANIMATION_PROCESS_PHYSICS); BIND_ENUM_CONSTANT(ANIMATION_PROCESS_IDLE); BIND_ENUM_CONSTANT(ANIMATION_PROCESS_MANUAL); + + ADD_SIGNAL(MethodInfo("animation_player_changed")); } AnimationTree::AnimationTree() { diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h index 0bfe615c9b..96c1279f82 100644 --- a/scene/animation/animation_tree.h +++ b/scene/animation/animation_tree.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef ANIMATION_GRAPH_PLAYER_H -#define ANIMATION_GRAPH_PLAYER_H +#ifndef ANIMATION_TREE_H +#define ANIMATION_TREE_H #include "animation_player.h" #include "scene/3d/node_3d.h" @@ -99,19 +99,19 @@ public: Array _get_filters() const; void _set_filters(const Array &p_filters); friend class AnimationNodeBlendTree; - double _blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true, real_t *r_max = nullptr); + double _blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_sync = true, real_t *r_max = nullptr); protected: void blend_animation(const StringName &p_animation, double p_time, double p_delta, bool p_seeked, bool p_seek_root, real_t p_blend, int p_pingponged = 0); - double blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true); - double blend_input(int p_input, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true); + double blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_sync = true); + double blend_input(int p_input, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_sync = true); void make_invalid(const String &p_reason); AnimationTree *get_animation_tree() const; static void _bind_methods(); - void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; GDVIRTUAL0RC(Dictionary, _get_child_nodes) GDVIRTUAL0RC(Array, _get_parameter_list) @@ -233,6 +233,7 @@ private: Variant init_value; Variant value; Vector<StringName> subpath; + bool is_using_angle = false; TrackCacheValue() { type = Animation::TYPE_VALUE; } }; @@ -281,6 +282,8 @@ private: bool cache_valid = false; void _node_removed(Node *p_node); + void _setup_animation_player(); + void _animation_player_changed(); void _clear_caches(); bool _update_caches(AnimationPlayer *player); void _process_graph(double p_delta); @@ -321,6 +324,8 @@ protected: void _notification(int p_what); static void _bind_methods(); + virtual Variant _post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx = -1); + public: void set_tree_root(const Ref<AnimationNode> &p_root); Ref<AnimationNode> get_tree_root() const; @@ -337,7 +342,7 @@ public: void set_advance_expression_base_node(const NodePath &p_advance_expression_base_node); NodePath get_advance_expression_base_node() const; - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; bool is_state_invalid() const; String get_invalid_state_reason() const; @@ -348,7 +353,7 @@ public: Transform3D get_root_motion_transform() const; real_t get_connection_activity(const StringName &p_path, int p_connection) const; - void advance(real_t p_time); + void advance(double p_time); void rename_parameter(const String &p_base, const String &p_new_base); @@ -359,4 +364,4 @@ public: VARIANT_ENUM_CAST(AnimationTree::AnimationProcessCallback) -#endif // ANIMATION_GRAPH_PLAYER_H +#endif // ANIMATION_TREE_H diff --git a/scene/animation/easing_equations.h b/scene/animation/easing_equations.h index 6d246c7a93..03d9e16454 100644 --- a/scene/animation/easing_equations.h +++ b/scene/animation/easing_equations.h @@ -28,6 +28,9 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#ifndef EASING_EQUATIONS_H +#define EASING_EQUATIONS_H + /* * Derived from Robert Penner's easing equations: http://robertpenner.com/easing/ * @@ -52,9 +55,6 @@ * SOFTWARE. */ -#ifndef EASING_EQUATIONS_H -#define EASING_EQUATIONS_H - namespace linear { static real_t in(real_t t, real_t b, real_t c, real_t d) { return c * t / d + b; @@ -402,4 +402,4 @@ static real_t out_in(real_t t, real_t b, real_t c, real_t d) { } }; // namespace back -#endif +#endif // EASING_EQUATIONS_H diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp index 5457da472f..4a0f870406 100644 --- a/scene/animation/tween.cpp +++ b/scene/animation/tween.cpp @@ -32,6 +32,7 @@ #include "scene/animation/easing_equations.h" #include "scene/main/node.h" +#include "scene/resources/animation.h" Tween::interpolater Tween::interpolaters[Tween::TRANS_MAX][Tween::EASE_MAX] = { { &linear::in, &linear::in, &linear::in, &linear::in }, // Linear is the same for each easing. @@ -70,22 +71,29 @@ void Tween::start_tweeners() { } } -Ref<PropertyTweener> Tween::tween_property(Object *p_target, NodePath p_property, Variant p_to, float p_duration) { +Ref<PropertyTweener> Tween::tween_property(Object *p_target, NodePath p_property, Variant p_to, double p_duration) { ERR_FAIL_NULL_V(p_target, nullptr); ERR_FAIL_COND_V_MSG(!valid, nullptr, "Tween invalid. Either finished or created outside scene tree."); ERR_FAIL_COND_V_MSG(started, nullptr, "Can't append to a Tween that has started. Use stop() first."); -#ifdef DEBUG_ENABLED Variant::Type property_type = p_target->get_indexed(p_property.get_as_property_path().get_subnames()).get_type(); - ERR_FAIL_COND_V_MSG(property_type != p_to.get_type(), Ref<PropertyTweener>(), "Type mismatch between property and final value: " + Variant::get_type_name(property_type) + " and " + Variant::get_type_name(p_to.get_type())); -#endif + if (property_type != p_to.get_type()) { + // Cast p_to between double and int to avoid minor annoyances. + if (property_type == Variant::FLOAT && p_to.get_type() == Variant::INT) { + p_to = double(p_to); + } else if (property_type == Variant::INT && p_to.get_type() == Variant::FLOAT) { + p_to = int(p_to); + } else { + ERR_FAIL_V_MSG(Ref<PropertyTweener>(), "Type mismatch between property and final value: " + Variant::get_type_name(property_type) + " and " + Variant::get_type_name(p_to.get_type())); + } + } Ref<PropertyTweener> tweener = memnew(PropertyTweener(p_target, p_property, p_to, p_duration)); append(tweener); return tweener; } -Ref<IntervalTweener> Tween::tween_interval(float p_time) { +Ref<IntervalTweener> Tween::tween_interval(double p_time) { ERR_FAIL_COND_V_MSG(!valid, nullptr, "Tween invalid. Either finished or created outside scene tree."); ERR_FAIL_COND_V_MSG(started, nullptr, "Can't append to a Tween that has started. Use stop() first."); @@ -103,7 +111,7 @@ Ref<CallbackTweener> Tween::tween_callback(Callable p_callback) { return tweener; } -Ref<MethodTweener> Tween::tween_method(Callable p_callback, Variant p_from, Variant p_to, float p_duration) { +Ref<MethodTweener> Tween::tween_method(Callable p_callback, Variant p_from, Variant p_to, double p_duration) { ERR_FAIL_COND_V_MSG(!valid, nullptr, "Tween invalid. Either finished or created outside scene tree."); ERR_FAIL_COND_V_MSG(started, nullptr, "Can't append to a Tween that has started. Use stop() first."); @@ -237,7 +245,7 @@ Ref<Tween> Tween::chain() { return this; } -bool Tween::custom_step(float p_delta) { +bool Tween::custom_step(double p_delta) { bool r = running; running = true; bool ret = step(p_delta); @@ -245,7 +253,7 @@ bool Tween::custom_step(float p_delta) { return ret; } -bool Tween::step(float p_delta) { +bool Tween::step(double p_delta) { if (dead) { return false; } @@ -274,22 +282,22 @@ bool Tween::step(float p_delta) { started = true; } - float rem_delta = p_delta * speed_scale; + double rem_delta = p_delta * speed_scale; bool step_active = false; total_time += rem_delta; #ifdef DEBUG_ENABLED - float initial_delta = rem_delta; + double initial_delta = rem_delta; bool potential_infinite = false; #endif while (rem_delta > 0 && running) { - float step_delta = rem_delta; + double step_delta = rem_delta; step_active = false; for (Ref<Tweener> &tweener : tweeners.write[current_step]) { // Modified inside Tweener.step(). - float temp_delta = rem_delta; + double temp_delta = rem_delta; // Turns to true if any Tweener returns true (i.e. is still not finished). step_active = tweener->step(temp_delta) || step_active; step_delta = MIN(temp_delta, step_delta); @@ -307,6 +315,7 @@ bool Tween::step(float p_delta) { running = false; dead = true; emit_signal(SNAME("finished")); + break; } else { emit_signal(SNAME("loop_finished"), loops_done); current_step = 0; @@ -350,7 +359,7 @@ Node *Tween::get_bound_node() const { } } -float Tween::get_total_time() const { +double Tween::get_total_time() const { return total_time; } @@ -364,261 +373,18 @@ real_t Tween::run_equation(TransitionType p_trans_type, EaseType p_ease_type, re return func(p_time, p_initial, p_delta, p_duration); } -Variant Tween::interpolate_variant(Variant p_initial_val, Variant p_delta_val, float p_time, float p_duration, TransitionType p_trans, EaseType p_ease) { +Variant Tween::interpolate_variant(Variant p_initial_val, Variant p_delta_val, double p_time, double p_duration, TransitionType p_trans, EaseType p_ease) { ERR_FAIL_INDEX_V(p_trans, TransitionType::TRANS_MAX, Variant()); ERR_FAIL_INDEX_V(p_ease, EaseType::EASE_MAX, Variant()); -// Helper macro to run equation on sub-elements of the value (e.g. x and y of Vector2). -#define APPLY_EQUATION(element) \ - r.element = run_equation(p_trans, p_ease, p_time, i.element, d.element, p_duration); - - switch (p_initial_val.get_type()) { - case Variant::BOOL: { - return (run_equation(p_trans, p_ease, p_time, p_initial_val, p_delta_val, p_duration)) >= 0.5; - } - - case Variant::INT: { - return (int)run_equation(p_trans, p_ease, p_time, (int)p_initial_val, (int)p_delta_val, p_duration); - } - - case Variant::FLOAT: { - return run_equation(p_trans, p_ease, p_time, (real_t)p_initial_val, (real_t)p_delta_val, p_duration); - } - - case Variant::VECTOR2: { - Vector2 i = p_initial_val; - Vector2 d = p_delta_val; - Vector2 r; - - APPLY_EQUATION(x); - APPLY_EQUATION(y); - return r; - } - - case Variant::VECTOR2I: { - Vector2i i = p_initial_val; - Vector2i d = p_delta_val; - Vector2i r; - - APPLY_EQUATION(x); - APPLY_EQUATION(y); - return r; - } - - case Variant::RECT2: { - Rect2 i = p_initial_val; - Rect2 d = p_delta_val; - Rect2 r; - - APPLY_EQUATION(position.x); - APPLY_EQUATION(position.y); - APPLY_EQUATION(size.x); - APPLY_EQUATION(size.y); - return r; - } - - case Variant::RECT2I: { - Rect2i i = p_initial_val; - Rect2i d = p_delta_val; - Rect2i r; - - APPLY_EQUATION(position.x); - APPLY_EQUATION(position.y); - APPLY_EQUATION(size.x); - APPLY_EQUATION(size.y); - return r; - } - - case Variant::VECTOR3: { - Vector3 i = p_initial_val; - Vector3 d = p_delta_val; - Vector3 r; - - APPLY_EQUATION(x); - APPLY_EQUATION(y); - APPLY_EQUATION(z); - return r; - } - - case Variant::VECTOR3I: { - Vector3i i = p_initial_val; - Vector3i d = p_delta_val; - Vector3i r; - - APPLY_EQUATION(x); - APPLY_EQUATION(y); - APPLY_EQUATION(z); - return r; - } - - case Variant::TRANSFORM2D: { - Transform2D i = p_initial_val; - Transform2D d = p_delta_val; - Transform2D r; - - APPLY_EQUATION(columns[0][0]); - APPLY_EQUATION(columns[0][1]); - APPLY_EQUATION(columns[1][0]); - APPLY_EQUATION(columns[1][1]); - APPLY_EQUATION(columns[2][0]); - APPLY_EQUATION(columns[2][1]); - return r; - } - - case Variant::QUATERNION: { - Quaternion i = p_initial_val; - Quaternion d = p_delta_val; - Quaternion r; - - APPLY_EQUATION(x); - APPLY_EQUATION(y); - APPLY_EQUATION(z); - APPLY_EQUATION(w); - return r; - } - - case Variant::AABB: { - AABB i = p_initial_val; - AABB d = p_delta_val; - AABB r; - - APPLY_EQUATION(position.x); - APPLY_EQUATION(position.y); - APPLY_EQUATION(position.z); - APPLY_EQUATION(size.x); - APPLY_EQUATION(size.y); - APPLY_EQUATION(size.z); - return r; - } - - case Variant::BASIS: { - Basis i = p_initial_val; - Basis d = p_delta_val; - Basis r; - - APPLY_EQUATION(rows[0][0]); - APPLY_EQUATION(rows[0][1]); - APPLY_EQUATION(rows[0][2]); - APPLY_EQUATION(rows[1][0]); - APPLY_EQUATION(rows[1][1]); - APPLY_EQUATION(rows[1][2]); - APPLY_EQUATION(rows[2][0]); - APPLY_EQUATION(rows[2][1]); - APPLY_EQUATION(rows[2][2]); - return r; - } - - case Variant::TRANSFORM3D: { - Transform3D i = p_initial_val; - Transform3D d = p_delta_val; - Transform3D r; - - APPLY_EQUATION(basis.rows[0][0]); - APPLY_EQUATION(basis.rows[0][1]); - APPLY_EQUATION(basis.rows[0][2]); - APPLY_EQUATION(basis.rows[1][0]); - APPLY_EQUATION(basis.rows[1][1]); - APPLY_EQUATION(basis.rows[1][2]); - APPLY_EQUATION(basis.rows[2][0]); - APPLY_EQUATION(basis.rows[2][1]); - APPLY_EQUATION(basis.rows[2][2]); - APPLY_EQUATION(origin.x); - APPLY_EQUATION(origin.y); - APPLY_EQUATION(origin.z); - return r; - } - - case Variant::COLOR: { - Color i = p_initial_val; - Color d = p_delta_val; - Color r; - - APPLY_EQUATION(r); - APPLY_EQUATION(g); - APPLY_EQUATION(b); - APPLY_EQUATION(a); - return r; - } - - default: { - return p_initial_val; - } - }; -#undef APPLY_EQUATION -} - -Variant Tween::calculate_delta_value(Variant p_intial_val, Variant p_final_val) { - ERR_FAIL_COND_V_MSG(p_intial_val.get_type() != p_final_val.get_type(), p_intial_val, "Type mismatch between initial and final value: " + Variant::get_type_name(p_intial_val.get_type()) + " and " + Variant::get_type_name(p_final_val.get_type())); - - switch (p_intial_val.get_type()) { - case Variant::BOOL: { - return (int)p_final_val - (int)p_intial_val; - } - - case Variant::RECT2: { - Rect2 i = p_intial_val; - Rect2 f = p_final_val; - return Rect2(f.position - i.position, f.size - i.size); - } - - case Variant::RECT2I: { - Rect2i i = p_intial_val; - Rect2i f = p_final_val; - return Rect2i(f.position - i.position, f.size - i.size); - } - - case Variant::TRANSFORM2D: { - Transform2D i = p_intial_val; - Transform2D f = p_final_val; - return Transform2D(f.columns[0][0] - i.columns[0][0], - f.columns[0][1] - i.columns[0][1], - f.columns[1][0] - i.columns[1][0], - f.columns[1][1] - i.columns[1][1], - f.columns[2][0] - i.columns[2][0], - f.columns[2][1] - i.columns[2][1]); - } - - case Variant::AABB: { - AABB i = p_intial_val; - AABB f = p_final_val; - return AABB(f.position - i.position, f.size - i.size); - } - - case Variant::BASIS: { - Basis i = p_intial_val; - Basis f = p_final_val; - return Basis(f.rows[0][0] - i.rows[0][0], - f.rows[0][1] - i.rows[0][1], - f.rows[0][2] - i.rows[0][2], - f.rows[1][0] - i.rows[1][0], - f.rows[1][1] - i.rows[1][1], - f.rows[1][2] - i.rows[1][2], - f.rows[2][0] - i.rows[2][0], - f.rows[2][1] - i.rows[2][1], - f.rows[2][2] - i.rows[2][2]); - } - - case Variant::TRANSFORM3D: { - Transform3D i = p_intial_val; - Transform3D f = p_final_val; - return Transform3D(f.basis.rows[0][0] - i.basis.rows[0][0], - f.basis.rows[0][1] - i.basis.rows[0][1], - f.basis.rows[0][2] - i.basis.rows[0][2], - f.basis.rows[1][0] - i.basis.rows[1][0], - f.basis.rows[1][1] - i.basis.rows[1][1], - f.basis.rows[1][2] - i.basis.rows[1][2], - f.basis.rows[2][0] - i.basis.rows[2][0], - f.basis.rows[2][1] - i.basis.rows[2][1], - f.basis.rows[2][2] - i.basis.rows[2][2], - f.origin.x - i.origin.x, - f.origin.y - i.origin.y, - f.origin.z - i.origin.z); - } + // Special case for bool. + if (p_initial_val.get_type() == Variant::BOOL) { + return run_equation(p_trans, p_ease, p_time, p_initial_val, p_delta_val, p_duration) >= 0.5; + } - default: { - return Variant::evaluate(Variant::OP_SUBTRACT, p_final_val, p_intial_val); - } - }; + Variant ret = Animation::add_variant(p_initial_val, p_delta_val); + ret = Animation::interpolate_variant(p_initial_val, ret, run_equation(p_trans, p_ease, p_time, 0.0, 1.0, p_duration)); + return ret; } void Tween::_bind_methods() { @@ -714,7 +480,7 @@ Ref<PropertyTweener> PropertyTweener::set_ease(Tween::EaseType p_ease) { return this; } -Ref<PropertyTweener> PropertyTweener::set_delay(float p_delay) { +Ref<PropertyTweener> PropertyTweener::set_delay(double p_delay) { delay = p_delay; return this; } @@ -734,13 +500,13 @@ void PropertyTweener::start() { } if (relative) { - final_val = Variant::evaluate(Variant::Operator::OP_ADD, initial_val, base_final_val); + final_val = Animation::add_variant(initial_val, base_final_val); } - delta_val = tween->calculate_delta_value(initial_val, final_val); + delta_val = Animation::subtract_variant(final_val, initial_val); } -bool PropertyTweener::step(float &r_delta) { +bool PropertyTweener::step(double &r_delta) { if (finished) { // This is needed in case there's a parallel Tweener with longer duration. return false; @@ -757,7 +523,7 @@ bool PropertyTweener::step(float &r_delta) { return true; } - float time = MIN(elapsed_time - delay, duration); + double time = MIN(elapsed_time - delay, duration); if (time < duration) { target_instance->set_indexed(property, tween->interpolate_variant(initial_val, delta_val, time, duration, trans_type, ease_type)); r_delta = 0; @@ -790,7 +556,7 @@ void PropertyTweener::_bind_methods() { ClassDB::bind_method(D_METHOD("set_delay", "delay"), &PropertyTweener::set_delay); } -PropertyTweener::PropertyTweener(Object *p_target, NodePath p_property, Variant p_to, float p_duration) { +PropertyTweener::PropertyTweener(Object *p_target, NodePath p_property, Variant p_to, double p_duration) { target = p_target->get_instance_id(); property = p_property.get_as_property_path().get_subnames(); initial_val = p_target->get_indexed(property); @@ -808,7 +574,7 @@ void IntervalTweener::start() { finished = false; } -bool IntervalTweener::step(float &r_delta) { +bool IntervalTweener::step(double &r_delta) { if (finished) { return false; } @@ -826,7 +592,7 @@ bool IntervalTweener::step(float &r_delta) { } } -IntervalTweener::IntervalTweener(float p_time) { +IntervalTweener::IntervalTweener(double p_time) { duration = p_time; } @@ -834,7 +600,7 @@ IntervalTweener::IntervalTweener() { ERR_FAIL_MSG("Can't create empty IntervalTweener. Use get_tree().tween_interval() instead."); } -Ref<CallbackTweener> CallbackTweener::set_delay(float p_delay) { +Ref<CallbackTweener> CallbackTweener::set_delay(double p_delay) { delay = p_delay; return this; } @@ -844,7 +610,7 @@ void CallbackTweener::start() { finished = false; } -bool CallbackTweener::step(float &r_delta) { +bool CallbackTweener::step(double &r_delta) { if (finished) { return false; } @@ -853,7 +619,7 @@ bool CallbackTweener::step(float &r_delta) { if (elapsed_time >= delay) { Variant result; Callable::CallError ce; - callback.call(nullptr, 0, result, ce); + callback.callp(nullptr, 0, result, ce); if (ce.error != Callable::CallError::CALL_OK) { ERR_FAIL_V_MSG(false, "Error calling method from CallbackTweener: " + Variant::get_callable_error_text(callback, nullptr, 0, ce)); } @@ -880,7 +646,7 @@ CallbackTweener::CallbackTweener() { ERR_FAIL_MSG("Can't create empty CallbackTweener. Use get_tree().tween_callback() instead."); } -Ref<MethodTweener> MethodTweener::set_delay(float p_delay) { +Ref<MethodTweener> MethodTweener::set_delay(double p_delay) { delay = p_delay; return this; } @@ -900,7 +666,7 @@ void MethodTweener::start() { finished = false; } -bool MethodTweener::step(float &r_delta) { +bool MethodTweener::step(double &r_delta) { if (finished) { return false; } @@ -913,7 +679,7 @@ bool MethodTweener::step(float &r_delta) { } Variant current_val; - float time = MIN(elapsed_time - delay, duration); + double time = MIN(elapsed_time - delay, duration); if (time < duration) { current_val = tween->interpolate_variant(initial_val, delta_val, time, duration, trans_type, ease_type); } else { @@ -924,7 +690,7 @@ bool MethodTweener::step(float &r_delta) { Variant result; Callable::CallError ce; - callback.call(argptr, 1, result, ce); + callback.callp(argptr, 1, result, ce); if (ce.error != Callable::CallError::CALL_OK) { ERR_FAIL_V_MSG(false, "Error calling method from MethodTweener: " + Variant::get_callable_error_text(callback, argptr, 1, ce)); } @@ -956,10 +722,10 @@ void MethodTweener::_bind_methods() { ClassDB::bind_method(D_METHOD("set_ease", "ease"), &MethodTweener::set_ease); } -MethodTweener::MethodTweener(Callable p_callback, Variant p_from, Variant p_to, float p_duration) { +MethodTweener::MethodTweener(Callable p_callback, Variant p_from, Variant p_to, double p_duration) { callback = p_callback; initial_val = p_from; - delta_val = tween->calculate_delta_value(p_from, p_to); + delta_val = Animation::subtract_variant(p_to, p_from); final_val = p_to; duration = p_duration; } diff --git a/scene/animation/tween.h b/scene/animation/tween.h index 40268405cf..345974ecc5 100644 --- a/scene/animation/tween.h +++ b/scene/animation/tween.h @@ -42,13 +42,13 @@ class Tweener : public RefCounted { public: virtual void set_tween(Ref<Tween> p_tween); virtual void start() = 0; - virtual bool step(float &r_delta) = 0; + virtual bool step(double &r_delta) = 0; void clear_tween(); protected: static void _bind_methods(); Ref<Tween> tween; - float elapsed_time = 0; + double elapsed_time = 0; bool finished = false; }; @@ -103,7 +103,7 @@ private: ObjectID bound_node; Vector<List<Ref<Tweener>>> tweeners; - float total_time = 0; + double total_time = 0; int current_step = -1; int loops = 1; int loops_done = 0; @@ -129,13 +129,13 @@ protected: static void _bind_methods(); public: - Ref<PropertyTweener> tween_property(Object *p_target, NodePath p_property, Variant p_to, float p_duration); - Ref<IntervalTweener> tween_interval(float p_time); + Ref<PropertyTweener> tween_property(Object *p_target, NodePath p_property, Variant p_to, double p_duration); + Ref<IntervalTweener> tween_interval(double p_time); Ref<CallbackTweener> tween_callback(Callable p_callback); - Ref<MethodTweener> tween_method(Callable p_callback, Variant p_from, Variant p_to, float p_duration); + Ref<MethodTweener> tween_method(Callable p_callback, Variant p_from, Variant p_to, double p_duration); void append(Ref<Tweener> p_tweener); - bool custom_step(float p_delta); + bool custom_step(double p_delta); void stop(); void pause(); void play(); @@ -163,13 +163,12 @@ public: Ref<Tween> chain(); static real_t run_equation(TransitionType p_trans_type, EaseType p_ease_type, real_t t, real_t b, real_t c, real_t d); - static Variant interpolate_variant(Variant p_initial_val, Variant p_delta_val, float p_time, float p_duration, Tween::TransitionType p_trans, Tween::EaseType p_ease); - Variant calculate_delta_value(Variant p_intial_val, Variant p_final_val); + static Variant interpolate_variant(Variant p_initial_val, Variant p_delta_val, double p_time, double p_duration, Tween::TransitionType p_trans, Tween::EaseType p_ease); - bool step(float p_delta); + bool step(double p_delta); bool can_process(bool p_tree_paused) const; Node *get_bound_node() const; - float get_total_time() const; + double get_total_time() const; Tween(); Tween(bool p_valid); @@ -189,13 +188,13 @@ public: Ref<PropertyTweener> as_relative(); Ref<PropertyTweener> set_trans(Tween::TransitionType p_trans); Ref<PropertyTweener> set_ease(Tween::EaseType p_ease); - Ref<PropertyTweener> set_delay(float p_delay); + Ref<PropertyTweener> set_delay(double p_delay); void set_tween(Ref<Tween> p_tween) override; void start() override; - bool step(float &r_delta) override; + bool step(double &r_delta) override; - PropertyTweener(Object *p_target, NodePath p_property, Variant p_to, float p_duration); + PropertyTweener(Object *p_target, NodePath p_property, Variant p_to, double p_duration); PropertyTweener(); protected: @@ -209,11 +208,11 @@ private: Variant final_val; Variant delta_val; - float duration = 0; + double duration = 0; Tween::TransitionType trans_type = Tween::TRANS_MAX; // This is set inside set_tween(); Tween::EaseType ease_type = Tween::EASE_MAX; - float delay = 0; + double delay = 0; bool do_continue = true; bool relative = false; }; @@ -223,23 +222,23 @@ class IntervalTweener : public Tweener { public: void start() override; - bool step(float &r_delta) override; + bool step(double &r_delta) override; - IntervalTweener(float p_time); + IntervalTweener(double p_time); IntervalTweener(); private: - float duration = 0; + double duration = 0; }; class CallbackTweener : public Tweener { GDCLASS(CallbackTweener, Tweener); public: - Ref<CallbackTweener> set_delay(float p_delay); + Ref<CallbackTweener> set_delay(double p_delay); void start() override; - bool step(float &r_delta) override; + bool step(double &r_delta) override; CallbackTweener(Callable p_callback); CallbackTweener(); @@ -249,7 +248,7 @@ protected: private: Callable callback; - float delay = 0; + double delay = 0; }; class MethodTweener : public Tweener { @@ -258,21 +257,21 @@ class MethodTweener : public Tweener { public: Ref<MethodTweener> set_trans(Tween::TransitionType p_trans); Ref<MethodTweener> set_ease(Tween::EaseType p_ease); - Ref<MethodTweener> set_delay(float p_delay); + Ref<MethodTweener> set_delay(double p_delay); void set_tween(Ref<Tween> p_tween) override; void start() override; - bool step(float &r_delta) override; + bool step(double &r_delta) override; - MethodTweener(Callable p_callback, Variant p_from, Variant p_to, float p_duration); + MethodTweener(Callable p_callback, Variant p_from, Variant p_to, double p_duration); MethodTweener(); protected: static void _bind_methods(); private: - float duration = 0; - float delay = 0; + double duration = 0; + double delay = 0; Tween::TransitionType trans_type = Tween::TRANS_MAX; Tween::EaseType ease_type = Tween::EASE_MAX; @@ -283,4 +282,4 @@ private: Callable callback; }; -#endif +#endif // TWEEN_H diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp index efb647af29..03115e765f 100644 --- a/scene/audio/audio_stream_player.cpp +++ b/scene/audio/audio_stream_player.cpp @@ -40,6 +40,7 @@ void AudioStreamPlayer::_notification(int p_what) { if (autoplay && !Engine::get_singleton()->is_editor_hint()) { play(); } + set_stream_paused(false); } break; case NOTIFICATION_INTERNAL_PROCESS: { @@ -64,6 +65,10 @@ void AudioStreamPlayer::_notification(int p_what) { } break; case NOTIFICATION_EXIT_TREE: { + set_stream_paused(true); + } break; + + case NOTIFICATION_PREDELETE: { for (Ref<AudioStreamPlayback> &playback : stream_playbacks) { AudioServer::get_singleton()->stop_playback_stream(playback); } @@ -136,7 +141,7 @@ void AudioStreamPlayer::play(float p_from_pos) { if (stream->is_monophonic() && is_playing()) { stop(); } - Ref<AudioStreamPlayback> stream_playback = stream->instance_playback(); + Ref<AudioStreamPlayback> stream_playback = stream->instantiate_playback(); ERR_FAIL_COND_MSG(stream_playback.is_null(), "Failed to instantiate playback."); AudioServer::get_singleton()->start_playback_stream(stream_playback, bus, _get_volume_vector(), p_from_pos, pitch_scale); @@ -256,7 +261,7 @@ Vector<AudioFrame> AudioStreamPlayer::_get_volume_vector() { channel_volume_db = AudioFrame(0, 0); } - float volume_linear = Math::db2linear(volume_db); + float volume_linear = Math::db_to_linear(volume_db); // Set the volume vector up according to the speaker mode and mix target. // TODO do we need to scale the volume down when we output to more channels? @@ -283,8 +288,8 @@ Vector<AudioFrame> AudioStreamPlayer::_get_volume_vector() { return volume_vector; } -void AudioStreamPlayer::_validate_property(PropertyInfo &property) const { - if (property.name == "bus") { +void AudioStreamPlayer::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "bus") { String options; for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) { if (i > 0) { @@ -294,10 +299,8 @@ void AudioStreamPlayer::_validate_property(PropertyInfo &property) const { options += name; } - property.hint_string = options; + p_property.hint_string = options; } - - Node::_validate_property(property); } void AudioStreamPlayer::_bus_layout_changed() { diff --git a/scene/audio/audio_stream_player.h b/scene/audio/audio_stream_player.h index 67e616312a..45a6d7663e 100644 --- a/scene/audio/audio_stream_player.h +++ b/scene/audio/audio_stream_player.h @@ -72,7 +72,7 @@ private: Vector<AudioFrame> _get_volume_vector(); protected: - void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; void _notification(int p_what); static void _bind_methods(); diff --git a/scene/debugger/scene_debugger.cpp b/scene/debugger/scene_debugger.cpp index e9c33b1839..4c5a63e52c 100644 --- a/scene/debugger/scene_debugger.cpp +++ b/scene/debugger/scene_debugger.cpp @@ -299,7 +299,7 @@ void SceneDebugger::_save_node(ObjectID id, const String &p_path) { Ref<PackedScene> ps = memnew(PackedScene); ps->pack(node); - ResourceSaver::save(p_path, ps); + ResourceSaver::save(ps, p_path); } void SceneDebugger::_send_object_id(ObjectID p_id, int p_max_size) { diff --git a/scene/debugger/scene_debugger.h b/scene/debugger/scene_debugger.h index 4ed126d36e..911363f45d 100644 --- a/scene/debugger/scene_debugger.h +++ b/scene/debugger/scene_debugger.h @@ -174,4 +174,4 @@ public: }; #endif -#endif +#endif // SCENE_DEBUGGER_H diff --git a/scene/gui/aspect_ratio_container.cpp b/scene/gui/aspect_ratio_container.cpp index 75f19ac452..e4a79c7aa3 100644 --- a/scene/gui/aspect_ratio_container.cpp +++ b/scene/gui/aspect_ratio_container.cpp @@ -51,21 +51,33 @@ Size2 AspectRatioContainer::get_minimum_size() const { } void AspectRatioContainer::set_ratio(float p_ratio) { + if (ratio == p_ratio) { + return; + } ratio = p_ratio; queue_sort(); } void AspectRatioContainer::set_stretch_mode(StretchMode p_mode) { + if (stretch_mode == p_mode) { + return; + } stretch_mode = p_mode; queue_sort(); } void AspectRatioContainer::set_alignment_horizontal(AlignmentMode p_alignment_horizontal) { + if (alignment_horizontal == p_alignment_horizontal) { + return; + } alignment_horizontal = p_alignment_horizontal; queue_sort(); } void AspectRatioContainer::set_alignment_vertical(AlignmentMode p_alignment_vertical) { + if (alignment_vertical == p_alignment_vertical) { + return; + } alignment_vertical = p_alignment_vertical; queue_sort(); } diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index 776623f7ce..af6a99ca62 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -64,7 +64,10 @@ void BaseButton::gui_input(const Ref<InputEvent> &p_event) { bool button_masked = mouse_button.is_valid() && (mouse_button_to_mask(mouse_button->get_button_index()) & button_mask) != MouseButton::NONE; if (button_masked || ui_accept) { + was_mouse_pressed = button_masked; on_action_event(p_event); + was_mouse_pressed = false; + return; } @@ -74,7 +77,7 @@ void BaseButton::gui_input(const Ref<InputEvent> &p_event) { bool last_press_inside = status.pressing_inside; status.pressing_inside = has_point(mouse_motion->get_position()); if (last_press_inside != status.pressing_inside) { - update(); + queue_redraw(); } } } @@ -84,32 +87,32 @@ void BaseButton::_notification(int p_what) { switch (p_what) { case NOTIFICATION_MOUSE_ENTER: { status.hovering = true; - update(); + queue_redraw(); } break; case NOTIFICATION_MOUSE_EXIT: { status.hovering = false; - update(); + queue_redraw(); } break; case NOTIFICATION_DRAG_BEGIN: case NOTIFICATION_SCROLL_BEGIN: { if (status.press_attempt) { status.press_attempt = false; - update(); + queue_redraw(); } } break; case NOTIFICATION_FOCUS_ENTER: { - update(); + queue_redraw(); } break; case NOTIFICATION_FOCUS_EXIT: { if (status.press_attempt) { status.press_attempt = false; - update(); + queue_redraw(); } else if (status.hovering) { - update(); + queue_redraw(); } } break; @@ -185,7 +188,7 @@ void BaseButton::on_action_event(Ref<InputEvent> p_event) { emit_signal(SNAME("button_up")); } - update(); + queue_redraw(); } void BaseButton::pressed() { @@ -207,7 +210,7 @@ void BaseButton::set_disabled(bool p_disabled) { status.press_attempt = false; status.pressing_inside = false; } - update(); + queue_redraw(); } bool BaseButton::is_disabled() const { @@ -231,7 +234,7 @@ void BaseButton::set_pressed(bool p_pressed) { } _toggled(status.pressed); - update(); + queue_redraw(); } void BaseButton::set_pressed_no_signal(bool p_pressed) { @@ -243,7 +246,7 @@ void BaseButton::set_pressed_no_signal(bool p_pressed) { } status.pressed = p_pressed; - update(); + queue_redraw(); } bool BaseButton::is_pressing() const { @@ -261,7 +264,7 @@ bool BaseButton::is_hovered() const { BaseButton::DrawMode BaseButton::get_draw_mode() const { if (status.disabled) { return DRAW_DISABLED; - }; + } if (!status.press_attempt && status.hovering) { if (status.pressed) { @@ -270,8 +273,7 @@ BaseButton::DrawMode BaseButton::get_draw_mode() const { return DRAW_HOVER; } else { - /* determine if pressed or not */ - + // Determine if pressed or not. bool pressing; if (status.press_attempt) { pressing = (status.pressing_inside || keep_pressed_outside); @@ -288,8 +290,6 @@ BaseButton::DrawMode BaseButton::get_draw_mode() const { return DRAW_NORMAL; } } - - return DRAW_NORMAL; } void BaseButton::set_toggle_mode(bool p_on) { @@ -382,7 +382,7 @@ void BaseButton::set_button_group(const Ref<ButtonGroup> &p_group) { button_group->buttons.insert(this); } - update(); //checkbox changes to radio if set a buttongroup + queue_redraw(); //checkbox changes to radio if set a buttongroup } Ref<ButtonGroup> BaseButton::get_button_group() const { @@ -417,6 +417,10 @@ bool BaseButton::_is_focus_owner_in_shortcut_context() const { return ctx_node && vp_focus && (ctx_node == vp_focus || ctx_node->is_ancestor_of(vp_focus)); } +bool BaseButton::_was_pressed_by_mouse() const { + return was_mouse_pressed; +} + void BaseButton::_bind_methods() { ClassDB::bind_method(D_METHOD("set_pressed", "pressed"), &BaseButton::set_pressed); ClassDB::bind_method(D_METHOD("is_pressed"), &BaseButton::is_pressed); @@ -462,7 +466,7 @@ void BaseButton::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_pressed_outside"), "set_keep_pressed_outside", "is_keep_pressed_outside"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shortcut", PROPERTY_HINT_RESOURCE_TYPE, "Shortcut"), "set_shortcut", "get_shortcut"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "button_group", PROPERTY_HINT_RESOURCE_TYPE, "ButtonGroup"), "set_button_group", "get_button_group"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shortcut_context", PROPERTY_HINT_RESOURCE_TYPE, "Node"), "set_shortcut_context", "get_shortcut_context"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shortcut_context", PROPERTY_HINT_NODE_TYPE, "Node"), "set_shortcut_context", "get_shortcut_context"); BIND_ENUM_CONSTANT(DRAW_NORMAL); BIND_ENUM_CONSTANT(DRAW_PRESSED); @@ -490,8 +494,8 @@ void ButtonGroup::get_buttons(List<BaseButton *> *r_buttons) { } } -Array ButtonGroup::_get_buttons() { - Array btns; +TypedArray<BaseButton> ButtonGroup::_get_buttons() { + TypedArray<BaseButton> btns; for (const BaseButton *E : buttons) { btns.push_back(E); } diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h index ba3852ec98..c83b08aadf 100644 --- a/scene/gui/base_button.h +++ b/scene/gui/base_button.h @@ -49,6 +49,7 @@ private: MouseButton button_mask = MouseButton::MASK_LEFT; bool toggle_mode = false; bool shortcut_in_tooltip = true; + bool was_mouse_pressed = false; bool keep_pressed_outside = false; Ref<Shortcut> shortcut; ObjectID shortcut_context; @@ -81,6 +82,7 @@ protected: void _notification(int p_what); bool _is_focus_owner_in_shortcut_context() const; + bool _was_pressed_by_mouse() const; GDVIRTUAL0(_pressed) GDVIRTUAL1(_toggled, bool) @@ -151,8 +153,8 @@ protected: public: BaseButton *get_pressed_button(); void get_buttons(List<BaseButton *> *r_buttons); - Array _get_buttons(); + TypedArray<BaseButton> _get_buttons(); ButtonGroup(); }; -#endif +#endif // BASE_BUTTON_H diff --git a/scene/gui/box_container.cpp b/scene/gui/box_container.cpp index df695feba8..151b0b93d4 100644 --- a/scene/gui/box_container.cpp +++ b/scene/gui/box_container.cpp @@ -44,7 +44,6 @@ void BoxContainer::_resort() { Size2i new_size = get_size(); - int sep = get_theme_constant(SNAME("separation")); //,vertical?"VBoxContainer":"HBoxContainer"); bool rtl = is_layout_rtl(); bool first = true; @@ -90,7 +89,7 @@ void BoxContainer::_resort() { return; } - int stretch_max = (vertical ? new_size.height : new_size.width) - (children_count - 1) * sep; + int stretch_max = (vertical ? new_size.height : new_size.width) - (children_count - 1) * theme_cache.separation; int stretch_diff = stretch_max - stretch_min; if (stretch_diff < 0) { //avoid negative stretch space @@ -214,7 +213,7 @@ void BoxContainer::_resort() { if (first) { first = false; } else { - ofs += sep; + ofs += theme_cache.separation; } int from = ofs; @@ -248,7 +247,6 @@ Size2 BoxContainer::get_minimum_size() const { /* Calculate MINIMUM SIZE */ Size2i minimum; - int sep = get_theme_constant(SNAME("separation")); //,vertical?"VBoxContainer":"HBoxContainer"); bool first = true; @@ -273,7 +271,7 @@ Size2 BoxContainer::get_minimum_size() const { minimum.width = size.width; } - minimum.height += size.height + (first ? 0 : sep); + minimum.height += size.height + (first ? 0 : theme_cache.separation); } else { /* HORIZONTAL */ @@ -281,7 +279,7 @@ Size2 BoxContainer::get_minimum_size() const { minimum.height = size.height; } - minimum.width += size.width + (first ? 0 : sep); + minimum.width += size.width + (first ? 0 : theme_cache.separation); } first = false; @@ -290,6 +288,12 @@ Size2 BoxContainer::get_minimum_size() const { return minimum; } +void BoxContainer::_update_theme_item_cache() { + Container::_update_theme_item_cache(); + + theme_cache.separation = get_theme_constant(SNAME("separation")); +} + void BoxContainer::_notification(int p_what) { switch (p_what) { case NOTIFICATION_SORT_CHILDREN: { @@ -307,7 +311,16 @@ void BoxContainer::_notification(int p_what) { } } +void BoxContainer::_validate_property(PropertyInfo &p_property) const { + if (is_fixed && p_property.name == "vertical") { + p_property.usage = PROPERTY_USAGE_NONE; + } +} + void BoxContainer::set_alignment(AlignmentMode p_alignment) { + if (alignment == p_alignment) { + return; + } alignment = p_alignment; _resort(); } @@ -316,6 +329,17 @@ BoxContainer::AlignmentMode BoxContainer::get_alignment() const { return alignment; } +void BoxContainer::set_vertical(bool p_vertical) { + ERR_FAIL_COND_MSG(is_fixed, "Can't change orientation of " + get_class() + "."); + vertical = p_vertical; + update_minimum_size(); + _resort(); +} + +bool BoxContainer::is_vertical() const { + return vertical; +} + Control *BoxContainer::add_spacer(bool p_begin) { Control *c = memnew(Control); c->set_mouse_filter(MOUSE_FILTER_PASS); //allow spacer to pass mouse events @@ -364,14 +388,17 @@ BoxContainer::BoxContainer(bool p_vertical) { void BoxContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("add_spacer", "begin"), &BoxContainer::add_spacer); - ClassDB::bind_method(D_METHOD("get_alignment"), &BoxContainer::get_alignment); ClassDB::bind_method(D_METHOD("set_alignment", "alignment"), &BoxContainer::set_alignment); + ClassDB::bind_method(D_METHOD("get_alignment"), &BoxContainer::get_alignment); + ClassDB::bind_method(D_METHOD("set_vertical", "vertical"), &BoxContainer::set_vertical); + ClassDB::bind_method(D_METHOD("is_vertical"), &BoxContainer::is_vertical); BIND_ENUM_CONSTANT(ALIGNMENT_BEGIN); BIND_ENUM_CONSTANT(ALIGNMENT_CENTER); BIND_ENUM_CONSTANT(ALIGNMENT_END); ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "Begin,Center,End"), "set_alignment", "get_alignment"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vertical"), "set_vertical", "is_vertical"); } MarginContainer *VBoxContainer::add_margin_child(const String &p_label, Control *p_control, bool p_expand) { diff --git a/scene/gui/box_container.h b/scene/gui/box_container.h index 3043c3ea45..0ee5bd1772 100644 --- a/scene/gui/box_container.h +++ b/scene/gui/box_container.h @@ -47,11 +47,19 @@ private: bool vertical = false; AlignmentMode alignment = ALIGNMENT_BEGIN; + struct ThemeCache { + int separation = 0; + } theme_cache; + void _resort(); protected: - void _notification(int p_what); + bool is_fixed = false; + + virtual void _update_theme_item_cache() override; + void _notification(int p_what); + void _validate_property(PropertyInfo &p_property) const; static void _bind_methods(); public: @@ -60,6 +68,9 @@ public: void set_alignment(AlignmentMode p_alignment); AlignmentMode get_alignment() const; + void set_vertical(bool p_vertical); + bool is_vertical() const; + virtual Size2 get_minimum_size() const override; virtual Vector<int> get_allowed_size_flags_horizontal() const override; @@ -73,7 +84,7 @@ class HBoxContainer : public BoxContainer { public: HBoxContainer() : - BoxContainer(false) {} + BoxContainer(false) { is_fixed = true; } }; class MarginContainer; @@ -84,7 +95,7 @@ public: MarginContainer *add_margin_child(const String &p_label, Control *p_control, bool p_expand = false); VBoxContainer() : - BoxContainer(true) {} + BoxContainer(true) { is_fixed = true; } }; VARIANT_ENUM_CAST(BoxContainer::AlignmentMode); diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp index d8de22d27c..c2b82e01d1 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -34,49 +34,61 @@ #include "servers/rendering_server.h" Size2 Button::get_minimum_size() const { - Size2 minsize = text_buf->get_size(); - if (clip_text || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) { - minsize.width = 0; - } - - if (!expand_icon) { - Ref<Texture2D> _icon; - if (icon.is_null() && has_theme_icon(SNAME("icon"))) { - _icon = Control::get_theme_icon(SNAME("icon")); - } else { - _icon = icon; - } - - if (!_icon.is_null()) { - minsize.height = MAX(minsize.height, _icon->get_height()); - - if (icon_alignment != HORIZONTAL_ALIGNMENT_CENTER) { - minsize.width += _icon->get_width(); - if (!xl_text.is_empty()) { - minsize.width += get_theme_constant(SNAME("h_separation")); - } - } else { - minsize.width = MAX(minsize.width, _icon->get_width()); - } - } - } - if (!xl_text.is_empty()) { - Ref<Font> font = get_theme_font(SNAME("font")); - float font_height = font->get_height(get_theme_font_size(SNAME("font_size"))); - minsize.height = MAX(font_height, minsize.height); + Ref<Texture2D> _icon = icon; + if (_icon.is_null() && has_theme_icon(SNAME("icon"))) { + _icon = theme_cache.icon; } - return get_theme_stylebox(SNAME("normal"))->get_minimum_size() + minsize; + return get_minimum_size_for_text_and_icon("", _icon); } void Button::_set_internal_margin(Side p_side, float p_value) { _internal_margin[p_side] = p_value; } +void Button::_update_theme_item_cache() { + BaseButton::_update_theme_item_cache(); + + theme_cache.normal = get_theme_stylebox(SNAME("normal")); + theme_cache.normal_mirrored = get_theme_stylebox(SNAME("normal_mirrored")); + theme_cache.pressed = get_theme_stylebox(SNAME("pressed")); + theme_cache.pressed_mirrored = get_theme_stylebox(SNAME("pressed_mirrored")); + theme_cache.hover = get_theme_stylebox(SNAME("hover")); + theme_cache.hover_mirrored = get_theme_stylebox(SNAME("hover_mirrored")); + theme_cache.hover_pressed = get_theme_stylebox(SNAME("hover_pressed")); + theme_cache.hover_pressed_mirrored = get_theme_stylebox(SNAME("hover_pressed_mirrored")); + theme_cache.disabled = get_theme_stylebox(SNAME("disabled")); + theme_cache.disabled_mirrored = get_theme_stylebox(SNAME("disabled_mirrored")); + theme_cache.focus = get_theme_stylebox(SNAME("focus")); + + theme_cache.font_color = get_theme_color(SNAME("font_color")); + theme_cache.font_focus_color = get_theme_color(SNAME("font_focus_color")); + theme_cache.font_pressed_color = get_theme_color(SNAME("font_pressed_color")); + theme_cache.font_hover_color = get_theme_color(SNAME("font_hover_color")); + theme_cache.font_hover_pressed_color = get_theme_color(SNAME("font_hover_pressed_color")); + theme_cache.font_disabled_color = get_theme_color(SNAME("font_disabled_color")); + + theme_cache.font = get_theme_font(SNAME("font")); + theme_cache.font_size = get_theme_font_size(SNAME("font_size")); + theme_cache.outline_size = get_theme_constant(SNAME("outline_size")); + theme_cache.font_outline_color = get_theme_color(SNAME("font_outline_color")); + + theme_cache.icon_normal_color = get_theme_color(SNAME("icon_normal_color")); + theme_cache.icon_focus_color = get_theme_color(SNAME("icon_focus_color")); + theme_cache.icon_pressed_color = get_theme_color(SNAME("icon_pressed_color")); + theme_cache.icon_hover_color = get_theme_color(SNAME("icon_hover_color")); + theme_cache.icon_hover_pressed_color = get_theme_color(SNAME("icon_hover_pressed_color")); + theme_cache.icon_disabled_color = get_theme_color(SNAME("icon_disabled_color")); + + theme_cache.icon = get_theme_icon(SNAME("icon")); + + theme_cache.h_separation = get_theme_constant(SNAME("h_separation")); +} + void Button::_notification(int p_what) { switch (p_what) { case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { - update(); + queue_redraw(); } break; case NOTIFICATION_TRANSLATION_CHANGED: { @@ -84,14 +96,14 @@ void Button::_notification(int p_what) { _shape(); update_minimum_size(); - update(); + queue_redraw(); } break; case NOTIFICATION_THEME_CHANGED: { _shape(); update_minimum_size(); - update(); + queue_redraw(); } break; case NOTIFICATION_DRAW: { @@ -100,15 +112,15 @@ void Button::_notification(int p_what) { Color color; Color color_icon(1, 1, 1, 1); - Ref<StyleBox> style = get_theme_stylebox(SNAME("normal")); + Ref<StyleBox> style = theme_cache.normal; bool rtl = is_layout_rtl(); switch (get_draw_mode()) { case DRAW_NORMAL: { if (rtl && has_theme_stylebox(SNAME("normal_mirrored"))) { - style = get_theme_stylebox(SNAME("normal_mirrored")); + style = theme_cache.normal_mirrored; } else { - style = get_theme_stylebox(SNAME("normal")); + style = theme_cache.normal; } if (!flat) { @@ -117,14 +129,14 @@ void Button::_notification(int p_what) { // Focus colors only take precedence over normal state. if (has_focus()) { - color = get_theme_color(SNAME("font_focus_color")); + color = theme_cache.font_focus_color; if (has_theme_color(SNAME("icon_focus_color"))) { - color_icon = get_theme_color(SNAME("icon_focus_color")); + color_icon = theme_cache.icon_focus_color; } } else { - color = get_theme_color(SNAME("font_color")); + color = theme_cache.font_color; if (has_theme_color(SNAME("icon_normal_color"))) { - color_icon = get_theme_color(SNAME("icon_normal_color")); + color_icon = theme_cache.icon_normal_color; } } } break; @@ -132,19 +144,19 @@ void Button::_notification(int p_what) { // Edge case for CheckButton and CheckBox. if (has_theme_stylebox("hover_pressed")) { if (rtl && has_theme_stylebox(SNAME("hover_pressed_mirrored"))) { - style = get_theme_stylebox(SNAME("hover_pressed_mirrored")); + style = theme_cache.hover_pressed_mirrored; } else { - style = get_theme_stylebox(SNAME("hover_pressed")); + style = theme_cache.hover_pressed; } if (!flat) { style->draw(ci, Rect2(Point2(0, 0), size)); } if (has_theme_color(SNAME("font_hover_pressed_color"))) { - color = get_theme_color(SNAME("font_hover_pressed_color")); + color = theme_cache.font_hover_pressed_color; } if (has_theme_color(SNAME("icon_hover_pressed_color"))) { - color_icon = get_theme_color(SNAME("icon_hover_pressed_color")); + color_icon = theme_cache.icon_hover_pressed_color; } break; @@ -153,53 +165,53 @@ void Button::_notification(int p_what) { } case DRAW_PRESSED: { if (rtl && has_theme_stylebox(SNAME("pressed_mirrored"))) { - style = get_theme_stylebox(SNAME("pressed_mirrored")); + style = theme_cache.pressed_mirrored; } else { - style = get_theme_stylebox(SNAME("pressed")); + style = theme_cache.pressed; } if (!flat) { style->draw(ci, Rect2(Point2(0, 0), size)); } if (has_theme_color(SNAME("font_pressed_color"))) { - color = get_theme_color(SNAME("font_pressed_color")); + color = theme_cache.font_pressed_color; } else { - color = get_theme_color(SNAME("font_color")); + color = theme_cache.font_color; } if (has_theme_color(SNAME("icon_pressed_color"))) { - color_icon = get_theme_color(SNAME("icon_pressed_color")); + color_icon = theme_cache.icon_pressed_color; } } break; case DRAW_HOVER: { if (rtl && has_theme_stylebox(SNAME("hover_mirrored"))) { - style = get_theme_stylebox(SNAME("hover_mirrored")); + style = theme_cache.hover_mirrored; } else { - style = get_theme_stylebox(SNAME("hover")); + style = theme_cache.hover; } if (!flat) { style->draw(ci, Rect2(Point2(0, 0), size)); } - color = get_theme_color(SNAME("font_hover_color")); + color = theme_cache.font_hover_color; if (has_theme_color(SNAME("icon_hover_color"))) { - color_icon = get_theme_color(SNAME("icon_hover_color")); + color_icon = theme_cache.icon_hover_color; } } break; case DRAW_DISABLED: { if (rtl && has_theme_stylebox(SNAME("disabled_mirrored"))) { - style = get_theme_stylebox(SNAME("disabled_mirrored")); + style = theme_cache.disabled_mirrored; } else { - style = get_theme_stylebox(SNAME("disabled")); + style = theme_cache.disabled; } if (!flat) { style->draw(ci, Rect2(Point2(0, 0), size)); } - color = get_theme_color(SNAME("font_disabled_color")); + color = theme_cache.font_disabled_color; if (has_theme_color(SNAME("icon_disabled_color"))) { - color_icon = get_theme_color(SNAME("icon_disabled_color")); + color_icon = theme_cache.icon_disabled_color; } else { color_icon.a = 0.4; } @@ -208,13 +220,13 @@ void Button::_notification(int p_what) { } if (has_focus()) { - Ref<StyleBox> style2 = get_theme_stylebox(SNAME("focus")); + Ref<StyleBox> style2 = theme_cache.focus; style2->draw(ci, Rect2(Point2(), size)); } Ref<Texture2D> _icon; if (icon.is_null() && has_theme_icon(SNAME("icon"))) { - _icon = Control::get_theme_icon(SNAME("icon")); + _icon = theme_cache.icon; } else { _icon = icon; } @@ -244,21 +256,21 @@ void Button::_notification(int p_what) { if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_LEFT) { style_offset.x = style->get_margin(SIDE_LEFT); if (_internal_margin[SIDE_LEFT] > 0) { - icon_ofs_region = _internal_margin[SIDE_LEFT] + get_theme_constant(SNAME("h_separation")); + icon_ofs_region = _internal_margin[SIDE_LEFT] + theme_cache.h_separation; } } else if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_CENTER) { style_offset.x = 0.0; } else if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_RIGHT) { style_offset.x = -style->get_margin(SIDE_RIGHT); if (_internal_margin[SIDE_RIGHT] > 0) { - icon_ofs_region = -_internal_margin[SIDE_RIGHT] - get_theme_constant(SNAME("h_separation")); + icon_ofs_region = -_internal_margin[SIDE_RIGHT] - theme_cache.h_separation; } } style_offset.y = style->get_margin(SIDE_TOP); if (expand_icon) { Size2 _size = get_size() - style->get_offset() * 2; - int icon_text_separation = text.is_empty() ? 0 : get_theme_constant(SNAME("h_separation")); + int icon_text_separation = text.is_empty() ? 0 : theme_cache.h_separation; _size.width -= icon_text_separation + icon_ofs_region; if (!clip_text && icon_align_rtl_checked != HORIZONTAL_ALIGNMENT_CENTER) { _size.width -= text_buf->get_size().width; @@ -283,11 +295,12 @@ void Button::_notification(int p_what) { } if (icon_region.size.width > 0) { - draw_texture_rect_region(_icon, icon_region, Rect2(Point2(), _icon->get_size()), color_icon); + Rect2 icon_region_rounded = Rect2(icon_region.position.round(), icon_region.size.round()); + draw_texture_rect(_icon, icon_region_rounded, false, color_icon); } } - Point2 icon_ofs = !_icon.is_null() ? Point2(icon_region.size.width + get_theme_constant(SNAME("h_separation")), 0) : Point2(); + Point2 icon_ofs = !_icon.is_null() ? Point2(icon_region.size.width + theme_cache.h_separation, 0) : Point2(); if (align_rtl_checked == HORIZONTAL_ALIGNMENT_CENTER && icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_CENTER) { icon_ofs.x = 0.0; } @@ -297,10 +310,10 @@ void Button::_notification(int p_what) { int text_width = MAX(1, (clip_text || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) ? MIN(text_clip, text_buf->get_size().x) : text_buf->get_size().x); if (_internal_margin[SIDE_LEFT] > 0) { - text_clip -= _internal_margin[SIDE_LEFT] + get_theme_constant(SNAME("h_separation")); + text_clip -= _internal_margin[SIDE_LEFT] + theme_cache.h_separation; } if (_internal_margin[SIDE_RIGHT] > 0) { - text_clip -= _internal_margin[SIDE_RIGHT] + get_theme_constant(SNAME("h_separation")); + text_clip -= _internal_margin[SIDE_RIGHT] + theme_cache.h_separation; } Point2 text_ofs = (size - style->get_minimum_size() - icon_ofs - text_buf->get_size() - Point2(_internal_margin[SIDE_RIGHT] - _internal_margin[SIDE_LEFT], 0)) / 2.0; @@ -314,7 +327,7 @@ void Button::_notification(int p_what) { icon_ofs.x = 0.0; } if (_internal_margin[SIDE_LEFT] > 0) { - text_ofs.x = style->get_margin(SIDE_LEFT) + icon_ofs.x + _internal_margin[SIDE_LEFT] + get_theme_constant(SNAME("h_separation")); + text_ofs.x = style->get_margin(SIDE_LEFT) + icon_ofs.x + _internal_margin[SIDE_LEFT] + theme_cache.h_separation; } else { text_ofs.x = style->get_margin(SIDE_LEFT) + icon_ofs.x; } @@ -331,7 +344,7 @@ void Button::_notification(int p_what) { } break; case HORIZONTAL_ALIGNMENT_RIGHT: { if (_internal_margin[SIDE_RIGHT] > 0) { - text_ofs.x = size.x - style->get_margin(SIDE_RIGHT) - text_width - _internal_margin[SIDE_RIGHT] - get_theme_constant(SNAME("h_separation")); + text_ofs.x = size.x - style->get_margin(SIDE_RIGHT) - text_width - _internal_margin[SIDE_RIGHT] - theme_cache.h_separation; } else { text_ofs.x = size.x - style->get_margin(SIDE_RIGHT) - text_width; } @@ -342,8 +355,8 @@ void Button::_notification(int p_what) { } break; } - Color font_outline_color = get_theme_color(SNAME("font_outline_color")); - int outline_size = get_theme_constant(SNAME("outline_size")); + Color font_outline_color = theme_cache.font_outline_color; + int outline_size = theme_cache.outline_size; if (outline_size > 0 && font_outline_color.a > 0) { text_buf->draw_outline(ci, text_ofs, outline_size, font_outline_color); } @@ -352,18 +365,67 @@ void Button::_notification(int p_what) { } } -void Button::_shape() { - Ref<Font> font = get_theme_font(SNAME("font")); - int font_size = get_theme_font_size(SNAME("font_size")); +Size2 Button::get_minimum_size_for_text_and_icon(const String &p_text, Ref<Texture2D> p_icon) const { + Ref<TextParagraph> paragraph; + if (p_text.is_empty()) { + paragraph = text_buf; + } else { + paragraph.instantiate(); + const_cast<Button *>(this)->_shape(paragraph, p_text); + } + + Size2 minsize = paragraph->get_size(); + if (clip_text || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) { + minsize.width = 0; + } + + if (!expand_icon && p_icon.is_valid()) { + minsize.height = MAX(minsize.height, p_icon->get_height()); + + if (icon_alignment != HORIZONTAL_ALIGNMENT_CENTER) { + minsize.width += p_icon->get_width(); + if (!xl_text.is_empty() || !p_text.is_empty()) { + minsize.width += MAX(0, theme_cache.h_separation); + } + } else { + minsize.width = MAX(minsize.width, p_icon->get_width()); + } + } + + if (!xl_text.is_empty() || !p_text.is_empty()) { + Ref<Font> font = theme_cache.font; + float font_height = font->get_height(theme_cache.font_size); + minsize.height = MAX(font_height, minsize.height); + } + + return theme_cache.normal->get_minimum_size() + minsize; +} + +void Button::_shape(Ref<TextParagraph> p_paragraph, String p_text) { + if (p_paragraph.is_null()) { + p_paragraph = text_buf; + } + + if (p_text.is_empty()) { + p_text = xl_text; + } + + p_paragraph->clear(); + + Ref<Font> font = theme_cache.font; + int font_size = theme_cache.font_size; + if (font.is_null() || font_size == 0) { + // Can't shape without a valid font and a non-zero size. + return; + } - text_buf->clear(); if (text_direction == Control::TEXT_DIRECTION_INHERITED) { - text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); + p_paragraph->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); } else { - text_buf->set_direction((TextServer::Direction)text_direction); + p_paragraph->set_direction((TextServer::Direction)text_direction); } - text_buf->add_string(xl_text, font, font_size, language); - text_buf->set_text_overrun_behavior(overrun_behavior); + p_paragraph->add_string(p_text, font, font_size, language); + p_paragraph->set_text_overrun_behavior(overrun_behavior); } void Button::set_text_overrun_behavior(TextServer::OverrunBehavior p_behavior) { @@ -371,7 +433,7 @@ void Button::set_text_overrun_behavior(TextServer::OverrunBehavior p_behavior) { overrun_behavior = p_behavior; _shape(); - update(); + queue_redraw(); update_minimum_size(); } } @@ -386,7 +448,7 @@ void Button::set_text(const String &p_text) { xl_text = atr(text); _shape(); - update(); + queue_redraw(); update_minimum_size(); } } @@ -400,7 +462,7 @@ void Button::set_text_direction(Control::TextDirection p_text_direction) { if (text_direction != p_text_direction) { text_direction = p_text_direction; _shape(); - update(); + queue_redraw(); } } @@ -412,7 +474,7 @@ void Button::set_language(const String &p_language) { if (language != p_language) { language = p_language; _shape(); - update(); + queue_redraw(); } } @@ -423,7 +485,7 @@ String Button::get_language() const { void Button::set_icon(const Ref<Texture2D> &p_icon) { if (icon != p_icon) { icon = p_icon; - update(); + queue_redraw(); update_minimum_size(); } } @@ -435,7 +497,7 @@ Ref<Texture2D> Button::get_icon() const { void Button::set_expand_icon(bool p_enabled) { if (expand_icon != p_enabled) { expand_icon = p_enabled; - update(); + queue_redraw(); update_minimum_size(); } } @@ -447,7 +509,7 @@ bool Button::is_expand_icon() const { void Button::set_flat(bool p_enabled) { if (flat != p_enabled) { flat = p_enabled; - update(); + queue_redraw(); } } @@ -458,7 +520,7 @@ bool Button::is_flat() const { void Button::set_clip_text(bool p_enabled) { if (clip_text != p_enabled) { clip_text = p_enabled; - update(); + queue_redraw(); update_minimum_size(); } } @@ -470,7 +532,7 @@ bool Button::get_clip_text() const { void Button::set_text_alignment(HorizontalAlignment p_alignment) { if (alignment != p_alignment) { alignment = p_alignment; - update(); + queue_redraw(); } } @@ -481,7 +543,7 @@ HorizontalAlignment Button::get_text_alignment() const { void Button::set_icon_alignment(HorizontalAlignment p_alignment) { icon_alignment = p_alignment; update_minimum_size(); - update(); + queue_redraw(); } HorizontalAlignment Button::get_icon_alignment() const { @@ -526,7 +588,7 @@ void Button::_bind_methods() { Button::Button(const String &p_text) { text_buf.instantiate(); - text_buf->set_flags(TextServer::BREAK_MANDATORY); + text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_TRIM_EDGE_SPACES); set_mouse_filter(MOUSE_FILTER_STOP); set_text(p_text); diff --git a/scene/gui/button.h b/scene/gui/button.h index 7a29cba677..9d9f9763db 100644 --- a/scene/gui/button.h +++ b/scene/gui/button.h @@ -54,16 +54,56 @@ private: HorizontalAlignment icon_alignment = HORIZONTAL_ALIGNMENT_LEFT; float _internal_margin[4] = {}; - void _shape(); + struct ThemeCache { + Ref<StyleBox> normal; + Ref<StyleBox> normal_mirrored; + Ref<StyleBox> pressed; + Ref<StyleBox> pressed_mirrored; + Ref<StyleBox> hover; + Ref<StyleBox> hover_mirrored; + Ref<StyleBox> hover_pressed; + Ref<StyleBox> hover_pressed_mirrored; + Ref<StyleBox> disabled; + Ref<StyleBox> disabled_mirrored; + Ref<StyleBox> focus; + + Color font_color; + Color font_focus_color; + Color font_pressed_color; + Color font_hover_color; + Color font_hover_pressed_color; + Color font_disabled_color; + + Ref<Font> font; + int font_size = 0; + int outline_size = 0; + Color font_outline_color; + + Color icon_normal_color; + Color icon_focus_color; + Color icon_pressed_color; + Color icon_hover_color; + Color icon_hover_pressed_color; + Color icon_disabled_color; + + Ref<Texture2D> icon; + + int h_separation = 0; + } theme_cache; + + void _shape(Ref<TextParagraph> p_paragraph = Ref<TextParagraph>(), String p_text = ""); protected: void _set_internal_margin(Side p_side, float p_value); + virtual void _update_theme_item_cache() override; void _notification(int p_what); static void _bind_methods(); public: virtual Size2 get_minimum_size() const override; + Size2 get_minimum_size_for_text_and_icon(const String &p_text, Ref<Texture2D> p_icon) const; + void set_text(const String &p_text); String get_text() const; @@ -98,4 +138,4 @@ public: ~Button(); }; -#endif +#endif // BUTTON_H diff --git a/scene/gui/check_box.cpp b/scene/gui/check_box.cpp index cb80f5b5ef..f5eb0b957f 100644 --- a/scene/gui/check_box.cpp +++ b/scene/gui/check_box.cpp @@ -33,39 +33,30 @@ #include "servers/rendering_server.h" Size2 CheckBox::get_icon_size() const { - Ref<Texture2D> checked = Control::get_theme_icon(SNAME("checked")); - Ref<Texture2D> unchecked = Control::get_theme_icon(SNAME("unchecked")); - Ref<Texture2D> radio_checked = Control::get_theme_icon(SNAME("radio_checked")); - Ref<Texture2D> radio_unchecked = Control::get_theme_icon(SNAME("radio_unchecked")); - Ref<Texture2D> checked_disabled = Control::get_theme_icon(SNAME("checked_disabled")); - Ref<Texture2D> unchecked_disabled = Control::get_theme_icon(SNAME("unchecked_disabled")); - Ref<Texture2D> radio_checked_disabled = Control::get_theme_icon(SNAME("radio_checked_disabled")); - Ref<Texture2D> radio_unchecked_disabled = Control::get_theme_icon(SNAME("radio_unchecked_disabled")); - Size2 tex_size = Size2(0, 0); - if (!checked.is_null()) { - tex_size = Size2(checked->get_width(), checked->get_height()); + if (!theme_cache.checked.is_null()) { + tex_size = Size2(theme_cache.checked->get_width(), theme_cache.checked->get_height()); } - if (!unchecked.is_null()) { - tex_size = Size2(MAX(tex_size.width, unchecked->get_width()), MAX(tex_size.height, unchecked->get_height())); + if (!theme_cache.unchecked.is_null()) { + tex_size = Size2(MAX(tex_size.width, theme_cache.unchecked->get_width()), MAX(tex_size.height, theme_cache.unchecked->get_height())); } - if (!radio_checked.is_null()) { - tex_size = Size2(MAX(tex_size.width, radio_checked->get_width()), MAX(tex_size.height, radio_checked->get_height())); + if (!theme_cache.radio_checked.is_null()) { + tex_size = Size2(MAX(tex_size.width, theme_cache.radio_checked->get_width()), MAX(tex_size.height, theme_cache.radio_checked->get_height())); } - if (!radio_unchecked.is_null()) { - tex_size = Size2(MAX(tex_size.width, radio_unchecked->get_width()), MAX(tex_size.height, radio_unchecked->get_height())); + if (!theme_cache.radio_unchecked.is_null()) { + tex_size = Size2(MAX(tex_size.width, theme_cache.radio_unchecked->get_width()), MAX(tex_size.height, theme_cache.radio_unchecked->get_height())); } - if (!checked_disabled.is_null()) { - tex_size = Size2(MAX(tex_size.width, checked_disabled->get_width()), MAX(tex_size.height, checked_disabled->get_height())); + if (!theme_cache.checked_disabled.is_null()) { + tex_size = Size2(MAX(tex_size.width, theme_cache.checked_disabled->get_width()), MAX(tex_size.height, theme_cache.checked_disabled->get_height())); } - if (!unchecked_disabled.is_null()) { - tex_size = Size2(MAX(tex_size.width, unchecked_disabled->get_width()), MAX(tex_size.height, unchecked_disabled->get_height())); + if (!theme_cache.unchecked_disabled.is_null()) { + tex_size = Size2(MAX(tex_size.width, theme_cache.unchecked_disabled->get_width()), MAX(tex_size.height, theme_cache.unchecked_disabled->get_height())); } - if (!radio_checked_disabled.is_null()) { - tex_size = Size2(MAX(tex_size.width, radio_checked_disabled->get_width()), MAX(tex_size.height, radio_checked_disabled->get_height())); + if (!theme_cache.radio_checked_disabled.is_null()) { + tex_size = Size2(MAX(tex_size.width, theme_cache.radio_checked_disabled->get_width()), MAX(tex_size.height, theme_cache.radio_checked_disabled->get_height())); } - if (!radio_unchecked_disabled.is_null()) { - tex_size = Size2(MAX(tex_size.width, radio_unchecked_disabled->get_width()), MAX(tex_size.height, radio_unchecked_disabled->get_height())); + if (!theme_cache.radio_unchecked_disabled.is_null()) { + tex_size = Size2(MAX(tex_size.width, theme_cache.radio_unchecked_disabled->get_width()), MAX(tex_size.height, theme_cache.radio_unchecked_disabled->get_height())); } return tex_size; } @@ -75,14 +66,30 @@ Size2 CheckBox::get_minimum_size() const { Size2 tex_size = get_icon_size(); minsize.width += tex_size.width; if (get_text().length() > 0) { - minsize.width += get_theme_constant(SNAME("h_separation")); + minsize.width += MAX(0, theme_cache.h_separation); } - Ref<StyleBox> sb = get_theme_stylebox(SNAME("normal")); - minsize.height = MAX(minsize.height, tex_size.height + sb->get_margin(SIDE_TOP) + sb->get_margin(SIDE_BOTTOM)); + minsize.height = MAX(minsize.height, tex_size.height + theme_cache.normal_style->get_margin(SIDE_TOP) + theme_cache.normal_style->get_margin(SIDE_BOTTOM)); return minsize; } +void CheckBox::_update_theme_item_cache() { + Button::_update_theme_item_cache(); + + theme_cache.h_separation = get_theme_constant(SNAME("h_separation")); + theme_cache.check_v_offset = get_theme_constant(SNAME("check_v_offset")); + theme_cache.normal_style = get_theme_stylebox(SNAME("normal")); + + theme_cache.checked = get_theme_icon(SNAME("checked")); + theme_cache.unchecked = get_theme_icon(SNAME("unchecked")); + theme_cache.radio_checked = get_theme_icon(SNAME("radio_checked")); + theme_cache.radio_unchecked = get_theme_icon(SNAME("radio_unchecked")); + theme_cache.checked_disabled = get_theme_icon(SNAME("checked_disabled")); + theme_cache.unchecked_disabled = get_theme_icon(SNAME("unchecked_disabled")); + theme_cache.radio_checked_disabled = get_theme_icon(SNAME("radio_checked_disabled")); + theme_cache.radio_unchecked_disabled = get_theme_icon(SNAME("radio_unchecked_disabled")); +} + void CheckBox::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: @@ -100,22 +107,39 @@ void CheckBox::_notification(int p_what) { case NOTIFICATION_DRAW: { RID ci = get_canvas_item(); - Ref<Texture2D> on = Control::get_theme_icon(vformat("%s%s", is_radio() ? "radio_checked" : "checked", is_disabled() ? "_disabled" : "")); - Ref<Texture2D> off = Control::get_theme_icon(vformat("%s%s", is_radio() ? "radio_unchecked" : "unchecked", is_disabled() ? "_disabled" : "")); - Ref<StyleBox> sb = get_theme_stylebox(SNAME("normal")); + Ref<Texture2D> on_tex; + Ref<Texture2D> off_tex; + + if (is_radio()) { + if (is_disabled()) { + on_tex = theme_cache.radio_checked_disabled; + off_tex = theme_cache.radio_unchecked_disabled; + } else { + on_tex = theme_cache.radio_checked; + off_tex = theme_cache.radio_unchecked; + } + } else { + if (is_disabled()) { + on_tex = theme_cache.checked_disabled; + off_tex = theme_cache.unchecked_disabled; + } else { + on_tex = theme_cache.checked; + off_tex = theme_cache.unchecked; + } + } Vector2 ofs; if (is_layout_rtl()) { - ofs.x = get_size().x - sb->get_margin(SIDE_RIGHT) - get_icon_size().width; + ofs.x = get_size().x - theme_cache.normal_style->get_margin(SIDE_RIGHT) - get_icon_size().width; } else { - ofs.x = sb->get_margin(SIDE_LEFT); + ofs.x = theme_cache.normal_style->get_margin(SIDE_LEFT); } - ofs.y = int((get_size().height - get_icon_size().height) / 2) + get_theme_constant(SNAME("check_v_adjust")); + ofs.y = int((get_size().height - get_icon_size().height) / 2) + theme_cache.check_v_offset; if (is_pressed()) { - on->draw(ci, ofs); + on_tex->draw(ci, ofs); } else { - off->draw(ci, ofs); + off_tex->draw(ci, ofs); } } break; } diff --git a/scene/gui/check_box.h b/scene/gui/check_box.h index fcdb2ce08c..8438d0e589 100644 --- a/scene/gui/check_box.h +++ b/scene/gui/check_box.h @@ -36,9 +36,26 @@ class CheckBox : public Button { GDCLASS(CheckBox, Button); + struct ThemeCache { + int h_separation = 0; + int check_v_offset = 0; + Ref<StyleBox> normal_style; + + Ref<Texture2D> checked; + Ref<Texture2D> unchecked; + Ref<Texture2D> radio_checked; + Ref<Texture2D> radio_unchecked; + Ref<Texture2D> checked_disabled; + Ref<Texture2D> unchecked_disabled; + Ref<Texture2D> radio_checked_disabled; + Ref<Texture2D> radio_unchecked_disabled; + } theme_cache; + protected: Size2 get_icon_size() const; Size2 get_minimum_size() const override; + + virtual void _update_theme_item_cache() override; void _notification(int p_what); bool is_radio(); diff --git a/scene/gui/check_button.cpp b/scene/gui/check_button.cpp index a09873ea4f..9466512699 100644 --- a/scene/gui/check_button.cpp +++ b/scene/gui/check_button.cpp @@ -34,14 +34,33 @@ #include "servers/rendering_server.h" Size2 CheckButton::get_icon_size() const { - Ref<Texture2D> on = Control::get_theme_icon(is_disabled() ? "on_disabled" : "on"); - Ref<Texture2D> off = Control::get_theme_icon(is_disabled() ? "off_disabled" : "off"); + Ref<Texture2D> on_tex; + Ref<Texture2D> off_tex; + + if (is_layout_rtl()) { + if (is_disabled()) { + on_tex = theme_cache.checked_disabled_mirrored; + off_tex = theme_cache.unchecked_disabled_mirrored; + } else { + on_tex = theme_cache.checked_mirrored; + off_tex = theme_cache.unchecked_mirrored; + } + } else { + if (is_disabled()) { + on_tex = theme_cache.checked_disabled; + off_tex = theme_cache.unchecked_disabled; + } else { + on_tex = theme_cache.checked; + off_tex = theme_cache.unchecked; + } + } + Size2 tex_size = Size2(0, 0); - if (!on.is_null()) { - tex_size = Size2(on->get_width(), on->get_height()); + if (!on_tex.is_null()) { + tex_size = Size2(on_tex->get_width(), on_tex->get_height()); } - if (!off.is_null()) { - tex_size = Size2(MAX(tex_size.width, off->get_width()), MAX(tex_size.height, off->get_height())); + if (!off_tex.is_null()) { + tex_size = Size2(MAX(tex_size.width, off_tex->get_width()), MAX(tex_size.height, off_tex->get_height())); } return tex_size; @@ -52,14 +71,30 @@ Size2 CheckButton::get_minimum_size() const { Size2 tex_size = get_icon_size(); minsize.width += tex_size.width; if (get_text().length() > 0) { - minsize.width += get_theme_constant(SNAME("h_separation")); + minsize.width += MAX(0, theme_cache.h_separation); } - Ref<StyleBox> sb = get_theme_stylebox(SNAME("normal")); - minsize.height = MAX(minsize.height, tex_size.height + sb->get_margin(SIDE_TOP) + sb->get_margin(SIDE_BOTTOM)); + minsize.height = MAX(minsize.height, tex_size.height + theme_cache.normal_style->get_margin(SIDE_TOP) + theme_cache.normal_style->get_margin(SIDE_BOTTOM)); return minsize; } +void CheckButton::_update_theme_item_cache() { + Button::_update_theme_item_cache(); + + theme_cache.h_separation = get_theme_constant(SNAME("h_separation")); + theme_cache.check_v_offset = get_theme_constant(SNAME("check_v_offset")); + theme_cache.normal_style = get_theme_stylebox(SNAME("normal")); + + theme_cache.checked = get_theme_icon(SNAME("checked")); + theme_cache.unchecked = get_theme_icon(SNAME("unchecked")); + theme_cache.checked_disabled = get_theme_icon(SNAME("checked_disabled")); + theme_cache.unchecked_disabled = get_theme_icon(SNAME("unchecked_disabled")); + theme_cache.checked_mirrored = get_theme_icon(SNAME("checked_mirrored")); + theme_cache.unchecked_mirrored = get_theme_icon(SNAME("unchecked_mirrored")); + theme_cache.checked_disabled_mirrored = get_theme_icon(SNAME("checked_disabled_mirrored")); + theme_cache.unchecked_disabled_mirrored = get_theme_icon(SNAME("unchecked_disabled_mirrored")); +} + void CheckButton::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: @@ -78,34 +113,41 @@ void CheckButton::_notification(int p_what) { RID ci = get_canvas_item(); bool rtl = is_layout_rtl(); - Ref<Texture2D> on; - if (rtl) { - on = Control::get_theme_icon(is_disabled() ? "on_disabled_mirrored" : "on_mirrored"); - } else { - on = Control::get_theme_icon(is_disabled() ? "on_disabled" : "on"); - } - Ref<Texture2D> off; + Ref<Texture2D> on_tex; + Ref<Texture2D> off_tex; + if (rtl) { - off = Control::get_theme_icon(is_disabled() ? "off_disabled_mirrored" : "off_mirrored"); + if (is_disabled()) { + on_tex = theme_cache.checked_disabled_mirrored; + off_tex = theme_cache.unchecked_disabled_mirrored; + } else { + on_tex = theme_cache.checked_mirrored; + off_tex = theme_cache.unchecked_mirrored; + } } else { - off = Control::get_theme_icon(is_disabled() ? "off_disabled" : "off"); + if (is_disabled()) { + on_tex = theme_cache.checked_disabled; + off_tex = theme_cache.unchecked_disabled; + } else { + on_tex = theme_cache.checked; + off_tex = theme_cache.unchecked; + } } - Ref<StyleBox> sb = get_theme_stylebox(SNAME("normal")); Vector2 ofs; Size2 tex_size = get_icon_size(); if (rtl) { - ofs.x = sb->get_margin(SIDE_LEFT); + ofs.x = theme_cache.normal_style->get_margin(SIDE_LEFT); } else { - ofs.x = get_size().width - (tex_size.width + sb->get_margin(SIDE_RIGHT)); + ofs.x = get_size().width - (tex_size.width + theme_cache.normal_style->get_margin(SIDE_RIGHT)); } - ofs.y = (get_size().height - tex_size.height) / 2 + get_theme_constant(SNAME("check_v_adjust")); + ofs.y = (get_size().height - tex_size.height) / 2 + theme_cache.check_v_offset; if (is_pressed()) { - on->draw(ci, ofs); + on_tex->draw(ci, ofs); } else { - off->draw(ci, ofs); + off_tex->draw(ci, ofs); } } break; } diff --git a/scene/gui/check_button.h b/scene/gui/check_button.h index 7d4bb8bdfc..3878c9628a 100644 --- a/scene/gui/check_button.h +++ b/scene/gui/check_button.h @@ -36,9 +36,26 @@ class CheckButton : public Button { GDCLASS(CheckButton, Button); + struct ThemeCache { + int h_separation = 0; + int check_v_offset = 0; + Ref<StyleBox> normal_style; + + Ref<Texture2D> checked; + Ref<Texture2D> unchecked; + Ref<Texture2D> checked_disabled; + Ref<Texture2D> unchecked_disabled; + Ref<Texture2D> checked_mirrored; + Ref<Texture2D> unchecked_mirrored; + Ref<Texture2D> checked_disabled_mirrored; + Ref<Texture2D> unchecked_disabled_mirrored; + } theme_cache; + protected: Size2 get_icon_size() const; virtual Size2 get_minimum_size() const override; + + virtual void _update_theme_item_cache() override; void _notification(int p_what); public: diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index 22f968eac7..8069ab465b 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -268,7 +268,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) { if (is_code_completion_scroll_pressed && mb->get_button_index() == MouseButton::LEFT) { is_code_completion_scroll_pressed = false; - update(); + queue_redraw(); return; } @@ -281,13 +281,13 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) { case MouseButton::WHEEL_UP: { if (code_completion_current_selected > 0) { code_completion_current_selected--; - update(); + queue_redraw(); } } break; case MouseButton::WHEEL_DOWN: { if (code_completion_current_selected < code_completion_options.size() - 1) { code_completion_current_selected++; - update(); + queue_redraw(); } } break; case MouseButton::LEFT: { @@ -295,7 +295,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) { if (mb->is_double_click()) { confirm_code_completion(); } - update(); + queue_redraw(); } break; default: break; @@ -310,7 +310,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) { is_code_completion_scroll_pressed = true; _update_scroll_selected_line(mb->get_position().y); - update(); + queue_redraw(); } return; @@ -344,7 +344,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) { } } else { if (mb->get_button_index() == MouseButton::LEFT) { - if (mb->is_command_pressed() && !symbol_lookup_word.is_empty()) { + if (mb->is_command_or_control_pressed() && !symbol_lookup_word.is_empty()) { Vector2i mpos = mb->get_position(); if (is_layout_rtl()) { mpos.x = get_size().x - mpos.x; @@ -371,7 +371,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) { } if (symbol_lookup_on_click_enabled) { - if (mm->is_command_pressed() && mm->get_button_mask() == MouseButton::NONE && !is_dragging_cursor()) { + if (mm->is_command_or_control_pressed() && mm->get_button_mask() == MouseButton::NONE && !is_dragging_cursor()) { symbol_lookup_new_word = get_word_at_pos(mpos); if (symbol_lookup_new_word != symbol_lookup_word) { emit_signal(SNAME("symbol_validate"), symbol_lookup_new_word); @@ -384,12 +384,12 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) { bool scroll_hovered = code_completion_scroll_rect.has_point(mpos); if (is_code_completion_scroll_hovered != scroll_hovered) { is_code_completion_scroll_hovered = scroll_hovered; - update(); + queue_redraw(); } if (is_code_completion_scroll_pressed) { _update_scroll_selected_line(mpos.y); - update(); + queue_redraw(); return; } } @@ -407,7 +407,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) { } /* Ctrl + Hover symbols */ -#ifdef OSX_ENABLED +#ifdef MACOS_ENABLED if (k->get_keycode() == Key::META) { #else if (k->get_keycode() == Key::CTRL) { @@ -432,7 +432,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) { /* Allow unicode handling if: */ /* No Modifiers are pressed (except shift) */ - bool allow_unicode_handling = !(k->is_command_pressed() || k->is_ctrl_pressed() || k->is_alt_pressed() || k->is_meta_pressed()); + bool allow_unicode_handling = !(k->is_command_or_control_pressed() || k->is_ctrl_pressed() || k->is_alt_pressed() || k->is_meta_pressed()); /* AUTO-COMPLETE */ if (code_completion_enabled && k->is_action("ui_text_completion_query", true)) { @@ -448,7 +448,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) { } else { code_completion_current_selected = code_completion_options.size() - 1; } - update(); + queue_redraw(); accept_event(); return; } @@ -458,31 +458,31 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) { } else { code_completion_current_selected = 0; } - update(); + queue_redraw(); accept_event(); return; } if (k->is_action("ui_page_up", true)) { code_completion_current_selected = MAX(0, code_completion_current_selected - code_completion_max_lines); - update(); + queue_redraw(); accept_event(); return; } if (k->is_action("ui_page_down", true)) { code_completion_current_selected = MIN(code_completion_options.size() - 1, code_completion_current_selected + code_completion_max_lines); - update(); + queue_redraw(); accept_event(); return; } if (k->is_action("ui_home", true)) { code_completion_current_selected = 0; - update(); + queue_redraw(); accept_event(); return; } if (k->is_action("ui_end", true)) { code_completion_current_selected = code_completion_options.size() - 1; - update(); + queue_redraw(); accept_event(); return; } @@ -1106,7 +1106,7 @@ bool CodeEdit::is_auto_brace_completion_enabled() const { void CodeEdit::set_highlight_matching_braces_enabled(bool p_enabled) { highlight_matching_braces_enabled = p_enabled; - update(); + queue_redraw(); } bool CodeEdit::is_highlight_matching_braces_enabled() const { @@ -1216,30 +1216,39 @@ bool CodeEdit::is_drawing_executing_lines_gutter() const { } void CodeEdit::_main_gutter_draw_callback(int p_line, int p_gutter, const Rect2 &p_region) { + bool shift_pressed = Input::get_singleton()->is_key_pressed(Key::SHIFT); + if (draw_breakpoints && breakpoint_icon.is_valid()) { bool hovering = p_region.has_point(get_local_mouse_pos()); bool breakpointed = is_line_breakpointed(p_line); - if (breakpointed || (hovering && !is_dragging_cursor())) { + if (breakpointed || (hovering && !is_dragging_cursor() && !shift_pressed)) { int padding = p_region.size.x / 6; Rect2 icon_region = p_region; icon_region.position += Point2(padding, padding); icon_region.size -= Point2(padding, padding) * 2; - // Darken icon when hovering & not yet breakpointed. - Color use_color = hovering && !breakpointed ? breakpoint_color.darkened(0.4) : breakpoint_color; + // Darken icon when hovering, shift not pressed & not yet breakpointed. + Color use_color = hovering && !breakpointed && !shift_pressed ? breakpoint_color.darkened(0.4) : breakpoint_color; breakpoint_icon->draw_rect(get_canvas_item(), icon_region, false, use_color); } } - if (draw_bookmarks && is_line_bookmarked(p_line) && bookmark_icon.is_valid()) { - int horizontal_padding = p_region.size.x / 2; - int vertical_padding = p_region.size.y / 4; + if (draw_bookmarks && bookmark_icon.is_valid()) { + bool hovering = p_region.has_point(get_local_mouse_pos()); + bool bookmarked = is_line_bookmarked(p_line); - Rect2 bookmark_region = p_region; - bookmark_region.position += Point2(horizontal_padding, 0); - bookmark_region.size -= Point2(horizontal_padding * 1.1, vertical_padding); - bookmark_icon->draw_rect(get_canvas_item(), bookmark_region, false, bookmark_color); + if (bookmarked || (hovering && !is_dragging_cursor() && shift_pressed)) { + int horizontal_padding = p_region.size.x / 2; + int vertical_padding = p_region.size.y / 4; + Rect2 icon_region = p_region; + icon_region.position += Point2(horizontal_padding, 0); + icon_region.size -= Point2(horizontal_padding * 1.1, vertical_padding); + + // Darken icon when hovering, shift pressed & not yet bookmarked. + Color use_color = hovering && !bookmarked && shift_pressed ? bookmark_color.darkened(0.4) : bookmark_color; + bookmark_icon->draw_rect(get_canvas_item(), icon_region, false, use_color); + } } if (draw_executing_lines && is_line_executing(p_line) && executing_line_icon.is_valid()) { @@ -1265,7 +1274,7 @@ void CodeEdit::set_line_as_breakpoint(int p_line, bool p_breakpointed) { breakpointed_lines.erase(p_line); } emit_signal(SNAME("breakpoint_toggled"), p_line); - update(); + queue_redraw(); } bool CodeEdit::is_line_breakpointed(int p_line) const { @@ -1280,8 +1289,8 @@ void CodeEdit::clear_breakpointed_lines() { } } -Array CodeEdit::get_breakpointed_lines() const { - Array ret; +PackedInt32Array CodeEdit::get_breakpointed_lines() const { + PackedInt32Array ret; for (int i = 0; i < get_line_count(); i++) { if (is_line_breakpointed(i)) { ret.append(i); @@ -1294,7 +1303,7 @@ Array CodeEdit::get_breakpointed_lines() const { void CodeEdit::set_line_as_bookmarked(int p_line, bool p_bookmarked) { int mask = get_line_gutter_metadata(p_line, main_gutter); set_line_gutter_metadata(p_line, main_gutter, p_bookmarked ? mask | MAIN_GUTTER_BOOKMARK : mask & ~MAIN_GUTTER_BOOKMARK); - update(); + queue_redraw(); } bool CodeEdit::is_line_bookmarked(int p_line) const { @@ -1309,8 +1318,8 @@ void CodeEdit::clear_bookmarked_lines() { } } -Array CodeEdit::get_bookmarked_lines() const { - Array ret; +PackedInt32Array CodeEdit::get_bookmarked_lines() const { + PackedInt32Array ret; for (int i = 0; i < get_line_count(); i++) { if (is_line_bookmarked(i)) { ret.append(i); @@ -1323,7 +1332,7 @@ Array CodeEdit::get_bookmarked_lines() const { void CodeEdit::set_line_as_executing(int p_line, bool p_executing) { int mask = get_line_gutter_metadata(p_line, main_gutter); set_line_gutter_metadata(p_line, main_gutter, p_executing ? mask | MAIN_GUTTER_EXECUTING : mask & ~MAIN_GUTTER_EXECUTING); - update(); + queue_redraw(); } bool CodeEdit::is_line_executing(int p_line) const { @@ -1338,8 +1347,8 @@ void CodeEdit::clear_executing_lines() { } } -Array CodeEdit::get_executing_lines() const { - Array ret; +PackedInt32Array CodeEdit::get_executing_lines() const { + PackedInt32Array ret; for (int i = 0; i < get_line_count(); i++) { if (is_line_executing(i)) { ret.append(i); @@ -1359,7 +1368,7 @@ bool CodeEdit::is_draw_line_numbers_enabled() const { void CodeEdit::set_line_numbers_zero_padded(bool p_zero_padded) { p_zero_padded ? line_number_padding = "0" : line_number_padding = " "; - update(); + queue_redraw(); } bool CodeEdit::is_line_numbers_zero_padded() const { @@ -1529,7 +1538,7 @@ void CodeEdit::fold_line(int p_line) { set_caret_line(p_line, false, false); set_caret_column(get_line(p_line).length(), false); } - update(); + queue_redraw(); } void CodeEdit::unfold_line(int p_line) { @@ -1552,14 +1561,14 @@ void CodeEdit::unfold_line(int p_line) { } _set_line_as_hidden(i, false); } - update(); + queue_redraw(); } void CodeEdit::fold_all_lines() { for (int i = 0; i < get_line_count(); i++) { fold_line(i); } - update(); + queue_redraw(); } void CodeEdit::unfold_all_lines() { @@ -1765,12 +1774,12 @@ Point2 CodeEdit::get_delimiter_end_position(int p_line, int p_column) const { void CodeEdit::set_code_hint(const String &p_hint) { code_hint = p_hint; code_hint_xpos = -0xFFFF; - update(); + queue_redraw(); } void CodeEdit::set_code_hint_draw_below(bool p_below) { code_hint_draw_below = p_below; - update(); + queue_redraw(); } /* Code Completion */ @@ -1929,7 +1938,7 @@ void CodeEdit::set_code_completion_selected_index(int p_index) { } ERR_FAIL_INDEX(p_index, code_completion_options.size()); code_completion_current_selected = p_index; - update(); + queue_redraw(); } void CodeEdit::confirm_code_completion(bool p_replace) { @@ -2043,13 +2052,13 @@ void CodeEdit::cancel_code_completion() { } code_completion_forced = false; code_completion_active = false; - update(); + queue_redraw(); } /* Line length guidelines */ void CodeEdit::set_line_length_guidelines(TypedArray<int> p_guideline_columns) { line_length_guideline_columns = p_guideline_columns; - update(); + queue_redraw(); } TypedArray<int> CodeEdit::get_line_length_guidelines() const { @@ -2378,9 +2387,13 @@ int CodeEdit::_get_auto_brace_pair_close_at_pos(int p_line, int p_col) { /* Gutters */ void CodeEdit::_gutter_clicked(int p_line, int p_gutter) { + bool shift_pressed = Input::get_singleton()->is_key_pressed(Key::SHIFT); + if (p_gutter == main_gutter) { - if (draw_breakpoints) { + if (draw_breakpoints && !shift_pressed) { set_line_as_breakpoint(p_line, !is_line_breakpointed(p_line)); + } else if (draw_bookmarks && shift_pressed) { + set_line_as_bookmarked(p_line, !is_line_bookmarked(p_line)); } return; } @@ -2769,7 +2782,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() { i++; } - Array completion_options; + TypedArray<Dictionary> completion_options; GDVIRTUAL_CALL(_filter_code_completion_candidates, completion_options_sources, completion_options); @@ -2802,7 +2815,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() { code_completion_longest_line = MIN(max_width, code_completion_max_width * font_size); code_completion_current_selected = 0; code_completion_active = true; - update(); + queue_redraw(); return; } @@ -3052,7 +3065,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() { code_completion_longest_line = MIN(max_width, code_completion_max_width * font_size); code_completion_current_selected = 0; code_completion_active = true; - update(); + queue_redraw(); } void CodeEdit::_lines_edited_from(int p_from_line, int p_to_line) { diff --git a/scene/gui/code_edit.h b/scene/gui/code_edit.h index a431d8a5b2..2065f3e681 100644 --- a/scene/gui/code_edit.h +++ b/scene/gui/code_edit.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef CODEEDIT_H -#define CODEEDIT_H +#ifndef CODE_EDIT_H +#define CODE_EDIT_H #include "scene/gui/text_edit.h" @@ -266,7 +266,7 @@ protected: GDVIRTUAL1(_confirm_code_completion, bool) GDVIRTUAL1(_request_code_completion, bool) - GDVIRTUAL1RC(Array, _filter_code_completion_candidates, TypedArray<Dictionary>) + GDVIRTUAL1RC(TypedArray<Dictionary>, _filter_code_completion_candidates, TypedArray<Dictionary>) public: /* General overrides */ @@ -322,19 +322,19 @@ public: void set_line_as_breakpoint(int p_line, bool p_breakpointed); bool is_line_breakpointed(int p_line) const; void clear_breakpointed_lines(); - Array get_breakpointed_lines() const; + PackedInt32Array get_breakpointed_lines() const; // bookmarks void set_line_as_bookmarked(int p_line, bool p_bookmarked); bool is_line_bookmarked(int p_line) const; void clear_bookmarked_lines(); - Array get_bookmarked_lines() const; + PackedInt32Array get_bookmarked_lines() const; // executing lines void set_line_as_executing(int p_line, bool p_executing); bool is_line_executing(int p_line) const; void clear_executing_lines(); - Array get_executing_lines() const; + PackedInt32Array get_executing_lines() const; /* Line numbers */ void set_draw_line_numbers(bool p_draw); @@ -433,4 +433,4 @@ public: VARIANT_ENUM_CAST(CodeEdit::CodeCompletionKind); -#endif // CODEEDIT_H +#endif // CODE_EDIT_H diff --git a/scene/gui/color_mode.cpp b/scene/gui/color_mode.cpp index af78d67e5a..ebd86e0937 100644 --- a/scene/gui/color_mode.cpp +++ b/scene/gui/color_mode.cpp @@ -159,7 +159,7 @@ void ColorModeHSV::slider_draw(int p_which) { } else if (p_which == 0) { Ref<Texture2D> hue = color_picker->get_theme_icon(SNAME("color_hue"), SNAME("ColorPicker")); slider->draw_set_transform(Point2(), -Math_PI / 2, Size2(1.0, 1.0)); - slider->draw_texture_rect(hue, Rect2(Vector2(margin * -2, 0), Vector2(slider->get_size().x, margin)), false, Color(1, 1, 1), true); + slider->draw_texture_rect(hue, Rect2(Vector2(margin * -2, 0), Vector2(margin, size.x)), false); return; } else { Color s_col; @@ -306,7 +306,7 @@ void ColorModeOKHSL::slider_draw(int p_which) { } else if (p_which == 0) { Ref<Texture2D> hue = color_picker->get_theme_icon(SNAME("color_hue"), SNAME("ColorPicker")); slider->draw_set_transform(Point2(), -Math_PI / 2, Size2(1.0, 1.0)); - slider->draw_texture_rect(hue, Rect2(Vector2(margin * -2, 0), Vector2(slider->get_size().x, margin)), false, Color(1, 1, 1), true); + slider->draw_texture_rect(hue, Rect2(Vector2(margin * -2, 0), Vector2(margin, size.x)), false); return; } else { Color s_col; diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index 492a81f933..5751c54877 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -171,6 +171,9 @@ uniform float v = 1.0; void fragment() { float x = UV.x - 0.5; float y = UV.y - 0.5; + float h = atan(y, x) / (2.0 * M_PI); + float s = sqrt(x * x + y * y) * 2.0; + vec3 col = okhsl_to_srgb(vec3(h, s, v)); x += 0.001; y += 0.001; float b = float(sqrt(x * x + y * y) < 0.5); @@ -180,9 +183,6 @@ void fragment() { float b3 = float(sqrt(x * x + y * y) < 0.5); x += 0.002; float b4 = float(sqrt(x * x + y * y) < 0.5); - float s = sqrt(x * x + y * y); - float h = atan(y, x) / (2.0*M_PI); - vec3 col = okhsl_to_srgb(vec3(h, s, v)); COLOR = vec4(col, (b + b2 + b3 + b4) / 4.00); })"); } @@ -264,15 +264,7 @@ void ColorPicker::_update_controls() { void ColorPicker::_set_pick_color(const Color &p_color, bool p_update_sliders) { color = p_color; if (color != last_color) { - if (_get_actual_shape() == SHAPE_OKHSL_CIRCLE) { - h = color.get_ok_hsl_h(); - s = color.get_ok_hsl_s(); - v = color.get_ok_hsl_l(); - } else { - h = color.get_h(); - s = color.get_s(); - v = color.get_v(); - } + _copy_color_to_hsv(); last_color = color; } @@ -300,6 +292,9 @@ bool ColorPicker::is_displaying_old_color() const { } void ColorPicker::set_edit_alpha(bool p_show) { + if (edit_alpha == p_show) { + return; + } edit_alpha = p_show; _update_controls(); @@ -308,7 +303,7 @@ void ColorPicker::set_edit_alpha(bool p_show) { } _update_color(); - sample->update(); + sample->queue_redraw(); } bool ColorPicker::is_editing_alpha() const { @@ -357,7 +352,7 @@ void ColorPicker::create_slider(GridContainer *gc, int idx) { s->set_h_size_flags(SIZE_EXPAND_FILL); s->connect("value_changed", callable_mp(this, &ColorPicker::_value_changed)); - s->connect("draw", callable_mp(this, &ColorPicker::_slider_draw), make_binds(idx)); + s->connect("draw", callable_mp(this, &ColorPicker::_slider_draw).bind(idx)); if (idx < SLIDER_COUNT) { sliders[idx] = s; @@ -386,6 +381,26 @@ Vector<float> ColorPicker::get_active_slider_values() { return values; } +void ColorPicker::_copy_color_to_hsv() { + if (_get_actual_shape() == SHAPE_OKHSL_CIRCLE) { + h = color.get_ok_hsl_h(); + s = color.get_ok_hsl_s(); + v = color.get_ok_hsl_l(); + } else { + h = color.get_h(); + s = color.get_s(); + v = color.get_v(); + } +} + +void ColorPicker::_copy_hsv_to_color() { + if (_get_actual_shape() == SHAPE_OKHSL_CIRCLE) { + color.set_ok_hsl(h, s, v, color.a); + } else { + color.set_hsv(h, s, v, color.a); + } +} + ColorPicker::PickerShapeType ColorPicker::_get_actual_shape() const { return modes[current_mode]->get_shape_override() != SHAPE_MAX ? modes[current_mode]->get_shape_override() : current_shape; } @@ -412,12 +427,15 @@ void ColorPicker::_html_submitted(const String &p_html) { return; } - float last_alpha = color.a; + Color previous_color = color; color = Color::html(p_html); if (!is_editing_alpha()) { - color.a = last_alpha; + color.a = previous_color.a; } + if (color == previous_color) { + return; + } if (!is_inside_tree()) { return; } @@ -443,15 +461,15 @@ void ColorPicker::_update_color(bool p_update_sliders) { _update_text_value(); - sample->update(); - uv_edit->update(); - w_edit->update(); + sample->queue_redraw(); + uv_edit->queue_redraw(); + w_edit->queue_redraw(); for (int i = 0; i < current_slider_count; i++) { - sliders[i]->update(); + sliders[i]->queue_redraw(); } - alpha_slider->update(); - wheel->update(); - wheel_uv->update(); + alpha_slider->queue_redraw(); + wheel->queue_redraw(); + wheel_uv->queue_redraw(); updating = false; } @@ -497,8 +515,13 @@ Color ColorPicker::get_pick_color() const { void ColorPicker::set_picker_shape(PickerShapeType p_shape) { ERR_FAIL_INDEX(p_shape, SHAPE_MAX); + if (current_shape == p_shape) { + return; + } current_shape = p_shape; + _copy_color_to_hsv(); + _update_controls(); _update_color(); } @@ -515,8 +538,8 @@ void ColorPicker::_add_preset_button(int p_size, const Color &p_color) { ColorPresetButton *btn_preset = memnew(ColorPresetButton(p_color)); btn_preset->set_preset_color(p_color); btn_preset->set_custom_minimum_size(Size2(p_size, p_size)); - btn_preset->connect("gui_input", callable_mp(this, &ColorPicker::_preset_input), varray(p_color)); - btn_preset->set_tooltip(vformat(RTR("Color: #%s\nLMB: Apply color\nRMB: Remove preset"), p_color.to_html(p_color.a < 1))); + btn_preset->connect("gui_input", callable_mp(this, &ColorPicker::_preset_input).bind(p_color)); + btn_preset->set_tooltip_text(vformat(RTR("Color: #%s\nLMB: Apply color\nRMB: Remove preset"), p_color.to_html(p_color.a < 1))); preset_container->add_child(btn_preset); } @@ -640,8 +663,7 @@ void ColorPicker::_sample_input(const Ref<InputEvent> &p_event) { const Rect2 rect_old = Rect2(Point2(), Size2(sample->get_size().width * 0.5, sample->get_size().height * 0.95)); if (rect_old.has_point(mb->get_position())) { // Revert to the old color when left-clicking the old color sample. - color = old_color; - _update_color(); + set_pick_color(old_color); emit_signal(SNAME("color_changed"), color); } } @@ -834,7 +856,7 @@ void ColorPicker::_hsv_draw(int p_which, Control *c) { } else if (p_which == 2) { c->draw_rect(Rect2(Point2(), c->get_size()), Color(1, 1, 1)); if (actual_shape == SHAPE_VHS_CIRCLE || actual_shape == SHAPE_OKHSL_CIRCLE) { - circle_mat->set_shader_param("v", v); + circle_mat->set_shader_parameter("v", v); } } } @@ -887,17 +909,14 @@ void ColorPicker::_uv_input(const Ref<InputEvent> &p_event, Control *c) { v = 1.0 - (y - c->get_position().y - corner_y) / real_size.y; } } + changing_color = true; - if (current_picker == SHAPE_OKHSL_CIRCLE) { - color.set_ok_hsl(h, s, v, color.a); - } else { - color.set_hsv(h, s, v, color.a); - } + _copy_hsv_to_color(); last_color = color; - set_pick_color(color); _update_color(); + if (!deferred_mode_enabled) { emit_signal(SNAME("color_changed"), color); } @@ -940,14 +959,12 @@ void ColorPicker::_uv_input(const Ref<InputEvent> &p_event, Control *c) { v = 1.0 - (y - corner_y) / real_size.y; } } - if (current_picker != SHAPE_OKHSL_CIRCLE) { - color.set_hsv(h, s, v, color.a); - } else { - color.set_ok_hsl(h, s, v, color.a); - } + + _copy_hsv_to_color(); last_color = color; set_pick_color(color); _update_color(); + if (!deferred_mode_enabled) { emit_signal(SNAME("color_changed"), color); } @@ -970,14 +987,12 @@ void ColorPicker::_w_input(const Ref<InputEvent> &p_event) { } else { changing_color = false; } - if (actual_shape != SHAPE_OKHSL_CIRCLE) { - color.set_hsv(h, s, v, color.a); - } else { - color.set_ok_hsl(h, s, v, color.a); - } + + _copy_hsv_to_color(); last_color = color; set_pick_color(color); _update_color(); + if (!deferred_mode_enabled) { emit_signal(SNAME("color_changed"), color); } else if (!bev->is_pressed() && bev->get_button_index() == MouseButton::LEFT) { @@ -998,15 +1013,11 @@ void ColorPicker::_w_input(const Ref<InputEvent> &p_event) { h = y / w_edit->get_size().height; } - if (current_mode == MODE_HSV) { - color.set_hsv(h, s, v, color.a); - } else if (current_mode == MODE_OKHSL) { - color.set_ok_hsl(h, s, v, color.a); - } - + _copy_hsv_to_color(); last_color = color; set_pick_color(color); _update_color(); + if (!deferred_mode_enabled) { emit_signal(SNAME("color_changed"), color); } @@ -1019,7 +1030,6 @@ void ColorPicker::_preset_input(const Ref<InputEvent> &p_event, const Color &p_c if (bev.is_valid()) { if (bev->is_pressed() && bev->get_button_index() == MouseButton::LEFT) { set_pick_color(p_color); - _update_color(); emit_signal(SNAME("color_changed"), p_color); } else if (bev->is_pressed() && bev->get_button_index() == MouseButton::RIGHT && presets_enabled) { erase_preset(p_color); @@ -1071,15 +1081,15 @@ void ColorPicker::_screen_pick_pressed() { screen = memnew(Control); r->add_child(screen); screen->set_as_top_level(true); - screen->set_anchors_and_offsets_preset(Control::PRESET_WIDE); + screen->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); screen->set_default_cursor_shape(CURSOR_POINTING_HAND); screen->connect("gui_input", callable_mp(this, &ColorPicker::_screen_input)); // It immediately toggles off in the first press otherwise. - screen->call_deferred(SNAME("connect"), "hidden", Callable(btn_pick, "set_pressed"), varray(false)); + screen->call_deferred(SNAME("connect"), "hidden", Callable(btn_pick, "set_pressed").bind(false)); } else { screen->show(); } - screen->raise(); + screen->move_to_front(); #ifndef _MSC_VER #warning show modal no longer works, needs to be converted to a popup #endif @@ -1130,6 +1140,9 @@ void ColorPicker::_html_focus_exit() { } void ColorPicker::set_presets_enabled(bool p_enabled) { + if (presets_enabled == p_enabled) { + return; + } presets_enabled = p_enabled; if (!p_enabled) { btn_add_preset->set_disabled(true); @@ -1145,6 +1158,9 @@ bool ColorPicker::are_presets_enabled() const { } void ColorPicker::set_presets_visible(bool p_visible) { + if (presets_visible == p_visible) { + return; + } presets_visible = p_visible; preset_separator->set_visible(p_visible); preset_container->set_visible(p_visible); @@ -1204,11 +1220,11 @@ ColorPicker::ColorPicker() : uv_edit = memnew(Control); hb_edit->add_child(uv_edit); - uv_edit->connect("gui_input", callable_mp(this, &ColorPicker::_uv_input), make_binds(uv_edit)); + uv_edit->connect("gui_input", callable_mp(this, &ColorPicker::_uv_input).bind(uv_edit)); uv_edit->set_mouse_filter(MOUSE_FILTER_PASS); uv_edit->set_h_size_flags(SIZE_EXPAND_FILL); uv_edit->set_v_size_flags(SIZE_EXPAND_FILL); - uv_edit->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw), make_binds(0, uv_edit)); + uv_edit->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw).bind(0, uv_edit)); HBoxContainer *hb_smpl = memnew(HBoxContainer); add_child(hb_smpl, false, INTERNAL_MODE_FRONT); @@ -1223,7 +1239,7 @@ ColorPicker::ColorPicker() : btn_pick->set_flat(true); hb_smpl->add_child(btn_pick); btn_pick->set_toggle_mode(true); - btn_pick->set_tooltip(RTR("Pick a color from the editor window.")); + btn_pick->set_tooltip_text(RTR("Pick a color from the editor window.")); btn_pick->connect("pressed", callable_mp(this, &ColorPicker::_screen_pick_pressed)); VBoxContainer *vbl = memnew(VBoxContainer); @@ -1263,7 +1279,7 @@ ColorPicker::ColorPicker() : text_type = memnew(Button); hhb->add_child(text_type); text_type->set_text("#"); - text_type->set_tooltip(RTR("Switch between hexadecimal and code values.")); + text_type->set_tooltip_text(RTR("Switch between hexadecimal and code values.")); if (Engine::get_singleton()->is_editor_hint()) { text_type->connect("pressed", callable_mp(this, &ColorPicker::_text_type_toggled)); } else { @@ -1295,19 +1311,19 @@ ColorPicker::ColorPicker() : wheel = memnew(Control); wheel_margin->add_child(wheel); wheel->set_mouse_filter(MOUSE_FILTER_PASS); - wheel->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw), make_binds(2, wheel)); + wheel->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw).bind(2, wheel)); wheel_uv = memnew(Control); wheel_margin->add_child(wheel_uv); - wheel_uv->connect("gui_input", callable_mp(this, &ColorPicker::_uv_input), make_binds(wheel_uv)); - wheel_uv->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw), make_binds(0, wheel_uv)); + wheel_uv->connect("gui_input", callable_mp(this, &ColorPicker::_uv_input).bind(wheel_uv)); + wheel_uv->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw).bind(0, wheel_uv)); w_edit = memnew(Control); hb_edit->add_child(w_edit); w_edit->set_h_size_flags(SIZE_FILL); w_edit->set_v_size_flags(SIZE_EXPAND_FILL); w_edit->connect("gui_input", callable_mp(this, &ColorPicker::_w_input)); - w_edit->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw), make_binds(1, w_edit)); + w_edit->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw).bind(1, w_edit)); _update_controls(); updating = false; @@ -1324,7 +1340,7 @@ ColorPicker::ColorPicker() : btn_add_preset = memnew(Button); btn_add_preset->set_icon_alignment(HORIZONTAL_ALIGNMENT_CENTER); - btn_add_preset->set_tooltip(RTR("Add current color as a preset.")); + btn_add_preset->set_tooltip_text(RTR("Add current color as a preset.")); btn_add_preset->connect("pressed", callable_mp(this, &ColorPicker::_add_preset_pressed)); preset_container->add_child(btn_add_preset); } @@ -1346,7 +1362,7 @@ void ColorPickerButton::_about_to_popup() { void ColorPickerButton::_color_changed(const Color &p_color) { color = p_color; - update(); + queue_redraw(); emit_signal(SNAME("color_changed"), color); } @@ -1418,12 +1434,15 @@ void ColorPickerButton::_notification(int p_what) { } void ColorPickerButton::set_pick_color(const Color &p_color) { + if (color == p_color) { + return; + } color = p_color; if (picker) { picker->set_pick_color(p_color); } - update(); + queue_redraw(); } Color ColorPickerButton::get_pick_color() const { @@ -1431,6 +1450,9 @@ Color ColorPickerButton::get_pick_color() const { } void ColorPickerButton::set_edit_alpha(bool p_show) { + if (edit_alpha == p_show) { + return; + } edit_alpha = p_show; if (picker) { picker->set_edit_alpha(p_show); @@ -1456,7 +1478,7 @@ void ColorPickerButton::_update_picker() { popup = memnew(PopupPanel); popup->set_wrap_controls(true); picker = memnew(ColorPicker); - picker->set_anchors_and_offsets_preset(PRESET_WIDE); + picker->set_anchors_and_offsets_preset(PRESET_FULL_RECT); popup->add_child(picker); add_child(popup, false, INTERNAL_MODE_FRONT); picker->connect("color_changed", callable_mp(this, &ColorPickerButton::_color_changed)); diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h index e219c78319..05b760b109 100644 --- a/scene/gui/color_picker.h +++ b/scene/gui/color_picker.h @@ -156,6 +156,9 @@ private: float v = 0.0; Color last_color; + void _copy_color_to_hsv(); + void _copy_hsv_to_color(); + PickerShapeType _get_actual_shape() const; void create_slider(GridContainer *gc, int idx); void _reset_theme(); @@ -276,4 +279,5 @@ public: VARIANT_ENUM_CAST(ColorPicker::PickerShapeType); VARIANT_ENUM_CAST(ColorPicker::ColorModeType); + #endif // COLOR_PICKER_H diff --git a/scene/gui/color_rect.cpp b/scene/gui/color_rect.cpp index 2955f74a0c..143662efc6 100644 --- a/scene/gui/color_rect.cpp +++ b/scene/gui/color_rect.cpp @@ -31,8 +31,11 @@ #include "color_rect.h" void ColorRect::set_color(const Color &p_color) { + if (color == p_color) { + return; + } color = p_color; - update(); + queue_redraw(); } Color ColorRect::get_color() const { diff --git a/scene/gui/container.cpp b/scene/gui/container.cpp index 5512c0f1fd..3c29c37479 100644 --- a/scene/gui/container.cpp +++ b/scene/gui/container.cpp @@ -192,8 +192,8 @@ void Container::_notification(int p_what) { } } -TypedArray<String> Container::get_configuration_warnings() const { - TypedArray<String> warnings = Control::get_configuration_warnings(); +PackedStringArray Container::get_configuration_warnings() const { + PackedStringArray warnings = Control::get_configuration_warnings(); if (get_class() == "Container" && get_script().is_null()) { warnings.push_back(RTR("Container by itself serves no purpose unless a script configures its children placement behavior.\nIf you don't intend to add a script, use a plain Control node instead.")); diff --git a/scene/gui/container.h b/scene/gui/container.h index 9ec4ad3200..21bdb95186 100644 --- a/scene/gui/container.h +++ b/scene/gui/container.h @@ -63,7 +63,7 @@ public: virtual Vector<int> get_allowed_size_flags_horizontal() const; virtual Vector<int> get_allowed_size_flags_vertical() const; - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; Container(); }; diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 15ff1d3ed6..dc9294df6d 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -43,6 +43,8 @@ #include "scene/main/canvas_layer.h" #include "scene/main/window.h" #include "scene/scene_string_names.h" +#include "scene/theme/theme_db.h" +#include "scene/theme/theme_owner.h" #include "servers/rendering_server.h" #include "servers/text_server.h" @@ -50,6 +52,9 @@ #include "editor/plugins/control_editor_plugin.h" #endif +// Editor plugin interoperability. + +// TODO: Decouple controls from their editor plugin and get rid of this. #ifdef TOOLS_ENABLED Dictionary Control::_edit_get_state() const { Dictionary s; @@ -181,6 +186,49 @@ Size2 Control::_edit_get_minimum_size() const { } #endif +// Editor integration. + +void Control::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { + Node::get_argument_options(p_function, p_idx, r_options); + + if (p_idx == 0) { + List<StringName> sn; + String pf = p_function; + if (pf == "add_theme_color_override" || pf == "has_theme_color" || pf == "has_theme_color_override" || pf == "get_theme_color") { + ThemeDB::get_singleton()->get_default_theme()->get_color_list(get_class(), &sn); + } else if (pf == "add_theme_style_override" || pf == "has_theme_style" || pf == "has_theme_style_override" || pf == "get_theme_style") { + ThemeDB::get_singleton()->get_default_theme()->get_stylebox_list(get_class(), &sn); + } else if (pf == "add_theme_font_override" || pf == "has_theme_font" || pf == "has_theme_font_override" || pf == "get_theme_font") { + ThemeDB::get_singleton()->get_default_theme()->get_font_list(get_class(), &sn); + } else if (pf == "add_theme_font_size_override" || pf == "has_theme_font_size" || pf == "has_theme_font_size_override" || pf == "get_theme_font_size") { + ThemeDB::get_singleton()->get_default_theme()->get_font_size_list(get_class(), &sn); + } else if (pf == "add_theme_constant_override" || pf == "has_theme_constant" || pf == "has_theme_constant_override" || pf == "get_theme_constant") { + ThemeDB::get_singleton()->get_default_theme()->get_constant_list(get_class(), &sn); + } + + sn.sort_custom<StringName::AlphCompare>(); + for (const StringName &name : sn) { + r_options->push_back(String(name).quote()); + } + } +} + +PackedStringArray Control::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); + + if (data.mouse_filter == MOUSE_FILTER_IGNORE && !data.tooltip.is_empty()) { + warnings.push_back(RTR("The Hint Tooltip won't be displayed as the control's Mouse Filter is set to \"Ignore\". To solve this, set the Mouse Filter to \"Stop\" or \"Pass\".")); + } + + return warnings; +} + +bool Control::is_text_field() const { + return false; +} + +// Dynamic properties. + String Control::properties_managed_by_container[] = { "offset_left", "offset_top", @@ -196,58 +244,6 @@ String Control::properties_managed_by_container[] = { "size" }; -void Control::accept_event() { - if (is_inside_tree()) { - get_viewport()->_gui_accept_event(); - } -} - -void Control::set_custom_minimum_size(const Size2 &p_custom) { - if (p_custom == data.custom_minimum_size) { - return; - } - data.custom_minimum_size = p_custom; - update_minimum_size(); -} - -Size2 Control::get_custom_minimum_size() const { - return data.custom_minimum_size; -} - -void Control::_update_minimum_size_cache() { - Size2 minsize = get_minimum_size(); - minsize.x = MAX(minsize.x, data.custom_minimum_size.x); - minsize.y = MAX(minsize.y, data.custom_minimum_size.y); - - bool size_changed = false; - if (data.minimum_size_cache != minsize) { - size_changed = true; - } - - data.minimum_size_cache = minsize; - data.minimum_size_valid = true; - - if (size_changed) { - update_minimum_size(); - } -} - -Size2 Control::get_combined_minimum_size() const { - if (!data.minimum_size_valid) { - const_cast<Control *>(this)->_update_minimum_size_cache(); - } - return data.minimum_size_cache; -} - -Transform2D Control::_get_internal_transform() const { - Transform2D rot_scale; - rot_scale.set_rotation_and_scale(data.rotation, data.scale); - Transform2D offset; - offset.set_origin(-data.pivot_offset); - - return offset.affine_inverse() * (rot_scale * offset); -} - bool Control::_set(const StringName &p_name, const Variant &p_value) { String name = p_name; if (!name.begins_with("theme_override")) { @@ -258,36 +254,36 @@ bool Control::_set(const StringName &p_name, const Variant &p_value) { if (name.begins_with("theme_override_icons/")) { String dname = name.get_slicec('/', 1); if (data.icon_override.has(dname)) { - data.icon_override[dname]->disconnect("changed", callable_mp(this, &Control::_override_changed)); + data.icon_override[dname]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed)); } data.icon_override.erase(dname); - notification(NOTIFICATION_THEME_CHANGED); + _notify_theme_override_changed(); } else if (name.begins_with("theme_override_styles/")) { String dname = name.get_slicec('/', 1); if (data.style_override.has(dname)) { - data.style_override[dname]->disconnect("changed", callable_mp(this, &Control::_override_changed)); + data.style_override[dname]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed)); } data.style_override.erase(dname); - notification(NOTIFICATION_THEME_CHANGED); + _notify_theme_override_changed(); } else if (name.begins_with("theme_override_fonts/")) { String dname = name.get_slicec('/', 1); if (data.font_override.has(dname)) { - data.font_override[dname]->disconnect("changed", callable_mp(this, &Control::_override_changed)); + data.font_override[dname]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed)); } data.font_override.erase(dname); - notification(NOTIFICATION_THEME_CHANGED); + _notify_theme_override_changed(); } else if (name.begins_with("theme_override_font_sizes/")) { String dname = name.get_slicec('/', 1); data.font_size_override.erase(dname); - notification(NOTIFICATION_THEME_CHANGED); + _notify_theme_override_changed(); } else if (name.begins_with("theme_override_colors/")) { String dname = name.get_slicec('/', 1); data.color_override.erase(dname); - notification(NOTIFICATION_THEME_CHANGED); + _notify_theme_override_changed(); } else if (name.begins_with("theme_override_constants/")) { String dname = name.get_slicec('/', 1); data.constant_override.erase(dname); - notification(NOTIFICATION_THEME_CHANGED); + _notify_theme_override_changed(); } else { return false; } @@ -318,21 +314,6 @@ bool Control::_set(const StringName &p_name, const Variant &p_value) { return true; } -void Control::_update_minimum_size() { - if (!is_inside_tree()) { - return; - } - - Size2 minsize = get_combined_minimum_size(); - data.updating_last_minimum_size = false; - - if (minsize != data.last_minimum_size) { - data.last_minimum_size = minsize; - _size_changed(); - emit_signal(SceneStringNames::get_singleton()->minimum_size_changed); - } -} - bool Control::_get(const StringName &p_name, Variant &r_ret) const { String sname = p_name; if (!sname.begins_with("theme_override")) { @@ -365,7 +346,7 @@ bool Control::_get(const StringName &p_name, Variant &r_ret) const { } void Control::_get_property_list(List<PropertyInfo> *p_list) const { - Ref<Theme> theme = Theme::get_default(); + Ref<Theme> theme = ThemeDB::get_singleton()->get_default_theme(); p_list->push_back(PropertyInfo(Variant::NIL, TTRC("Theme Overrides"), PROPERTY_HINT_NONE, "theme_override_", PROPERTY_USAGE_GROUP)); @@ -443,16 +424,16 @@ void Control::_get_property_list(List<PropertyInfo> *p_list) const { } } -void Control::_validate_property(PropertyInfo &property) const { +void Control::_validate_property(PropertyInfo &p_property) const { // Update theme type variation options. - if (property.name == "theme_type_variation") { + if (p_property.name == "theme_type_variation") { List<StringName> names; // Only the default theme and the project theme are used for the list of options. // This is an imposed limitation to simplify the logic needed to leverage those options. - Theme::get_default()->get_type_variation_list(get_class_name(), &names); - if (Theme::get_project_default().is_valid()) { - Theme::get_project_default()->get_type_variation_list(get_class_name(), &names); + ThemeDB::get_singleton()->get_default_theme()->get_type_variation_list(get_class_name(), &names); + if (ThemeDB::get_singleton()->get_project_theme().is_valid()) { + ThemeDB::get_singleton()->get_project_theme()->get_type_variation_list(get_class_name(), &names); } names.sort_custom<StringName::AlphCompare>(); @@ -468,18 +449,18 @@ void Control::_validate_property(PropertyInfo &property) const { unique_names.append(E); } - property.hint_string = hint_string; + p_property.hint_string = hint_string; } - if (property.name == "mouse_force_pass_scroll_events") { + if (p_property.name == "mouse_force_pass_scroll_events") { // Disable force pass if the control is not stopping the event. if (data.mouse_filter != MOUSE_FILTER_STOP) { - property.usage |= PROPERTY_USAGE_READ_ONLY; + p_property.usage |= PROPERTY_USAGE_READ_ONLY; } } - if (property.name == "scale") { - property.hint = PROPERTY_HINT_LINK; + if (p_property.name == "scale") { + p_property.hint = PROPERTY_HINT_LINK; } // Validate which positioning properties should be displayed depending on the parent and the layout mode. @@ -488,33 +469,33 @@ void Control::_validate_property(PropertyInfo &property) const { // If there is no parent, display both anchor and container options. // Set the layout mode to be disabled with the proper value. - if (property.name == "layout_mode") { - property.hint_string = "Position,Anchors,Container,Uncontrolled"; - property.usage |= PROPERTY_USAGE_READ_ONLY; + if (p_property.name == "layout_mode") { + p_property.hint_string = "Position,Anchors,Container,Uncontrolled"; + p_property.usage |= PROPERTY_USAGE_READ_ONLY; } // Use the layout mode to display or hide advanced anchoring properties. bool use_custom_anchors = _get_anchors_layout_preset() == -1; // Custom "preset". - if (!use_custom_anchors && (property.name.begins_with("anchor_") || property.name.begins_with("offset_") || property.name.begins_with("grow_"))) { - property.usage ^= PROPERTY_USAGE_EDITOR; + if (!use_custom_anchors && (p_property.name.begins_with("anchor_") || p_property.name.begins_with("offset_") || p_property.name.begins_with("grow_"))) { + p_property.usage ^= PROPERTY_USAGE_EDITOR; } } else if (Object::cast_to<Container>(parent_node)) { // If the parent is a container, display only container-related properties. - if (property.name.begins_with("anchor_") || property.name.begins_with("offset_") || property.name.begins_with("grow_") || property.name == "anchors_preset" || - property.name == "position" || property.name == "rotation" || property.name == "scale" || property.name == "size" || property.name == "pivot_offset") { - property.usage ^= PROPERTY_USAGE_EDITOR; + if (p_property.name.begins_with("anchor_") || p_property.name.begins_with("offset_") || p_property.name.begins_with("grow_") || p_property.name == "anchors_preset" || + p_property.name == "position" || p_property.name == "rotation" || p_property.name == "scale" || p_property.name == "size" || p_property.name == "pivot_offset") { + p_property.usage ^= PROPERTY_USAGE_EDITOR; - } else if (property.name == "layout_mode") { + } else if (p_property.name == "layout_mode") { // Set the layout mode to be disabled with the proper value. - property.hint_string = "Position,Anchors,Container,Uncontrolled"; - property.usage |= PROPERTY_USAGE_READ_ONLY; - } else if (property.name == "size_flags_horizontal" || property.name == "size_flags_vertical") { + p_property.hint_string = "Position,Anchors,Container,Uncontrolled"; + p_property.usage |= PROPERTY_USAGE_READ_ONLY; + } else if (p_property.name == "size_flags_horizontal" || p_property.name == "size_flags_vertical") { // Filter allowed size flags based on the parent container configuration. Container *parent_container = Object::cast_to<Container>(parent_node); Vector<int> size_flags; - if (property.name == "size_flags_horizontal") { + if (p_property.name == "size_flags_horizontal") { size_flags = parent_container->get_allowed_size_flags_horizontal(); - } else if (property.name == "size_flags_vertical") { + } else if (p_property.name == "size_flags_vertical") { size_flags = parent_container->get_allowed_size_flags_vertical(); } @@ -543,30 +524,30 @@ void Control::_validate_property(PropertyInfo &property) const { } if (hint_string.is_empty()) { - property.hint_string = ""; - property.usage |= PROPERTY_USAGE_READ_ONLY; + p_property.hint_string = ""; + p_property.usage |= PROPERTY_USAGE_READ_ONLY; } else { - property.hint_string = hint_string; + p_property.hint_string = hint_string; } } } else { // If the parent is NOT a container or not a control at all, display only anchoring-related properties. - if (property.name.begins_with("size_flags_")) { - property.usage ^= PROPERTY_USAGE_EDITOR; + if (p_property.name.begins_with("size_flags_")) { + p_property.usage ^= PROPERTY_USAGE_EDITOR; - } else if (property.name == "layout_mode") { + } else if (p_property.name == "layout_mode") { // Set the layout mode to be enabled with proper options. - property.hint_string = "Position,Anchors"; + p_property.hint_string = "Position,Anchors"; } // Use the layout mode to display or hide advanced anchoring properties. bool use_anchors = _get_layout_mode() == LayoutMode::LAYOUT_MODE_ANCHORS; - if (!use_anchors && property.name == "anchors_preset") { - property.usage ^= PROPERTY_USAGE_EDITOR; + if (!use_anchors && p_property.name == "anchors_preset") { + p_property.usage ^= PROPERTY_USAGE_EDITOR; } bool use_custom_anchors = use_anchors && _get_anchors_layout_preset() == -1; // Custom "preset". - if (!use_custom_anchors && (property.name.begins_with("anchor_") || property.name.begins_with("offset_") || property.name.begins_with("grow_"))) { - property.usage ^= PROPERTY_USAGE_EDITOR; + if (!use_custom_anchors && (p_property.name.begins_with("anchor_") || p_property.name.begins_with("offset_") || p_property.name.begins_with("grow_"))) { + p_property.usage ^= PROPERTY_USAGE_EDITOR; } } @@ -576,959 +557,293 @@ void Control::_validate_property(PropertyInfo &property) const { } bool property_is_managed_by_container = false; for (unsigned i = 0; i < properties_managed_by_container_count; i++) { - property_is_managed_by_container = properties_managed_by_container[i] == property.name; + property_is_managed_by_container = properties_managed_by_container[i] == p_property.name; if (property_is_managed_by_container) { break; } } if (property_is_managed_by_container) { - property.usage |= PROPERTY_USAGE_READ_ONLY; + p_property.usage |= PROPERTY_USAGE_READ_ONLY; } } -Control *Control::get_parent_control() const { - return data.parent; -} - -Window *Control::get_parent_window() const { - return data.parent_window; -} - -void Control::set_layout_direction(Control::LayoutDirection p_direction) { - ERR_FAIL_INDEX((int)p_direction, 4); - - data.layout_dir = p_direction; - data.is_rtl_dirty = true; - - propagate_notification(NOTIFICATION_LAYOUT_DIRECTION_CHANGED); -} - -Control::LayoutDirection Control::get_layout_direction() const { - return data.layout_dir; -} - -bool Control::is_layout_rtl() const { - if (data.is_rtl_dirty) { - const_cast<Control *>(this)->data.is_rtl_dirty = false; - if (data.layout_dir == LAYOUT_DIRECTION_INHERITED) { - Window *parent_window = get_parent_window(); - Control *parent_control = get_parent_control(); - if (parent_control) { - const_cast<Control *>(this)->data.is_rtl = parent_control->is_layout_rtl(); - } else if (parent_window) { - const_cast<Control *>(this)->data.is_rtl = parent_window->is_layout_rtl(); - } else { - if (GLOBAL_GET(SNAME("internationalization/rendering/force_right_to_left_layout_direction"))) { - const_cast<Control *>(this)->data.is_rtl = true; - } else { - String locale = TranslationServer::get_singleton()->get_tool_locale(); - const_cast<Control *>(this)->data.is_rtl = TS->is_locale_right_to_left(locale); - } - } - } else if (data.layout_dir == LAYOUT_DIRECTION_LOCALE) { - if (GLOBAL_GET(SNAME("internationalization/rendering/force_right_to_left_layout_direction"))) { - const_cast<Control *>(this)->data.is_rtl = true; - } else { - String locale = TranslationServer::get_singleton()->get_tool_locale(); - const_cast<Control *>(this)->data.is_rtl = TS->is_locale_right_to_left(locale); - } - } else { - const_cast<Control *>(this)->data.is_rtl = (data.layout_dir == LAYOUT_DIRECTION_RTL); - } +bool Control::_property_can_revert(const StringName &p_name) const { + if (p_name == "layout_mode" || p_name == "anchors_preset") { + return true; } - return data.is_rtl; -} - -void Control::set_auto_translate(bool p_enable) { - if (p_enable == data.auto_translate) { - return; - } - - data.auto_translate = p_enable; - - notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED); -} - -bool Control::is_auto_translating() const { - return data.auto_translate; -} - -void Control::_clear_size_warning() { - data.size_warning = false; -} - -//moved theme configuration here, so controls can set up even if still not inside active scene - -void Control::add_child_notify(Node *p_child) { - Control *child_c = Object::cast_to<Control>(p_child); - - if (child_c && child_c->data.theme.is_null() && (data.theme_owner || data.theme_owner_window)) { - _propagate_theme_changed(child_c, data.theme_owner, data.theme_owner_window); //need to propagate here, since many controls may require setting up stuff - } - - Window *child_w = Object::cast_to<Window>(p_child); - - if (child_w && child_w->theme.is_null() && (data.theme_owner || data.theme_owner_window)) { - _propagate_theme_changed(child_w, data.theme_owner, data.theme_owner_window); //need to propagate here, since many controls may require setting up stuff - } -} - -void Control::remove_child_notify(Node *p_child) { - Control *child_c = Object::cast_to<Control>(p_child); - - if (child_c && (child_c->data.theme_owner || child_c->data.theme_owner_window) && child_c->data.theme.is_null()) { - _propagate_theme_changed(child_c, nullptr, nullptr); - } - - Window *child_w = Object::cast_to<Window>(p_child); - - if (child_w && (child_w->theme_owner || child_w->theme_owner_window) && child_w->theme.is_null()) { - _propagate_theme_changed(child_w, nullptr, nullptr); - } -} - -void Control::_update_canvas_item_transform() { - Transform2D xform = _get_internal_transform(); - xform[2] += get_position(); - - // We use a little workaround to avoid flickering when moving the pivot with _edit_set_pivot() - if (is_inside_tree() && Math::abs(Math::sin(data.rotation * 4.0f)) < 0.00001f && get_viewport()->is_snap_controls_to_pixels_enabled()) { - xform[2] = xform[2].round(); - } - - RenderingServer::get_singleton()->canvas_item_set_transform(get_canvas_item(), xform); -} - -void Control::_notification(int p_notification) { - switch (p_notification) { - case NOTIFICATION_POST_ENTER_TREE: { - data.minimum_size_valid = false; - data.is_rtl_dirty = true; - _size_changed(); - } break; - - case NOTIFICATION_EXIT_TREE: { - release_focus(); - get_viewport()->_gui_remove_control(this); - } break; - - case NOTIFICATION_READY: { -#ifdef DEBUG_ENABLED - connect("ready", callable_mp(this, &Control::_clear_size_warning), varray(), CONNECT_DEFERRED | CONNECT_ONESHOT); -#endif - } break; - - case NOTIFICATION_ENTER_CANVAS: { - data.parent = Object::cast_to<Control>(get_parent()); - data.parent_window = Object::cast_to<Window>(get_parent()); - data.is_rtl_dirty = true; - - if (data.theme.is_null()) { - if (data.parent && (data.parent->data.theme_owner || data.parent->data.theme_owner_window)) { - data.theme_owner = data.parent->data.theme_owner; - data.theme_owner_window = data.parent->data.theme_owner_window; - notification(NOTIFICATION_THEME_CHANGED); - } else if (data.parent_window && (data.parent_window->theme_owner || data.parent_window->theme_owner_window)) { - data.theme_owner = data.parent_window->theme_owner; - data.theme_owner_window = data.parent_window->theme_owner_window; - notification(NOTIFICATION_THEME_CHANGED); - } - } - - CanvasItem *node = this; - bool has_parent_control = false; - - while (!node->is_set_as_top_level()) { - CanvasItem *parent = Object::cast_to<CanvasItem>(node->get_parent()); - if (!parent) { - break; - } - - Control *parent_control = Object::cast_to<Control>(parent); - if (parent_control) { - has_parent_control = true; - break; - } - node = parent; - } - - if (has_parent_control) { - // Do nothing, has a parent control. - } else { - // Is a regular root control or top_level. - Viewport *viewport = get_viewport(); - ERR_FAIL_COND(!viewport); - data.RI = viewport->_gui_add_root_control(this); - } - - data.parent_canvas_item = get_parent_item(); - - if (data.parent_canvas_item) { - data.parent_canvas_item->connect("item_rect_changed", callable_mp(this, &Control::_size_changed)); - } else { - // Connect viewport. - Viewport *viewport = get_viewport(); - ERR_FAIL_COND(!viewport); - viewport->connect("size_changed", callable_mp(this, &Control::_size_changed)); - } - } break; - - case NOTIFICATION_EXIT_CANVAS: { - if (data.parent_canvas_item) { - data.parent_canvas_item->disconnect("item_rect_changed", callable_mp(this, &Control::_size_changed)); - data.parent_canvas_item = nullptr; - } else if (!is_set_as_top_level()) { - //disconnect viewport - Viewport *viewport = get_viewport(); - ERR_FAIL_COND(!viewport); - viewport->disconnect("size_changed", callable_mp(this, &Control::_size_changed)); - } - - if (data.RI) { - get_viewport()->_gui_remove_root_control(data.RI); - data.RI = nullptr; - } - - data.parent = nullptr; - data.parent_canvas_item = nullptr; - data.parent_window = nullptr; - data.is_rtl_dirty = true; - } break; - - case NOTIFICATION_MOVED_IN_PARENT: { - // some parents need to know the order of the children to draw (like TabContainer) - // update if necessary - if (data.parent) { - data.parent->update(); - } - update(); - - if (data.RI) { - get_viewport()->_gui_set_root_order_dirty(); - } - } break; - - case NOTIFICATION_RESIZED: { - emit_signal(SceneStringNames::get_singleton()->resized); - } break; - - case NOTIFICATION_DRAW: { - _update_canvas_item_transform(); - RenderingServer::get_singleton()->canvas_item_set_custom_rect(get_canvas_item(), !data.disable_visibility_clip, Rect2(Point2(), get_size())); - RenderingServer::get_singleton()->canvas_item_set_clip(get_canvas_item(), data.clip_contents); - } break; - - case NOTIFICATION_MOUSE_ENTER: { - emit_signal(SceneStringNames::get_singleton()->mouse_entered); - } break; - - case NOTIFICATION_MOUSE_EXIT: { - emit_signal(SceneStringNames::get_singleton()->mouse_exited); - } break; - - case NOTIFICATION_FOCUS_ENTER: { - emit_signal(SceneStringNames::get_singleton()->focus_entered); - update(); - } break; - - case NOTIFICATION_FOCUS_EXIT: { - emit_signal(SceneStringNames::get_singleton()->focus_exited); - update(); - } break; - - case NOTIFICATION_THEME_CHANGED: { - update_minimum_size(); - update(); - } break; - - case NOTIFICATION_VISIBILITY_CHANGED: { - if (!is_visible_in_tree()) { - if (get_viewport() != nullptr) { - get_viewport()->_gui_hide_control(this); - } - } else { - data.minimum_size_valid = false; - _update_minimum_size(); - _size_changed(); - } - } break; - - case NOTIFICATION_TRANSLATION_CHANGED: - case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { - if (is_inside_tree()) { - data.is_rtl_dirty = true; - _size_changed(); - } - } break; - } -} - -bool Control::has_point(const Point2 &p_point) const { - bool ret; - if (GDVIRTUAL_CALL(_has_point, p_point, ret)) { - return ret; - } - return Rect2(Point2(), get_size()).has_point(p_point); -} - -void Control::set_drag_forwarding(Object *p_target) { - if (p_target) { - data.drag_owner = p_target->get_instance_id(); - } else { - data.drag_owner = ObjectID(); - } -} - -Variant Control::get_drag_data(const Point2 &p_point) { - if (data.drag_owner.is_valid()) { - Object *obj = ObjectDB::get_instance(data.drag_owner); - if (obj) { - return obj->call("_get_drag_data_fw", p_point, this); - } - } - - Variant dd; - if (GDVIRTUAL_CALL(_get_drag_data, p_point, dd)) { - return dd; - } - - return Variant(); -} - -bool Control::can_drop_data(const Point2 &p_point, const Variant &p_data) const { - if (data.drag_owner.is_valid()) { - Object *obj = ObjectDB::get_instance(data.drag_owner); - if (obj) { - return obj->call("_can_drop_data_fw", p_point, p_data, this); - } - } - - bool ret; - if (GDVIRTUAL_CALL(_can_drop_data, p_point, p_data, ret)) { - return ret; - } return false; } -void Control::drop_data(const Point2 &p_point, const Variant &p_data) { - if (data.drag_owner.is_valid()) { - Object *obj = ObjectDB::get_instance(data.drag_owner); - if (obj) { - obj->call("_drop_data_fw", p_point, p_data, this); - return; - } +bool Control::_property_get_revert(const StringName &p_name, Variant &r_property) const { + if (p_name == "layout_mode") { + r_property = _get_default_layout_mode(); + return true; + } else if (p_name == "anchors_preset") { + r_property = LayoutPreset::PRESET_TOP_LEFT; + return true; } - GDVIRTUAL_CALL(_drop_data, p_point, p_data); -} - -void Control::force_drag(const Variant &p_data, Control *p_control) { - ERR_FAIL_COND(!is_inside_tree()); - ERR_FAIL_COND(p_data.get_type() == Variant::NIL); - - get_viewport()->_gui_force_drag(this, p_data, p_control); + return false; } -void Control::set_drag_preview(Control *p_control) { - ERR_FAIL_COND(!is_inside_tree()); - ERR_FAIL_COND(!get_viewport()->gui_is_dragging()); - get_viewport()->_gui_set_drag_preview(this, p_control); -} +// Global relations. -bool Control::is_drag_successful() const { - return is_inside_tree() && get_viewport()->gui_is_drag_successful(); +bool Control::is_top_level_control() const { + return is_inside_tree() && (!data.parent_canvas_item && !data.RI && is_set_as_top_level()); } -void Control::_call_gui_input(const Ref<InputEvent> &p_event) { - emit_signal(SceneStringNames::get_singleton()->gui_input, p_event); //signal should be first, so it's possible to override an event (and then accept it) - if (!is_inside_tree() || get_viewport()->is_input_handled()) { - return; //input was handled, abort - } - GDVIRTUAL_CALL(_gui_input, p_event); - if (!is_inside_tree() || get_viewport()->is_input_handled()) { - return; //input was handled, abort - } - gui_input(p_event); -} -void Control::gui_input(const Ref<InputEvent> &p_event) { +Control *Control::get_parent_control() const { + return data.parent; } -Size2 Control::get_minimum_size() const { - Vector2 ms; - if (GDVIRTUAL_CALL(_get_minimum_size, ms)) { - return ms; - } - return Vector2(); +Window *Control::get_parent_window() const { + return data.parent_window; } -template <class T> -T Control::get_theme_item_in_types(Control *p_theme_owner, Window *p_theme_owner_window, Theme::DataType p_data_type, const StringName &p_name, List<StringName> p_theme_types) { - ERR_FAIL_COND_V_MSG(p_theme_types.size() == 0, T(), "At least one theme type must be specified."); - - // First, look through each control or window node in the branch, until no valid parent can be found. - // Only nodes with a theme resource attached are considered. - Control *theme_owner = p_theme_owner; - Window *theme_owner_window = p_theme_owner_window; - - while (theme_owner || theme_owner_window) { - // For each theme resource check the theme types provided and see if p_name exists with any of them. - for (const StringName &E : p_theme_types) { - if (theme_owner && theme_owner->data.theme->has_theme_item(p_data_type, p_name, E)) { - return theme_owner->data.theme->get_theme_item(p_data_type, p_name, E); - } +Control *Control::get_root_parent_control() const { + const CanvasItem *ci = this; + const Control *root = this; - if (theme_owner_window && theme_owner_window->theme->has_theme_item(p_data_type, p_name, E)) { - return theme_owner_window->theme->get_theme_item(p_data_type, p_name, E); - } - } + while (ci) { + const Control *c = Object::cast_to<Control>(ci); + if (c) { + root = c; - Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent(); - Control *parent_c = Object::cast_to<Control>(parent); - if (parent_c) { - theme_owner = parent_c->data.theme_owner; - theme_owner_window = parent_c->data.theme_owner_window; - } else { - Window *parent_w = Object::cast_to<Window>(parent); - if (parent_w) { - theme_owner = parent_w->theme_owner; - theme_owner_window = parent_w->theme_owner_window; - } else { - theme_owner = nullptr; - theme_owner_window = nullptr; + if (c->data.RI || c->is_top_level_control()) { + break; } } - } - // Secondly, check the project-defined Theme resource. - if (Theme::get_project_default().is_valid()) { - for (const StringName &E : p_theme_types) { - if (Theme::get_project_default()->has_theme_item(p_data_type, p_name, E)) { - return Theme::get_project_default()->get_theme_item(p_data_type, p_name, E); - } - } + ci = ci->get_parent_item(); } - // Lastly, fall back on the items defined in the default Theme, if they exist. - for (const StringName &E : p_theme_types) { - if (Theme::get_default()->has_theme_item(p_data_type, p_name, E)) { - return Theme::get_default()->get_theme_item(p_data_type, p_name, E); - } - } - // If they don't exist, use any type to return the default/empty value. - return Theme::get_default()->get_theme_item(p_data_type, p_name, p_theme_types[0]); + return const_cast<Control *>(root); } -bool Control::has_theme_item_in_types(Control *p_theme_owner, Window *p_theme_owner_window, Theme::DataType p_data_type, const StringName &p_name, List<StringName> p_theme_types) { - ERR_FAIL_COND_V_MSG(p_theme_types.size() == 0, false, "At least one theme type must be specified."); - - // First, look through each control or window node in the branch, until no valid parent can be found. - // Only nodes with a theme resource attached are considered. - Control *theme_owner = p_theme_owner; - Window *theme_owner_window = p_theme_owner_window; - - while (theme_owner || theme_owner_window) { - // For each theme resource check the theme types provided and see if p_name exists with any of them. - for (const StringName &E : p_theme_types) { - if (theme_owner && theme_owner->data.theme->has_theme_item(p_data_type, p_name, E)) { - return true; - } - - if (theme_owner_window && theme_owner_window->theme->has_theme_item(p_data_type, p_name, E)) { - return true; - } - } - - Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent(); - Control *parent_c = Object::cast_to<Control>(parent); - if (parent_c) { - theme_owner = parent_c->data.theme_owner; - theme_owner_window = parent_c->data.theme_owner_window; - } else { - Window *parent_w = Object::cast_to<Window>(parent); - if (parent_w) { - theme_owner = parent_w->theme_owner; - theme_owner_window = parent_w->theme_owner_window; - } else { - theme_owner = nullptr; - theme_owner_window = nullptr; - } - } - } - - // Secondly, check the project-defined Theme resource. - if (Theme::get_project_default().is_valid()) { - for (const StringName &E : p_theme_types) { - if (Theme::get_project_default()->has_theme_item(p_data_type, p_name, E)) { - return true; - } - } - } - - // Lastly, fall back on the items defined in the default Theme, if they exist. - for (const StringName &E : p_theme_types) { - if (Theme::get_default()->has_theme_item(p_data_type, p_name, E)) { - return true; - } +Rect2 Control::get_parent_anchorable_rect() const { + if (!is_inside_tree()) { + return Rect2(); } - return false; -} -void Control::_get_theme_type_dependencies(const StringName &p_theme_type, List<StringName> *p_list) const { - if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { - if (Theme::get_project_default().is_valid() && Theme::get_project_default()->get_type_variation_base(data.theme_type_variation) != StringName()) { - Theme::get_project_default()->get_type_dependencies(get_class_name(), data.theme_type_variation, p_list); + Rect2 parent_rect; + if (data.parent_canvas_item) { + parent_rect = data.parent_canvas_item->get_anchorable_rect(); + } else { +#ifdef TOOLS_ENABLED + Node *edited_root = get_tree()->get_edited_scene_root(); + if (edited_root && (this == edited_root || edited_root->is_ancestor_of(this))) { + parent_rect.size = Size2(ProjectSettings::get_singleton()->get("display/window/size/viewport_width"), ProjectSettings::get_singleton()->get("display/window/size/viewport_height")); } else { - Theme::get_default()->get_type_dependencies(get_class_name(), data.theme_type_variation, p_list); + parent_rect = get_viewport()->get_visible_rect(); } - } else { - Theme::get_default()->get_type_dependencies(p_theme_type, StringName(), p_list); - } -} -Ref<Texture2D> Control::get_theme_icon(const StringName &p_name, const StringName &p_theme_type) const { - if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { - const Ref<Texture2D> *tex = data.icon_override.getptr(p_name); - if (tex) { - return *tex; - } +#else + parent_rect = get_viewport()->get_visible_rect(); +#endif } - List<StringName> theme_types; - _get_theme_type_dependencies(p_theme_type, &theme_types); - return get_theme_item_in_types<Ref<Texture2D>>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_ICON, p_name, theme_types); + return parent_rect; } -Ref<StyleBox> Control::get_theme_stylebox(const StringName &p_name, const StringName &p_theme_type) const { - if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { - const Ref<StyleBox> *style = data.style_override.getptr(p_name); - if (style) { - return *style; - } - } - - List<StringName> theme_types; - _get_theme_type_dependencies(p_theme_type, &theme_types); - return get_theme_item_in_types<Ref<StyleBox>>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_STYLEBOX, p_name, theme_types); +Size2 Control::get_parent_area_size() const { + return get_parent_anchorable_rect().size; } -Ref<Font> Control::get_theme_font(const StringName &p_name, const StringName &p_theme_type) const { - if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { - const Ref<Font> *font = data.font_override.getptr(p_name); - if (font) { - return *font; - } - } +// Positioning and sizing. - List<StringName> theme_types; - _get_theme_type_dependencies(p_theme_type, &theme_types); - return get_theme_item_in_types<Ref<Font>>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_FONT, p_name, theme_types); -} - -int Control::get_theme_font_size(const StringName &p_name, const StringName &p_theme_type) const { - if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { - const int *font_size = data.font_size_override.getptr(p_name); - if (font_size && (*font_size) > 0) { - return *font_size; - } - } +Transform2D Control::_get_internal_transform() const { + Transform2D rot_scale; + rot_scale.set_rotation_and_scale(data.rotation, data.scale); + Transform2D offset; + offset.set_origin(-data.pivot_offset); - List<StringName> theme_types; - _get_theme_type_dependencies(p_theme_type, &theme_types); - return get_theme_item_in_types<int>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_FONT_SIZE, p_name, theme_types); + return offset.affine_inverse() * (rot_scale * offset); } -Color Control::get_theme_color(const StringName &p_name, const StringName &p_theme_type) const { - if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { - const Color *color = data.color_override.getptr(p_name); - if (color) { - return *color; - } - } - - List<StringName> theme_types; - _get_theme_type_dependencies(p_theme_type, &theme_types); - return get_theme_item_in_types<Color>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_COLOR, p_name, theme_types); -} +void Control::_update_canvas_item_transform() { + Transform2D xform = _get_internal_transform(); + xform[2] += get_position(); -int Control::get_theme_constant(const StringName &p_name, const StringName &p_theme_type) const { - if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { - const int *constant = data.constant_override.getptr(p_name); - if (constant) { - return *constant; - } + // We use a little workaround to avoid flickering when moving the pivot with _edit_set_pivot() + if (is_inside_tree() && Math::abs(Math::sin(data.rotation * 4.0f)) < 0.00001f && get_viewport()->is_snap_controls_to_pixels_enabled()) { + xform[2] = xform[2].round(); } - List<StringName> theme_types; - _get_theme_type_dependencies(p_theme_type, &theme_types); - return get_theme_item_in_types<int>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_CONSTANT, p_name, theme_types); + RenderingServer::get_singleton()->canvas_item_set_transform(get_canvas_item(), xform); } -bool Control::has_theme_icon_override(const StringName &p_name) const { - const Ref<Texture2D> *tex = data.icon_override.getptr(p_name); - return tex != nullptr; +Transform2D Control::get_transform() const { + Transform2D xform = _get_internal_transform(); + xform[2] += get_position(); + return xform; } -bool Control::has_theme_stylebox_override(const StringName &p_name) const { - const Ref<StyleBox> *style = data.style_override.getptr(p_name); - return style != nullptr; -} +/// Anchors and offsets. -bool Control::has_theme_font_override(const StringName &p_name) const { - const Ref<Font> *font = data.font_override.getptr(p_name); - return font != nullptr; +void Control::_set_anchor(Side p_side, real_t p_anchor) { + set_anchor(p_side, p_anchor); } -bool Control::has_theme_font_size_override(const StringName &p_name) const { - const int *font_size = data.font_size_override.getptr(p_name); - return font_size != nullptr; -} +void Control::set_anchor(Side p_side, real_t p_anchor, bool p_keep_offset, bool p_push_opposite_anchor) { + ERR_FAIL_INDEX((int)p_side, 4); -bool Control::has_theme_color_override(const StringName &p_name) const { - const Color *color = data.color_override.getptr(p_name); - return color != nullptr; -} + Rect2 parent_rect = get_parent_anchorable_rect(); + real_t parent_range = (p_side == SIDE_LEFT || p_side == SIDE_RIGHT) ? parent_rect.size.x : parent_rect.size.y; + real_t previous_pos = data.offset[p_side] + data.anchor[p_side] * parent_range; + real_t previous_opposite_pos = data.offset[(p_side + 2) % 4] + data.anchor[(p_side + 2) % 4] * parent_range; -bool Control::has_theme_constant_override(const StringName &p_name) const { - const int *constant = data.constant_override.getptr(p_name); - return constant != nullptr; -} + data.anchor[p_side] = p_anchor; -bool Control::has_theme_icon(const StringName &p_name, const StringName &p_theme_type) const { - if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { - if (has_theme_icon_override(p_name)) { - return true; + if (((p_side == SIDE_LEFT || p_side == SIDE_TOP) && data.anchor[p_side] > data.anchor[(p_side + 2) % 4]) || + ((p_side == SIDE_RIGHT || p_side == SIDE_BOTTOM) && data.anchor[p_side] < data.anchor[(p_side + 2) % 4])) { + if (p_push_opposite_anchor) { + data.anchor[(p_side + 2) % 4] = data.anchor[p_side]; + } else { + data.anchor[p_side] = data.anchor[(p_side + 2) % 4]; } } - List<StringName> theme_types; - _get_theme_type_dependencies(p_theme_type, &theme_types); - return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_ICON, p_name, theme_types); -} - -bool Control::has_theme_stylebox(const StringName &p_name, const StringName &p_theme_type) const { - if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { - if (has_theme_stylebox_override(p_name)) { - return true; + if (!p_keep_offset) { + data.offset[p_side] = previous_pos - data.anchor[p_side] * parent_range; + if (p_push_opposite_anchor) { + data.offset[(p_side + 2) % 4] = previous_opposite_pos - data.anchor[(p_side + 2) % 4] * parent_range; } } - - List<StringName> theme_types; - _get_theme_type_dependencies(p_theme_type, &theme_types); - return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_STYLEBOX, p_name, theme_types); -} - -bool Control::has_theme_font(const StringName &p_name, const StringName &p_theme_type) const { - if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { - if (has_theme_font_override(p_name)) { - return true; - } + if (is_inside_tree()) { + _size_changed(); } - List<StringName> theme_types; - _get_theme_type_dependencies(p_theme_type, &theme_types); - return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_FONT, p_name, theme_types); + queue_redraw(); } -bool Control::has_theme_font_size(const StringName &p_name, const StringName &p_theme_type) const { - if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { - if (has_theme_font_size_override(p_name)) { - return true; - } - } +real_t Control::get_anchor(Side p_side) const { + ERR_FAIL_INDEX_V(int(p_side), 4, 0.0); - List<StringName> theme_types; - _get_theme_type_dependencies(p_theme_type, &theme_types); - return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_FONT_SIZE, p_name, theme_types); + return data.anchor[p_side]; } -bool Control::has_theme_color(const StringName &p_name, const StringName &p_theme_type) const { - if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { - if (has_theme_color_override(p_name)) { - return true; - } +void Control::set_offset(Side p_side, real_t p_value) { + ERR_FAIL_INDEX((int)p_side, 4); + if (data.offset[p_side] == p_value) { + return; } - List<StringName> theme_types; - _get_theme_type_dependencies(p_theme_type, &theme_types); - return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_COLOR, p_name, theme_types); + data.offset[p_side] = p_value; + _size_changed(); } -bool Control::has_theme_constant(const StringName &p_name, const StringName &p_theme_type) const { - if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { - if (has_theme_constant_override(p_name)) { - return true; - } - } +real_t Control::get_offset(Side p_side) const { + ERR_FAIL_INDEX_V((int)p_side, 4, 0); - List<StringName> theme_types; - _get_theme_type_dependencies(p_theme_type, &theme_types); - return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_CONSTANT, p_name, theme_types); + return data.offset[p_side]; } -float Control::fetch_theme_default_base_scale(Control *p_theme_owner, Window *p_theme_owner_window) { - // First, look through each control or window node in the branch, until no valid parent can be found. - // Only nodes with a theme resource attached are considered. - // For each theme resource see if their assigned theme has the default value defined and valid. - Control *theme_owner = p_theme_owner; - Window *theme_owner_window = p_theme_owner_window; - - while (theme_owner || theme_owner_window) { - if (theme_owner && theme_owner->data.theme->has_default_base_scale()) { - return theme_owner->data.theme->get_default_base_scale(); - } - - if (theme_owner_window && theme_owner_window->theme->has_default_base_scale()) { - return theme_owner_window->theme->get_default_base_scale(); - } - - Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent(); - Control *parent_c = Object::cast_to<Control>(parent); - if (parent_c) { - theme_owner = parent_c->data.theme_owner; - theme_owner_window = parent_c->data.theme_owner_window; - } else { - Window *parent_w = Object::cast_to<Window>(parent); - if (parent_w) { - theme_owner = parent_w->theme_owner; - theme_owner_window = parent_w->theme_owner_window; - } else { - theme_owner = nullptr; - theme_owner_window = nullptr; - } - } - } +void Control::set_anchor_and_offset(Side p_side, real_t p_anchor, real_t p_pos, bool p_push_opposite_anchor) { + set_anchor(p_side, p_anchor, false, p_push_opposite_anchor); + set_offset(p_side, p_pos); +} - // Secondly, check the project-defined Theme resource. - if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_default_base_scale()) { - return Theme::get_project_default()->get_default_base_scale(); - } +void Control::set_begin(const Size2 &p_point) { + if (data.offset[0] == p_point.x && data.offset[1] == p_point.y) { + return; } - // Lastly, fall back on the default Theme. - if (Theme::get_default()->has_default_base_scale()) { - return Theme::get_default()->get_default_base_scale(); - } - return Theme::get_fallback_base_scale(); + data.offset[0] = p_point.x; + data.offset[1] = p_point.y; + _size_changed(); } -float Control::get_theme_default_base_scale() const { - return fetch_theme_default_base_scale(data.theme_owner, data.theme_owner_window); +Size2 Control::get_begin() const { + return Size2(data.offset[0], data.offset[1]); } -Ref<Font> Control::fetch_theme_default_font(Control *p_theme_owner, Window *p_theme_owner_window) { - // First, look through each control or window node in the branch, until no valid parent can be found. - // Only nodes with a theme resource attached are considered. - // For each theme resource see if their assigned theme has the default value defined and valid. - Control *theme_owner = p_theme_owner; - Window *theme_owner_window = p_theme_owner_window; - - while (theme_owner || theme_owner_window) { - if (theme_owner && theme_owner->data.theme->has_default_font()) { - return theme_owner->data.theme->get_default_font(); - } - - if (theme_owner_window && theme_owner_window->theme->has_default_font()) { - return theme_owner_window->theme->get_default_font(); - } - - Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent(); - Control *parent_c = Object::cast_to<Control>(parent); - if (parent_c) { - theme_owner = parent_c->data.theme_owner; - theme_owner_window = parent_c->data.theme_owner_window; - } else { - Window *parent_w = Object::cast_to<Window>(parent); - if (parent_w) { - theme_owner = parent_w->theme_owner; - theme_owner_window = parent_w->theme_owner_window; - } else { - theme_owner = nullptr; - theme_owner_window = nullptr; - } - } - } - - // Secondly, check the project-defined Theme resource. - if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_default_font()) { - return Theme::get_project_default()->get_default_font(); - } +void Control::set_end(const Size2 &p_point) { + if (data.offset[2] == p_point.x && data.offset[3] == p_point.y) { + return; } - // Lastly, fall back on the default Theme. - if (Theme::get_default()->has_default_font()) { - return Theme::get_default()->get_default_font(); - } - return Theme::get_fallback_font(); + data.offset[2] = p_point.x; + data.offset[3] = p_point.y; + _size_changed(); } -Ref<Font> Control::get_theme_default_font() const { - return fetch_theme_default_font(data.theme_owner, data.theme_owner_window); +Size2 Control::get_end() const { + return Size2(data.offset[2], data.offset[3]); } -int Control::fetch_theme_default_font_size(Control *p_theme_owner, Window *p_theme_owner_window) { - // First, look through each control or window node in the branch, until no valid parent can be found. - // Only nodes with a theme resource attached are considered. - // For each theme resource see if their assigned theme has the default value defined and valid. - Control *theme_owner = p_theme_owner; - Window *theme_owner_window = p_theme_owner_window; - - while (theme_owner || theme_owner_window) { - if (theme_owner && theme_owner->data.theme->has_default_font_size()) { - return theme_owner->data.theme->get_default_font_size(); - } - - if (theme_owner_window && theme_owner_window->theme->has_default_font_size()) { - return theme_owner_window->theme->get_default_font_size(); - } - - Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent(); - Control *parent_c = Object::cast_to<Control>(parent); - if (parent_c) { - theme_owner = parent_c->data.theme_owner; - theme_owner_window = parent_c->data.theme_owner_window; - } else { - Window *parent_w = Object::cast_to<Window>(parent); - if (parent_w) { - theme_owner = parent_w->theme_owner; - theme_owner_window = parent_w->theme_owner_window; - } else { - theme_owner = nullptr; - theme_owner_window = nullptr; - } - } +void Control::set_h_grow_direction(GrowDirection p_direction) { + if (data.h_grow == p_direction) { + return; } - // Secondly, check the project-defined Theme resource. - if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_default_font_size()) { - return Theme::get_project_default()->get_default_font_size(); - } - } + ERR_FAIL_INDEX((int)p_direction, 3); - // Lastly, fall back on the default Theme. - if (Theme::get_default()->has_default_font_size()) { - return Theme::get_default()->get_default_font_size(); - } - return Theme::get_fallback_font_size(); + data.h_grow = p_direction; + _size_changed(); } -int Control::get_theme_default_font_size() const { - return fetch_theme_default_font_size(data.theme_owner, data.theme_owner_window); +Control::GrowDirection Control::get_h_grow_direction() const { + return data.h_grow; } -Rect2 Control::get_parent_anchorable_rect() const { - if (!is_inside_tree()) { - return Rect2(); +void Control::set_v_grow_direction(GrowDirection p_direction) { + if (data.v_grow == p_direction) { + return; } - Rect2 parent_rect; - if (data.parent_canvas_item) { - parent_rect = data.parent_canvas_item->get_anchorable_rect(); - } else { -#ifdef TOOLS_ENABLED - Node *edited_root = get_tree()->get_edited_scene_root(); - if (edited_root && (this == edited_root || edited_root->is_ancestor_of(this))) { - parent_rect.size = Size2(ProjectSettings::get_singleton()->get("display/window/size/viewport_width"), ProjectSettings::get_singleton()->get("display/window/size/viewport_height")); - } else { - parent_rect = get_viewport()->get_visible_rect(); - } - -#else - parent_rect = get_viewport()->get_visible_rect(); -#endif - } + ERR_FAIL_INDEX((int)p_direction, 3); - return parent_rect; + data.v_grow = p_direction; + _size_changed(); } -Size2 Control::get_parent_area_size() const { - return get_parent_anchorable_rect().size; +Control::GrowDirection Control::get_v_grow_direction() const { + return data.v_grow; } -void Control::_size_changed() { - Rect2 parent_rect = get_parent_anchorable_rect(); - - real_t edge_pos[4]; - - for (int i = 0; i < 4; i++) { - real_t area = parent_rect.size[i & 1]; - edge_pos[i] = data.offset[i] + (data.anchor[i] * area); - } - - Point2 new_pos_cache = Point2(edge_pos[0], edge_pos[1]); - Size2 new_size_cache = Point2(edge_pos[2], edge_pos[3]) - new_pos_cache; - - Size2 minimum_size = get_combined_minimum_size(); - - if (minimum_size.width > new_size_cache.width) { - if (data.h_grow == GROW_DIRECTION_BEGIN) { - new_pos_cache.x += new_size_cache.width - minimum_size.width; - } else if (data.h_grow == GROW_DIRECTION_BOTH) { - new_pos_cache.x += 0.5 * (new_size_cache.width - minimum_size.width); - } - - new_size_cache.width = minimum_size.width; - } +void Control::_compute_anchors(Rect2 p_rect, const real_t p_offsets[4], real_t (&r_anchors)[4]) { + Size2 parent_rect_size = get_parent_anchorable_rect().size; + ERR_FAIL_COND(parent_rect_size.x == 0.0); + ERR_FAIL_COND(parent_rect_size.y == 0.0); + real_t x = p_rect.position.x; if (is_layout_rtl()) { - new_pos_cache.x = parent_rect.size.x - new_pos_cache.x - new_size_cache.x; - } - - if (minimum_size.height > new_size_cache.height) { - if (data.v_grow == GROW_DIRECTION_BEGIN) { - new_pos_cache.y += new_size_cache.height - minimum_size.height; - } else if (data.v_grow == GROW_DIRECTION_BOTH) { - new_pos_cache.y += 0.5 * (new_size_cache.height - minimum_size.height); - } - - new_size_cache.height = minimum_size.height; + x = parent_rect_size.x - x - p_rect.size.x; } + r_anchors[0] = (x - p_offsets[0]) / parent_rect_size.x; + r_anchors[1] = (p_rect.position.y - p_offsets[1]) / parent_rect_size.y; + r_anchors[2] = (x + p_rect.size.x - p_offsets[2]) / parent_rect_size.x; + r_anchors[3] = (p_rect.position.y + p_rect.size.y - p_offsets[3]) / parent_rect_size.y; +} - bool pos_changed = new_pos_cache != data.pos_cache; - bool size_changed = new_size_cache != data.size_cache; - - data.pos_cache = new_pos_cache; - data.size_cache = new_size_cache; - - if (is_inside_tree()) { - if (size_changed) { - notification(NOTIFICATION_RESIZED); - } - if (pos_changed || size_changed) { - item_rect_changed(size_changed); - _notify_transform(); - } +void Control::_compute_offsets(Rect2 p_rect, const real_t p_anchors[4], real_t (&r_offsets)[4]) { + Size2 parent_rect_size = get_parent_anchorable_rect().size; - if (pos_changed && !size_changed) { - _update_canvas_item_transform(); //move because it won't be updated - } + real_t x = p_rect.position.x; + if (is_layout_rtl()) { + x = parent_rect_size.x - x - p_rect.size.x; } + r_offsets[0] = x - (p_anchors[0] * parent_rect_size.x); + r_offsets[1] = p_rect.position.y - (p_anchors[1] * parent_rect_size.y); + r_offsets[2] = x + p_rect.size.x - (p_anchors[2] * parent_rect_size.x); + r_offsets[3] = p_rect.position.y + p_rect.size.y - (p_anchors[3] * parent_rect_size.y); } +/// Presets and layout modes. + void Control::_set_layout_mode(LayoutMode p_mode) { bool list_changed = false; - if (p_mode == LayoutMode::LAYOUT_MODE_POSITION || p_mode == LayoutMode::LAYOUT_MODE_ANCHORS) { - if ((int)get_meta("_edit_layout_mode", p_mode) != (int)p_mode) { - list_changed = true; - } - - set_meta("_edit_layout_mode", (int)p_mode); + if (data.stored_layout_mode != p_mode) { + list_changed = true; + data.stored_layout_mode = p_mode; + } - if (p_mode == LayoutMode::LAYOUT_MODE_POSITION) { - remove_meta("_edit_layout_mode"); - remove_meta("_edit_use_custom_anchors"); - set_anchors_and_offsets_preset(LayoutPreset::PRESET_TOP_LEFT, LayoutPresetMode::PRESET_MODE_KEEP_SIZE); - set_grow_direction_preset(LayoutPreset::PRESET_TOP_LEFT); - } - } else { - if (has_meta("_edit_layout_mode")) { - remove_meta("_edit_layout_mode"); - list_changed = true; - } + if (data.stored_layout_mode == LayoutMode::LAYOUT_MODE_POSITION) { + data.stored_use_custom_anchors = false; + set_anchors_and_offsets_preset(LayoutPreset::PRESET_TOP_LEFT, LayoutPresetMode::PRESET_MODE_KEEP_SIZE); + set_grow_direction_preset(LayoutPreset::PRESET_TOP_LEFT); } if (list_changed) { @@ -1549,74 +864,43 @@ Control::LayoutMode Control::_get_layout_mode() const { if (_get_anchors_layout_preset() != (int)LayoutPreset::PRESET_TOP_LEFT) { return LayoutMode::LAYOUT_MODE_ANCHORS; } - // Otherwise check what was saved. - if (has_meta("_edit_layout_mode")) { - return (LayoutMode)(int)get_meta("_edit_layout_mode"); - } - // Or fallback on default. - return LayoutMode::LAYOUT_MODE_POSITION; -} - -void Control::set_anchor(Side p_side, real_t p_anchor, bool p_keep_offset, bool p_push_opposite_anchor) { - ERR_FAIL_INDEX((int)p_side, 4); - - Rect2 parent_rect = get_parent_anchorable_rect(); - real_t parent_range = (p_side == SIDE_LEFT || p_side == SIDE_RIGHT) ? parent_rect.size.x : parent_rect.size.y; - real_t previous_pos = data.offset[p_side] + data.anchor[p_side] * parent_range; - real_t previous_opposite_pos = data.offset[(p_side + 2) % 4] + data.anchor[(p_side + 2) % 4] * parent_range; - data.anchor[p_side] = p_anchor; - - if (((p_side == SIDE_LEFT || p_side == SIDE_TOP) && data.anchor[p_side] > data.anchor[(p_side + 2) % 4]) || - ((p_side == SIDE_RIGHT || p_side == SIDE_BOTTOM) && data.anchor[p_side] < data.anchor[(p_side + 2) % 4])) { - if (p_push_opposite_anchor) { - data.anchor[(p_side + 2) % 4] = data.anchor[p_side]; - } else { - data.anchor[p_side] = data.anchor[(p_side + 2) % 4]; - } - } - - if (!p_keep_offset) { - data.offset[p_side] = previous_pos - data.anchor[p_side] * parent_range; - if (p_push_opposite_anchor) { - data.offset[(p_side + 2) % 4] = previous_opposite_pos - data.anchor[(p_side + 2) % 4] * parent_range; - } - } - if (is_inside_tree()) { - _size_changed(); - } - - update(); + // Otherwise fallback on what's stored. + return data.stored_layout_mode; } -void Control::_set_anchor(Side p_side, real_t p_anchor) { - set_anchor(p_side, p_anchor); -} +Control::LayoutMode Control::_get_default_layout_mode() const { + Node *parent_node = get_parent_control(); + // In these modes the property is read-only. + if (!parent_node) { + return LayoutMode::LAYOUT_MODE_UNCONTROLLED; + } else if (Object::cast_to<Container>(parent_node)) { + return LayoutMode::LAYOUT_MODE_CONTAINER; + } -void Control::set_anchor_and_offset(Side p_side, real_t p_anchor, real_t p_pos, bool p_push_opposite_anchor) { - set_anchor(p_side, p_anchor, false, p_push_opposite_anchor); - set_offset(p_side, p_pos); + // Otherwise fallback on the position mode. + return LayoutMode::LAYOUT_MODE_POSITION; } void Control::_set_anchors_layout_preset(int p_preset) { bool list_changed = false; - if (get_meta("_edit_layout_mode", LayoutMode::LAYOUT_MODE_ANCHORS).operator int() != LayoutMode::LAYOUT_MODE_ANCHORS) { + if (data.stored_layout_mode != LayoutMode::LAYOUT_MODE_ANCHORS) { list_changed = true; - set_meta("_edit_layout_mode", LayoutMode::LAYOUT_MODE_ANCHORS); + data.stored_layout_mode = LayoutMode::LAYOUT_MODE_ANCHORS; } if (p_preset == -1) { - if (!get_meta("_edit_use_custom_anchors", false)) { - set_meta("_edit_use_custom_anchors", true); + if (!data.stored_use_custom_anchors) { + data.stored_use_custom_anchors = true; notify_property_list_changed(); } return; // Keep settings as is. } - if (get_meta("_edit_use_custom_anchors", true)) { + if (data.stored_use_custom_anchors) { list_changed = true; - remove_meta("_edit_use_custom_anchors"); + data.stored_use_custom_anchors = false; } LayoutPreset preset = (LayoutPreset)p_preset; @@ -1642,7 +926,7 @@ void Control::_set_anchors_layout_preset(int p_preset) { case PRESET_BOTTOM_WIDE: case PRESET_VCENTER_WIDE: case PRESET_HCENTER_WIDE: - case PRESET_WIDE: + case PRESET_FULL_RECT: set_offsets_preset(preset, LayoutPresetMode::PRESET_MODE_MINSIZE); break; } @@ -1657,7 +941,7 @@ void Control::_set_anchors_layout_preset(int p_preset) { int Control::_get_anchors_layout_preset() const { // If the custom preset was selected by user, use it. - if ((bool)get_meta("_edit_use_custom_anchors", false)) { + if (data.stored_use_custom_anchors) { return -1; } @@ -1718,7 +1002,7 @@ int Control::_get_anchors_layout_preset() const { } if (left == ANCHOR_BEGIN && right == ANCHOR_END && top == ANCHOR_BEGIN && bottom == ANCHOR_END) { - return (int)LayoutPreset::PRESET_WIDE; + return (int)LayoutPreset::PRESET_FULL_RECT; } // Does not match any preset, return "Custom". @@ -1737,7 +1021,7 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_offsets) { case PRESET_BOTTOM_WIDE: case PRESET_LEFT_WIDE: case PRESET_HCENTER_WIDE: - case PRESET_WIDE: + case PRESET_FULL_RECT: set_anchor(SIDE_LEFT, ANCHOR_BEGIN, p_keep_offsets); break; @@ -1765,7 +1049,7 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_offsets) { case PRESET_RIGHT_WIDE: case PRESET_TOP_WIDE: case PRESET_VCENTER_WIDE: - case PRESET_WIDE: + case PRESET_FULL_RECT: set_anchor(SIDE_TOP, ANCHOR_BEGIN, p_keep_offsets); break; @@ -1807,7 +1091,7 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_offsets) { case PRESET_RIGHT_WIDE: case PRESET_BOTTOM_WIDE: case PRESET_HCENTER_WIDE: - case PRESET_WIDE: + case PRESET_FULL_RECT: set_anchor(SIDE_RIGHT, ANCHOR_END, p_keep_offsets); break; } @@ -1835,7 +1119,7 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_offsets) { case PRESET_RIGHT_WIDE: case PRESET_BOTTOM_WIDE: case PRESET_VCENTER_WIDE: - case PRESET_WIDE: + case PRESET_FULL_RECT: set_anchor(SIDE_BOTTOM, ANCHOR_END, p_keep_offsets); break; } @@ -1870,7 +1154,7 @@ void Control::set_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz case PRESET_BOTTOM_WIDE: case PRESET_LEFT_WIDE: case PRESET_HCENTER_WIDE: - case PRESET_WIDE: + case PRESET_FULL_RECT: data.offset[0] = x * (0.0 - data.anchor[0]) + p_margin + parent_rect.position.x; break; @@ -1898,7 +1182,7 @@ void Control::set_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz case PRESET_RIGHT_WIDE: case PRESET_TOP_WIDE: case PRESET_VCENTER_WIDE: - case PRESET_WIDE: + case PRESET_FULL_RECT: data.offset[1] = parent_rect.size.y * (0.0 - data.anchor[1]) + p_margin + parent_rect.position.y; break; @@ -1940,7 +1224,7 @@ void Control::set_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz case PRESET_RIGHT_WIDE: case PRESET_BOTTOM_WIDE: case PRESET_HCENTER_WIDE: - case PRESET_WIDE: + case PRESET_FULL_RECT: data.offset[2] = x * (1.0 - data.anchor[2]) - p_margin + parent_rect.position.x; break; } @@ -1968,7 +1252,7 @@ void Control::set_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz case PRESET_RIGHT_WIDE: case PRESET_BOTTOM_WIDE: case PRESET_VCENTER_WIDE: - case PRESET_WIDE: + case PRESET_FULL_RECT: data.offset[3] = parent_rect.size.y * (1.0 - data.anchor[3]) - p_margin + parent_rect.position.y; break; } @@ -2003,7 +1287,7 @@ void Control::set_grow_direction_preset(LayoutPreset p_preset) { case PRESET_BOTTOM_WIDE: case PRESET_VCENTER_WIDE: case PRESET_HCENTER_WIDE: - case PRESET_WIDE: + case PRESET_FULL_RECT: set_h_grow_direction(GrowDirection::GROW_DIRECTION_BOTH); break; } @@ -2031,49 +1315,43 @@ void Control::set_grow_direction_preset(LayoutPreset p_preset) { case PRESET_RIGHT_WIDE: case PRESET_VCENTER_WIDE: case PRESET_HCENTER_WIDE: - case PRESET_WIDE: + case PRESET_FULL_RECT: set_v_grow_direction(GrowDirection::GROW_DIRECTION_BOTH); break; } } -real_t Control::get_anchor(Side p_side) const { - ERR_FAIL_INDEX_V(int(p_side), 4, 0.0); +/// Manual positioning. - return data.anchor[p_side]; +void Control::_set_position(const Size2 &p_point) { + set_position(p_point); } -void Control::set_offset(Side p_side, real_t p_value) { - ERR_FAIL_INDEX((int)p_side, 4); - - data.offset[p_side] = p_value; +void Control::set_position(const Size2 &p_point, bool p_keep_offsets) { + if (p_keep_offsets) { + _compute_anchors(Rect2(p_point, data.size_cache), data.offset, data.anchor); + } else { + _compute_offsets(Rect2(p_point, data.size_cache), data.anchor, data.offset); + } _size_changed(); } -void Control::set_begin(const Size2 &p_point) { - data.offset[0] = p_point.x; - data.offset[1] = p_point.y; - _size_changed(); +Size2 Control::get_position() const { + return data.pos_cache; } -void Control::set_end(const Size2 &p_point) { - data.offset[2] = p_point.x; - data.offset[3] = p_point.y; - _size_changed(); +void Control::_set_global_position(const Point2 &p_point) { + set_global_position(p_point); } -real_t Control::get_offset(Side p_side) const { - ERR_FAIL_INDEX_V((int)p_side, 4, 0); - - return data.offset[p_side]; -} +void Control::set_global_position(const Point2 &p_point, bool p_keep_offsets) { + Transform2D inv; -Size2 Control::get_begin() const { - return Size2(data.offset[0], data.offset[1]); -} + if (data.parent_canvas_item) { + inv = data.parent_canvas_item->get_global_transform().affine_inverse(); + } -Size2 Control::get_end() const { - return Size2(data.offset[2], data.offset[3]); + set_position(inv.xform(p_point), p_keep_offsets); } Point2 Control::get_global_position() const { @@ -2091,72 +1369,6 @@ Point2 Control::get_screen_position() const { return global_pos; } -void Control::_set_global_position(const Point2 &p_point) { - set_global_position(p_point); -} - -void Control::set_global_position(const Point2 &p_point, bool p_keep_offsets) { - Transform2D inv; - - if (data.parent_canvas_item) { - inv = data.parent_canvas_item->get_global_transform().affine_inverse(); - } - - set_position(inv.xform(p_point), p_keep_offsets); -} - -void Control::_compute_anchors(Rect2 p_rect, const real_t p_offsets[4], real_t (&r_anchors)[4]) { - Size2 parent_rect_size = get_parent_anchorable_rect().size; - ERR_FAIL_COND(parent_rect_size.x == 0.0); - ERR_FAIL_COND(parent_rect_size.y == 0.0); - - real_t x = p_rect.position.x; - if (is_layout_rtl()) { - x = parent_rect_size.x - x - p_rect.size.x; - } - r_anchors[0] = (x - p_offsets[0]) / parent_rect_size.x; - r_anchors[1] = (p_rect.position.y - p_offsets[1]) / parent_rect_size.y; - r_anchors[2] = (x + p_rect.size.x - p_offsets[2]) / parent_rect_size.x; - r_anchors[3] = (p_rect.position.y + p_rect.size.y - p_offsets[3]) / parent_rect_size.y; -} - -void Control::_compute_offsets(Rect2 p_rect, const real_t p_anchors[4], real_t (&r_offsets)[4]) { - Size2 parent_rect_size = get_parent_anchorable_rect().size; - - real_t x = p_rect.position.x; - if (is_layout_rtl()) { - x = parent_rect_size.x - x - p_rect.size.x; - } - r_offsets[0] = x - (p_anchors[0] * parent_rect_size.x); - r_offsets[1] = p_rect.position.y - (p_anchors[1] * parent_rect_size.y); - r_offsets[2] = x + p_rect.size.x - (p_anchors[2] * parent_rect_size.x); - r_offsets[3] = p_rect.position.y + p_rect.size.y - (p_anchors[3] * parent_rect_size.y); -} - -void Control::_set_position(const Size2 &p_point) { - set_position(p_point); -} - -void Control::set_position(const Size2 &p_point, bool p_keep_offsets) { - if (p_keep_offsets) { - _compute_anchors(Rect2(p_point, data.size_cache), data.offset, data.anchor); - } else { - _compute_offsets(Rect2(p_point, data.size_cache), data.anchor, data.offset); - } - _size_changed(); -} - -void Control::set_rect(const Rect2 &p_rect) { - for (int i = 0; i < 4; i++) { - data.anchor[i] = ANCHOR_BEGIN; - } - - _compute_offsets(p_rect, data.anchor, data.offset); - if (is_inside_tree()) { - _size_changed(); - } -} - void Control::_set_size(const Size2 &p_size) { #ifdef DEBUG_ENABLED if (data.size_warning && (data.anchor[SIDE_LEFT] != data.anchor[SIDE_RIGHT] || data.anchor[SIDE_TOP] != data.anchor[SIDE_BOTTOM])) { @@ -2184,10 +1396,6 @@ void Control::set_size(const Size2 &p_size, bool p_keep_offsets) { _size_changed(); } -Size2 Control::get_position() const { - return data.pos_cache; -} - Size2 Control::get_size() const { return data.size_cache; } @@ -2196,6 +1404,21 @@ void Control::reset_size() { set_size(Size2()); } +void Control::set_rect(const Rect2 &p_rect) { + for (int i = 0; i < 4; i++) { + data.anchor[i] = ANCHOR_BEGIN; + } + + _compute_offsets(p_rect, data.anchor, data.offset); + if (is_inside_tree()) { + _size_changed(); + } +} + +Rect2 Control::get_rect() const { + return Rect2(get_position(), get_size()); +} + Rect2 Control::get_global_rect() const { return Rect2(get_global_position(), get_size()); } @@ -2220,118 +1443,394 @@ Rect2 Control::get_window_rect() const { return gr; } -Rect2 Control::get_rect() const { - return Rect2(get_position(), get_size()); -} - Rect2 Control::get_anchorable_rect() const { return Rect2(Point2(), get_size()); } -void Control::begin_bulk_theme_override() { - data.bulk_theme_override = true; +void Control::set_scale(const Vector2 &p_scale) { + if (data.scale == p_scale) { + return; + } + + data.scale = p_scale; + // Avoid having 0 scale values, can lead to errors in physics and rendering. + if (data.scale.x == 0) { + data.scale.x = CMP_EPSILON; + } + if (data.scale.y == 0) { + data.scale.y = CMP_EPSILON; + } + queue_redraw(); + _notify_transform(); } -void Control::end_bulk_theme_override() { - ERR_FAIL_COND(!data.bulk_theme_override); +Vector2 Control::get_scale() const { + return data.scale; +} - data.bulk_theme_override = false; - _notify_theme_changed(); +void Control::set_rotation(real_t p_radians) { + if (data.rotation == p_radians) { + return; + } + + data.rotation = p_radians; + queue_redraw(); + _notify_transform(); } -void Control::add_theme_icon_override(const StringName &p_name, const Ref<Texture2D> &p_icon) { - ERR_FAIL_COND(!p_icon.is_valid()); +real_t Control::get_rotation() const { + return data.rotation; +} - if (data.icon_override.has(p_name)) { - data.icon_override[p_name]->disconnect("changed", callable_mp(this, &Control::_override_changed)); +void Control::set_pivot_offset(const Vector2 &p_pivot) { + if (data.pivot_offset == p_pivot) { + return; } - data.icon_override[p_name] = p_icon; - data.icon_override[p_name]->connect("changed", callable_mp(this, &Control::_override_changed), Vector<Variant>(), CONNECT_REFERENCE_COUNTED); - _notify_theme_changed(); + data.pivot_offset = p_pivot; + queue_redraw(); + _notify_transform(); } -void Control::add_theme_style_override(const StringName &p_name, const Ref<StyleBox> &p_style) { - ERR_FAIL_COND(!p_style.is_valid()); +Vector2 Control::get_pivot_offset() const { + return data.pivot_offset; +} - if (data.style_override.has(p_name)) { - data.style_override[p_name]->disconnect("changed", callable_mp(this, &Control::_override_changed)); +/// Sizes. + +void Control::_update_minimum_size() { + if (!is_inside_tree()) { + return; } - data.style_override[p_name] = p_style; - data.style_override[p_name]->connect("changed", callable_mp(this, &Control::_override_changed), Vector<Variant>(), CONNECT_REFERENCE_COUNTED); - _notify_theme_changed(); + Size2 minsize = get_combined_minimum_size(); + data.updating_last_minimum_size = false; + + if (minsize != data.last_minimum_size) { + data.last_minimum_size = minsize; + _size_changed(); + emit_signal(SceneStringNames::get_singleton()->minimum_size_changed); + } } -void Control::add_theme_font_override(const StringName &p_name, const Ref<Font> &p_font) { - ERR_FAIL_COND(!p_font.is_valid()); +void Control::update_minimum_size() { + if (!is_inside_tree() || data.block_minimum_size_adjust) { + return; + } - if (data.font_override.has(p_name)) { - data.font_override[p_name]->disconnect("changed", callable_mp(this, &Control::_override_changed)); + Control *invalidate = this; + + //invalidate cache upwards + while (invalidate && invalidate->data.minimum_size_valid) { + invalidate->data.minimum_size_valid = false; + if (invalidate->is_set_as_top_level()) { + break; // do not go further up + } + if (!invalidate->data.parent && get_parent()) { + Window *parent_window = Object::cast_to<Window>(get_parent()); + if (parent_window && parent_window->is_wrapping_controls()) { + parent_window->child_controls_changed(); + } + } + invalidate = invalidate->data.parent; } - data.font_override[p_name] = p_font; - data.font_override[p_name]->connect("changed", callable_mp(this, &Control::_override_changed), Vector<Variant>(), CONNECT_REFERENCE_COUNTED); - _notify_theme_changed(); + if (!is_visible_in_tree()) { + return; + } + + if (data.updating_last_minimum_size) { + return; + } + + data.updating_last_minimum_size = true; + + MessageQueue::get_singleton()->push_call(this, "_update_minimum_size"); } -void Control::add_theme_font_size_override(const StringName &p_name, int p_font_size) { - data.font_size_override[p_name] = p_font_size; - _notify_theme_changed(); +void Control::set_block_minimum_size_adjust(bool p_block) { + data.block_minimum_size_adjust = p_block; } -void Control::add_theme_color_override(const StringName &p_name, const Color &p_color) { - data.color_override[p_name] = p_color; - _notify_theme_changed(); +bool Control::is_minimum_size_adjust_blocked() const { + return data.block_minimum_size_adjust; } -void Control::add_theme_constant_override(const StringName &p_name, int p_constant) { - data.constant_override[p_name] = p_constant; - _notify_theme_changed(); +Size2 Control::get_minimum_size() const { + Vector2 ms; + if (GDVIRTUAL_CALL(_get_minimum_size, ms)) { + return ms; + } + return Vector2(); } -void Control::remove_theme_icon_override(const StringName &p_name) { - if (data.icon_override.has(p_name)) { - data.icon_override[p_name]->disconnect("changed", callable_mp(this, &Control::_override_changed)); +void Control::set_custom_minimum_size(const Size2i &p_custom) { + if (p_custom == data.custom_minimum_size) { + return; } + data.custom_minimum_size = p_custom; + update_minimum_size(); +} - data.icon_override.erase(p_name); - _notify_theme_changed(); +Size2i Control::get_custom_minimum_size() const { + return data.custom_minimum_size; } -void Control::remove_theme_style_override(const StringName &p_name) { - if (data.style_override.has(p_name)) { - data.style_override[p_name]->disconnect("changed", callable_mp(this, &Control::_override_changed)); +void Control::_update_minimum_size_cache() { + Size2 minsize = get_minimum_size(); + minsize.x = MAX(minsize.x, data.custom_minimum_size.x); + minsize.y = MAX(minsize.y, data.custom_minimum_size.y); + + bool size_changed = false; + if (data.minimum_size_cache != minsize) { + size_changed = true; } - data.style_override.erase(p_name); - _notify_theme_changed(); + data.minimum_size_cache = minsize; + data.minimum_size_valid = true; + + if (size_changed) { + update_minimum_size(); + } } -void Control::remove_theme_font_override(const StringName &p_name) { - if (data.font_override.has(p_name)) { - data.font_override[p_name]->disconnect("changed", callable_mp(this, &Control::_override_changed)); +Size2 Control::get_combined_minimum_size() const { + if (!data.minimum_size_valid) { + const_cast<Control *>(this)->_update_minimum_size_cache(); } + return data.minimum_size_cache; +} - data.font_override.erase(p_name); - _notify_theme_changed(); +void Control::_size_changed() { + Rect2 parent_rect = get_parent_anchorable_rect(); + + real_t edge_pos[4]; + + for (int i = 0; i < 4; i++) { + real_t area = parent_rect.size[i & 1]; + edge_pos[i] = data.offset[i] + (data.anchor[i] * area); + } + + Point2 new_pos_cache = Point2(edge_pos[0], edge_pos[1]); + Size2 new_size_cache = Point2(edge_pos[2], edge_pos[3]) - new_pos_cache; + + Size2 minimum_size = get_combined_minimum_size(); + + if (minimum_size.width > new_size_cache.width) { + if (data.h_grow == GROW_DIRECTION_BEGIN) { + new_pos_cache.x += new_size_cache.width - minimum_size.width; + } else if (data.h_grow == GROW_DIRECTION_BOTH) { + new_pos_cache.x += 0.5 * (new_size_cache.width - minimum_size.width); + } + + new_size_cache.width = minimum_size.width; + } + + if (is_layout_rtl()) { + new_pos_cache.x = parent_rect.size.x - new_pos_cache.x - new_size_cache.x; + } + + if (minimum_size.height > new_size_cache.height) { + if (data.v_grow == GROW_DIRECTION_BEGIN) { + new_pos_cache.y += new_size_cache.height - minimum_size.height; + } else if (data.v_grow == GROW_DIRECTION_BOTH) { + new_pos_cache.y += 0.5 * (new_size_cache.height - minimum_size.height); + } + + new_size_cache.height = minimum_size.height; + } + + bool pos_changed = new_pos_cache != data.pos_cache; + bool size_changed = new_size_cache != data.size_cache; + + data.pos_cache = new_pos_cache; + data.size_cache = new_size_cache; + + if (is_inside_tree()) { + if (size_changed) { + notification(NOTIFICATION_RESIZED); + } + if (pos_changed || size_changed) { + item_rect_changed(size_changed); + _notify_transform(); + } + + if (pos_changed && !size_changed) { + _update_canvas_item_transform(); //move because it won't be updated + } + } } -void Control::remove_theme_font_size_override(const StringName &p_name) { - data.font_size_override.erase(p_name); - _notify_theme_changed(); +void Control::_clear_size_warning() { + data.size_warning = false; } -void Control::remove_theme_color_override(const StringName &p_name) { - data.color_override.erase(p_name); - _notify_theme_changed(); +// Container sizing. + +void Control::set_h_size_flags(int p_flags) { + if (data.h_size_flags == p_flags) { + return; + } + data.h_size_flags = p_flags; + emit_signal(SceneStringNames::get_singleton()->size_flags_changed); } -void Control::remove_theme_constant_override(const StringName &p_name) { - data.constant_override.erase(p_name); - _notify_theme_changed(); +int Control::get_h_size_flags() const { + return data.h_size_flags; } +void Control::set_v_size_flags(int p_flags) { + if (data.v_size_flags == p_flags) { + return; + } + data.v_size_flags = p_flags; + emit_signal(SceneStringNames::get_singleton()->size_flags_changed); +} + +int Control::get_v_size_flags() const { + return data.v_size_flags; +} + +void Control::set_stretch_ratio(real_t p_ratio) { + if (data.expand == p_ratio) { + return; + } + + data.expand = p_ratio; + emit_signal(SceneStringNames::get_singleton()->size_flags_changed); +} + +real_t Control::get_stretch_ratio() const { + return data.expand; +} + +// Input events. + +void Control::_call_gui_input(const Ref<InputEvent> &p_event) { + emit_signal(SceneStringNames::get_singleton()->gui_input, p_event); //signal should be first, so it's possible to override an event (and then accept it) + if (!is_inside_tree() || get_viewport()->is_input_handled()) { + return; //input was handled, abort + } + GDVIRTUAL_CALL(_gui_input, p_event); + if (!is_inside_tree() || get_viewport()->is_input_handled()) { + return; //input was handled, abort + } + gui_input(p_event); +} + +void Control::gui_input(const Ref<InputEvent> &p_event) { +} + +void Control::accept_event() { + if (is_inside_tree()) { + get_viewport()->_gui_accept_event(); + } +} + +bool Control::has_point(const Point2 &p_point) const { + bool ret; + if (GDVIRTUAL_CALL(_has_point, p_point, ret)) { + return ret; + } + return Rect2(Point2(), get_size()).has_point(p_point); +} + +void Control::set_mouse_filter(MouseFilter p_filter) { + ERR_FAIL_INDEX(p_filter, 3); + data.mouse_filter = p_filter; + notify_property_list_changed(); + update_configuration_warnings(); +} + +Control::MouseFilter Control::get_mouse_filter() const { + return data.mouse_filter; +} + +void Control::set_force_pass_scroll_events(bool p_force_pass_scroll_events) { + data.force_pass_scroll_events = p_force_pass_scroll_events; +} + +bool Control::is_force_pass_scroll_events() const { + return data.force_pass_scroll_events; +} + +void Control::warp_mouse(const Point2 &p_position) { + ERR_FAIL_COND(!is_inside_tree()); + get_viewport()->warp_mouse(get_global_transform_with_canvas().xform(p_position)); +} + +// Drag and drop handling. + +void Control::set_drag_forwarding(Object *p_target) { + if (p_target) { + data.drag_owner = p_target->get_instance_id(); + } else { + data.drag_owner = ObjectID(); + } +} + +Variant Control::get_drag_data(const Point2 &p_point) { + if (data.drag_owner.is_valid()) { + Object *obj = ObjectDB::get_instance(data.drag_owner); + if (obj) { + return obj->call("_get_drag_data_fw", p_point, this); + } + } + + Variant dd; + if (GDVIRTUAL_CALL(_get_drag_data, p_point, dd)) { + return dd; + } + + return Variant(); +} + +bool Control::can_drop_data(const Point2 &p_point, const Variant &p_data) const { + if (data.drag_owner.is_valid()) { + Object *obj = ObjectDB::get_instance(data.drag_owner); + if (obj) { + return obj->call("_can_drop_data_fw", p_point, p_data, this); + } + } + + bool ret; + if (GDVIRTUAL_CALL(_can_drop_data, p_point, p_data, ret)) { + return ret; + } + return false; +} + +void Control::drop_data(const Point2 &p_point, const Variant &p_data) { + if (data.drag_owner.is_valid()) { + Object *obj = ObjectDB::get_instance(data.drag_owner); + if (obj) { + obj->call("_drop_data_fw", p_point, p_data, this); + return; + } + } + + GDVIRTUAL_CALL(_drop_data, p_point, p_data); +} + +void Control::force_drag(const Variant &p_data, Control *p_control) { + ERR_FAIL_COND(!is_inside_tree()); + ERR_FAIL_COND(p_data.get_type() == Variant::NIL); + + get_viewport()->_gui_force_drag(this, p_data, p_control); +} + +void Control::set_drag_preview(Control *p_control) { + ERR_FAIL_COND(!is_inside_tree()); + ERR_FAIL_COND(!get_viewport()->gui_is_dragging()); + get_viewport()->_gui_set_drag_preview(this, p_control); +} + +bool Control::is_drag_successful() const { + return is_inside_tree() && get_viewport()->gui_is_drag_successful(); +} + +// Focus. + void Control::set_focus_mode(FocusMode p_focus_mode) { ERR_FAIL_INDEX((int)p_focus_mode, 3); @@ -2342,6 +1841,41 @@ void Control::set_focus_mode(FocusMode p_focus_mode) { data.focus_mode = p_focus_mode; } +Control::FocusMode Control::get_focus_mode() const { + return data.focus_mode; +} + +bool Control::has_focus() const { + return is_inside_tree() && get_viewport()->_gui_control_has_focus(this); +} + +void Control::grab_focus() { + ERR_FAIL_COND(!is_inside_tree()); + + if (data.focus_mode == FOCUS_NONE) { + WARN_PRINT("This control can't grab focus. Use set_focus_mode() to allow a control to get focus."); + return; + } + + get_viewport()->_gui_control_grab_focus(this); +} + +void Control::grab_click_focus() { + ERR_FAIL_COND(!is_inside_tree()); + + get_viewport()->_gui_grab_click_focus(this); +} + +void Control::release_focus() { + ERR_FAIL_COND(!is_inside_tree()); + + if (!has_focus()) { + return; + } + + get_viewport()->gui_release_focus(); +} + static Control *_next_control(Control *p_from) { if (p_from->is_set_as_top_level()) { return nullptr; // Can't go above. @@ -2520,181 +2054,6 @@ Control *Control::find_prev_valid_focus() const { return nullptr; } -Control::FocusMode Control::get_focus_mode() const { - return data.focus_mode; -} - -bool Control::has_focus() const { - return is_inside_tree() && get_viewport()->_gui_control_has_focus(this); -} - -void Control::grab_focus() { - ERR_FAIL_COND(!is_inside_tree()); - - if (data.focus_mode == FOCUS_NONE) { - WARN_PRINT("This control can't grab focus. Use set_focus_mode() to allow a control to get focus."); - return; - } - - get_viewport()->_gui_control_grab_focus(this); -} - -void Control::release_focus() { - ERR_FAIL_COND(!is_inside_tree()); - - if (!has_focus()) { - return; - } - - get_viewport()->gui_release_focus(); -} - -bool Control::is_top_level_control() const { - return is_inside_tree() && (!data.parent_canvas_item && !data.RI && is_set_as_top_level()); -} - -void Control::_propagate_theme_changed(Node *p_at, Control *p_owner, Window *p_owner_window, bool p_assign) { - Control *c = Object::cast_to<Control>(p_at); - - if (c && c != p_owner && c->data.theme.is_valid()) { // has a theme, this can't be propagated - return; - } - - Window *w = c == nullptr ? Object::cast_to<Window>(p_at) : nullptr; - - if (w && w != p_owner_window && w->theme.is_valid()) { // has a theme, this can't be propagated - return; - } - - for (int i = 0; i < p_at->get_child_count(); i++) { - CanvasItem *child = Object::cast_to<CanvasItem>(p_at->get_child(i)); - if (child) { - _propagate_theme_changed(child, p_owner, p_owner_window, p_assign); - } else { - Window *window = Object::cast_to<Window>(p_at->get_child(i)); - if (window) { - _propagate_theme_changed(window, p_owner, p_owner_window, p_assign); - } - } - } - - if (c) { - if (p_assign) { - c->data.theme_owner = p_owner; - c->data.theme_owner_window = p_owner_window; - } - c->notification(Control::NOTIFICATION_THEME_CHANGED); - c->emit_signal(SceneStringNames::get_singleton()->theme_changed); - } - - if (w) { - if (p_assign) { - w->theme_owner = p_owner; - w->theme_owner_window = p_owner_window; - } - w->notification(Window::NOTIFICATION_THEME_CHANGED); - w->emit_signal(SceneStringNames::get_singleton()->theme_changed); - } -} - -void Control::_theme_changed() { - _propagate_theme_changed(this, this, nullptr, false); -} - -void Control::_notify_theme_changed() { - if (!data.bulk_theme_override) { - notification(NOTIFICATION_THEME_CHANGED); - } -} - -void Control::set_theme(const Ref<Theme> &p_theme) { - if (data.theme == p_theme) { - return; - } - - if (data.theme.is_valid()) { - data.theme->disconnect("changed", callable_mp(this, &Control::_theme_changed)); - } - - data.theme = p_theme; - if (!p_theme.is_null()) { - data.theme_owner = this; - data.theme_owner_window = nullptr; - _propagate_theme_changed(this, this, nullptr); - } else { - Control *parent_c = Object::cast_to<Control>(get_parent()); - - if (parent_c && (parent_c->data.theme_owner || parent_c->data.theme_owner_window)) { - Control::_propagate_theme_changed(this, parent_c->data.theme_owner, parent_c->data.theme_owner_window); - } else { - Window *parent_w = cast_to<Window>(get_parent()); - if (parent_w && (parent_w->theme_owner || parent_w->theme_owner_window)) { - Control::_propagate_theme_changed(this, parent_w->theme_owner, parent_w->theme_owner_window); - } else { - Control::_propagate_theme_changed(this, nullptr, nullptr); - } - } - } - - if (data.theme.is_valid()) { - data.theme->connect("changed", callable_mp(this, &Control::_theme_changed), varray(), CONNECT_DEFERRED); - } -} - -Ref<Theme> Control::get_theme() const { - return data.theme; -} - -void Control::set_theme_type_variation(const StringName &p_theme_type) { - data.theme_type_variation = p_theme_type; - _propagate_theme_changed(this, data.theme_owner, data.theme_owner_window); -} - -StringName Control::get_theme_type_variation() const { - return data.theme_type_variation; -} - -void Control::set_tooltip(const String &p_tooltip) { - data.tooltip = p_tooltip; - update_configuration_warnings(); -} - -String Control::get_tooltip(const Point2 &p_pos) const { - return data.tooltip; -} - -Control *Control::make_custom_tooltip(const String &p_text) const { - Object *ret = nullptr; - if (GDVIRTUAL_CALL(_make_custom_tooltip, p_text, ret)) { - return Object::cast_to<Control>(ret); - } - return nullptr; -} - -void Control::set_default_cursor_shape(CursorShape p_shape) { - ERR_FAIL_INDEX(int(p_shape), CURSOR_MAX); - - data.default_cursor = p_shape; -} - -Control::CursorShape Control::get_default_cursor_shape() const { - return data.default_cursor; -} - -Control::CursorShape Control::get_cursor_shape(const Point2 &p_pos) const { - return data.default_cursor; -} - -Transform2D Control::get_transform() const { - Transform2D xform = _get_internal_transform(); - xform[2] += get_position(); - return xform; -} - -String Control::_get_tooltip() const { - return data.tooltip; -} - void Control::set_focus_neighbor(Side p_side, const NodePath &p_neighbor) { ERR_FAIL_INDEX((int)p_side, 4); data.focus_neighbor[p_side] = p_neighbor; @@ -2861,273 +2220,763 @@ void Control::_window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, cons } } -void Control::set_h_size_flags(int p_flags) { - if (data.h_size_flags == p_flags) { +// Rendering. + +void Control::set_default_cursor_shape(CursorShape p_shape) { + ERR_FAIL_INDEX(int(p_shape), CURSOR_MAX); + + if (data.default_cursor == p_shape) { return; } - data.h_size_flags = p_flags; - emit_signal(SceneStringNames::get_singleton()->size_flags_changed); + data.default_cursor = p_shape; + + if (!is_inside_tree()) { + return; + } + if (!get_global_rect().has_point(get_global_mouse_position())) { + return; + } + + get_viewport()->get_base_window()->update_mouse_cursor_shape(); } -int Control::get_h_size_flags() const { - return data.h_size_flags; +Control::CursorShape Control::get_default_cursor_shape() const { + return data.default_cursor; } -void Control::set_v_size_flags(int p_flags) { - if (data.v_size_flags == p_flags) { +Control::CursorShape Control::get_cursor_shape(const Point2 &p_pos) const { + return data.default_cursor; +} + +void Control::set_disable_visibility_clip(bool p_ignore) { + if (data.disable_visibility_clip == p_ignore) { return; } - data.v_size_flags = p_flags; - emit_signal(SceneStringNames::get_singleton()->size_flags_changed); + data.disable_visibility_clip = p_ignore; + queue_redraw(); } -void Control::set_stretch_ratio(real_t p_ratio) { - if (data.expand == p_ratio) { +bool Control::is_visibility_clip_disabled() const { + return data.disable_visibility_clip; +} + +void Control::set_clip_contents(bool p_clip) { + if (data.clip_contents == p_clip) { return; } + data.clip_contents = p_clip; + queue_redraw(); +} - data.expand = p_ratio; - emit_signal(SceneStringNames::get_singleton()->size_flags_changed); +bool Control::is_clipping_contents() { + return data.clip_contents; } -real_t Control::get_stretch_ratio() const { - return data.expand; +// Theming. + +void Control::_theme_changed() { + if (is_inside_tree()) { + data.theme_owner->propagate_theme_changed(this, this, true, false); + } } -void Control::grab_click_focus() { - ERR_FAIL_COND(!is_inside_tree()); +void Control::_notify_theme_override_changed() { + if (!data.bulk_theme_override && is_inside_tree()) { + notification(NOTIFICATION_THEME_CHANGED); + } +} - get_viewport()->_gui_grab_click_focus(this); +void Control::_invalidate_theme_cache() { + data.theme_icon_cache.clear(); + data.theme_style_cache.clear(); + data.theme_font_cache.clear(); + data.theme_font_size_cache.clear(); + data.theme_color_cache.clear(); + data.theme_constant_cache.clear(); } -void Control::update_minimum_size() { - if (!is_inside_tree() || data.block_minimum_size_adjust) { +void Control::_update_theme_item_cache() { +} + +void Control::set_theme_owner_node(Node *p_node) { + data.theme_owner->set_owner_node(p_node); +} + +Node *Control::get_theme_owner_node() const { + return data.theme_owner->get_owner_node(); +} + +bool Control::has_theme_owner_node() const { + return data.theme_owner->has_owner_node(); +} + +void Control::set_theme(const Ref<Theme> &p_theme) { + if (data.theme == p_theme) { return; } - Control *invalidate = this; + if (data.theme.is_valid()) { + data.theme->disconnect("changed", callable_mp(this, &Control::_theme_changed)); + } - //invalidate cache upwards - while (invalidate && invalidate->data.minimum_size_valid) { - invalidate->data.minimum_size_valid = false; - if (invalidate->is_set_as_top_level()) { - break; // do not go further up - } - if (!invalidate->data.parent && get_parent()) { - Window *parent_window = Object::cast_to<Window>(get_parent()); - if (parent_window && parent_window->is_wrapping_controls()) { - parent_window->child_controls_changed(); - } - } - invalidate = invalidate->data.parent; + data.theme = p_theme; + if (data.theme.is_valid()) { + data.theme_owner->propagate_theme_changed(this, this, is_inside_tree(), true); + data.theme->connect("changed", callable_mp(this, &Control::_theme_changed), CONNECT_DEFERRED); + return; } - if (!is_visible_in_tree()) { + Control *parent_c = Object::cast_to<Control>(get_parent()); + if (parent_c && parent_c->has_theme_owner_node()) { + data.theme_owner->propagate_theme_changed(this, parent_c->get_theme_owner_node(), is_inside_tree(), true); return; } - if (data.updating_last_minimum_size) { + Window *parent_w = cast_to<Window>(get_parent()); + if (parent_w && parent_w->has_theme_owner_node()) { + data.theme_owner->propagate_theme_changed(this, parent_w->get_theme_owner_node(), is_inside_tree(), true); return; } - data.updating_last_minimum_size = true; + data.theme_owner->propagate_theme_changed(this, nullptr, is_inside_tree(), true); +} - MessageQueue::get_singleton()->push_call(this, "_update_minimum_size"); +Ref<Theme> Control::get_theme() const { + return data.theme; } -int Control::get_v_size_flags() const { - return data.v_size_flags; +void Control::set_theme_type_variation(const StringName &p_theme_type) { + if (data.theme_type_variation == p_theme_type) { + return; + } + data.theme_type_variation = p_theme_type; + if (is_inside_tree()) { + notification(NOTIFICATION_THEME_CHANGED); + } } -void Control::set_mouse_filter(MouseFilter p_filter) { - ERR_FAIL_INDEX(p_filter, 3); - data.mouse_filter = p_filter; - notify_property_list_changed(); - update_configuration_warnings(); +StringName Control::get_theme_type_variation() const { + return data.theme_type_variation; } -Control::MouseFilter Control::get_mouse_filter() const { - return data.mouse_filter; +/// Theme property lookup. + +Ref<Texture2D> Control::get_theme_icon(const StringName &p_name, const StringName &p_theme_type) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { + const Ref<Texture2D> *tex = data.icon_override.getptr(p_name); + if (tex) { + return *tex; + } + } + + if (data.theme_icon_cache.has(p_theme_type) && data.theme_icon_cache[p_theme_type].has(p_name)) { + return data.theme_icon_cache[p_theme_type][p_name]; + } + + List<StringName> theme_types; + data.theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types); + Ref<Texture2D> icon = data.theme_owner->get_theme_item_in_types(Theme::DATA_TYPE_ICON, p_name, theme_types); + data.theme_icon_cache[p_theme_type][p_name] = icon; + return icon; } -void Control::set_force_pass_scroll_events(bool p_force_pass_scroll_events) { - data.force_pass_scroll_events = p_force_pass_scroll_events; +Ref<StyleBox> Control::get_theme_stylebox(const StringName &p_name, const StringName &p_theme_type) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { + const Ref<StyleBox> *style = data.style_override.getptr(p_name); + if (style) { + return *style; + } + } + + if (data.theme_style_cache.has(p_theme_type) && data.theme_style_cache[p_theme_type].has(p_name)) { + return data.theme_style_cache[p_theme_type][p_name]; + } + + List<StringName> theme_types; + data.theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types); + Ref<StyleBox> style = data.theme_owner->get_theme_item_in_types(Theme::DATA_TYPE_STYLEBOX, p_name, theme_types); + data.theme_style_cache[p_theme_type][p_name] = style; + return style; } -bool Control::is_force_pass_scroll_events() const { - return data.force_pass_scroll_events; +Ref<Font> Control::get_theme_font(const StringName &p_name, const StringName &p_theme_type) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { + const Ref<Font> *font = data.font_override.getptr(p_name); + if (font) { + return *font; + } + } + + if (data.theme_font_cache.has(p_theme_type) && data.theme_font_cache[p_theme_type].has(p_name)) { + return data.theme_font_cache[p_theme_type][p_name]; + } + + List<StringName> theme_types; + data.theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types); + Ref<Font> font = data.theme_owner->get_theme_item_in_types(Theme::DATA_TYPE_FONT, p_name, theme_types); + data.theme_font_cache[p_theme_type][p_name] = font; + return font; } -void Control::warp_mouse(const Point2 &p_position) { - ERR_FAIL_COND(!is_inside_tree()); - get_viewport()->warp_mouse(get_global_transform_with_canvas().xform(p_position)); +int Control::get_theme_font_size(const StringName &p_name, const StringName &p_theme_type) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { + const int *font_size = data.font_size_override.getptr(p_name); + if (font_size && (*font_size) > 0) { + return *font_size; + } + } + + if (data.theme_font_size_cache.has(p_theme_type) && data.theme_font_size_cache[p_theme_type].has(p_name)) { + return data.theme_font_size_cache[p_theme_type][p_name]; + } + + List<StringName> theme_types; + data.theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types); + int font_size = data.theme_owner->get_theme_item_in_types(Theme::DATA_TYPE_FONT_SIZE, p_name, theme_types); + data.theme_font_size_cache[p_theme_type][p_name] = font_size; + return font_size; } -bool Control::is_text_field() const { - return false; +Color Control::get_theme_color(const StringName &p_name, const StringName &p_theme_type) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { + const Color *color = data.color_override.getptr(p_name); + if (color) { + return *color; + } + } + + if (data.theme_color_cache.has(p_theme_type) && data.theme_color_cache[p_theme_type].has(p_name)) { + return data.theme_color_cache[p_theme_type][p_name]; + } + + List<StringName> theme_types; + data.theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types); + Color color = data.theme_owner->get_theme_item_in_types(Theme::DATA_TYPE_COLOR, p_name, theme_types); + data.theme_color_cache[p_theme_type][p_name] = color; + return color; } -Array Control::structured_text_parser(TextServer::StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const { - if (p_parser_type == TextServer::STRUCTURED_TEXT_CUSTOM) { - Array ret; - if (GDVIRTUAL_CALL(_structured_text_parser, p_args, p_text, ret)) { - return ret; - } else { - return Array(); +int Control::get_theme_constant(const StringName &p_name, const StringName &p_theme_type) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { + const int *constant = data.constant_override.getptr(p_name); + if (constant) { + return *constant; } - } else { - return TS->parse_structured_text(p_parser_type, p_args, p_text); } + + if (data.theme_constant_cache.has(p_theme_type) && data.theme_constant_cache[p_theme_type].has(p_name)) { + return data.theme_constant_cache[p_theme_type][p_name]; + } + + List<StringName> theme_types; + data.theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types); + int constant = data.theme_owner->get_theme_item_in_types(Theme::DATA_TYPE_CONSTANT, p_name, theme_types); + data.theme_constant_cache[p_theme_type][p_name] = constant; + return constant; } -void Control::set_rotation(real_t p_radians) { - data.rotation = p_radians; - update(); - _notify_transform(); +bool Control::has_theme_icon(const StringName &p_name, const StringName &p_theme_type) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { + if (has_theme_icon_override(p_name)) { + return true; + } + } + + List<StringName> theme_types; + data.theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types); + return data.theme_owner->has_theme_item_in_types(Theme::DATA_TYPE_ICON, p_name, theme_types); } -real_t Control::get_rotation() const { - return data.rotation; +bool Control::has_theme_stylebox(const StringName &p_name, const StringName &p_theme_type) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { + if (has_theme_stylebox_override(p_name)) { + return true; + } + } + + List<StringName> theme_types; + data.theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types); + return data.theme_owner->has_theme_item_in_types(Theme::DATA_TYPE_STYLEBOX, p_name, theme_types); } -void Control::_override_changed() { - notification(NOTIFICATION_THEME_CHANGED); - emit_signal(SceneStringNames::get_singleton()->theme_changed); - update_minimum_size(); // Overrides are likely to affect minimum size. +bool Control::has_theme_font(const StringName &p_name, const StringName &p_theme_type) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { + if (has_theme_font_override(p_name)) { + return true; + } + } + + List<StringName> theme_types; + data.theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types); + return data.theme_owner->has_theme_item_in_types(Theme::DATA_TYPE_FONT, p_name, theme_types); } -void Control::set_pivot_offset(const Vector2 &p_pivot) { - data.pivot_offset = p_pivot; - update(); - _notify_transform(); +bool Control::has_theme_font_size(const StringName &p_name, const StringName &p_theme_type) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { + if (has_theme_font_size_override(p_name)) { + return true; + } + } + + List<StringName> theme_types; + data.theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types); + return data.theme_owner->has_theme_item_in_types(Theme::DATA_TYPE_FONT_SIZE, p_name, theme_types); } -Vector2 Control::get_pivot_offset() const { - return data.pivot_offset; +bool Control::has_theme_color(const StringName &p_name, const StringName &p_theme_type) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { + if (has_theme_color_override(p_name)) { + return true; + } + } + + List<StringName> theme_types; + data.theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types); + return data.theme_owner->has_theme_item_in_types(Theme::DATA_TYPE_COLOR, p_name, theme_types); } -void Control::set_scale(const Vector2 &p_scale) { - data.scale = p_scale; - // Avoid having 0 scale values, can lead to errors in physics and rendering. - if (data.scale.x == 0) { - data.scale.x = CMP_EPSILON; +bool Control::has_theme_constant(const StringName &p_name, const StringName &p_theme_type) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { + if (has_theme_constant_override(p_name)) { + return true; + } } - if (data.scale.y == 0) { - data.scale.y = CMP_EPSILON; + + List<StringName> theme_types; + data.theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types); + return data.theme_owner->has_theme_item_in_types(Theme::DATA_TYPE_CONSTANT, p_name, theme_types); +} + +/// Local property overrides. + +void Control::add_theme_icon_override(const StringName &p_name, const Ref<Texture2D> &p_icon) { + ERR_FAIL_COND(!p_icon.is_valid()); + + if (data.icon_override.has(p_name)) { + data.icon_override[p_name]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed)); } - update(); - _notify_transform(); + + data.icon_override[p_name] = p_icon; + data.icon_override[p_name]->connect("changed", callable_mp(this, &Control::_notify_theme_override_changed), CONNECT_REFERENCE_COUNTED); + _notify_theme_override_changed(); } -Vector2 Control::get_scale() const { - return data.scale; +void Control::add_theme_style_override(const StringName &p_name, const Ref<StyleBox> &p_style) { + ERR_FAIL_COND(!p_style.is_valid()); + + if (data.style_override.has(p_name)) { + data.style_override[p_name]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed)); + } + + data.style_override[p_name] = p_style; + data.style_override[p_name]->connect("changed", callable_mp(this, &Control::_notify_theme_override_changed), CONNECT_REFERENCE_COUNTED); + _notify_theme_override_changed(); } -Control *Control::get_root_parent_control() const { - const CanvasItem *ci = this; - const Control *root = this; +void Control::add_theme_font_override(const StringName &p_name, const Ref<Font> &p_font) { + ERR_FAIL_COND(!p_font.is_valid()); - while (ci) { - const Control *c = Object::cast_to<Control>(ci); - if (c) { - root = c; + if (data.font_override.has(p_name)) { + data.font_override[p_name]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed)); + } - if (c->data.RI || c->is_top_level_control()) { - break; - } - } + data.font_override[p_name] = p_font; + data.font_override[p_name]->connect("changed", callable_mp(this, &Control::_notify_theme_override_changed), CONNECT_REFERENCE_COUNTED); + _notify_theme_override_changed(); +} - ci = ci->get_parent_item(); +void Control::add_theme_font_size_override(const StringName &p_name, int p_font_size) { + data.font_size_override[p_name] = p_font_size; + _notify_theme_override_changed(); +} + +void Control::add_theme_color_override(const StringName &p_name, const Color &p_color) { + data.color_override[p_name] = p_color; + _notify_theme_override_changed(); +} + +void Control::add_theme_constant_override(const StringName &p_name, int p_constant) { + data.constant_override[p_name] = p_constant; + _notify_theme_override_changed(); +} + +void Control::remove_theme_icon_override(const StringName &p_name) { + if (data.icon_override.has(p_name)) { + data.icon_override[p_name]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed)); } - return const_cast<Control *>(root); + data.icon_override.erase(p_name); + _notify_theme_override_changed(); } -void Control::set_block_minimum_size_adjust(bool p_block) { - data.block_minimum_size_adjust = p_block; +void Control::remove_theme_style_override(const StringName &p_name) { + if (data.style_override.has(p_name)) { + data.style_override[p_name]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed)); + } + + data.style_override.erase(p_name); + _notify_theme_override_changed(); } -bool Control::is_minimum_size_adjust_blocked() const { - return data.block_minimum_size_adjust; +void Control::remove_theme_font_override(const StringName &p_name) { + if (data.font_override.has(p_name)) { + data.font_override[p_name]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed)); + } + + data.font_override.erase(p_name); + _notify_theme_override_changed(); } -void Control::set_disable_visibility_clip(bool p_ignore) { - data.disable_visibility_clip = p_ignore; - update(); +void Control::remove_theme_font_size_override(const StringName &p_name) { + data.font_size_override.erase(p_name); + _notify_theme_override_changed(); } -bool Control::is_visibility_clip_disabled() const { - return data.disable_visibility_clip; +void Control::remove_theme_color_override(const StringName &p_name) { + data.color_override.erase(p_name); + _notify_theme_override_changed(); } -void Control::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { - Node::get_argument_options(p_function, p_idx, r_options); +void Control::remove_theme_constant_override(const StringName &p_name) { + data.constant_override.erase(p_name); + _notify_theme_override_changed(); +} - if (p_idx == 0) { - List<StringName> sn; - String pf = p_function; - if (pf == "add_theme_color_override" || pf == "has_theme_color" || pf == "has_theme_color_override" || pf == "get_theme_color") { - Theme::get_default()->get_color_list(get_class(), &sn); - } else if (pf == "add_theme_style_override" || pf == "has_theme_style" || pf == "has_theme_style_override" || pf == "get_theme_style") { - Theme::get_default()->get_stylebox_list(get_class(), &sn); - } else if (pf == "add_theme_font_override" || pf == "has_theme_font" || pf == "has_theme_font_override" || pf == "get_theme_font") { - Theme::get_default()->get_font_list(get_class(), &sn); - } else if (pf == "add_theme_font_size_override" || pf == "has_theme_font_size" || pf == "has_theme_font_size_override" || pf == "get_theme_font_size") { - Theme::get_default()->get_font_size_list(get_class(), &sn); - } else if (pf == "add_theme_constant_override" || pf == "has_theme_constant" || pf == "has_theme_constant_override" || pf == "get_theme_constant") { - Theme::get_default()->get_constant_list(get_class(), &sn); - } +bool Control::has_theme_icon_override(const StringName &p_name) const { + const Ref<Texture2D> *tex = data.icon_override.getptr(p_name); + return tex != nullptr; +} - sn.sort_custom<StringName::AlphCompare>(); - for (const StringName &name : sn) { - r_options->push_back(String(name).quote()); +bool Control::has_theme_stylebox_override(const StringName &p_name) const { + const Ref<StyleBox> *style = data.style_override.getptr(p_name); + return style != nullptr; +} + +bool Control::has_theme_font_override(const StringName &p_name) const { + const Ref<Font> *font = data.font_override.getptr(p_name); + return font != nullptr; +} + +bool Control::has_theme_font_size_override(const StringName &p_name) const { + const int *font_size = data.font_size_override.getptr(p_name); + return font_size != nullptr; +} + +bool Control::has_theme_color_override(const StringName &p_name) const { + const Color *color = data.color_override.getptr(p_name); + return color != nullptr; +} + +bool Control::has_theme_constant_override(const StringName &p_name) const { + const int *constant = data.constant_override.getptr(p_name); + return constant != nullptr; +} + +/// Default theme properties. + +float Control::get_theme_default_base_scale() const { + return data.theme_owner->get_theme_default_base_scale(); +} + +Ref<Font> Control::get_theme_default_font() const { + return data.theme_owner->get_theme_default_font(); +} + +int Control::get_theme_default_font_size() const { + return data.theme_owner->get_theme_default_font_size(); +} + +/// Bulk actions. + +void Control::begin_bulk_theme_override() { + data.bulk_theme_override = true; +} + +void Control::end_bulk_theme_override() { + ERR_FAIL_COND(!data.bulk_theme_override); + + data.bulk_theme_override = false; + _notify_theme_override_changed(); +} + +// Internationalization. + +TypedArray<Vector2i> Control::structured_text_parser(TextServer::StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const { + if (p_parser_type == TextServer::STRUCTURED_TEXT_CUSTOM) { + TypedArray<Vector2i> ret; + if (GDVIRTUAL_CALL(_structured_text_parser, p_args, p_text, ret)) { + return ret; + } else { + return TypedArray<Vector2i>(); } + } else { + return TS->parse_structured_text(p_parser_type, p_args, p_text); } } -TypedArray<String> Control::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); - - if (data.mouse_filter == MOUSE_FILTER_IGNORE && !data.tooltip.is_empty()) { - warnings.push_back(RTR("The Hint Tooltip won't be displayed as the control's Mouse Filter is set to \"Ignore\". To solve this, set the Mouse Filter to \"Stop\" or \"Pass\".")); +void Control::set_layout_direction(Control::LayoutDirection p_direction) { + if (data.layout_dir == p_direction) { + return; } + ERR_FAIL_INDEX((int)p_direction, 4); - return warnings; + data.layout_dir = p_direction; + data.is_rtl_dirty = true; + + propagate_notification(NOTIFICATION_LAYOUT_DIRECTION_CHANGED); } -void Control::set_clip_contents(bool p_clip) { - data.clip_contents = p_clip; - update(); +Control::LayoutDirection Control::get_layout_direction() const { + return data.layout_dir; } -bool Control::is_clipping_contents() { - return data.clip_contents; +bool Control::is_layout_rtl() const { + if (data.is_rtl_dirty) { + const_cast<Control *>(this)->data.is_rtl_dirty = false; + if (data.layout_dir == LAYOUT_DIRECTION_INHERITED) { + Window *parent_window = get_parent_window(); + Control *parent_control = get_parent_control(); + if (parent_control) { + const_cast<Control *>(this)->data.is_rtl = parent_control->is_layout_rtl(); + } else if (parent_window) { + const_cast<Control *>(this)->data.is_rtl = parent_window->is_layout_rtl(); + } else { + if (GLOBAL_GET(SNAME("internationalization/rendering/force_right_to_left_layout_direction"))) { + const_cast<Control *>(this)->data.is_rtl = true; + } else { + String locale = TranslationServer::get_singleton()->get_tool_locale(); + const_cast<Control *>(this)->data.is_rtl = TS->is_locale_right_to_left(locale); + } + } + } else if (data.layout_dir == LAYOUT_DIRECTION_LOCALE) { + if (GLOBAL_GET(SNAME("internationalization/rendering/force_right_to_left_layout_direction"))) { + const_cast<Control *>(this)->data.is_rtl = true; + } else { + String locale = TranslationServer::get_singleton()->get_tool_locale(); + const_cast<Control *>(this)->data.is_rtl = TS->is_locale_right_to_left(locale); + } + } else { + const_cast<Control *>(this)->data.is_rtl = (data.layout_dir == LAYOUT_DIRECTION_RTL); + } + } + return data.is_rtl; } -void Control::set_h_grow_direction(GrowDirection p_direction) { - ERR_FAIL_INDEX((int)p_direction, 3); +void Control::set_auto_translate(bool p_enable) { + if (p_enable == data.auto_translate) { + return; + } - data.h_grow = p_direction; - _size_changed(); + data.auto_translate = p_enable; + + notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED); } -Control::GrowDirection Control::get_h_grow_direction() const { - return data.h_grow; +bool Control::is_auto_translating() const { + return data.auto_translate; } -void Control::set_v_grow_direction(GrowDirection p_direction) { - ERR_FAIL_INDEX((int)p_direction, 3); +// Extra properties. - data.v_grow = p_direction; - _size_changed(); +void Control::set_tooltip_text(const String &p_hint) { + data.tooltip = p_hint; + update_configuration_warnings(); } -Control::GrowDirection Control::get_v_grow_direction() const { - return data.v_grow; +String Control::get_tooltip_text() const { + return data.tooltip; +} + +String Control::get_tooltip(const Point2 &p_pos) const { + return data.tooltip; +} + +Control *Control::make_custom_tooltip(const String &p_text) const { + Object *ret = nullptr; + if (GDVIRTUAL_CALL(_make_custom_tooltip, p_text, ret)) { + return Object::cast_to<Control>(ret); + } + return nullptr; +} + +// Base object overrides. + +void Control::_notification(int p_notification) { + switch (p_notification) { + case NOTIFICATION_POSTINITIALIZE: { + _invalidate_theme_cache(); + _update_theme_item_cache(); + } break; + + case NOTIFICATION_PARENTED: { + data.theme_owner->assign_theme_on_parented(this); + } break; + + case NOTIFICATION_UNPARENTED: { + data.theme_owner->clear_theme_on_unparented(this); + } break; + + case NOTIFICATION_ENTER_TREE: { + notification(NOTIFICATION_THEME_CHANGED); + } break; + + case NOTIFICATION_POST_ENTER_TREE: { + data.minimum_size_valid = false; + data.is_rtl_dirty = true; + _size_changed(); + } break; + + case NOTIFICATION_EXIT_TREE: { + release_focus(); + get_viewport()->_gui_remove_control(this); + } break; + + case NOTIFICATION_READY: { +#ifdef DEBUG_ENABLED + connect("ready", callable_mp(this, &Control::_clear_size_warning), CONNECT_DEFERRED | CONNECT_ONE_SHOT); +#endif + } break; + + case NOTIFICATION_ENTER_CANVAS: { + data.parent = Object::cast_to<Control>(get_parent()); + data.parent_window = Object::cast_to<Window>(get_parent()); + data.is_rtl_dirty = true; + + CanvasItem *node = this; + bool has_parent_control = false; + + while (!node->is_set_as_top_level()) { + CanvasItem *parent = Object::cast_to<CanvasItem>(node->get_parent()); + if (!parent) { + break; + } + + Control *parent_control = Object::cast_to<Control>(parent); + if (parent_control) { + has_parent_control = true; + break; + } + + node = parent; + } + + if (has_parent_control) { + // Do nothing, has a parent control. + } else { + // Is a regular root control or top_level. + Viewport *viewport = get_viewport(); + ERR_FAIL_COND(!viewport); + data.RI = viewport->_gui_add_root_control(this); + } + + data.parent_canvas_item = get_parent_item(); + + if (data.parent_canvas_item) { + data.parent_canvas_item->connect("item_rect_changed", callable_mp(this, &Control::_size_changed)); + } else { + // Connect viewport. + Viewport *viewport = get_viewport(); + ERR_FAIL_COND(!viewport); + viewport->connect("size_changed", callable_mp(this, &Control::_size_changed)); + } + } break; + + case NOTIFICATION_EXIT_CANVAS: { + if (data.parent_canvas_item) { + data.parent_canvas_item->disconnect("item_rect_changed", callable_mp(this, &Control::_size_changed)); + data.parent_canvas_item = nullptr; + } else if (!is_set_as_top_level()) { + //disconnect viewport + Viewport *viewport = get_viewport(); + ERR_FAIL_COND(!viewport); + viewport->disconnect("size_changed", callable_mp(this, &Control::_size_changed)); + } + + if (data.RI) { + get_viewport()->_gui_remove_root_control(data.RI); + data.RI = nullptr; + } + + data.parent = nullptr; + data.parent_canvas_item = nullptr; + data.parent_window = nullptr; + data.is_rtl_dirty = true; + } break; + + case NOTIFICATION_MOVED_IN_PARENT: { + // some parents need to know the order of the children to draw (like TabContainer) + // update if necessary + if (data.parent) { + data.parent->queue_redraw(); + } + queue_redraw(); + + if (data.RI) { + get_viewport()->_gui_set_root_order_dirty(); + } + } break; + + case NOTIFICATION_RESIZED: { + emit_signal(SceneStringNames::get_singleton()->resized); + } break; + + case NOTIFICATION_DRAW: { + _update_canvas_item_transform(); + RenderingServer::get_singleton()->canvas_item_set_custom_rect(get_canvas_item(), !data.disable_visibility_clip, Rect2(Point2(), get_size())); + RenderingServer::get_singleton()->canvas_item_set_clip(get_canvas_item(), data.clip_contents); + } break; + + case NOTIFICATION_MOUSE_ENTER: { + emit_signal(SceneStringNames::get_singleton()->mouse_entered); + } break; + + case NOTIFICATION_MOUSE_EXIT: { + emit_signal(SceneStringNames::get_singleton()->mouse_exited); + } break; + + case NOTIFICATION_FOCUS_ENTER: { + emit_signal(SceneStringNames::get_singleton()->focus_entered); + queue_redraw(); + } break; + + case NOTIFICATION_FOCUS_EXIT: { + emit_signal(SceneStringNames::get_singleton()->focus_exited); + queue_redraw(); + } break; + + case NOTIFICATION_THEME_CHANGED: { + emit_signal(SceneStringNames::get_singleton()->theme_changed); + _invalidate_theme_cache(); + _update_theme_item_cache(); + update_minimum_size(); + queue_redraw(); + } break; + + case NOTIFICATION_VISIBILITY_CHANGED: { + if (!is_visible_in_tree()) { + if (get_viewport() != nullptr) { + get_viewport()->_gui_hide_control(this); + } + } else { + data.minimum_size_valid = false; + _update_minimum_size(); + _size_changed(); + } + } break; + + case NOTIFICATION_TRANSLATION_CHANGED: + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { + if (is_inside_tree()) { + data.is_rtl_dirty = true; + _invalidate_theme_cache(); + _update_theme_item_cache(); + _size_changed(); + } + } break; + } } void Control::_bind_methods() { - //ClassDB::bind_method(D_METHOD("_window_resize_event"),&Control::_window_resize_event); ClassDB::bind_method(D_METHOD("_update_minimum_size"), &Control::_update_minimum_size); ClassDB::bind_method(D_METHOD("accept_event"), &Control::accept_event); @@ -3248,9 +3097,9 @@ void Control::_bind_methods() { ClassDB::bind_method(D_METHOD("set_v_grow_direction", "direction"), &Control::set_v_grow_direction); ClassDB::bind_method(D_METHOD("get_v_grow_direction"), &Control::get_v_grow_direction); - ClassDB::bind_method(D_METHOD("set_tooltip", "tooltip"), &Control::set_tooltip); + ClassDB::bind_method(D_METHOD("set_tooltip_text", "hint"), &Control::set_tooltip_text); + ClassDB::bind_method(D_METHOD("get_tooltip_text"), &Control::get_tooltip_text); ClassDB::bind_method(D_METHOD("get_tooltip", "at_position"), &Control::get_tooltip, DEFVAL(Point2())); - ClassDB::bind_method(D_METHOD("_get_tooltip"), &Control::_get_tooltip); ClassDB::bind_method(D_METHOD("set_default_cursor_shape", "shape"), &Control::set_default_cursor_shape); ClassDB::bind_method(D_METHOD("get_default_cursor_shape"), &Control::get_default_cursor_shape); @@ -3295,24 +3144,24 @@ void Control::_bind_methods() { ADD_GROUP("Layout", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_contents"), "set_clip_contents", "is_clipping_contents"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "custom_minimum_size", PROPERTY_HINT_NONE, "suffix:px"), "set_custom_minimum_size", "get_custom_minimum_size"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "custom_minimum_size", PROPERTY_HINT_NONE, "suffix:px"), "set_custom_minimum_size", "get_custom_minimum_size"); ADD_PROPERTY(PropertyInfo(Variant::INT, "layout_direction", PROPERTY_HINT_ENUM, "Inherited,Locale,Left-to-Right,Right-to-Left"), "set_layout_direction", "get_layout_direction"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "layout_mode", PROPERTY_HINT_ENUM, "Position,Anchors,Container,Uncontrolled", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_layout_mode", "_get_layout_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "layout_mode", PROPERTY_HINT_ENUM, "Position,Anchors,Container,Uncontrolled", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "_set_layout_mode", "_get_layout_mode"); ADD_PROPERTY_DEFAULT("layout_mode", LayoutMode::LAYOUT_MODE_POSITION); - const String anchors_presets_options = "Custom:-1,PresetWide:15," + const String anchors_presets_options = "Custom:-1,PresetFullRect:15," "PresetTopLeft:0,PresetTopRight:1,PresetBottomRight:3,PresetBottomLeft:2," "PresetCenterLeft:4,PresetCenterTop:5,PresetCenterRight:6,PresetCenterBottom:7,PresetCenter:8," "PresetLeftWide:9,PresetTopWide:10,PresetRightWide:11,PresetBottomWide:12,PresetVCenterWide:13,PresetHCenterWide:14"; - ADD_PROPERTY(PropertyInfo(Variant::INT, "anchors_preset", PROPERTY_HINT_ENUM, anchors_presets_options, PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_anchors_layout_preset", "_get_anchors_layout_preset"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "anchors_preset", PROPERTY_HINT_ENUM, anchors_presets_options, PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "_set_anchors_layout_preset", "_get_anchors_layout_preset"); ADD_PROPERTY_DEFAULT("anchors_preset", -1); ADD_SUBGROUP_INDENT("Anchor Points", "anchor_", 1); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anchor_left", PROPERTY_HINT_RANGE, "0,1,0.001,or_lesser,or_greater"), "_set_anchor", "get_anchor", SIDE_LEFT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anchor_top", PROPERTY_HINT_RANGE, "0,1,0.001,or_lesser,or_greater"), "_set_anchor", "get_anchor", SIDE_TOP); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anchor_right", PROPERTY_HINT_RANGE, "0,1,0.001,or_lesser,or_greater"), "_set_anchor", "get_anchor", SIDE_RIGHT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anchor_bottom", PROPERTY_HINT_RANGE, "0,1,0.001,or_lesser,or_greater"), "_set_anchor", "get_anchor", SIDE_BOTTOM); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anchor_left", PROPERTY_HINT_RANGE, "0,1,0.001,or_less,or_greater"), "_set_anchor", "get_anchor", SIDE_LEFT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anchor_top", PROPERTY_HINT_RANGE, "0,1,0.001,or_less,or_greater"), "_set_anchor", "get_anchor", SIDE_TOP); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anchor_right", PROPERTY_HINT_RANGE, "0,1,0.001,or_less,or_greater"), "_set_anchor", "get_anchor", SIDE_RIGHT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anchor_bottom", PROPERTY_HINT_RANGE, "0,1,0.001,or_less,or_greater"), "_set_anchor", "get_anchor", SIDE_BOTTOM); ADD_SUBGROUP_INDENT("Anchor Offsets", "offset_", 1); ADD_PROPERTYI(PropertyInfo(Variant::INT, "offset_left", PROPERTY_HINT_RANGE, "-4096,4096,suffix:px"), "set_offset", "get_offset", SIDE_LEFT); @@ -3328,7 +3177,7 @@ void Control::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size", PROPERTY_HINT_NONE, "suffix:px", PROPERTY_USAGE_EDITOR), "_set_size", "get_size"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position", PROPERTY_HINT_NONE, "suffix:px", PROPERTY_USAGE_EDITOR), "_set_position", "get_position"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "global_position", PROPERTY_HINT_NONE, "suffix:px", PROPERTY_USAGE_NONE), "_set_global_position", "get_global_position"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_lesser,or_greater,radians"), "set_rotation", "get_rotation"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_less,or_greater,radians"), "set_rotation", "get_rotation"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scale"), "set_scale", "get_scale"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "pivot_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_pivot_offset", "get_pivot_offset"); @@ -3340,8 +3189,8 @@ void Control::_bind_methods() { ADD_GROUP("Auto Translate", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_translate"), "set_auto_translate", "is_auto_translating"); - ADD_GROUP("Hint", "hint_"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "hint_tooltip", PROPERTY_HINT_MULTILINE_TEXT), "set_tooltip", "_get_tooltip"); + ADD_GROUP("Tooltip", "tooltip_"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "tooltip_text", PROPERTY_HINT_MULTILINE_TEXT), "set_tooltip_text", "get_tooltip_text"); ADD_GROUP("Focus", "focus_"); ADD_PROPERTYI(PropertyInfo(Variant::NODE_PATH, "focus_neighbor_left", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbor", "get_focus_neighbor", SIDE_LEFT); @@ -3408,7 +3257,7 @@ void Control::_bind_methods() { BIND_ENUM_CONSTANT(PRESET_BOTTOM_WIDE); BIND_ENUM_CONSTANT(PRESET_VCENTER_WIDE); BIND_ENUM_CONSTANT(PRESET_HCENTER_WIDE); - BIND_ENUM_CONSTANT(PRESET_WIDE); + BIND_ENUM_CONSTANT(PRESET_FULL_RECT); BIND_ENUM_CONSTANT(PRESET_MODE_MINSIZE); BIND_ENUM_CONSTANT(PRESET_MODE_KEEP_WIDTH); @@ -3464,3 +3313,30 @@ void Control::_bind_methods() { GDVIRTUAL_BIND(_gui_input, "event"); } + +Control::Control() { + data.theme_owner = memnew(ThemeOwner); +} + +Control::~Control() { + memdelete(data.theme_owner); + + // Resources need to be disconnected. + for (KeyValue<StringName, Ref<Texture2D>> &E : data.icon_override) { + E.value->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed)); + } + for (KeyValue<StringName, Ref<StyleBox>> &E : data.style_override) { + E.value->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed)); + } + for (KeyValue<StringName, Ref<Font>> &E : data.font_override) { + E.value->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed)); + } + + // Then override maps can be simply cleared. + data.icon_override.clear(); + data.style_override.clear(); + data.font_override.clear(); + data.font_size_override.clear(); + data.color_override.clear(); + data.constant_override.clear(); +} diff --git a/scene/gui/control.h b/scene/gui/control.h index f18dd99bff..ee6443c81c 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -41,6 +41,7 @@ class Viewport; class Label; class Panel; +class ThemeOwner; class Control : public CanvasItem { GDCLASS(Control, CanvasItem); @@ -116,7 +117,7 @@ public: PRESET_BOTTOM_WIDE, PRESET_VCENTER_WIDE, PRESET_HCENTER_WIDE, - PRESET_WIDE + PRESET_FULL_RECT }; enum LayoutPresetMode { @@ -159,13 +160,19 @@ private: }; struct Data { - Point2 pos_cache; - Size2 size_cache; - Size2 minimum_size_cache; - bool minimum_size_valid = false; + // Global relations. - Size2 last_minimum_size; - bool updating_last_minimum_size = false; + List<Control *>::Element *RI = nullptr; + + Control *parent = nullptr; + Window *parent_window = nullptr; + CanvasItem *parent_canvas_item = nullptr; + ObjectID drag_owner; + + // Positioning and sizing. + + LayoutMode stored_layout_mode = LayoutMode::LAYOUT_MODE_POSITION; + bool stored_use_custom_anchors = false; real_t offset[4] = { 0.0, 0.0, 0.0, 0.0 }; real_t anchor[4] = { ANCHOR_BEGIN, ANCHOR_BEGIN, ANCHOR_BEGIN, ANCHOR_BEGIN }; @@ -173,49 +180,50 @@ private: GrowDirection h_grow = GROW_DIRECTION_END; GrowDirection v_grow = GROW_DIRECTION_END; - LayoutDirection layout_dir = LAYOUT_DIRECTION_INHERITED; - bool is_rtl_dirty = true; - bool is_rtl = false; - - bool auto_translate = true; - real_t rotation = 0.0; Vector2 scale = Vector2(1, 1); Vector2 pivot_offset; + + Point2 pos_cache; + Size2 size_cache; + Size2 minimum_size_cache; + bool minimum_size_valid = false; + + Size2 last_minimum_size; + bool updating_last_minimum_size = false; + bool block_minimum_size_adjust = false; + bool size_warning = true; + // Container sizing. + int h_size_flags = SIZE_FILL; int v_size_flags = SIZE_FILL; real_t expand = 1.0; - Point2 custom_minimum_size; + Point2i custom_minimum_size; + + // Input events and rendering. MouseFilter mouse_filter = MOUSE_FILTER_STOP; bool force_pass_scroll_events = true; bool clip_contents = false; - - bool block_minimum_size_adjust = false; bool disable_visibility_clip = false; - Control *parent = nullptr; - ObjectID drag_owner; - Ref<Theme> theme; - Control *theme_owner = nullptr; - Window *theme_owner_window = nullptr; - Window *parent_window = nullptr; - StringName theme_type_variation; - - String tooltip; CursorShape default_cursor = CURSOR_ARROW; - List<Control *>::Element *RI = nullptr; - - CanvasItem *parent_canvas_item = nullptr; + // Focus. NodePath focus_neighbor[4]; NodePath focus_next; NodePath focus_prev; + // Theming. + + ThemeOwner *theme_owner = nullptr; + Ref<Theme> theme; + StringName theme_type_variation; + bool bulk_theme_override = false; Theme::ThemeIconMap icon_override; Theme::ThemeStyleMap style_override; @@ -224,78 +232,108 @@ private: Theme::ThemeColorMap color_override; Theme::ThemeConstantMap constant_override; + mutable HashMap<StringName, Theme::ThemeIconMap> theme_icon_cache; + mutable HashMap<StringName, Theme::ThemeStyleMap> theme_style_cache; + mutable HashMap<StringName, Theme::ThemeFontMap> theme_font_cache; + mutable HashMap<StringName, Theme::ThemeFontSizeMap> theme_font_size_cache; + mutable HashMap<StringName, Theme::ThemeColorMap> theme_color_cache; + mutable HashMap<StringName, Theme::ThemeConstantMap> theme_constant_cache; + + // Internationalization. + + LayoutDirection layout_dir = LAYOUT_DIRECTION_INHERITED; + bool is_rtl_dirty = true; + bool is_rtl = false; + + bool auto_translate = true; + + // Extra properties. + + String tooltip; + } data; + // Dynamic properties. + static constexpr unsigned properties_managed_by_container_count = 12; static String properties_managed_by_container[properties_managed_by_container_count]; - void _window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, const Point2 *p_points, real_t p_min, real_t &r_closest_dist, Control **r_closest); - Control *_get_focus_neighbor(Side p_side, int p_count = 0); + // Global relations. + + friend class Viewport; + + // Positioning and sizing. + + void _update_canvas_item_transform(); + Transform2D _get_internal_transform() const; void _set_anchor(Side p_side, real_t p_anchor); void _set_position(const Point2 &p_point); void _set_global_position(const Point2 &p_point); void _set_size(const Size2 &p_size); + void _compute_offsets(Rect2 p_rect, const real_t p_anchors[4], real_t (&r_offsets)[4]); + void _compute_anchors(Rect2 p_rect, const real_t p_offsets[4], real_t (&r_anchors)[4]); + void _set_layout_mode(LayoutMode p_mode); LayoutMode _get_layout_mode() const; - + LayoutMode _get_default_layout_mode() const; void _set_anchors_layout_preset(int p_preset); int _get_anchors_layout_preset() const; - void _theme_changed(); - void _notify_theme_changed(); - + void _update_minimum_size_cache(); void _update_minimum_size(); + void _size_changed(); void _clear_size_warning(); - void _compute_offsets(Rect2 p_rect, const real_t p_anchors[4], real_t (&r_offsets)[4]); - void _compute_anchors(Rect2 p_rect, const real_t p_offsets[4], real_t (&r_anchors)[4]); - - void _size_changed(); - String _get_tooltip() const; + // Input events. - void _override_changed(); + void _call_gui_input(const Ref<InputEvent> &p_event); - void _update_canvas_item_transform(); + // Focus. - Transform2D _get_internal_transform() const; + void _window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, const Point2 *p_points, real_t p_min, real_t &r_closest_dist, Control **r_closest); + Control *_get_focus_neighbor(Side p_side, int p_count = 0); - friend class Viewport; + // Theming. - void _call_gui_input(const Ref<InputEvent> &p_event); + void _theme_changed(); + void _notify_theme_override_changed(); + void _invalidate_theme_cache(); - void _update_minimum_size_cache(); - friend class Window; - static void _propagate_theme_changed(Node *p_at, Control *p_owner, Window *p_owner_window, bool p_assign = true); + // Extra properties. - template <class T> - static T get_theme_item_in_types(Control *p_theme_owner, Window *p_theme_owner_window, Theme::DataType p_data_type, const StringName &p_name, List<StringName> p_theme_types); - static bool has_theme_item_in_types(Control *p_theme_owner, Window *p_theme_owner_window, Theme::DataType p_data_type, const StringName &p_name, List<StringName> p_theme_types); - _FORCE_INLINE_ void _get_theme_type_dependencies(const StringName &p_theme_type, List<StringName> *p_list) const; + String get_tooltip_text() const; protected: - virtual void add_child_notify(Node *p_child) override; - virtual void remove_child_notify(Node *p_child) override; - - //virtual void _window_gui_input(InputEvent p_event); - - virtual Array structured_text_parser(TextServer::StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const; + // Dynamic properties. bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; + void _validate_property(PropertyInfo &p_property) const; + + bool _property_can_revert(const StringName &p_name) const; + bool _property_get_revert(const StringName &p_name, Variant &r_property) const; + + // Theming. + + virtual void _update_theme_item_cache(); + + // Internationalization. - virtual void _validate_property(PropertyInfo &property) const override; + virtual TypedArray<Vector2i> structured_text_parser(TextServer::StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const; + + // Base object overrides. void _notification(int p_notification); static void _bind_methods(); - //bind helpers + // Exposed virtual methods. GDVIRTUAL1RC(bool, _has_point, Vector2) - GDVIRTUAL2RC(Array, _structured_text_parser, Array, String) + GDVIRTUAL2RC(TypedArray<Vector2i>, _structured_text_parser, Array, String) GDVIRTUAL0RC(Vector2, _get_minimum_size) GDVIRTUAL1RC(Variant, _get_drag_data, Vector2) @@ -307,8 +345,6 @@ protected: public: enum { - /* NOTIFICATION_DRAW=30, - NOTIFICATION_VISIBILITY_CHANGED=38*/ NOTIFICATION_RESIZED = 40, NOTIFICATION_MOUSE_ENTER = 41, NOTIFICATION_MOUSE_EXIT = 42, @@ -318,10 +354,11 @@ public: NOTIFICATION_SCROLL_BEGIN = 47, NOTIFICATION_SCROLL_END = 48, NOTIFICATION_LAYOUT_DIRECTION_CHANGED = 49, - }; - /* EDITOR */ + // Editor plugin interoperability. + + // TODO: Decouple controls from their editor plugin and get rid of this. #ifdef TOOLS_ENABLED virtual Dictionary _edit_get_state() const override; virtual void _edit_set_state(const Dictionary &p_state) override; @@ -347,56 +384,50 @@ public: virtual Size2 _edit_get_minimum_size() const override; #endif - virtual void gui_input(const Ref<InputEvent> &p_event); + // Editor integration. - void accept_event(); + virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; + PackedStringArray get_configuration_warnings() const override; - virtual Size2 get_minimum_size() const; - virtual Size2 get_combined_minimum_size() const; - virtual bool has_point(const Point2 &p_point) const; - virtual void set_drag_forwarding(Object *p_target); - virtual Variant get_drag_data(const Point2 &p_point); - virtual bool can_drop_data(const Point2 &p_point, const Variant &p_data) const; - virtual void drop_data(const Point2 &p_point, const Variant &p_data); - void set_drag_preview(Control *p_control); - void force_drag(const Variant &p_data, Control *p_control); - bool is_drag_successful() const; + virtual bool is_text_field() const; + + // Global relations. - void set_custom_minimum_size(const Size2 &p_custom); - Size2 get_custom_minimum_size() const; + bool is_top_level_control() const; Control *get_parent_control() const; Window *get_parent_window() const; + Control *get_root_parent_control() const; - void set_layout_direction(LayoutDirection p_direction); - LayoutDirection get_layout_direction() const; - virtual bool is_layout_rtl() const; - - void set_auto_translate(bool p_enable); - bool is_auto_translating() const; - _FORCE_INLINE_ String atr(const String p_string) const { return is_auto_translating() ? tr(p_string) : p_string; }; + Size2 get_parent_area_size() const; + Rect2 get_parent_anchorable_rect() const; - /* POSITIONING */ + // Positioning and sizing. - void set_anchors_preset(LayoutPreset p_preset, bool p_keep_offsets = true); - void set_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode = PRESET_MODE_MINSIZE, int p_margin = 0); - void set_anchors_and_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode = PRESET_MODE_MINSIZE, int p_margin = 0); - void set_grow_direction_preset(LayoutPreset p_preset); + virtual Transform2D get_transform() const override; void set_anchor(Side p_side, real_t p_anchor, bool p_keep_offset = true, bool p_push_opposite_anchor = true); real_t get_anchor(Side p_side) const; - void set_offset(Side p_side, real_t p_value); real_t get_offset(Side p_side) const; - void set_anchor_and_offset(Side p_side, real_t p_anchor, real_t p_pos, bool p_push_opposite_anchor = true); - void set_begin(const Point2 &p_point); // helper - void set_end(const Point2 &p_point); // helper - + // TODO: Rename to set_begin/end_offsets ? + void set_begin(const Point2 &p_point); Point2 get_begin() const; + void set_end(const Point2 &p_point); Point2 get_end() const; + void set_h_grow_direction(GrowDirection p_direction); + GrowDirection get_h_grow_direction() const; + void set_v_grow_direction(GrowDirection p_direction); + GrowDirection get_v_grow_direction() const; + + void set_anchors_preset(LayoutPreset p_preset, bool p_keep_offsets = true); + void set_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode = PRESET_MODE_MINSIZE, int p_margin = 0); + void set_anchors_and_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode = PRESET_MODE_MINSIZE, int p_margin = 0); + void set_grow_direction_preset(LayoutPreset p_preset); + void set_position(const Point2 &p_point, bool p_keep_offsets = false); void set_global_position(const Point2 &p_point, bool p_keep_offsets = false); Point2 get_position() const; @@ -407,52 +438,72 @@ public: Size2 get_size() const; void reset_size(); + void set_rect(const Rect2 &p_rect); // Reset anchors to begin and set rect, for faster container children sorting. Rect2 get_rect() const; Rect2 get_global_rect() const; Rect2 get_screen_rect() const; Rect2 get_window_rect() const; ///< use with care, as it blocks waiting for the rendering server Rect2 get_anchorable_rect() const override; - void set_rect(const Rect2 &p_rect); // Reset anchors to begin and set rect, for faster container children sorting. - + void set_scale(const Vector2 &p_scale); + Vector2 get_scale() const; void set_rotation(real_t p_radians); real_t get_rotation() const; - - void set_h_grow_direction(GrowDirection p_direction); - GrowDirection get_h_grow_direction() const; - - void set_v_grow_direction(GrowDirection p_direction); - GrowDirection get_v_grow_direction() const; - void set_pivot_offset(const Vector2 &p_pivot); Vector2 get_pivot_offset() const; - void set_scale(const Vector2 &p_scale); - Vector2 get_scale() const; + void update_minimum_size(); - void set_theme(const Ref<Theme> &p_theme); - Ref<Theme> get_theme() const; + void set_block_minimum_size_adjust(bool p_block); + bool is_minimum_size_adjust_blocked() const; - void set_theme_type_variation(const StringName &p_theme_type); - StringName get_theme_type_variation() const; + virtual Size2 get_minimum_size() const; + virtual Size2 get_combined_minimum_size() const; + + void set_custom_minimum_size(const Size2i &p_custom); + Size2i get_custom_minimum_size() const; + + // Container sizing. void set_h_size_flags(int p_flags); int get_h_size_flags() const; - void set_v_size_flags(int p_flags); int get_v_size_flags() const; - void set_stretch_ratio(real_t p_ratio); real_t get_stretch_ratio() const; - void update_minimum_size(); + // Input events. + + virtual void gui_input(const Ref<InputEvent> &p_event); + void accept_event(); + + virtual bool has_point(const Point2 &p_point) const; + + void set_mouse_filter(MouseFilter p_filter); + MouseFilter get_mouse_filter() const; + + void set_force_pass_scroll_events(bool p_force_pass_scroll_events); + bool is_force_pass_scroll_events() const; + + void warp_mouse(const Point2 &p_position); + + // Drag and drop handling. + + virtual void set_drag_forwarding(Object *p_target); + virtual Variant get_drag_data(const Point2 &p_point); + virtual bool can_drop_data(const Point2 &p_point, const Variant &p_data) const; + virtual void drop_data(const Point2 &p_point, const Variant &p_data); + void set_drag_preview(Control *p_control); + void force_drag(const Variant &p_data, Control *p_control); + bool is_drag_successful() const; - /* FOCUS */ + // Focus. void set_focus_mode(FocusMode p_focus_mode); FocusMode get_focus_mode() const; bool has_focus() const; void grab_focus(); + void grab_click_focus(); void release_focus(); Control *find_next_valid_focus() const; @@ -466,13 +517,29 @@ public: void set_focus_previous(const NodePath &p_prev); NodePath get_focus_previous() const; - void set_mouse_filter(MouseFilter p_filter); - MouseFilter get_mouse_filter() const; + // Rendering. - void set_force_pass_scroll_events(bool p_force_pass_scroll_events); - bool is_force_pass_scroll_events() const; + void set_default_cursor_shape(CursorShape p_shape); + CursorShape get_default_cursor_shape() const; + virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const; + + void set_clip_contents(bool p_clip); + bool is_clipping_contents(); + + void set_disable_visibility_clip(bool p_ignore); + bool is_visibility_clip_disabled() const; - /* SKINNING */ + // Theming. + + void set_theme_owner_node(Node *p_node); + Node *get_theme_owner_node() const; + bool has_theme_owner_node() const; + + void set_theme(const Ref<Theme> &p_theme); + Ref<Theme> get_theme() const; + + void set_theme_type_variation(const StringName &p_theme_type); + StringName get_theme_type_variation() const; void begin_bulk_theme_override(); void end_bulk_theme_override(); @@ -512,54 +579,30 @@ public: bool has_theme_color(const StringName &p_name, const StringName &p_theme_type = StringName()) const; bool has_theme_constant(const StringName &p_name, const StringName &p_theme_type = StringName()) const; - static float fetch_theme_default_base_scale(Control *p_theme_owner, Window *p_theme_owner_window); - static Ref<Font> fetch_theme_default_font(Control *p_theme_owner, Window *p_theme_owner_window); - static int fetch_theme_default_font_size(Control *p_theme_owner, Window *p_theme_owner_window); - float get_theme_default_base_scale() const; Ref<Font> get_theme_default_font() const; int get_theme_default_font_size() const; - /* TOOLTIP */ - - void set_tooltip(const String &p_tooltip); - virtual String get_tooltip(const Point2 &p_pos) const; - virtual Control *make_custom_tooltip(const String &p_text) const; - - /* CURSOR */ - - void set_default_cursor_shape(CursorShape p_shape); - CursorShape get_default_cursor_shape() const; - virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const; + // Internationalization. - virtual Transform2D get_transform() const override; - - bool is_top_level_control() const; - - Size2 get_parent_area_size() const; - Rect2 get_parent_anchorable_rect() const; - - void grab_click_focus(); - - void warp_mouse(const Point2 &p_position); - - virtual bool is_text_field() const; - - Control *get_root_parent_control() const; - - void set_clip_contents(bool p_clip); - bool is_clipping_contents(); + void set_layout_direction(LayoutDirection p_direction); + LayoutDirection get_layout_direction() const; + virtual bool is_layout_rtl() const; - void set_block_minimum_size_adjust(bool p_block); - bool is_minimum_size_adjust_blocked() const; + void set_auto_translate(bool p_enable); + bool is_auto_translating() const; + _FORCE_INLINE_ String atr(const String p_string) const { + return is_auto_translating() ? tr(p_string) : p_string; + }; - void set_disable_visibility_clip(bool p_ignore); - bool is_visibility_clip_disabled() const; + // Extra properties. - virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; - TypedArray<String> get_configuration_warnings() const override; + void set_tooltip_text(const String &text); + virtual String get_tooltip(const Point2 &p_pos) const; + virtual Control *make_custom_tooltip(const String &p_text) const; - Control() {} + Control(); + ~Control(); }; VARIANT_ENUM_CAST(Control::FocusMode); @@ -574,4 +617,4 @@ VARIANT_ENUM_CAST(Control::LayoutMode); VARIANT_ENUM_CAST(Control::LayoutDirection); VARIANT_ENUM_CAST(Control::TextDirection); -#endif +#endif // CONTROL_H diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp index a2b05ee50d..f5edaf02d8 100644 --- a/scene/gui/dialogs.cpp +++ b/scene/gui/dialogs.cpp @@ -50,12 +50,27 @@ void AcceptDialog::_parent_focused() { } } +void AcceptDialog::_update_theme_item_cache() { + Window::_update_theme_item_cache(); + + theme_cache.panel_style = get_theme_stylebox(SNAME("panel")); + theme_cache.buttons_separation = get_theme_constant(SNAME("buttons_separation")); +} + void AcceptDialog::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_VISIBILITY_CHANGED: { + case NOTIFICATION_POST_ENTER_TREE: { if (is_visible()) { get_ok_button()->grab_focus(); + } + } break; + case NOTIFICATION_VISIBILITY_CHANGED: { + if (is_visible()) { + if (get_ok_button()->is_inside_tree()) { + get_ok_button()->grab_focus(); + } _update_child_rects(); + parent_visible = get_parent_visible_window(); if (parent_visible) { parent_visible->connect("focus_entered", callable_mp(this, &AcceptDialog::_parent_focused)); @@ -69,7 +84,12 @@ void AcceptDialog::_notification(int p_what) { } break; case NOTIFICATION_THEME_CHANGED: { - bg->add_theme_style_override("panel", bg->get_theme_stylebox(SNAME("panel"), SNAME("AcceptDialog"))); + bg_panel->add_theme_style_override("panel", theme_cache.panel_style); + + child_controls_changed(); + if (is_visible()) { + _update_child_rects(); + } } break; case NOTIFICATION_EXIT_TREE: { @@ -126,11 +146,16 @@ void AcceptDialog::_cancel_pressed() { } String AcceptDialog::get_text() const { - return label->get_text(); + return message_label->get_text(); } void AcceptDialog::set_text(String p_text) { - label->set_text(p_text); + if (message_label->get_text() == p_text) { + return; + } + + message_label->set_text(p_text); + child_controls_changed(); if (is_visible()) { _update_child_rects(); @@ -154,11 +179,24 @@ bool AcceptDialog::get_close_on_escape() const { } void AcceptDialog::set_autowrap(bool p_autowrap) { - label->set_autowrap_mode(p_autowrap ? TextServer::AUTOWRAP_WORD : TextServer::AUTOWRAP_OFF); + message_label->set_autowrap_mode(p_autowrap ? TextServer::AUTOWRAP_WORD : TextServer::AUTOWRAP_OFF); } bool AcceptDialog::has_autowrap() { - return label->get_autowrap_mode() != TextServer::AUTOWRAP_OFF; + return message_label->get_autowrap_mode() != TextServer::AUTOWRAP_OFF; +} + +void AcceptDialog::set_ok_button_text(String p_ok_button_text) { + ok_button->set_text(p_ok_button_text); + + child_controls_changed(); + if (is_visible()) { + _update_child_rects(); + } +} + +String AcceptDialog::get_ok_button_text() const { + return ok_button->get_text(); } void AcceptDialog::register_text_enter(Control *p_line_edit) { @@ -170,69 +208,79 @@ void AcceptDialog::register_text_enter(Control *p_line_edit) { } void AcceptDialog::_update_child_rects() { - Size2 label_size = label->get_minimum_size(); - if (label->get_text().is_empty()) { - label_size.height = 0; - } - int margin = hbc->get_theme_constant(SNAME("margin"), SNAME("Dialogs")); Size2 size = get_size(); - Size2 hminsize = hbc->get_combined_minimum_size(); + float h_margins = theme_cache.panel_style->get_margin(SIDE_LEFT) + theme_cache.panel_style->get_margin(SIDE_RIGHT); + float v_margins = theme_cache.panel_style->get_margin(SIDE_TOP) + theme_cache.panel_style->get_margin(SIDE_BOTTOM); + + // Fill the entire size of the window with the background. + bg_panel->set_position(Point2()); + bg_panel->set_size(size); - Vector2 cpos(margin, margin + label_size.height); - Vector2 csize(size.x - margin * 2, size.y - margin * 3 - hminsize.y - label_size.height); + // Place the buttons from the bottom edge to their minimum required size. + Size2 buttons_minsize = buttons_hbox->get_combined_minimum_size(); + Size2 buttons_size = Size2(size.x - h_margins, buttons_minsize.y); + Point2 buttons_position = Point2(theme_cache.panel_style->get_margin(SIDE_LEFT), size.y - theme_cache.panel_style->get_margin(SIDE_BOTTOM) - buttons_size.y); + buttons_hbox->set_position(buttons_position); + buttons_hbox->set_size(buttons_size); + + // Place the content from the top to fill the rest of the space (minus the separation). + Point2 content_position = Point2(theme_cache.panel_style->get_margin(SIDE_LEFT), theme_cache.panel_style->get_margin(SIDE_TOP)); + Size2 content_size = Size2(size.x - h_margins, size.y - v_margins - buttons_size.y - theme_cache.buttons_separation); for (int i = 0; i < get_child_count(); i++) { Control *c = Object::cast_to<Control>(get_child(i)); if (!c) { continue; } - - if (c == hbc || c == label || c == bg || c->is_set_as_top_level()) { + if (c == buttons_hbox || c == bg_panel || c->is_set_as_top_level()) { continue; } - c->set_position(cpos); - c->set_size(csize); + c->set_position(content_position); + c->set_size(content_size); } - - cpos.y += csize.y + margin; - csize.y = hminsize.y; - - hbc->set_position(cpos); - hbc->set_size(csize); - - bg->set_position(Point2()); - bg->set_size(size); } Size2 AcceptDialog::_get_contents_minimum_size() const { - int margin = hbc->get_theme_constant(SNAME("margin"), SNAME("Dialogs")); - Size2 minsize = label->get_combined_minimum_size(); - + // First, we then iterate over the label and any other custom controls + // to try and find the size that encompasses all content. + Size2 content_minsize; for (int i = 0; i < get_child_count(); i++) { Control *c = Object::cast_to<Control>(get_child(i)); if (!c) { continue; } - if (c == hbc || c == label || c->is_set_as_top_level()) { + // Buttons will be included afterwards. + // The panel only displays the stylebox and doesn't contribute to the size. + if (c == buttons_hbox || c == bg_panel || c->is_set_as_top_level()) { continue; } - Size2 cminsize = c->get_combined_minimum_size(); - minsize.x = MAX(cminsize.x, minsize.x); - minsize.y = MAX(cminsize.y, minsize.y); + Size2 child_minsize = c->get_combined_minimum_size(); + content_minsize.x = MAX(child_minsize.x, content_minsize.x); + content_minsize.y = MAX(child_minsize.y, content_minsize.y); } - Size2 hminsize = hbc->get_combined_minimum_size(); - minsize.x = MAX(hminsize.x, minsize.x); - minsize.y += hminsize.y; - minsize.x += margin * 2; - minsize.y += margin * 3; //one as separation between hbc and child + // Then we take the background panel as it provides the offsets, + // which are always added to the minimum size. + if (theme_cache.panel_style.is_valid()) { + content_minsize += theme_cache.panel_style->get_minimum_size(); + } - Size2 wmsize = get_min_size(); - minsize.x = MAX(wmsize.x, minsize.x); - return minsize; + // Then we add buttons. Horizontally we're interested in whichever + // value is the biggest. Vertically buttons add to the overall size. + Size2 buttons_minsize = buttons_hbox->get_combined_minimum_size(); + content_minsize.x = MAX(buttons_minsize.x, content_minsize.x); + content_minsize.y += buttons_minsize.y; + // Plus there is a separation size added on top. + content_minsize.y += theme_cache.buttons_separation; + + // Last, we make sure that we aren't below the minimum window size. + Size2 window_minsize = get_min_size(); + content_minsize.x = MAX(window_minsize.x, content_minsize.x); + content_minsize.y = MAX(window_minsize.y, content_minsize.y); + return content_minsize; } void AcceptDialog::_custom_action(const String &p_action) { @@ -243,17 +291,23 @@ void AcceptDialog::_custom_action(const String &p_action) { Button *AcceptDialog::add_button(const String &p_text, bool p_right, const String &p_action) { Button *button = memnew(Button); button->set_text(p_text); + if (p_right) { - hbc->add_child(button); - hbc->add_spacer(); + buttons_hbox->add_child(button); + buttons_hbox->add_spacer(); } else { - hbc->add_child(button); - hbc->move_child(button, 0); - hbc->add_spacer(true); + buttons_hbox->add_child(button); + buttons_hbox->move_child(button, 0); + buttons_hbox->add_spacer(true); + } + + child_controls_changed(); + if (is_visible()) { + _update_child_rects(); } if (!p_action.is_empty()) { - button->connect("pressed", callable_mp(this, &AcceptDialog::_custom_action), varray(p_action)); + button->connect("pressed", callable_mp(this, &AcceptDialog::_custom_action).bind(p_action)); } return button; @@ -262,26 +316,21 @@ Button *AcceptDialog::add_button(const String &p_text, bool p_right, const Strin Button *AcceptDialog::add_cancel_button(const String &p_cancel) { String c = p_cancel; if (p_cancel.is_empty()) { - c = RTR("Cancel"); + c = "Cancel"; } + Button *b = swap_cancel_ok ? add_button(c, true) : add_button(c); + b->connect("pressed", callable_mp(this, &AcceptDialog::_cancel_pressed)); + return b; } void AcceptDialog::remove_button(Control *p_button) { Button *button = Object::cast_to<Button>(p_button); ERR_FAIL_NULL(button); - ERR_FAIL_COND_MSG(button->get_parent() != hbc, vformat("Cannot remove button %s as it does not belong to this dialog.", button->get_name())); - ERR_FAIL_COND_MSG(button == ok, "Cannot remove dialog's OK button."); - - Node *right_spacer = hbc->get_child(button->get_index() + 1); - // Should always be valid but let's avoid crashing - if (right_spacer) { - hbc->remove_child(right_spacer); - memdelete(right_spacer); - } - hbc->remove_child(button); + ERR_FAIL_COND_MSG(button->get_parent() != buttons_hbox, vformat("Cannot remove button %s as it does not belong to this dialog.", button->get_name())); + ERR_FAIL_COND_MSG(button == ok_button, "Cannot remove dialog's OK button."); if (button->is_connected("pressed", callable_mp(this, &AcceptDialog::_custom_action))) { button->disconnect("pressed", callable_mp(this, &AcceptDialog::_custom_action)); @@ -289,6 +338,19 @@ void AcceptDialog::remove_button(Control *p_button) { if (button->is_connected("pressed", callable_mp(this, &AcceptDialog::_cancel_pressed))) { button->disconnect("pressed", callable_mp(this, &AcceptDialog::_cancel_pressed)); } + + Node *right_spacer = buttons_hbox->get_child(button->get_index() + 1); + // Should always be valid but let's avoid crashing. + if (right_spacer) { + buttons_hbox->remove_child(right_spacer); + memdelete(right_spacer); + } + buttons_hbox->remove_child(button); + + child_controls_changed(); + if (is_visible()) { + _update_child_rects(); + } } void AcceptDialog::_bind_methods() { @@ -306,11 +368,15 @@ void AcceptDialog::_bind_methods() { ClassDB::bind_method(D_METHOD("get_text"), &AcceptDialog::get_text); ClassDB::bind_method(D_METHOD("set_autowrap", "autowrap"), &AcceptDialog::set_autowrap); ClassDB::bind_method(D_METHOD("has_autowrap"), &AcceptDialog::has_autowrap); + ClassDB::bind_method(D_METHOD("set_ok_button_text", "text"), &AcceptDialog::set_ok_button_text); + ClassDB::bind_method(D_METHOD("get_ok_button_text"), &AcceptDialog::get_ok_button_text); ADD_SIGNAL(MethodInfo("confirmed")); ADD_SIGNAL(MethodInfo("cancelled")); ADD_SIGNAL(MethodInfo("custom_action", PropertyInfo(Variant::STRING_NAME, "action"))); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "ok_button_text"), "set_ok_button_text", "get_ok_button_text"); + ADD_GROUP("Dialog", "dialog"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "dialog_text", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT_INTL), "set_text", "get_text"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dialog_hide_on_ok"), "set_hide_on_ok", "get_hide_on_ok"); @@ -330,30 +396,25 @@ AcceptDialog::AcceptDialog() { set_exclusive(true); set_clamp_to_embedder(true); - bg = memnew(Panel); - add_child(bg, false, INTERNAL_MODE_FRONT); + bg_panel = memnew(Panel); + add_child(bg_panel, false, INTERNAL_MODE_FRONT); - hbc = memnew(HBoxContainer); + buttons_hbox = memnew(HBoxContainer); - int margin = hbc->get_theme_constant(SNAME("margin"), SNAME("Dialogs")); - int button_margin = hbc->get_theme_constant(SNAME("button_margin"), SNAME("Dialogs")); + message_label = memnew(Label); + message_label->set_anchor(SIDE_RIGHT, Control::ANCHOR_END); + message_label->set_anchor(SIDE_BOTTOM, Control::ANCHOR_END); + add_child(message_label, false, INTERNAL_MODE_FRONT); - label = memnew(Label); - label->set_anchor(SIDE_RIGHT, Control::ANCHOR_END); - label->set_anchor(SIDE_BOTTOM, Control::ANCHOR_END); - label->set_begin(Point2(margin, margin)); - label->set_end(Point2(-margin, -button_margin - 10)); - add_child(label, false, INTERNAL_MODE_FRONT); + add_child(buttons_hbox, false, INTERNAL_MODE_FRONT); - add_child(hbc, false, INTERNAL_MODE_FRONT); + buttons_hbox->add_spacer(); + ok_button = memnew(Button); + ok_button->set_text("OK"); + buttons_hbox->add_child(ok_button); + buttons_hbox->add_spacer(); - hbc->add_spacer(); - ok = memnew(Button); - ok->set_text(RTR("OK")); - hbc->add_child(ok); - hbc->add_spacer(); - - ok->connect("pressed", callable_mp(this, &AcceptDialog::_ok_pressed)); + ok_button->connect("pressed", callable_mp(this, &AcceptDialog::_ok_pressed)); set_title(TTRC("Alert!")); @@ -365,8 +426,20 @@ AcceptDialog::~AcceptDialog() { // ConfirmationDialog +void ConfirmationDialog::set_cancel_button_text(String p_cancel_button_text) { + cancel->set_text(p_cancel_button_text); +} + +String ConfirmationDialog::get_cancel_button_text() const { + return cancel->get_text(); +} + void ConfirmationDialog::_bind_methods() { ClassDB::bind_method(D_METHOD("get_cancel_button"), &ConfirmationDialog::get_cancel_button); + ClassDB::bind_method(D_METHOD("set_cancel_button_text", "text"), &ConfirmationDialog::set_cancel_button_text); + ClassDB::bind_method(D_METHOD("get_cancel_button_text"), &ConfirmationDialog::get_cancel_button_text); + + ADD_PROPERTY(PropertyInfo(Variant::STRING, "cancel_button_text"), "set_cancel_button_text", "get_cancel_button_text"); } Button *ConfirmationDialog::get_cancel_button() { diff --git a/scene/gui/dialogs.h b/scene/gui/dialogs.h index 41fd9c0a10..81e82d851e 100644 --- a/scene/gui/dialogs.h +++ b/scene/gui/dialogs.h @@ -45,13 +45,20 @@ class AcceptDialog : public Window { GDCLASS(AcceptDialog, Window); Window *parent_visible = nullptr; - Panel *bg = nullptr; - HBoxContainer *hbc = nullptr; - Label *label = nullptr; - Button *ok = nullptr; + + Panel *bg_panel = nullptr; + Label *message_label = nullptr; + HBoxContainer *buttons_hbox = nullptr; + Button *ok_button = nullptr; + bool hide_on_ok = true; bool close_on_escape = true; + struct ThemeCache { + Ref<StyleBox> panel_style; + int buttons_separation = 0; + } theme_cache; + void _custom_action(const String &p_action); void _update_child_rects(); @@ -62,6 +69,7 @@ class AcceptDialog : public Window { protected: virtual Size2 _get_contents_minimum_size() const override; + virtual void _update_theme_item_cache() override; void _notification(int p_what); static void _bind_methods(); @@ -75,12 +83,12 @@ protected: void _cancel_pressed(); public: - Label *get_label() { return label; } + Label *get_label() { return message_label; } static void set_swap_cancel_ok(bool p_swap); void register_text_enter(Control *p_line_edit); - Button *get_ok_button() { return ok; } + Button *get_ok_button() { return ok_button; } Button *add_button(const String &p_text, bool p_right = false, const String &p_action = ""); Button *add_cancel_button(const String &p_cancel = ""); void remove_button(Control *p_button); @@ -97,6 +105,9 @@ public: void set_autowrap(bool p_autowrap); bool has_autowrap(); + void set_ok_button_text(String p_ok_button_text); + String get_ok_button_text() const; + AcceptDialog(); ~AcceptDialog(); }; @@ -110,7 +121,11 @@ protected: public: Button *get_cancel_button(); + + void set_cancel_button_text(String p_cancel_button_text); + String get_cancel_button_text() const; + ConfirmationDialog(); }; -#endif +#endif // DIALOGS_H diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index 6bb4ac9c6f..cf7f439aef 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -59,36 +59,26 @@ VBoxContainer *FileDialog::get_vbox() { return vbox; } -void FileDialog::_theme_changed() { - Color font_color = vbox->get_theme_color(SNAME("font_color"), SNAME("Button")); - Color font_hover_color = vbox->get_theme_color(SNAME("font_hover_color"), SNAME("Button")); - Color font_focus_color = vbox->get_theme_color(SNAME("font_focus_color"), SNAME("Button")); - Color font_pressed_color = vbox->get_theme_color(SNAME("font_pressed_color"), SNAME("Button")); - - dir_up->add_theme_color_override("icon_normal_color", font_color); - dir_up->add_theme_color_override("icon_hover_color", font_hover_color); - dir_up->add_theme_color_override("icon_focus_color", font_focus_color); - dir_up->add_theme_color_override("icon_pressed_color", font_pressed_color); - - dir_prev->add_theme_color_override("icon_color_normal", font_color); - dir_prev->add_theme_color_override("icon_color_hover", font_hover_color); - dir_prev->add_theme_color_override("icon_focus_color", font_focus_color); - dir_prev->add_theme_color_override("icon_color_pressed", font_pressed_color); - - dir_next->add_theme_color_override("icon_color_normal", font_color); - dir_next->add_theme_color_override("icon_color_hover", font_hover_color); - dir_next->add_theme_color_override("icon_focus_color", font_focus_color); - dir_next->add_theme_color_override("icon_color_pressed", font_pressed_color); - - refresh->add_theme_color_override("icon_normal_color", font_color); - refresh->add_theme_color_override("icon_hover_color", font_hover_color); - refresh->add_theme_color_override("icon_focus_color", font_focus_color); - refresh->add_theme_color_override("icon_pressed_color", font_pressed_color); - - show_hidden->add_theme_color_override("icon_normal_color", font_color); - show_hidden->add_theme_color_override("icon_hover_color", font_hover_color); - show_hidden->add_theme_color_override("icon_focus_color", font_focus_color); - show_hidden->add_theme_color_override("icon_pressed_color", font_pressed_color); +void FileDialog::_update_theme_item_cache() { + ConfirmationDialog::_update_theme_item_cache(); + + theme_cache.parent_folder = get_theme_icon(SNAME("parent_folder")); + theme_cache.forward_folder = get_theme_icon(SNAME("forward_folder")); + theme_cache.back_folder = get_theme_icon(SNAME("back_folder")); + theme_cache.reload = get_theme_icon(SNAME("reload")); + theme_cache.toggle_hidden = get_theme_icon(SNAME("toggle_hidden")); + theme_cache.folder = get_theme_icon(SNAME("folder")); + theme_cache.file = get_theme_icon(SNAME("file")); + + theme_cache.folder_icon_color = get_theme_color(SNAME("folder_icon_color")); + theme_cache.file_icon_color = get_theme_color(SNAME("file_icon_color")); + theme_cache.file_disabled_color = get_theme_color(SNAME("file_disabled_color")); + + // TODO: Define own colors? + theme_cache.icon_normal_color = get_theme_color(SNAME("font_color"), SNAME("Button")); + theme_cache.icon_hover_color = get_theme_color(SNAME("font_hover_color"), SNAME("Button")); + theme_cache.icon_focus_color = get_theme_color(SNAME("font_focus_color"), SNAME("Button")); + theme_cache.icon_pressed_color = get_theme_color(SNAME("font_pressed_color"), SNAME("Button")); } void FileDialog::_notification(int p_what) { @@ -97,20 +87,46 @@ void FileDialog::_notification(int p_what) { if (!is_visible()) { set_process_shortcut_input(false); } + + invalidate(); // Put it here to preview in the editor. } break; - case NOTIFICATION_ENTER_TREE: { - dir_up->set_icon(vbox->get_theme_icon(SNAME("parent_folder"), SNAME("FileDialog"))); + case NOTIFICATION_THEME_CHANGED: { + dir_up->set_icon(theme_cache.parent_folder); if (vbox->is_layout_rtl()) { - dir_prev->set_icon(vbox->get_theme_icon(SNAME("forward_folder"), SNAME("FileDialog"))); - dir_next->set_icon(vbox->get_theme_icon(SNAME("back_folder"), SNAME("FileDialog"))); + dir_prev->set_icon(theme_cache.forward_folder); + dir_next->set_icon(theme_cache.back_folder); } else { - dir_prev->set_icon(vbox->get_theme_icon(SNAME("back_folder"), SNAME("FileDialog"))); - dir_next->set_icon(vbox->get_theme_icon(SNAME("forward_folder"), SNAME("FileDialog"))); + dir_prev->set_icon(theme_cache.back_folder); + dir_next->set_icon(theme_cache.forward_folder); } - refresh->set_icon(vbox->get_theme_icon(SNAME("reload"), SNAME("FileDialog"))); - show_hidden->set_icon(vbox->get_theme_icon(SNAME("toggle_hidden"), SNAME("FileDialog"))); - _theme_changed(); + refresh->set_icon(theme_cache.reload); + show_hidden->set_icon(theme_cache.toggle_hidden); + + dir_up->add_theme_color_override("icon_normal_color", theme_cache.icon_normal_color); + dir_up->add_theme_color_override("icon_hover_color", theme_cache.icon_hover_color); + dir_up->add_theme_color_override("icon_focus_color", theme_cache.icon_focus_color); + dir_up->add_theme_color_override("icon_pressed_color", theme_cache.icon_pressed_color); + + dir_prev->add_theme_color_override("icon_color_normal", theme_cache.icon_normal_color); + dir_prev->add_theme_color_override("icon_color_hover", theme_cache.icon_hover_color); + dir_prev->add_theme_color_override("icon_focus_color", theme_cache.icon_focus_color); + dir_prev->add_theme_color_override("icon_color_pressed", theme_cache.icon_pressed_color); + + dir_next->add_theme_color_override("icon_color_normal", theme_cache.icon_normal_color); + dir_next->add_theme_color_override("icon_color_hover", theme_cache.icon_hover_color); + dir_next->add_theme_color_override("icon_focus_color", theme_cache.icon_focus_color); + dir_next->add_theme_color_override("icon_color_pressed", theme_cache.icon_pressed_color); + + refresh->add_theme_color_override("icon_normal_color", theme_cache.icon_normal_color); + refresh->add_theme_color_override("icon_hover_color", theme_cache.icon_hover_color); + refresh->add_theme_color_override("icon_focus_color", theme_cache.icon_focus_color); + refresh->add_theme_color_override("icon_pressed_color", theme_cache.icon_pressed_color); + + show_hidden->add_theme_color_override("icon_normal_color", theme_cache.icon_normal_color); + show_hidden->add_theme_color_override("icon_hover_color", theme_cache.icon_hover_color); + show_hidden->add_theme_color_override("icon_focus_color", theme_cache.icon_focus_color); + show_hidden->add_theme_color_override("icon_pressed_color", theme_cache.icon_pressed_color); } break; case NOTIFICATION_TRANSLATION_CHANGED: { @@ -129,7 +145,7 @@ void FileDialog::shortcut_input(const Ref<InputEvent> &p_event) { switch (k->get_keycode()) { case Key::H: { - if (k->is_command_pressed()) { + if (k->is_command_or_control_pressed()) { set_show_hidden_files(!show_hidden_files); } else { handled = false; @@ -156,18 +172,20 @@ void FileDialog::shortcut_input(const Ref<InputEvent> &p_event) { void FileDialog::set_enable_multiple_selection(bool p_enable) { tree->set_select_mode(p_enable ? Tree::SELECT_MULTI : Tree::SELECT_SINGLE); -}; +} Vector<String> FileDialog::get_selected_files() const { Vector<String> list; TreeItem *item = tree->get_root(); - while ((item = tree->get_next_selected(item))) { - list.push_back(dir_access->get_current_dir().plus_file(item->get_text(0))); - }; + item = tree->get_next_selected(item); + while (item) { + list.push_back(dir_access->get_current_dir().path_join(item->get_text(0))); + item = tree->get_next_selected(item); + } return list; -}; +} void FileDialog::update_dir() { if (root_prefix.is_empty()) { @@ -192,7 +210,7 @@ void FileDialog::update_dir() { } void FileDialog::_dir_submitted(String p_dir) { - _change_dir(root_prefix.plus_file(p_dir)); + _change_dir(root_prefix.path_join(p_dir)); file->set_text(""); _push_history(); } @@ -202,17 +220,13 @@ void FileDialog::_file_submitted(const String &p_file) { } void FileDialog::_save_confirm_pressed() { - String f = dir_access->get_current_dir().plus_file(file->get_text()); + String f = dir_access->get_current_dir().path_join(file->get_text()); emit_signal(SNAME("file_selected"), f); hide(); } void FileDialog::_post_popup() { ConfirmationDialog::_post_popup(); - if (invalidated) { - update_file_list(); - invalidated = false; - } if (mode == FILE_MODE_SAVE_FILE) { file->grab_focus(); } else { @@ -252,7 +266,7 @@ void FileDialog::_action_pressed() { Vector<String> files; while (ti) { - files.push_back(fbase.plus_file(ti->get_text(0))); + files.push_back(fbase.path_join(ti->get_text(0))); ti = tree->get_next_selected(ti); } @@ -265,7 +279,7 @@ void FileDialog::_action_pressed() { } String file_text = file->get_text(); - String f = file_text.is_absolute_path() ? file_text : dir_access->get_current_dir().plus_file(file_text); + String f = file_text.is_absolute_path() ? file_text : dir_access->get_current_dir().path_join(file_text); if ((mode == FILE_MODE_OPEN_ANY || mode == FILE_MODE_OPEN_FILE) && dir_access->file_exists(f)) { emit_signal(SNAME("file_selected"), f); @@ -278,7 +292,7 @@ void FileDialog::_action_pressed() { if (item) { Dictionary d = item->get_metadata(0); if (d["dir"] && d["name"] != "..") { - path = path.plus_file(d["name"]); + path = path.path_join(d["name"]); } } @@ -419,10 +433,10 @@ void FileDialog::deselect_all() { switch (mode) { case FILE_MODE_OPEN_FILE: case FILE_MODE_OPEN_FILES: - get_ok_button()->set_text(RTR("Open")); + set_ok_button_text(RTR("Open")); break; case FILE_MODE_OPEN_DIR: - get_ok_button()->set_text(RTR("Select Current Folder")); + set_ok_button_text(RTR("Select Current Folder")); break; case FILE_MODE_OPEN_ANY: case FILE_MODE_SAVE_FILE: @@ -446,7 +460,7 @@ void FileDialog::_tree_selected() { if (!d["dir"]) { file->set_text(d["name"]); } else if (mode == FILE_MODE_OPEN_DIR) { - get_ok_button()->set_text(RTR("Select This Folder")); + set_ok_button_text(RTR("Select This Folder")); } get_ok_button()->set_disabled(_is_open_should_be_disabled()); @@ -506,10 +520,6 @@ void FileDialog::update_file_list() { } TreeItem *root = tree->create_item(); - Ref<Texture2D> folder = vbox->get_theme_icon(SNAME("folder"), SNAME("FileDialog")); - Ref<Texture2D> file_icon = vbox->get_theme_icon(SNAME("file"), SNAME("FileDialog")); - const Color folder_color = vbox->get_theme_color(SNAME("folder_icon_modulate"), SNAME("FileDialog")); - const Color file_color = vbox->get_theme_color(SNAME("file_icon_modulate"), SNAME("FileDialog")); List<String> files; List<String> dirs; @@ -541,8 +551,8 @@ void FileDialog::update_file_list() { String &dir_name = dirs.front()->get(); TreeItem *ti = tree->create_item(root); ti->set_text(0, dir_name); - ti->set_icon(0, folder); - ti->set_icon_modulate(0, folder_color); + ti->set_icon(0, theme_cache.folder); + ti->set_icon_modulate(0, theme_cache.folder_icon_color); Dictionary d; d["name"] = dir_name; @@ -598,15 +608,15 @@ void FileDialog::update_file_list() { ti->set_text(0, files.front()->get()); if (get_icon_func) { - Ref<Texture2D> icon = get_icon_func(base_dir.plus_file(files.front()->get())); + Ref<Texture2D> icon = get_icon_func(base_dir.path_join(files.front()->get())); ti->set_icon(0, icon); } else { - ti->set_icon(0, file_icon); + ti->set_icon(0, theme_cache.file); } - ti->set_icon_modulate(0, file_color); + ti->set_icon_modulate(0, theme_cache.file_icon_color); if (mode == FILE_MODE_OPEN_DIR) { - ti->set_custom_color(0, vbox->get_theme_color(SNAME("files_disabled"), SNAME("FileDialog"))); + ti->set_custom_color(0, theme_cache.file_disabled_color); ti->set_selectable(0, false); } Dictionary d; @@ -673,14 +683,21 @@ void FileDialog::clear_filters() { invalidate(); } -void FileDialog::add_filter(const String &p_filter) { +void FileDialog::add_filter(const String &p_filter, const String &p_description) { ERR_FAIL_COND_MSG(p_filter.begins_with("."), "Filter must be \"filename.extension\", can't start with dot."); - filters.push_back(p_filter); + if (p_description.is_empty()) { + filters.push_back(p_filter); + } else { + filters.push_back(vformat("%s ; %s", p_filter, p_description)); + } update_filters(); invalidate(); } void FileDialog::set_filters(const Vector<String> &p_filters) { + if (filters == p_filters) { + return; + } filters = p_filters; update_filters(); invalidate(); @@ -699,15 +716,19 @@ String FileDialog::get_current_file() const { } String FileDialog::get_current_path() const { - return dir->get_text().plus_file(file->get_text()); + return dir->get_text().path_join(file->get_text()); } void FileDialog::set_current_dir(const String &p_dir) { _change_dir(p_dir); + _push_history(); } void FileDialog::set_current_file(const String &p_file) { + if (file->get_text() == p_file) { + return; + } file->set_text(p_file); update_dir(); invalidate(); @@ -760,39 +781,41 @@ bool FileDialog::is_mode_overriding_title() const { void FileDialog::set_file_mode(FileMode p_mode) { ERR_FAIL_INDEX((int)p_mode, 5); - + if (mode == p_mode) { + return; + } mode = p_mode; switch (mode) { case FILE_MODE_OPEN_FILE: - get_ok_button()->set_text(RTR("Open")); + set_ok_button_text(RTR("Open")); if (mode_overrides_title) { set_title(TTRC("Open a File")); } makedir->hide(); break; case FILE_MODE_OPEN_FILES: - get_ok_button()->set_text(RTR("Open")); + set_ok_button_text(RTR("Open")); if (mode_overrides_title) { set_title(TTRC("Open File(s)")); } makedir->hide(); break; case FILE_MODE_OPEN_DIR: - get_ok_button()->set_text(RTR("Select Current Folder")); + set_ok_button_text(RTR("Select Current Folder")); if (mode_overrides_title) { set_title(TTRC("Open a Directory")); } makedir->show(); break; case FILE_MODE_OPEN_ANY: - get_ok_button()->set_text(RTR("Open")); + set_ok_button_text(RTR("Open")); if (mode_overrides_title) { set_title(TTRC("Open a File or Directory")); } makedir->show(); break; case FILE_MODE_SAVE_FILE: - get_ok_button()->set_text(RTR("Save")); + set_ok_button_text(RTR("Save")); if (mode_overrides_title) { set_title(TTRC("Save a File")); } @@ -919,7 +942,7 @@ void FileDialog::_bind_methods() { ClassDB::bind_method(D_METHOD("_cancel_pressed"), &FileDialog::_cancel_pressed); ClassDB::bind_method(D_METHOD("clear_filters"), &FileDialog::clear_filters); - ClassDB::bind_method(D_METHOD("add_filter", "filter"), &FileDialog::add_filter); + ClassDB::bind_method(D_METHOD("add_filter", "filter", "description"), &FileDialog::add_filter, DEFVAL("")); ClassDB::bind_method(D_METHOD("set_filters", "filters"), &FileDialog::set_filters); ClassDB::bind_method(D_METHOD("get_filters"), &FileDialog::get_filters); ClassDB::bind_method(D_METHOD("get_current_dir"), &FileDialog::get_current_dir); @@ -973,6 +996,9 @@ void FileDialog::_bind_methods() { } void FileDialog::set_show_hidden_files(bool p_show) { + if (show_hidden_files == p_show) { + return; + } show_hidden_files = p_show; invalidate(); } @@ -990,7 +1016,6 @@ FileDialog::FileDialog() { vbox = memnew(VBoxContainer); add_child(vbox, false, INTERNAL_MODE_FRONT); - vbox->connect("theme_changed", callable_mp(this, &FileDialog::_theme_changed)); mode = FILE_MODE_SAVE_FILE; set_title(TTRC("Save a File")); @@ -999,13 +1024,13 @@ FileDialog::FileDialog() { dir_prev = memnew(Button); dir_prev->set_flat(true); - dir_prev->set_tooltip(RTR("Go to previous folder.")); + dir_prev->set_tooltip_text(RTR("Go to previous folder.")); dir_next = memnew(Button); dir_next->set_flat(true); - dir_next->set_tooltip(RTR("Go to next folder.")); + dir_next->set_tooltip_text(RTR("Go to next folder.")); dir_up = memnew(Button); dir_up->set_flat(true); - dir_up->set_tooltip(RTR("Go to parent folder.")); + dir_up->set_tooltip_text(RTR("Go to parent folder.")); hbc->add_child(dir_prev); hbc->add_child(dir_next); hbc->add_child(dir_up); @@ -1029,7 +1054,7 @@ FileDialog::FileDialog() { refresh = memnew(Button); refresh->set_flat(true); - refresh->set_tooltip(RTR("Refresh files.")); + refresh->set_tooltip_text(RTR("Refresh files.")); refresh->connect("pressed", callable_mp(this, &FileDialog::update_file_list)); hbc->add_child(refresh); @@ -1037,7 +1062,7 @@ FileDialog::FileDialog() { show_hidden->set_flat(true); show_hidden->set_toggle_mode(true); show_hidden->set_pressed(is_showing_hidden_files()); - show_hidden->set_tooltip(RTR("Toggle the visibility of hidden files.")); + show_hidden->set_tooltip_text(RTR("Toggle the visibility of hidden files.")); show_hidden->connect("toggled", callable_mp(this, &FileDialog::set_show_hidden_files)); hbc->add_child(show_hidden); @@ -1056,7 +1081,7 @@ FileDialog::FileDialog() { message = memnew(Label); message->hide(); - message->set_anchors_and_offsets_preset(Control::PRESET_WIDE); + message->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); message->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); message->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER); tree->add_child(message); @@ -1079,9 +1104,9 @@ FileDialog::FileDialog() { _update_drives(); connect("confirmed", callable_mp(this, &FileDialog::_action_pressed)); - tree->connect("multi_selected", callable_mp(this, &FileDialog::_tree_multi_selected), varray(), CONNECT_DEFERRED); - tree->connect("cell_selected", callable_mp(this, &FileDialog::_tree_selected), varray(), CONNECT_DEFERRED); - tree->connect("item_activated", callable_mp(this, &FileDialog::_tree_item_activated), varray()); + tree->connect("multi_selected", callable_mp(this, &FileDialog::_tree_multi_selected), CONNECT_DEFERRED); + tree->connect("cell_selected", callable_mp(this, &FileDialog::_tree_selected), CONNECT_DEFERRED); + tree->connect("item_activated", callable_mp(this, &FileDialog::_tree_item_activated)); tree->connect("nothing_selected", callable_mp(this, &FileDialog::deselect_all)); dir->connect("text_submitted", callable_mp(this, &FileDialog::_dir_submitted)); file->connect("text_submitted", callable_mp(this, &FileDialog::_file_submitted)); diff --git a/scene/gui/file_dialog.h b/scene/gui/file_dialog.h index 8b8d93920c..1add0a9cf5 100644 --- a/scene/gui/file_dialog.h +++ b/scene/gui/file_dialog.h @@ -109,6 +109,25 @@ private: bool invalidated = true; + struct ThemeCache { + Ref<Texture2D> parent_folder; + Ref<Texture2D> forward_folder; + Ref<Texture2D> back_folder; + Ref<Texture2D> reload; + Ref<Texture2D> toggle_hidden; + Ref<Texture2D> folder; + Ref<Texture2D> file; + + Color folder_icon_color; + Color file_icon_color; + Color file_disabled_color; + + Color icon_normal_color; + Color icon_hover_color; + Color icon_focus_color; + Color icon_pressed_color; + } theme_cache; + void update_dir(); void update_file_name(); void update_file_list(); @@ -143,7 +162,7 @@ private: virtual void _post_popup() override; protected: - void _theme_changed(); + virtual void _update_theme_item_cache() override; void _notification(int p_what); static void _bind_methods(); @@ -151,7 +170,7 @@ protected: public: void popup_file_dialog(); void clear_filters(); - void add_filter(const String &p_filter); + void add_filter(const String &p_filter, const String &p_description = ""); void set_filters(const Vector<String> &p_filters); Vector<String> get_filters() const; @@ -196,4 +215,4 @@ public: VARIANT_ENUM_CAST(FileDialog::FileMode); VARIANT_ENUM_CAST(FileDialog::Access); -#endif +#endif // FILE_DIALOG_H diff --git a/scene/gui/flow_container.cpp b/scene/gui/flow_container.cpp index 30b694da76..b0d15aa7f4 100644 --- a/scene/gui/flow_container.cpp +++ b/scene/gui/flow_container.cpp @@ -44,9 +44,6 @@ void FlowContainer::_resort() { return; } - int separation_horizontal = get_theme_constant(SNAME("h_separation")); - int separation_vertical = get_theme_constant(SNAME("v_separation")); - bool rtl = is_layout_rtl(); HashMap<Control *, Size2i> children_minsize_cache; @@ -74,14 +71,14 @@ void FlowContainer::_resort() { if (vertical) { /* VERTICAL */ if (children_in_current_line > 0) { - ofs.y += separation_vertical; + ofs.y += theme_cache.v_separation; } if (ofs.y + child_msc.y > current_container_size) { - line_length = ofs.y - separation_vertical; + line_length = ofs.y - theme_cache.v_separation; lines_data.push_back(_LineData{ children_in_current_line, line_height, line_length, current_container_size - line_length, line_stretch_ratio_total }); // Move in new column (vertical line). - ofs.x += line_height + separation_horizontal; + ofs.x += line_height + theme_cache.h_separation; ofs.y = 0; line_height = 0; line_stretch_ratio_total = 0; @@ -96,14 +93,14 @@ void FlowContainer::_resort() { } else { /* HORIZONTAL */ if (children_in_current_line > 0) { - ofs.x += separation_horizontal; + ofs.x += theme_cache.h_separation; } if (ofs.x + child_msc.x > current_container_size) { - line_length = ofs.x - separation_horizontal; + line_length = ofs.x - theme_cache.h_separation; lines_data.push_back(_LineData{ children_in_current_line, line_height, line_length, current_container_size - line_length, line_stretch_ratio_total }); // Move in new line. - ofs.y += line_height + separation_vertical; + ofs.y += line_height + theme_cache.v_separation; ofs.x = 0; line_height = 0; line_stretch_ratio_total = 0; @@ -146,11 +143,11 @@ void FlowContainer::_resort() { current_line_idx++; child_idx_in_line = 0; if (vertical) { - ofs.x += line_data.min_line_height + separation_horizontal; + ofs.x += line_data.min_line_height + theme_cache.h_separation; ofs.y = 0; } else { ofs.x = 0; - ofs.y += line_data.min_line_height + separation_vertical; + ofs.y += line_data.min_line_height + theme_cache.v_separation; } line_data = lines_data[current_line_idx]; } @@ -184,9 +181,9 @@ void FlowContainer::_resort() { fit_child_in_rect(child, child_rect); if (vertical) { /* VERTICAL */ - ofs.y += child_size.height + separation_vertical; + ofs.y += child_size.height + theme_cache.v_separation; } else { /* HORIZONTAL */ - ofs.x += child_size.width + separation_horizontal; + ofs.x += child_size.width + theme_cache.h_separation; } child_idx_in_line++; @@ -250,6 +247,13 @@ Vector<int> FlowContainer::get_allowed_size_flags_vertical() const { return flags; } +void FlowContainer::_update_theme_item_cache() { + Container::_update_theme_item_cache(); + + theme_cache.h_separation = get_theme_constant(SNAME("h_separation")); + theme_cache.v_separation = get_theme_constant(SNAME("v_separation")); +} + void FlowContainer::_notification(int p_what) { switch (p_what) { case NOTIFICATION_SORT_CHILDREN: { @@ -268,14 +272,36 @@ void FlowContainer::_notification(int p_what) { } } +void FlowContainer::_validate_property(PropertyInfo &p_property) const { + if (is_fixed && p_property.name == "vertical") { + p_property.usage = PROPERTY_USAGE_NONE; + } +} + int FlowContainer::get_line_count() const { return cached_line_count; } +void FlowContainer::set_vertical(bool p_vertical) { + ERR_FAIL_COND_MSG(is_fixed, "Can't change orientation of " + get_class() + "."); + vertical = p_vertical; + update_minimum_size(); + _resort(); +} + +bool FlowContainer::is_vertical() const { + return vertical; +} + FlowContainer::FlowContainer(bool p_vertical) { vertical = p_vertical; } void FlowContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_line_count"), &FlowContainer::get_line_count); + + ClassDB::bind_method(D_METHOD("set_vertical", "vertical"), &FlowContainer::set_vertical); + ClassDB::bind_method(D_METHOD("is_vertical"), &FlowContainer::is_vertical); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vertical"), "set_vertical", "is_vertical"); } diff --git a/scene/gui/flow_container.h b/scene/gui/flow_container.h index a2da43e071..536df27ad6 100644 --- a/scene/gui/flow_container.h +++ b/scene/gui/flow_container.h @@ -42,16 +42,28 @@ private: bool vertical = false; + struct ThemeCache { + int h_separation = 0; + int v_separation = 0; + } theme_cache; + void _resort(); protected: - void _notification(int p_what); + bool is_fixed = false; + + virtual void _update_theme_item_cache() override; + void _notification(int p_what); + void _validate_property(PropertyInfo &p_property) const; static void _bind_methods(); public: int get_line_count() const; + void set_vertical(bool p_vertical); + bool is_vertical() const; + virtual Size2 get_minimum_size() const override; virtual Vector<int> get_allowed_size_flags_horizontal() const override; @@ -65,7 +77,7 @@ class HFlowContainer : public FlowContainer { public: HFlowContainer() : - FlowContainer(false) {} + FlowContainer(false) { is_fixed = true; } }; class VFlowContainer : public FlowContainer { @@ -73,7 +85,7 @@ class VFlowContainer : public FlowContainer { public: VFlowContainer() : - FlowContainer(true) {} + FlowContainer(true) { is_fixed = true; } }; #endif // FLOW_CONTAINER_H diff --git a/scene/gui/gradient_edit.cpp b/scene/gui/gradient_edit.cpp deleted file mode 100644 index 9459bed63b..0000000000 --- a/scene/gui/gradient_edit.cpp +++ /dev/null @@ -1,442 +0,0 @@ -/*************************************************************************/ -/* gradient_edit.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "gradient_edit.h" - -#include "core/os/keyboard.h" - -GradientEdit::GradientEdit() { - set_focus_mode(FOCUS_ALL); - - popup = memnew(PopupPanel); - picker = memnew(ColorPicker); - popup->add_child(picker); - - gradient_cache.instantiate(); - preview_texture.instantiate(); - - preview_texture->set_width(1024); - add_child(popup, false, INTERNAL_MODE_FRONT); -} - -int GradientEdit::_get_point_from_pos(int x) { - int result = -1; - int total_w = get_size().width - get_size().height - draw_spacing; - float min_distance = 1e20; - for (int i = 0; i < points.size(); i++) { - // Check if we clicked at point. - float distance = ABS(x - points[i].offset * total_w); - float min = (draw_point_width / 2 * 1.7); //make it easier to grab - if (distance <= min && distance < min_distance) { - result = i; - min_distance = distance; - } - } - return result; -} - -void GradientEdit::_show_color_picker() { - if (grabbed == -1) { - return; - } - picker->set_pick_color(points[grabbed].color); - Size2 minsize = popup->get_contents_minimum_size(); - bool show_above = false; - if (get_global_position().y + get_size().y + minsize.y > get_viewport_rect().size.y) { - show_above = true; - } - if (show_above) { - popup->set_position(get_screen_position() - Vector2(0, minsize.y)); - } else { - popup->set_position(get_screen_position() + Vector2(0, get_size().y)); - } - popup->popup(); -} - -GradientEdit::~GradientEdit() { -} - -void GradientEdit::gui_input(const Ref<InputEvent> &p_event) { - ERR_FAIL_COND(p_event.is_null()); - - Ref<InputEventKey> k = p_event; - - if (k.is_valid() && k->is_pressed() && k->get_keycode() == Key::KEY_DELETE && grabbed != -1) { - points.remove_at(grabbed); - grabbed = -1; - grabbing = false; - update(); - emit_signal(SNAME("ramp_changed")); - accept_event(); - } - - Ref<InputEventMouseButton> mb = p_event; - // Show color picker on double click. - if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_double_click() && mb->is_pressed()) { - grabbed = _get_point_from_pos(mb->get_position().x); - _show_color_picker(); - accept_event(); - } - - // Delete point on right click. - if (mb.is_valid() && mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) { - grabbed = _get_point_from_pos(mb->get_position().x); - if (grabbed != -1) { - points.remove_at(grabbed); - grabbed = -1; - grabbing = false; - update(); - emit_signal(SNAME("ramp_changed")); - accept_event(); - } - } - - // Hold alt key to duplicate selected color. - if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed() && mb->is_alt_pressed()) { - int x = mb->get_position().x; - grabbed = _get_point_from_pos(x); - - if (grabbed != -1) { - int total_w = get_size().width - get_size().height - draw_spacing; - Gradient::Point new_point = points[grabbed]; - new_point.offset = CLAMP(x / float(total_w), 0, 1); - - points.push_back(new_point); - points.sort(); - for (int i = 0; i < points.size(); ++i) { - if (points[i].offset == new_point.offset) { - grabbed = i; - break; - } - } - - emit_signal(SNAME("ramp_changed")); - update(); - } - } - - // Select. - if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) { - update(); - int x = mb->get_position().x; - int total_w = get_size().width - get_size().height - draw_spacing; - - //Check if color selector was clicked. - if (x > total_w + draw_spacing) { - _show_color_picker(); - return; - } - - grabbing = true; - - grabbed = _get_point_from_pos(x); - //grab or select - if (grabbed != -1) { - return; - } - - // Insert point. - Gradient::Point new_point; - new_point.offset = CLAMP(x / float(total_w), 0, 1); - - Gradient::Point prev; - Gradient::Point next; - - int pos = -1; - for (int i = 0; i < points.size(); i++) { - if (points[i].offset < new_point.offset) { - pos = i; - } - } - - if (pos == -1) { - prev.color = Color(0, 0, 0); - prev.offset = 0; - if (points.size()) { - next = points[0]; - } else { - next.color = Color(1, 1, 1); - next.offset = 1.0; - } - } else { - if (pos == points.size() - 1) { - next.color = Color(1, 1, 1); - next.offset = 1.0; - } else { - next = points[pos + 1]; - } - prev = points[pos]; - } - - new_point.color = prev.color.lerp(next.color, (new_point.offset - prev.offset) / (next.offset - prev.offset)); - - points.push_back(new_point); - points.sort(); - for (int i = 0; i < points.size(); i++) { - if (points[i].offset == new_point.offset) { - grabbed = i; - break; - } - } - - emit_signal(SNAME("ramp_changed")); - } - - if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && !mb->is_pressed()) { - if (grabbing) { - grabbing = false; - emit_signal(SNAME("ramp_changed")); - } - update(); - } - - Ref<InputEventMouseMotion> mm = p_event; - - if (mm.is_valid() && grabbing) { - int total_w = get_size().width - get_size().height - draw_spacing; - - int x = mm->get_position().x; - - float newofs = CLAMP(x / float(total_w), 0, 1); - - // Snap to "round" coordinates if holding Ctrl. - // Be more precise if holding Shift as well. - if (mm->is_ctrl_pressed()) { - newofs = Math::snapped(newofs, mm->is_shift_pressed() ? 0.025 : 0.1); - } else if (mm->is_shift_pressed()) { - // Snap to nearest point if holding just Shift - const float snap_threshold = 0.03; - float smallest_ofs = snap_threshold; - bool found = false; - int nearest_point = 0; - for (int i = 0; i < points.size(); ++i) { - if (i != grabbed) { - float temp_ofs = ABS(points[i].offset - newofs); - if (temp_ofs < smallest_ofs) { - smallest_ofs = temp_ofs; - nearest_point = i; - if (found) { - break; - } - found = true; - } - } - } - if (found) { - if (points[nearest_point].offset < newofs) { - newofs = points[nearest_point].offset + 0.00001; - } else { - newofs = points[nearest_point].offset - 0.00001; - } - newofs = CLAMP(newofs, 0, 1); - } - } - - bool valid = true; - for (int i = 0; i < points.size(); i++) { - if (points[i].offset == newofs && i != grabbed) { - valid = false; - break; - } - } - - if (!valid || grabbed == -1) { - return; - } - points.write[grabbed].offset = newofs; - - points.sort(); - for (int i = 0; i < points.size(); i++) { - if (points[i].offset == newofs) { - grabbed = i; - break; - } - } - - emit_signal(SNAME("ramp_changed")); - - update(); - } -} - -void GradientEdit::_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_ENTER_TREE: { - if (!picker->is_connected("color_changed", callable_mp(this, &GradientEdit::_color_changed))) { - picker->connect("color_changed", callable_mp(this, &GradientEdit::_color_changed)); - } - [[fallthrough]]; - } - case NOTIFICATION_THEME_CHANGED: { - draw_spacing = BASE_SPACING * get_theme_default_base_scale(); - draw_point_width = BASE_POINT_WIDTH * get_theme_default_base_scale(); - } break; - - case NOTIFICATION_DRAW: { - int w = get_size().x; - int h = get_size().y; - - if (w == 0 || h == 0) { - return; // Safety check. We have division by 'h'. And in any case there is nothing to draw with such size. - } - - int total_w = get_size().width - get_size().height - draw_spacing; - - // Draw checker pattern for ramp. - draw_texture_rect(get_theme_icon(SNAME("GuiMiniCheckerboard"), SNAME("EditorIcons")), Rect2(0, 0, total_w, h), true); - - // Draw color ramp. - gradient_cache->set_points(points); - gradient_cache->set_interpolation_mode(interpolation_mode); - preview_texture->set_gradient(gradient_cache); - draw_texture_rect(preview_texture, Rect2(0, 0, total_w, h)); - - // Draw point markers. - for (int i = 0; i < points.size(); i++) { - Color col = points[i].color.inverted(); - col.a = 0.9; - - draw_line(Vector2(points[i].offset * total_w, 0), Vector2(points[i].offset * total_w, h / 2), col); - Rect2 rect = Rect2(points[i].offset * total_w - draw_point_width / 2, h / 2, draw_point_width, h / 2); - draw_rect(rect, points[i].color, true); - draw_rect(rect, col, false); - if (grabbed == i) { - rect = rect.grow(-1); - if (has_focus()) { - draw_rect(rect, Color(1, 0, 0, 0.9), false); - } else { - draw_rect(rect, Color(0.6, 0, 0, 0.9), false); - } - - rect = rect.grow(-1); - draw_rect(rect, col, false); - } - } - - // Draw "button" for color selector. - draw_texture_rect(get_theme_icon(SNAME("GuiMiniCheckerboard"), SNAME("EditorIcons")), Rect2(total_w + draw_spacing, 0, h, h), true); - if (grabbed != -1) { - // Draw with selection color. - draw_rect(Rect2(total_w + draw_spacing, 0, h, h), points[grabbed].color); - } else { - // If no color selected draw grey color with 'X' on top. - draw_rect(Rect2(total_w + draw_spacing, 0, h, h), Color(0.5, 0.5, 0.5, 1)); - draw_line(Vector2(total_w + draw_spacing, 0), Vector2(total_w + draw_spacing + h, h), Color(1, 1, 1, 0.6)); - draw_line(Vector2(total_w + draw_spacing, h), Vector2(total_w + draw_spacing + h, 0), Color(1, 1, 1, 0.6)); - } - - // Draw borders around color ramp if in focus. - if (has_focus()) { - draw_line(Vector2(-1, -1), Vector2(total_w + 1, -1), Color(1, 1, 1, 0.6)); - draw_line(Vector2(total_w + 1, -1), Vector2(total_w + 1, h + 1), Color(1, 1, 1, 0.6)); - draw_line(Vector2(total_w + 1, h + 1), Vector2(-1, h + 1), Color(1, 1, 1, 0.6)); - draw_line(Vector2(-1, -1), Vector2(-1, h + 1), Color(1, 1, 1, 0.6)); - } - } break; - - case NOTIFICATION_VISIBILITY_CHANGED: { - if (!is_visible()) { - grabbing = false; - } - } break; - } -} - -Size2 GradientEdit::get_minimum_size() const { - return Vector2(0, 16); -} - -void GradientEdit::_color_changed(const Color &p_color) { - if (grabbed == -1) { - return; - } - points.write[grabbed].color = p_color; - update(); - emit_signal(SNAME("ramp_changed")); -} - -void GradientEdit::set_ramp(const Vector<float> &p_offsets, const Vector<Color> &p_colors) { - ERR_FAIL_COND(p_offsets.size() != p_colors.size()); - points.clear(); - for (int i = 0; i < p_offsets.size(); i++) { - Gradient::Point p; - p.offset = p_offsets[i]; - p.color = p_colors[i]; - points.push_back(p); - } - - points.sort(); - update(); -} - -Vector<float> GradientEdit::get_offsets() const { - Vector<float> ret; - for (int i = 0; i < points.size(); i++) { - ret.push_back(points[i].offset); - } - return ret; -} - -Vector<Color> GradientEdit::get_colors() const { - Vector<Color> ret; - for (int i = 0; i < points.size(); i++) { - ret.push_back(points[i].color); - } - return ret; -} - -void GradientEdit::set_points(Vector<Gradient::Point> &p_points) { - if (points.size() != p_points.size()) { - grabbed = -1; - } - points.clear(); - points = p_points; - points.sort(); -} - -Vector<Gradient::Point> &GradientEdit::get_points() { - return points; -} - -void GradientEdit::set_interpolation_mode(Gradient::InterpolationMode p_interp_mode) { - interpolation_mode = p_interp_mode; -} - -Gradient::InterpolationMode GradientEdit::get_interpolation_mode() { - return interpolation_mode; -} - -ColorPicker *GradientEdit::get_picker() { - return picker; -} - -void GradientEdit::_bind_methods() { - ADD_SIGNAL(MethodInfo("ramp_changed")); -} diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index c219eafbf7..7295ab9e9d 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -176,7 +176,7 @@ void GraphEditMinimap::gui_input(const Ref<InputEvent> &p_ev) { new_minimap_size.y = MIN(get_size().y - mm->get_relative().y, ge->get_size().y - 2.0 * minimap_padding.y); ge->set_minimap_size(new_minimap_size); - update(); + queue_redraw(); } else { Vector2 click_position = _convert_to_graph_position(mm->get_position() - minimap_padding) - graph_padding; _adjust_graph_scroll(click_position); @@ -190,6 +190,14 @@ void GraphEditMinimap::_adjust_graph_scroll(const Vector2 &p_offset) { ge->set_scroll_ofs(p_offset + graph_offset - camera_size / 2); } +PackedStringArray GraphEdit::get_configuration_warnings() const { + PackedStringArray warnings = Control::get_configuration_warnings(); + + warnings.push_back(RTR("Please be aware that GraphEdit and GraphNode will undergo extensive refactoring in a future beta version involving compatibility-breaking API changes.")); + + return warnings; +} + Error GraphEdit::connect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port) { if (is_node_connected(p_from, p_from_port, p_to, p_to_port)) { return OK; @@ -201,10 +209,10 @@ Error GraphEdit::connect_node(const StringName &p_from, int p_from_port, const S c.to_port = p_to_port; c.activity = 0; connections.push_back(c); - top_layer->update(); - minimap->update(); - update(); - connections_layer->update(); + top_layer->queue_redraw(); + minimap->queue_redraw(); + queue_redraw(); + connections_layer->queue_redraw(); return OK; } @@ -223,10 +231,10 @@ void GraphEdit::disconnect_node(const StringName &p_from, int p_from_port, const for (const List<Connection>::Element *E = connections.front(); E; E = E->next()) { 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) { connections.erase(E); - top_layer->update(); - minimap->update(); - update(); - connections_layer->update(); + top_layer->queue_redraw(); + minimap->queue_redraw(); + queue_redraw(); + connections_layer->queue_redraw(); return; } } @@ -253,9 +261,9 @@ void GraphEdit::_scroll_moved(double) { call_deferred(SNAME("_update_scroll_offset")); awaiting_scroll_offset_update = true; } - top_layer->update(); - minimap->update(); - update(); + top_layer->queue_redraw(); + minimap->queue_redraw(); + queue_redraw(); if (!setting_scroll_ofs) { //in godot, signals on change value are avoided as a convention emit_signal(SNAME("scroll_offset_changed"), get_scroll_ofs()); @@ -351,27 +359,40 @@ void GraphEdit::_graph_node_raised(Node *p_gn) { if (gn->is_comment()) { move_child(gn, 0); } else { - gn->raise(); + gn->move_to_front(); } - emit_signal(SNAME("node_selected"), p_gn); +} + +void GraphEdit::_graph_node_selected(Node *p_gn) { + GraphNode *gn = Object::cast_to<GraphNode>(p_gn); + ERR_FAIL_COND(!gn); + + emit_signal(SNAME("node_selected"), gn); +} + +void GraphEdit::_graph_node_deselected(Node *p_gn) { + GraphNode *gn = Object::cast_to<GraphNode>(p_gn); + ERR_FAIL_COND(!gn); + + emit_signal(SNAME("node_deselected"), gn); } void GraphEdit::_graph_node_moved(Node *p_gn) { GraphNode *gn = Object::cast_to<GraphNode>(p_gn); ERR_FAIL_COND(!gn); - top_layer->update(); - minimap->update(); - update(); - connections_layer->update(); + top_layer->queue_redraw(); + minimap->queue_redraw(); + queue_redraw(); + connections_layer->queue_redraw(); } void GraphEdit::_graph_node_slot_updated(int p_index, Node *p_gn) { GraphNode *gn = Object::cast_to<GraphNode>(p_gn); ERR_FAIL_COND(!gn); - top_layer->update(); - minimap->update(); - update(); - connections_layer->update(); + top_layer->queue_redraw(); + minimap->queue_redraw(); + queue_redraw(); + connections_layer->queue_redraw(); } void GraphEdit::add_child_notify(Node *p_child) { @@ -382,11 +403,13 @@ void GraphEdit::add_child_notify(Node *p_child) { GraphNode *gn = Object::cast_to<GraphNode>(p_child); if (gn) { gn->set_scale(Vector2(zoom, zoom)); - gn->connect("position_offset_changed", callable_mp(this, &GraphEdit::_graph_node_moved), varray(gn)); - gn->connect("slot_updated", callable_mp(this, &GraphEdit::_graph_node_slot_updated), varray(gn)); - gn->connect("raise_request", callable_mp(this, &GraphEdit::_graph_node_raised), varray(gn)); - gn->connect("item_rect_changed", callable_mp((CanvasItem *)connections_layer, &CanvasItem::update)); - gn->connect("item_rect_changed", callable_mp((CanvasItem *)minimap, &GraphEditMinimap::update)); + gn->connect("position_offset_changed", callable_mp(this, &GraphEdit::_graph_node_moved).bind(gn)); + gn->connect("selected", callable_mp(this, &GraphEdit::_graph_node_selected).bind(gn)); + gn->connect("deselected", callable_mp(this, &GraphEdit::_graph_node_deselected).bind(gn)); + gn->connect("slot_updated", callable_mp(this, &GraphEdit::_graph_node_slot_updated).bind(gn)); + gn->connect("raise_request", callable_mp(this, &GraphEdit::_graph_node_raised).bind(gn)); + gn->connect("item_rect_changed", callable_mp((CanvasItem *)connections_layer, &CanvasItem::queue_redraw)); + gn->connect("item_rect_changed", callable_mp((CanvasItem *)minimap, &GraphEditMinimap::queue_redraw)); _graph_node_moved(gn); gn->set_mouse_filter(MOUSE_FILTER_PASS); } @@ -409,15 +432,17 @@ void GraphEdit::remove_child_notify(Node *p_child) { GraphNode *gn = Object::cast_to<GraphNode>(p_child); if (gn) { gn->disconnect("position_offset_changed", callable_mp(this, &GraphEdit::_graph_node_moved)); + gn->disconnect("selected", callable_mp(this, &GraphEdit::_graph_node_selected)); + gn->disconnect("deselected", callable_mp(this, &GraphEdit::_graph_node_deselected)); gn->disconnect("slot_updated", callable_mp(this, &GraphEdit::_graph_node_slot_updated)); gn->disconnect("raise_request", callable_mp(this, &GraphEdit::_graph_node_raised)); // In case of the whole GraphEdit being destroyed these references can already be freed. if (connections_layer != nullptr && connections_layer->is_inside_tree()) { - gn->disconnect("item_rect_changed", callable_mp((CanvasItem *)connections_layer, &CanvasItem::update)); + gn->disconnect("item_rect_changed", callable_mp((CanvasItem *)connections_layer, &CanvasItem::queue_redraw)); } if (minimap != nullptr && minimap->is_inside_tree()) { - gn->disconnect("item_rect_changed", callable_mp((CanvasItem *)minimap, &GraphEditMinimap::update)); + gn->disconnect("item_rect_changed", callable_mp((CanvasItem *)minimap, &GraphEditMinimap::queue_redraw)); } } } @@ -500,8 +525,8 @@ void GraphEdit::_notification(int p_what) { case NOTIFICATION_RESIZED: { _update_scroll(); - top_layer->update(); - minimap->update(); + top_layer->queue_redraw(); + minimap->queue_redraw(); } break; } } @@ -607,13 +632,16 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { connecting_color = Object::cast_to<GraphNode>(to)->get_connection_input_color(E.to_port); connecting_target = false; connecting_to = pos; - just_disconnected = true; - emit_signal(SNAME("disconnection_request"), E.from, E.from_port, E.to, E.to_port); - to = get_node(String(connecting_from)); //maybe it was erased - if (Object::cast_to<GraphNode>(to)) { - connecting = true; - emit_signal(SNAME("connection_drag_started"), connecting_from, connecting_index, false); + if (connecting_type >= 0) { + just_disconnected = true; + + emit_signal(SNAME("disconnection_request"), E.from, E.from_port, E.to, E.to_port); + to = get_node(String(connecting_from)); //maybe it was erased + if (Object::cast_to<GraphNode>(to)) { + connecting = true; + emit_signal(SNAME("connection_drag_started"), connecting_from, connecting_index, false); + } } return; } @@ -621,7 +649,6 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { } } - connecting = true; connecting_from = gn->get_name(); connecting_index = j; connecting_out = true; @@ -629,8 +656,11 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { connecting_color = gn->get_connection_output_color(j); connecting_target = false; connecting_to = pos; - just_disconnected = false; - emit_signal(SNAME("connection_drag_started"), connecting_from, connecting_index, true); + if (connecting_type >= 0) { + connecting = true; + just_disconnected = false; + emit_signal(SNAME("connection_drag_started"), connecting_from, connecting_index, true); + } return; } } @@ -657,11 +687,13 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { connecting_to = pos; just_disconnected = true; - emit_signal(SNAME("disconnection_request"), E.from, E.from_port, E.to, E.to_port); - fr = get_node(String(connecting_from)); //maybe it was erased - if (Object::cast_to<GraphNode>(fr)) { - connecting = true; - emit_signal(SNAME("connection_drag_started"), connecting_from, connecting_index, true); + if (connecting_type >= 0) { + emit_signal(SNAME("disconnection_request"), E.from, E.from_port, E.to, E.to_port); + fr = get_node(String(connecting_from)); //maybe it was erased + if (Object::cast_to<GraphNode>(fr)) { + connecting = true; + emit_signal(SNAME("connection_drag_started"), connecting_from, connecting_index, true); + } } return; } @@ -669,7 +701,6 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { } } - connecting = true; connecting_from = gn->get_name(); connecting_index = j; connecting_out = false; @@ -677,8 +708,11 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { connecting_color = gn->get_connection_input_color(j); connecting_target = false; connecting_to = pos; - just_disconnected = false; - emit_signal(SNAME("connection_drag_started"), connecting_from, connecting_index, false); + if (connecting_type >= 0) { + connecting = true; + just_disconnected = false; + emit_signal(SNAME("connection_drag_started"), connecting_from, connecting_index, false); + } return; } } @@ -689,8 +723,8 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { if (mm.is_valid() && connecting) { connecting_to = mm->get_position(); connecting_target = false; - top_layer->update(); - minimap->update(); + top_layer->queue_redraw(); + minimap->queue_redraw(); connecting_valid = just_disconnected || click_pos.distance_to(connecting_to / zoom) > 20.0; if (connecting_valid) { @@ -710,6 +744,9 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { int type = gn->get_connection_output_type(j); if ((type == connecting_type || valid_connection_types.has(ConnType(connecting_type, type))) && is_in_output_hotzone(gn, j, mpos, port_size)) { + if (!is_node_hover_valid(gn->get_name(), j, connecting_from, connecting_index)) { + continue; + } connecting_target = true; connecting_to = pos; connecting_target_to = gn->get_name(); @@ -725,6 +762,9 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { int type = gn->get_connection_input_type(j); if ((type == connecting_type || valid_connection_types.has(ConnType(connecting_type, type))) && is_in_input_hotzone(gn, j, mpos, port_size)) { + if (!is_node_hover_valid(connecting_from, connecting_index, gn->get_name(), j)) { + continue; + } connecting_target = true; connecting_to = pos; connecting_target_to = gn->get_name(); @@ -741,25 +781,25 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { if (connecting_valid) { if (connecting && connecting_target) { String from = connecting_from; - int from_slot = connecting_index; + int from_port = connecting_index; String to = connecting_target_to; - int to_slot = connecting_target_index; + int to_port = connecting_target_index; if (!connecting_out) { SWAP(from, to); - SWAP(from_slot, to_slot); + SWAP(from_port, to_port); } - emit_signal(SNAME("connection_request"), from, from_slot, to, to_slot); + emit_signal(SNAME("connection_request"), from, from_port, to, to_port); } else if (!just_disconnected) { String from = connecting_from; - int from_slot = connecting_index; + int from_port = connecting_index; Vector2 ofs = mb->get_position(); if (!connecting_out) { - emit_signal(SNAME("connection_from_empty"), from, from_slot, ofs); + emit_signal(SNAME("connection_from_empty"), from, from_port, ofs); } else { - emit_signal(SNAME("connection_to_empty"), from, from_slot, ofs); + emit_signal(SNAME("connection_to_empty"), from, from_port, ofs); } } } @@ -798,22 +838,22 @@ bool GraphEdit::_check_clickable_control(Control *p_control, const Vector2 &mpos } } -bool GraphEdit::is_in_input_hotzone(GraphNode *p_graph_node, int p_slot_index, const Vector2 &p_mouse_pos, const Vector2i &p_port_size) { +bool GraphEdit::is_in_input_hotzone(GraphNode *p_node, int p_port, const Vector2 &p_mouse_pos, const Vector2i &p_port_size) { bool success; - if (GDVIRTUAL_CALL(_is_in_input_hotzone, p_graph_node, p_slot_index, p_mouse_pos, success)) { + if (GDVIRTUAL_CALL(_is_in_input_hotzone, p_node, p_port, p_mouse_pos, success)) { return success; } else { - Vector2 pos = p_graph_node->get_connection_input_position(p_slot_index) + p_graph_node->get_position(); + Vector2 pos = p_node->get_connection_input_position(p_port) + p_node->get_position(); return is_in_port_hotzone(pos / zoom, p_mouse_pos, p_port_size, true); } } -bool GraphEdit::is_in_output_hotzone(GraphNode *p_graph_node, int p_slot_index, const Vector2 &p_mouse_pos, const Vector2i &p_port_size) { +bool GraphEdit::is_in_output_hotzone(GraphNode *p_node, int p_port, const Vector2 &p_mouse_pos, const Vector2i &p_port_size) { bool success; - if (GDVIRTUAL_CALL(_is_in_output_hotzone, p_graph_node, p_slot_index, p_mouse_pos, success)) { + if (GDVIRTUAL_CALL(_is_in_output_hotzone, p_node, p_port, p_mouse_pos, success)) { return success; } else { - Vector2 pos = p_graph_node->get_connection_output_position(p_slot_index) + p_graph_node->get_position(); + Vector2 pos = p_node->get_connection_output_position(p_port) + p_node->get_position(); return is_in_port_hotzone(pos / zoom, p_mouse_pos, p_port_size, false); } } @@ -1068,11 +1108,11 @@ void GraphEdit::_minimap_draw() { continue; } - Vector2 from_slot_position = gfrom->get_position_offset() * zoom + gfrom->get_connection_output_position(E.from_port); - Vector2 from_position = minimap->_convert_from_graph_position(from_slot_position - graph_offset) + minimap_offset; + Vector2 from_port_position = gfrom->get_position_offset() * zoom + gfrom->get_connection_output_position(E.from_port); + Vector2 from_position = minimap->_convert_from_graph_position(from_port_position - graph_offset) + minimap_offset; Color from_color = gfrom->get_connection_output_color(E.from_port); - Vector2 to_slot_position = gto->get_position_offset() * zoom + gto->get_connection_input_position(E.to_port); - Vector2 to_position = minimap->_convert_from_graph_position(to_slot_position - graph_offset) + minimap_offset; + Vector2 to_port_position = gto->get_position_offset() * zoom + gto->get_connection_input_position(E.to_port); + Vector2 to_position = minimap->_convert_from_graph_position(to_port_position - graph_offset) + minimap_offset; Color to_color = gto->get_connection_input_color(E.to_port); if (E.activity > 0) { @@ -1121,7 +1161,7 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) { drag_accum += mm->get_relative(); for (int i = get_child_count() - 1; i >= 0; i--) { GraphNode *gn = Object::cast_to<GraphNode>(get_child(i)); - if (gn && gn->is_selected()) { + if (gn && gn->is_selected() && gn->is_draggable()) { Vector2 pos = (gn->get_drag_from() * zoom + drag_accum) / zoom; // Snapping can be toggled temporarily by holding down Ctrl. @@ -1155,25 +1195,14 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) { bool in_box = r.intersects(box_selecting_rect); if (in_box) { - if (!gn->is_selected() && box_selection_mode_additive) { - emit_signal(SNAME("node_selected"), gn); - } else if (gn->is_selected() && !box_selection_mode_additive) { - emit_signal(SNAME("node_deselected"), gn); - } gn->set_selected(box_selection_mode_additive); } else { - bool select = (previous_selected.find(gn) != nullptr); - if (gn->is_selected() && !select) { - emit_signal(SNAME("node_deselected"), gn); - } else if (!gn->is_selected() && select) { - emit_signal(SNAME("node_selected"), gn); - } - gn->set_selected(select); + gn->set_selected(previous_selected.find(gn) != nullptr); } } - top_layer->update(); - minimap->update(); + top_layer->queue_redraw(); + minimap->queue_redraw(); } Ref<InputEventMouseButton> b = p_ev; @@ -1187,16 +1216,10 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) { continue; } - bool select = (previous_selected.find(gn) != nullptr); - if (gn->is_selected() && !select) { - emit_signal(SNAME("node_deselected"), gn); - } else if (!gn->is_selected() && select) { - emit_signal(SNAME("node_selected"), gn); - } - gn->set_selected(select); + gn->set_selected(previous_selected.find(gn) != nullptr); } - top_layer->update(); - minimap->update(); + top_layer->queue_redraw(); + minimap->queue_redraw(); } else { if (connecting) { force_connection_drag_end(); @@ -1216,7 +1239,6 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) { Rect2 r = gn->get_rect(); r.size *= zoom; if (r.has_point(b->get_position())) { - emit_signal(SNAME("node_deselected"), gn); gn->set_selected(false); } } @@ -1242,27 +1264,30 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) { dragging = false; - top_layer->update(); - minimap->update(); - update(); - connections_layer->update(); + top_layer->queue_redraw(); + minimap->queue_redraw(); + queue_redraw(); + connections_layer->queue_redraw(); } if (b->get_button_index() == MouseButton::LEFT && b->is_pressed()) { GraphNode *gn = nullptr; + // Find node which was clicked on. for (int i = get_child_count() - 1; i >= 0; i--) { GraphNode *gn_selected = Object::cast_to<GraphNode>(get_child(i)); - if (gn_selected) { - if (gn_selected->is_resizing()) { - continue; - } + if (!gn_selected) { + continue; + } - if (gn_selected->has_point((b->get_position() - gn_selected->get_position()) / zoom)) { - gn = gn_selected; - break; - } + if (gn_selected->is_resizing()) { + continue; + } + + if (gn_selected->has_point((b->get_position() - gn_selected->get_position()) / zoom)) { + gn = gn_selected; + break; } } @@ -1271,22 +1296,18 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) { return; } + // Left-clicked on a node, select it. dragging = true; drag_accum = Vector2(); just_selected = !gn->is_selected(); if (!gn->is_selected() && !Input::get_singleton()->is_key_pressed(Key::CTRL)) { for (int i = 0; i < get_child_count(); i++) { GraphNode *o_gn = Object::cast_to<GraphNode>(get_child(i)); - if (o_gn) { - if (o_gn == gn) { - o_gn->set_selected(true); - } else { - if (o_gn->is_selected()) { - emit_signal(SNAME("node_deselected"), o_gn); - } - o_gn->set_selected(false); - } + if (!o_gn) { + continue; } + + o_gn->set_selected(o_gn == gn); } } @@ -1313,6 +1334,7 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) { return; } + // Left-clicked on empty space, start box select. box_selecting = true; box_selecting_from = b->get_position(); if (b->is_ctrl_pressed()) { @@ -1345,9 +1367,7 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) { if (!gn2) { continue; } - if (gn2->is_selected()) { - emit_signal(SNAME("node_deselected"), gn2); - } + gn2->set_selected(false); } } @@ -1355,11 +1375,12 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) { } if (b->get_button_index() == MouseButton::LEFT && !b->is_pressed() && box_selecting) { + // Box selection ended. Nodes were selected during mouse movement. box_selecting = false; box_selecting_rect = Rect2(); previous_selected.clear(); - top_layer->update(); - minimap->update(); + top_layer->queue_redraw(); + minimap->queue_redraw(); } } @@ -1425,9 +1446,9 @@ void GraphEdit::set_connection_activity(const StringName &p_from, int p_from_por if (E.from == p_from && E.from_port == p_from_port && E.to == p_to && E.to_port == p_to_port) { if (Math::is_equal_approx(E.activity, p_activity)) { //update only if changed - top_layer->update(); - minimap->update(); - connections_layer->update(); + top_layer->queue_redraw(); + minimap->queue_redraw(); + connections_layer->queue_redraw(); } E.activity = p_activity; return; @@ -1437,22 +1458,30 @@ void GraphEdit::set_connection_activity(const StringName &p_from, int p_from_por void GraphEdit::clear_connections() { connections.clear(); - minimap->update(); - update(); - connections_layer->update(); + minimap->queue_redraw(); + queue_redraw(); + connections_layer->queue_redraw(); } void GraphEdit::force_connection_drag_end() { ERR_FAIL_COND_MSG(!connecting, "Drag end requested without active drag!"); connecting = false; connecting_valid = false; - top_layer->update(); - minimap->update(); - update(); - connections_layer->update(); + top_layer->queue_redraw(); + minimap->queue_redraw(); + queue_redraw(); + connections_layer->queue_redraw(); emit_signal(SNAME("connection_drag_ended")); } +bool GraphEdit::is_node_hover_valid(const StringName &p_from, const int p_from_port, const StringName &p_to, const int p_to_port) { + bool valid; + if (GDVIRTUAL_CALL(_is_node_hover_valid, p_from, p_from_port, p_to, p_to_port, valid)) { + return valid; + } + return true; +} + void GraphEdit::set_panning_scheme(PanningScheme p_scheme) { panning_scheme = p_scheme; panner->set_control_scheme((ViewPanner::ControlScheme)p_scheme); @@ -1475,14 +1504,14 @@ void GraphEdit::set_zoom_custom(float p_zoom, const Vector2 &p_center) { Vector2 sbofs = (Vector2(h_scroll->get_value(), v_scroll->get_value()) + p_center) / zoom; zoom = p_zoom; - top_layer->update(); + top_layer->queue_redraw(); zoom_minus->set_disabled(zoom == zoom_min); zoom_plus->set_disabled(zoom == zoom_max); _update_scroll(); - minimap->update(); - connections_layer->update(); + minimap->queue_redraw(); + connections_layer->queue_redraw(); if (is_visible_in_tree()) { Vector2 ofs = sbofs * zoom - p_center; @@ -1491,7 +1520,7 @@ void GraphEdit::set_zoom_custom(float p_zoom, const Vector2 &p_center) { } _update_zoom_label(); - update(); + queue_redraw(); } float GraphEdit::get_zoom() const { @@ -1577,10 +1606,10 @@ void GraphEdit::remove_valid_left_disconnect_type(int p_type) { valid_left_disconnect_types.erase(p_type); } -Array GraphEdit::_get_connection_list() const { +TypedArray<Dictionary> GraphEdit::_get_connection_list() const { List<Connection> conns; get_connection_list(&conns); - Array arr; + TypedArray<Dictionary> arr; for (const Connection &E : conns) { Dictionary d; d["from"] = E.from; @@ -1626,8 +1655,11 @@ bool GraphEdit::is_valid_connection_type(int p_type, int p_with_type) const { } void GraphEdit::set_use_snap(bool p_enable) { + if (snap_button->is_pressed() == p_enable) { + return; + } snap_button->set_pressed(p_enable); - update(); + queue_redraw(); } bool GraphEdit::is_using_snap() const { @@ -1641,15 +1673,15 @@ int GraphEdit::get_snap() const { void GraphEdit::set_snap(int p_snap) { ERR_FAIL_COND(p_snap < 5); snap_amount->set_value(p_snap); - update(); + queue_redraw(); } void GraphEdit::_snap_toggled() { - update(); + queue_redraw(); } void GraphEdit::_snap_value_changed(double) { - update(); + queue_redraw(); } void GraphEdit::set_minimap_size(Vector2 p_size) { @@ -1661,7 +1693,7 @@ void GraphEdit::set_minimap_size(Vector2 p_size) { minimap->set_offset(Side::SIDE_TOP, -minimap_size.y - MINIMAP_OFFSET); minimap->set_offset(Side::SIDE_RIGHT, -MINIMAP_OFFSET); minimap->set_offset(Side::SIDE_BOTTOM, -MINIMAP_OFFSET); - minimap->update(); + minimap->queue_redraw(); } Vector2 GraphEdit::get_minimap_size() const { @@ -1669,8 +1701,11 @@ Vector2 GraphEdit::get_minimap_size() const { } void GraphEdit::set_minimap_opacity(float p_opacity) { + if (minimap->get_modulate().a == p_opacity) { + return; + } minimap->set_modulate(Color(1, 1, 1, p_opacity)); - minimap->update(); + minimap->queue_redraw(); } float GraphEdit::get_minimap_opacity() const { @@ -1679,19 +1714,35 @@ float GraphEdit::get_minimap_opacity() const { } void GraphEdit::set_minimap_enabled(bool p_enable) { + if (minimap_button->is_pressed() == p_enable) { + return; + } minimap_button->set_pressed(p_enable); _minimap_toggled(); - minimap->update(); + minimap->queue_redraw(); } bool GraphEdit::is_minimap_enabled() const { return minimap_button->is_pressed(); } +void GraphEdit::set_arrange_nodes_button_hidden(bool p_enable) { + arrange_nodes_button_hidden = p_enable; + if (arrange_nodes_button_hidden) { + layout_button->hide(); + } else { + layout_button->show(); + } +} + +bool GraphEdit::is_arrange_nodes_button_hidden() const { + return arrange_nodes_button_hidden; +} + void GraphEdit::_minimap_toggled() { if (is_minimap_enabled()) { minimap->set_visible(true); - minimap->update(); + minimap->queue_redraw(); } else { minimap->set_visible(false); } @@ -1699,7 +1750,7 @@ void GraphEdit::_minimap_toggled() { void GraphEdit::set_connection_lines_curvature(float p_curvature) { lines_curvature = p_curvature; - update(); + queue_redraw(); } float GraphEdit::get_connection_lines_curvature() const { @@ -1707,8 +1758,11 @@ float GraphEdit::get_connection_lines_curvature() const { } void GraphEdit::set_connection_lines_thickness(float p_thickness) { + if (lines_thickness == p_thickness) { + return; + } lines_thickness = p_thickness; - update(); + queue_redraw(); } float GraphEdit::get_connection_lines_thickness() const { @@ -1716,8 +1770,11 @@ float GraphEdit::get_connection_lines_thickness() const { } void GraphEdit::set_connection_lines_antialiased(bool p_antialiased) { + if (lines_antialiased == p_antialiased) { + return; + } lines_antialiased = p_antialiased; - update(); + queue_redraw(); } bool GraphEdit::is_connection_lines_antialiased() const { @@ -1809,7 +1866,20 @@ HashMap<int, Vector<StringName>> GraphEdit::_layering(const HashSet<StringName> } if (!selected) { current_layer++; + uint32_t previous_size_z = z.size(); _set_operations(GraphEdit::UNION, z, u); + if (z.size() == previous_size_z) { + WARN_PRINT("Graph contains cycle(s). The cycle(s) will not be rearranged accurately."); + Vector<StringName> t; + if (l.has(0)) { + t.append_array(l[0]); + } + for (const StringName &E : p) { + t.push_back(E); + } + l.insert(0, t); + break; + } } selected = false; } @@ -2124,7 +2194,7 @@ void GraphEdit::arrange_nodes() { HashSet<StringName> s; for (List<Connection>::Element *E = connections.front(); E; E = E->next()) { GraphNode *p_from = Object::cast_to<GraphNode>(node_names[E->get().from]); - if (E->get().to == gn->get_name() && p_from->is_selected()) { + if (E->get().to == gn->get_name() && p_from->is_selected() && E->get().to != E->get().from) { if (!s.has(p_from->get_name())) { s.insert(p_from->get_name()); } @@ -2241,10 +2311,10 @@ void GraphEdit::arrange_nodes() { } void GraphEdit::_bind_methods() { - ClassDB::bind_method(D_METHOD("connect_node", "from", "from_port", "to", "to_port"), &GraphEdit::connect_node); - ClassDB::bind_method(D_METHOD("is_node_connected", "from", "from_port", "to", "to_port"), &GraphEdit::is_node_connected); - ClassDB::bind_method(D_METHOD("disconnect_node", "from", "from_port", "to", "to_port"), &GraphEdit::disconnect_node); - ClassDB::bind_method(D_METHOD("set_connection_activity", "from", "from_port", "to", "to_port", "amount"), &GraphEdit::set_connection_activity); + ClassDB::bind_method(D_METHOD("connect_node", "from_node", "from_port", "to_node", "to_port"), &GraphEdit::connect_node); + ClassDB::bind_method(D_METHOD("is_node_connected", "from_node", "from_port", "to_node", "to_port"), &GraphEdit::is_node_connected); + ClassDB::bind_method(D_METHOD("disconnect_node", "from_node", "from_port", "to_node", "to_port"), &GraphEdit::disconnect_node); + ClassDB::bind_method(D_METHOD("set_connection_activity", "from_node", "from_port", "to_node", "to_port", "amount"), &GraphEdit::set_connection_activity); ClassDB::bind_method(D_METHOD("get_connection_list"), &GraphEdit::_get_connection_list); ClassDB::bind_method(D_METHOD("clear_connections"), &GraphEdit::clear_connections); ClassDB::bind_method(D_METHOD("force_connection_drag_end"), &GraphEdit::force_connection_drag_end); @@ -2258,7 +2328,7 @@ void GraphEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("add_valid_connection_type", "from_type", "to_type"), &GraphEdit::add_valid_connection_type); ClassDB::bind_method(D_METHOD("remove_valid_connection_type", "from_type", "to_type"), &GraphEdit::remove_valid_connection_type); ClassDB::bind_method(D_METHOD("is_valid_connection_type", "from_type", "to_type"), &GraphEdit::is_valid_connection_type); - ClassDB::bind_method(D_METHOD("get_connection_line", "from", "to"), &GraphEdit::get_connection_line); + ClassDB::bind_method(D_METHOD("get_connection_line", "from_node", "to_node"), &GraphEdit::get_connection_line); ClassDB::bind_method(D_METHOD("set_panning_scheme", "scheme"), &GraphEdit::set_panning_scheme); ClassDB::bind_method(D_METHOD("get_panning_scheme"), &GraphEdit::get_panning_scheme); @@ -2301,12 +2371,15 @@ void GraphEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("set_minimap_enabled", "enable"), &GraphEdit::set_minimap_enabled); ClassDB::bind_method(D_METHOD("is_minimap_enabled"), &GraphEdit::is_minimap_enabled); + ClassDB::bind_method(D_METHOD("set_arrange_nodes_button_hidden", "enable"), &GraphEdit::set_arrange_nodes_button_hidden); + ClassDB::bind_method(D_METHOD("is_arrange_nodes_button_hidden"), &GraphEdit::is_arrange_nodes_button_hidden); + ClassDB::bind_method(D_METHOD("set_right_disconnects", "enable"), &GraphEdit::set_right_disconnects); ClassDB::bind_method(D_METHOD("is_right_disconnects_enabled"), &GraphEdit::is_right_disconnects_enabled); ClassDB::bind_method(D_METHOD("_update_scroll_offset"), &GraphEdit::_update_scroll_offset); - GDVIRTUAL_BIND(_is_in_input_hotzone, "graph_node", "slot_index", "mouse_position"); - GDVIRTUAL_BIND(_is_in_output_hotzone, "graph_node", "slot_index", "mouse_position"); + GDVIRTUAL_BIND(_is_in_input_hotzone, "in_node", "in_port", "mouse_position"); + GDVIRTUAL_BIND(_is_in_output_hotzone, "in_node", "in_port", "mouse_position"); ClassDB::bind_method(D_METHOD("get_zoom_hbox"), &GraphEdit::get_zoom_hbox); @@ -2314,7 +2387,8 @@ void GraphEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("set_selected", "node"), &GraphEdit::set_selected); - GDVIRTUAL_BIND(_get_connection_line, "from", "to") + GDVIRTUAL_BIND(_get_connection_line, "from_position", "to_position") + GDVIRTUAL_BIND(_is_node_hover_valid, "from_node", "from_port", "to_node", "to_port"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "right_disconnects"), "set_right_disconnects", "is_right_disconnects_enabled"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scroll_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_scroll_ofs", "get_scroll_ofs"); @@ -2339,21 +2413,24 @@ void GraphEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "minimap_size", PROPERTY_HINT_NONE, "suffix:px"), "set_minimap_size", "get_minimap_size"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "minimap_opacity"), "set_minimap_opacity", "get_minimap_opacity"); - ADD_SIGNAL(MethodInfo("connection_request", PropertyInfo(Variant::STRING_NAME, "from"), PropertyInfo(Variant::INT, "from_slot"), PropertyInfo(Variant::STRING_NAME, "to"), PropertyInfo(Variant::INT, "to_slot"))); - ADD_SIGNAL(MethodInfo("disconnection_request", PropertyInfo(Variant::STRING_NAME, "from"), PropertyInfo(Variant::INT, "from_slot"), PropertyInfo(Variant::STRING_NAME, "to"), PropertyInfo(Variant::INT, "to_slot"))); + ADD_GROUP("UI", ""); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "arrange_nodes_button_hidden"), "set_arrange_nodes_button_hidden", "is_arrange_nodes_button_hidden"); + + ADD_SIGNAL(MethodInfo("connection_request", PropertyInfo(Variant::STRING_NAME, "from_node"), PropertyInfo(Variant::INT, "from_port"), PropertyInfo(Variant::STRING_NAME, "to_node"), PropertyInfo(Variant::INT, "to_port"))); + ADD_SIGNAL(MethodInfo("disconnection_request", PropertyInfo(Variant::STRING_NAME, "from_node"), PropertyInfo(Variant::INT, "from_port"), PropertyInfo(Variant::STRING_NAME, "to_node"), PropertyInfo(Variant::INT, "to_port"))); ADD_SIGNAL(MethodInfo("popup_request", PropertyInfo(Variant::VECTOR2, "position"))); ADD_SIGNAL(MethodInfo("duplicate_nodes_request")); ADD_SIGNAL(MethodInfo("copy_nodes_request")); ADD_SIGNAL(MethodInfo("paste_nodes_request")); ADD_SIGNAL(MethodInfo("node_selected", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); ADD_SIGNAL(MethodInfo("node_deselected", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); - ADD_SIGNAL(MethodInfo("connection_to_empty", PropertyInfo(Variant::STRING_NAME, "from"), PropertyInfo(Variant::INT, "from_slot"), PropertyInfo(Variant::VECTOR2, "release_position"))); - ADD_SIGNAL(MethodInfo("connection_from_empty", PropertyInfo(Variant::STRING_NAME, "to"), PropertyInfo(Variant::INT, "to_slot"), PropertyInfo(Variant::VECTOR2, "release_position"))); + ADD_SIGNAL(MethodInfo("connection_to_empty", PropertyInfo(Variant::STRING_NAME, "from_node"), PropertyInfo(Variant::INT, "from_port"), PropertyInfo(Variant::VECTOR2, "release_position"))); + ADD_SIGNAL(MethodInfo("connection_from_empty", PropertyInfo(Variant::STRING_NAME, "to_node"), PropertyInfo(Variant::INT, "to_port"), PropertyInfo(Variant::VECTOR2, "release_position"))); ADD_SIGNAL(MethodInfo("delete_nodes_request", PropertyInfo(Variant::ARRAY, "nodes", PROPERTY_HINT_ARRAY_TYPE, "StringName"))); ADD_SIGNAL(MethodInfo("begin_node_move")); ADD_SIGNAL(MethodInfo("end_node_move")); ADD_SIGNAL(MethodInfo("scroll_offset_changed", PropertyInfo(Variant::VECTOR2, "offset"))); - ADD_SIGNAL(MethodInfo("connection_drag_started", PropertyInfo(Variant::STRING, "from"), PropertyInfo(Variant::STRING, "slot"), PropertyInfo(Variant::BOOL, "is_output"))); + ADD_SIGNAL(MethodInfo("connection_drag_started", PropertyInfo(Variant::STRING, "from_node"), PropertyInfo(Variant::INT, "from_port"), PropertyInfo(Variant::BOOL, "is_output"))); ADD_SIGNAL(MethodInfo("connection_drag_ended")); BIND_ENUM_CONSTANT(SCROLL_ZOOMS); @@ -2376,7 +2453,7 @@ GraphEdit::GraphEdit() { top_layer = memnew(GraphEditFilter(this)); add_child(top_layer, false, INTERNAL_MODE_BACK); top_layer->set_mouse_filter(MOUSE_FILTER_PASS); - top_layer->set_anchors_and_offsets_preset(Control::PRESET_WIDE); + top_layer->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); top_layer->connect("draw", callable_mp(this, &GraphEdit::_top_layer_draw)); top_layer->connect("gui_input", callable_mp(this, &GraphEdit::_top_layer_input)); top_layer->connect("focus_exited", callable_mp(panner.ptr(), &ViewPanner::release_pan_key)); @@ -2421,28 +2498,28 @@ GraphEdit::GraphEdit() { zoom_minus = memnew(Button); zoom_minus->set_flat(true); zoom_hb->add_child(zoom_minus); - zoom_minus->set_tooltip(RTR("Zoom Out")); + zoom_minus->set_tooltip_text(RTR("Zoom Out")); zoom_minus->connect("pressed", callable_mp(this, &GraphEdit::_zoom_minus)); zoom_minus->set_focus_mode(FOCUS_NONE); zoom_reset = memnew(Button); zoom_reset->set_flat(true); zoom_hb->add_child(zoom_reset); - zoom_reset->set_tooltip(RTR("Zoom Reset")); + zoom_reset->set_tooltip_text(RTR("Zoom Reset")); zoom_reset->connect("pressed", callable_mp(this, &GraphEdit::_zoom_reset)); zoom_reset->set_focus_mode(FOCUS_NONE); zoom_plus = memnew(Button); zoom_plus->set_flat(true); zoom_hb->add_child(zoom_plus); - zoom_plus->set_tooltip(RTR("Zoom In")); + zoom_plus->set_tooltip_text(RTR("Zoom In")); zoom_plus->connect("pressed", callable_mp(this, &GraphEdit::_zoom_plus)); zoom_plus->set_focus_mode(FOCUS_NONE); snap_button = memnew(Button); snap_button->set_flat(true); snap_button->set_toggle_mode(true); - snap_button->set_tooltip(RTR("Enable snap and show grid.")); + snap_button->set_tooltip_text(RTR("Enable snap and show grid.")); snap_button->connect("pressed", callable_mp(this, &GraphEdit::_snap_toggled)); snap_button->set_pressed(true); snap_button->set_focus_mode(FOCUS_NONE); @@ -2459,7 +2536,7 @@ GraphEdit::GraphEdit() { minimap_button = memnew(Button); minimap_button->set_flat(true); minimap_button->set_toggle_mode(true); - minimap_button->set_tooltip(RTR("Enable grid minimap.")); + minimap_button->set_tooltip_text(RTR("Enable grid minimap.")); minimap_button->connect("pressed", callable_mp(this, &GraphEdit::_minimap_toggled)); minimap_button->set_pressed(true); minimap_button->set_focus_mode(FOCUS_NONE); @@ -2468,7 +2545,7 @@ GraphEdit::GraphEdit() { layout_button = memnew(Button); layout_button->set_flat(true); zoom_hb->add_child(layout_button); - layout_button->set_tooltip(RTR("Arrange nodes.")); + layout_button->set_tooltip_text(RTR("Arrange nodes.")); layout_button->connect("pressed", callable_mp(this, &GraphEdit::arrange_nodes)); layout_button->set_focus_mode(FOCUS_NONE); diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h index 02e90e4717..101087bdbd 100644 --- a/scene/gui/graph_edit.h +++ b/scene/gui/graph_edit.h @@ -133,6 +133,8 @@ private: void _pan_callback(Vector2 p_scroll_vec); void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt); + bool arrange_nodes_button_hidden = false; + bool connecting = false; String connecting_from; bool connecting_out = false; @@ -184,6 +186,8 @@ private: PackedVector2Array get_connection_line(const Vector2 &p_from, const Vector2 &p_to); void _draw_connection_line(CanvasItem *p_where, const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, const Color &p_to_color, float p_width, float p_zoom); + void _graph_node_selected(Node *p_gn); + void _graph_node_deselected(Node *p_gn); void _graph_node_raised(Node *p_gn); void _graph_node_moved(Node *p_gn); void _graph_node_slot_updated(int p_index, Node *p_gn); @@ -197,8 +201,8 @@ private: GraphEditMinimap *minimap = nullptr; void _top_layer_input(const Ref<InputEvent> &p_ev); - bool is_in_input_hotzone(GraphNode *p_graph_node, int p_slot_index, const Vector2 &p_mouse_pos, const Vector2i &p_port_size); - bool is_in_output_hotzone(GraphNode *p_graph_node, int p_slot_index, const Vector2 &p_mouse_pos, const Vector2i &p_port_size); + bool is_in_input_hotzone(GraphNode *p_node, int p_port, const Vector2 &p_mouse_pos, const Vector2i &p_port_size); + bool is_in_output_hotzone(GraphNode *p_node, int p_port, const Vector2 &p_mouse_pos, const Vector2i &p_port_size); bool is_in_port_hotzone(const Vector2 &pos, const Vector2 &p_mouse_pos, const Vector2i &p_port_size, bool p_left); void _top_layer_draw(); @@ -206,7 +210,7 @@ private: void _minimap_draw(); void _update_scroll_offset(); - Array _get_connection_list() const; + TypedArray<Dictionary> _get_connection_list() const; bool lines_on_bg = false; @@ -280,13 +284,17 @@ protected: GDVIRTUAL2RC(Vector<Vector2>, _get_connection_line, Vector2, Vector2) GDVIRTUAL3R(bool, _is_in_input_hotzone, Object *, int, Vector2) GDVIRTUAL3R(bool, _is_in_output_hotzone, Object *, int, Vector2) + GDVIRTUAL4R(bool, _is_node_hover_valid, StringName, int, StringName, int); public: + PackedStringArray get_configuration_warnings() const override; + Error connect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port); bool is_node_connected(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port); void disconnect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port); void clear_connections(); void force_connection_drag_end(); + virtual bool is_node_hover_valid(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port); void set_connection_activity(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port, float p_activity); @@ -321,6 +329,9 @@ public: void set_minimap_enabled(bool p_enable); bool is_minimap_enabled() const; + void set_arrange_nodes_button_hidden(bool p_enable); + bool is_arrange_nodes_button_hidden() const; + GraphEditFilter *get_top_layer() const { return top_layer; } GraphEditMinimap *get_minimap() const { return minimap; } void get_connection_list(List<Connection> *r_connections) const; @@ -365,4 +376,4 @@ public: VARIANT_ENUM_CAST(GraphEdit::PanningScheme); -#endif // GRAPHEdit_H +#endif // GRAPH_EDIT_H diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp index 92016ca42e..21c0b5a842 100644 --- a/scene/gui/graph_node.cpp +++ b/scene/gui/graph_node.cpp @@ -32,9 +32,7 @@ #include "core/string/translation.h" -#ifdef TOOLS_ENABLED #include "graph_edit.h" -#endif struct _MinSizeCache { int min_size; @@ -80,7 +78,7 @@ bool GraphNode::_set(const StringName &p_name, const Variant &p_value) { } set_slot(idx, si.enable_left, si.type_left, si.color_left, si.enable_right, si.type_right, si.color_right, si.custom_slot_left, si.custom_slot_right, si.draw_stylebox); - update(); + queue_redraw(); return true; } @@ -290,7 +288,7 @@ void GraphNode::_resort() { idx++; } - update(); + queue_redraw(); connpos_dirty = true; } @@ -418,7 +416,7 @@ void GraphNode::_notification(int p_what) { _shape(); update_minimum_size(); - update(); + queue_redraw(); } break; } } @@ -445,17 +443,16 @@ void GraphNode::_edit_set_position(const Point2 &p_position) { } set_position(p_position); } +#endif -void GraphNode::_validate_property(PropertyInfo &property) const { - Control::_validate_property(property); +void GraphNode::_validate_property(PropertyInfo &p_property) const { GraphEdit *graph = Object::cast_to<GraphEdit>(get_parent()); if (graph) { - if (property.name == "rect_position") { - property.usage |= PROPERTY_USAGE_READ_ONLY; + if (p_property.name == "position") { + p_property.usage |= PROPERTY_USAGE_READ_ONLY; } } } -#endif void GraphNode::set_slot(int p_idx, bool p_enable_left, int p_type_left, const Color &p_color_left, bool p_enable_right, int p_type_right, const Color &p_color_right, const Ref<Texture2D> &p_custom_left, const Ref<Texture2D> &p_custom_right, bool p_draw_stylebox) { ERR_FAIL_COND_MSG(p_idx < 0, vformat("Cannot set slot with p_idx (%d) lesser than zero.", p_idx)); @@ -478,7 +475,7 @@ void GraphNode::set_slot(int p_idx, bool p_enable_left, int p_type_left, const C s.custom_slot_right = p_custom_right; s.draw_stylebox = p_draw_stylebox; slot_info[p_idx] = s; - update(); + queue_redraw(); connpos_dirty = true; emit_signal(SNAME("slot_updated"), p_idx); @@ -486,13 +483,13 @@ void GraphNode::set_slot(int p_idx, bool p_enable_left, int p_type_left, const C void GraphNode::clear_slot(int p_idx) { slot_info.erase(p_idx); - update(); + queue_redraw(); connpos_dirty = true; } void GraphNode::clear_all_slots() { slot_info.clear(); - update(); + queue_redraw(); connpos_dirty = true; } @@ -506,8 +503,12 @@ bool GraphNode::is_slot_enabled_left(int p_idx) const { void GraphNode::set_slot_enabled_left(int p_idx, bool p_enable_left) { ERR_FAIL_COND_MSG(p_idx < 0, vformat("Cannot set enable_left for the slot with p_idx (%d) lesser than zero.", p_idx)); + if (slot_info[p_idx].enable_left == p_enable_left) { + return; + } + slot_info[p_idx].enable_left = p_enable_left; - update(); + queue_redraw(); connpos_dirty = true; emit_signal(SNAME("slot_updated"), p_idx); @@ -516,8 +517,12 @@ void GraphNode::set_slot_enabled_left(int p_idx, bool p_enable_left) { void GraphNode::set_slot_type_left(int p_idx, int p_type_left) { ERR_FAIL_COND_MSG(!slot_info.has(p_idx), vformat("Cannot set type_left for the slot '%d' because it hasn't been enabled.", p_idx)); + if (slot_info[p_idx].type_left == p_type_left) { + return; + } + slot_info[p_idx].type_left = p_type_left; - update(); + queue_redraw(); connpos_dirty = true; emit_signal(SNAME("slot_updated"), p_idx); @@ -533,8 +538,12 @@ int GraphNode::get_slot_type_left(int p_idx) const { void GraphNode::set_slot_color_left(int p_idx, const Color &p_color_left) { ERR_FAIL_COND_MSG(!slot_info.has(p_idx), vformat("Cannot set color_left for the slot '%d' because it hasn't been enabled.", p_idx)); + if (slot_info[p_idx].color_left == p_color_left) { + return; + } + slot_info[p_idx].color_left = p_color_left; - update(); + queue_redraw(); connpos_dirty = true; emit_signal(SNAME("slot_updated"), p_idx); @@ -557,8 +566,12 @@ bool GraphNode::is_slot_enabled_right(int p_idx) const { void GraphNode::set_slot_enabled_right(int p_idx, bool p_enable_right) { ERR_FAIL_COND_MSG(p_idx < 0, vformat("Cannot set enable_right for the slot with p_idx (%d) lesser than zero.", p_idx)); + if (slot_info[p_idx].enable_right == p_enable_right) { + return; + } + slot_info[p_idx].enable_right = p_enable_right; - update(); + queue_redraw(); connpos_dirty = true; emit_signal(SNAME("slot_updated"), p_idx); @@ -567,8 +580,12 @@ void GraphNode::set_slot_enabled_right(int p_idx, bool p_enable_right) { void GraphNode::set_slot_type_right(int p_idx, int p_type_right) { ERR_FAIL_COND_MSG(!slot_info.has(p_idx), vformat("Cannot set type_right for the slot '%d' because it hasn't been enabled.", p_idx)); + if (slot_info[p_idx].type_right == p_type_right) { + return; + } + slot_info[p_idx].type_right = p_type_right; - update(); + queue_redraw(); connpos_dirty = true; emit_signal(SNAME("slot_updated"), p_idx); @@ -584,8 +601,12 @@ int GraphNode::get_slot_type_right(int p_idx) const { void GraphNode::set_slot_color_right(int p_idx, const Color &p_color_right) { ERR_FAIL_COND_MSG(!slot_info.has(p_idx), vformat("Cannot set color_right for the slot '%d' because it hasn't been enabled.", p_idx)); + if (slot_info[p_idx].color_right == p_color_right) { + return; + } + slot_info[p_idx].color_right = p_color_right; - update(); + queue_redraw(); connpos_dirty = true; emit_signal(SNAME("slot_updated"), p_idx); @@ -609,7 +630,7 @@ void GraphNode::set_slot_draw_stylebox(int p_idx, bool p_enable) { ERR_FAIL_COND_MSG(p_idx < 0, vformat("Cannot set draw_stylebox for the slot with p_idx (%d) lesser than zero.", p_idx)); slot_info[p_idx].draw_stylebox = p_enable; - update(); + queue_redraw(); connpos_dirty = true; emit_signal(SNAME("slot_updated"), p_idx); @@ -667,7 +688,7 @@ void GraphNode::set_title(const String &p_title) { title = p_title; _shape(); - update(); + queue_redraw(); update_minimum_size(); } @@ -680,7 +701,7 @@ void GraphNode::set_text_direction(Control::TextDirection p_text_direction) { if (text_direction != p_text_direction) { text_direction = p_text_direction; _shape(); - update(); + queue_redraw(); } } @@ -692,7 +713,7 @@ void GraphNode::set_language(const String &p_language) { if (language != p_language) { language = p_language; _shape(); - update(); + queue_redraw(); } } @@ -701,9 +722,13 @@ String GraphNode::get_language() const { } void GraphNode::set_position_offset(const Vector2 &p_offset) { + if (position_offset == p_offset) { + return; + } + position_offset = p_offset; emit_signal(SNAME("position_offset_changed")); - update(); + queue_redraw(); } Vector2 GraphNode::get_position_offset() const { @@ -711,8 +736,13 @@ Vector2 GraphNode::get_position_offset() const { } void GraphNode::set_selected(bool p_selected) { + if (!is_selectable() || selected == p_selected) { + return; + } + selected = p_selected; - update(); + emit_signal(p_selected ? SNAME("selected") : SNAME("deselected")); + queue_redraw(); } bool GraphNode::is_selected() { @@ -732,8 +762,12 @@ Vector2 GraphNode::get_drag_from() { } void GraphNode::set_show_close_button(bool p_enable) { + if (show_close == p_enable) { + return; + } + show_close = p_enable; - update(); + queue_redraw(); } bool GraphNode::is_close_button_visible() const { @@ -745,8 +779,8 @@ void GraphNode::_connpos_update() { int sep = get_theme_constant(SNAME("separation")); Ref<StyleBox> sb = get_theme_stylebox(SNAME("frame")); - conn_input_cache.clear(); - conn_output_cache.clear(); + left_port_cache.clear(); + right_port_cache.clear(); int vofs = 0; int idx = 0; @@ -767,20 +801,26 @@ void GraphNode::_connpos_update() { if (slot_info.has(idx)) { if (slot_info[idx].enable_left) { - ConnCache cc; - cc.pos = Point2i(edgeofs, y + h / 2); + PortCache cc; + cc.position = Point2i(edgeofs, y + h / 2); + cc.height = size.height; + + cc.slot_idx = idx; cc.type = slot_info[idx].type_left; cc.color = slot_info[idx].color_left; - cc.height = size.height; - conn_input_cache.push_back(cc); + + left_port_cache.push_back(cc); } if (slot_info[idx].enable_right) { - ConnCache cc; - cc.pos = Point2i(get_size().width - edgeofs, y + h / 2); + PortCache cc; + cc.position = Point2i(get_size().width - edgeofs, y + h / 2); + cc.height = size.height; + + cc.slot_idx = idx; cc.type = slot_info[idx].type_right; cc.color = slot_info[idx].color_right; - cc.height = size.height; - conn_output_cache.push_back(cc); + + right_port_cache.push_back(cc); } } @@ -797,46 +837,55 @@ int GraphNode::get_connection_input_count() { _connpos_update(); } - return conn_input_cache.size(); + return left_port_cache.size(); } -int GraphNode::get_connection_input_height(int p_idx) { +int GraphNode::get_connection_input_height(int p_port) { if (connpos_dirty) { _connpos_update(); } - ERR_FAIL_INDEX_V(p_idx, conn_input_cache.size(), 0); - return conn_input_cache[p_idx].height; + ERR_FAIL_INDEX_V(p_port, left_port_cache.size(), 0); + return left_port_cache[p_port].height; } -Vector2 GraphNode::get_connection_input_position(int p_idx) { +Vector2 GraphNode::get_connection_input_position(int p_port) { if (connpos_dirty) { _connpos_update(); } - ERR_FAIL_INDEX_V(p_idx, conn_input_cache.size(), Vector2()); - Vector2 pos = conn_input_cache[p_idx].pos; + ERR_FAIL_INDEX_V(p_port, left_port_cache.size(), Vector2()); + Vector2 pos = left_port_cache[p_port].position; pos.x *= get_scale().x; pos.y *= get_scale().y; return pos; } -int GraphNode::get_connection_input_type(int p_idx) { +int GraphNode::get_connection_input_type(int p_port) { + if (connpos_dirty) { + _connpos_update(); + } + + ERR_FAIL_INDEX_V(p_port, left_port_cache.size(), 0); + return left_port_cache[p_port].type; +} + +Color GraphNode::get_connection_input_color(int p_port) { if (connpos_dirty) { _connpos_update(); } - ERR_FAIL_INDEX_V(p_idx, conn_input_cache.size(), 0); - return conn_input_cache[p_idx].type; + ERR_FAIL_INDEX_V(p_port, left_port_cache.size(), Color()); + return left_port_cache[p_port].color; } -Color GraphNode::get_connection_input_color(int p_idx) { +int GraphNode::get_connection_input_slot(int p_port) { if (connpos_dirty) { _connpos_update(); } - ERR_FAIL_INDEX_V(p_idx, conn_input_cache.size(), Color()); - return conn_input_cache[p_idx].color; + ERR_FAIL_INDEX_V(p_port, left_port_cache.size(), -1); + return left_port_cache[p_port].slot_idx; } int GraphNode::get_connection_output_count() { @@ -844,46 +893,55 @@ int GraphNode::get_connection_output_count() { _connpos_update(); } - return conn_output_cache.size(); + return right_port_cache.size(); } -int GraphNode::get_connection_output_height(int p_idx) { +int GraphNode::get_connection_output_height(int p_port) { if (connpos_dirty) { _connpos_update(); } - ERR_FAIL_INDEX_V(p_idx, conn_output_cache.size(), 0); - return conn_output_cache[p_idx].height; + ERR_FAIL_INDEX_V(p_port, right_port_cache.size(), 0); + return right_port_cache[p_port].height; } -Vector2 GraphNode::get_connection_output_position(int p_idx) { +Vector2 GraphNode::get_connection_output_position(int p_port) { if (connpos_dirty) { _connpos_update(); } - ERR_FAIL_INDEX_V(p_idx, conn_output_cache.size(), Vector2()); - Vector2 pos = conn_output_cache[p_idx].pos; + ERR_FAIL_INDEX_V(p_port, right_port_cache.size(), Vector2()); + Vector2 pos = right_port_cache[p_port].position; pos.x *= get_scale().x; pos.y *= get_scale().y; return pos; } -int GraphNode::get_connection_output_type(int p_idx) { +int GraphNode::get_connection_output_type(int p_port) { if (connpos_dirty) { _connpos_update(); } - ERR_FAIL_INDEX_V(p_idx, conn_output_cache.size(), 0); - return conn_output_cache[p_idx].type; + ERR_FAIL_INDEX_V(p_port, right_port_cache.size(), 0); + return right_port_cache[p_port].type; } -Color GraphNode::get_connection_output_color(int p_idx) { +Color GraphNode::get_connection_output_color(int p_port) { if (connpos_dirty) { _connpos_update(); } - ERR_FAIL_INDEX_V(p_idx, conn_output_cache.size(), Color()); - return conn_output_cache[p_idx].color; + ERR_FAIL_INDEX_V(p_port, right_port_cache.size(), Color()); + return right_port_cache[p_port].color; +} + +int GraphNode::get_connection_output_slot(int p_port) { + if (connpos_dirty) { + _connpos_update(); + } + + ERR_FAIL_INDEX_V(p_port, right_port_cache.size(), -1); + return right_port_cache[p_port].slot_idx; } void GraphNode::gui_input(const Ref<InputEvent> &p_ev) { @@ -932,8 +990,12 @@ void GraphNode::gui_input(const Ref<InputEvent> &p_ev) { } void GraphNode::set_overlay(Overlay p_overlay) { + if (overlay == p_overlay) { + return; + } + overlay = p_overlay; - update(); + queue_redraw(); } GraphNode::Overlay GraphNode::get_overlay() const { @@ -941,8 +1003,12 @@ GraphNode::Overlay GraphNode::get_overlay() const { } void GraphNode::set_comment(bool p_enable) { + if (comment == p_enable) { + return; + } + comment = p_enable; - update(); + queue_redraw(); } bool GraphNode::is_comment() const { @@ -950,14 +1016,37 @@ bool GraphNode::is_comment() const { } void GraphNode::set_resizable(bool p_enable) { + if (resizable == p_enable) { + return; + } + resizable = p_enable; - update(); + queue_redraw(); } bool GraphNode::is_resizable() const { return resizable; } +void GraphNode::set_draggable(bool p_draggable) { + draggable = p_draggable; +} + +bool GraphNode::is_draggable() { + return draggable; +} + +void GraphNode::set_selectable(bool p_selectable) { + if (!p_selectable) { + set_selected(false); + } + selectable = p_selectable; +} + +bool GraphNode::is_selectable() { + return selectable; +} + Vector<int> GraphNode::get_allowed_size_flags_horizontal() const { Vector<int> flags; flags.append(SIZE_FILL); @@ -985,30 +1074,30 @@ void GraphNode::_bind_methods() { ClassDB::bind_method(D_METHOD("set_language", "language"), &GraphNode::set_language); ClassDB::bind_method(D_METHOD("get_language"), &GraphNode::get_language); - ClassDB::bind_method(D_METHOD("set_slot", "idx", "enable_left", "type_left", "color_left", "enable_right", "type_right", "color_right", "custom_left", "custom_right", "enable"), &GraphNode::set_slot, DEFVAL(Ref<Texture2D>()), DEFVAL(Ref<Texture2D>()), DEFVAL(true)); - ClassDB::bind_method(D_METHOD("clear_slot", "idx"), &GraphNode::clear_slot); + ClassDB::bind_method(D_METHOD("set_slot", "slot_index", "enable_left_port", "type_left", "color_left", "enable_right_port", "type_right", "color_right", "custom_icon_left", "custom_icon_right", "draw_stylebox"), &GraphNode::set_slot, DEFVAL(Ref<Texture2D>()), DEFVAL(Ref<Texture2D>()), DEFVAL(true)); + ClassDB::bind_method(D_METHOD("clear_slot", "slot_index"), &GraphNode::clear_slot); ClassDB::bind_method(D_METHOD("clear_all_slots"), &GraphNode::clear_all_slots); - ClassDB::bind_method(D_METHOD("is_slot_enabled_left", "idx"), &GraphNode::is_slot_enabled_left); - ClassDB::bind_method(D_METHOD("set_slot_enabled_left", "idx", "enable_left"), &GraphNode::set_slot_enabled_left); + ClassDB::bind_method(D_METHOD("set_slot_enabled_left", "slot_index", "enable"), &GraphNode::set_slot_enabled_left); + ClassDB::bind_method(D_METHOD("is_slot_enabled_left", "slot_index"), &GraphNode::is_slot_enabled_left); - ClassDB::bind_method(D_METHOD("set_slot_type_left", "idx", "type_left"), &GraphNode::set_slot_type_left); - ClassDB::bind_method(D_METHOD("get_slot_type_left", "idx"), &GraphNode::get_slot_type_left); + ClassDB::bind_method(D_METHOD("set_slot_type_left", "slot_index", "type"), &GraphNode::set_slot_type_left); + ClassDB::bind_method(D_METHOD("get_slot_type_left", "slot_index"), &GraphNode::get_slot_type_left); - ClassDB::bind_method(D_METHOD("set_slot_color_left", "idx", "color_left"), &GraphNode::set_slot_color_left); - ClassDB::bind_method(D_METHOD("get_slot_color_left", "idx"), &GraphNode::get_slot_color_left); + ClassDB::bind_method(D_METHOD("set_slot_color_left", "slot_index", "color"), &GraphNode::set_slot_color_left); + ClassDB::bind_method(D_METHOD("get_slot_color_left", "slot_index"), &GraphNode::get_slot_color_left); - ClassDB::bind_method(D_METHOD("is_slot_enabled_right", "idx"), &GraphNode::is_slot_enabled_right); - ClassDB::bind_method(D_METHOD("set_slot_enabled_right", "idx", "enable_right"), &GraphNode::set_slot_enabled_right); + ClassDB::bind_method(D_METHOD("set_slot_enabled_right", "slot_index", "enable"), &GraphNode::set_slot_enabled_right); + ClassDB::bind_method(D_METHOD("is_slot_enabled_right", "slot_index"), &GraphNode::is_slot_enabled_right); - ClassDB::bind_method(D_METHOD("set_slot_type_right", "idx", "type_right"), &GraphNode::set_slot_type_right); - ClassDB::bind_method(D_METHOD("get_slot_type_right", "idx"), &GraphNode::get_slot_type_right); + ClassDB::bind_method(D_METHOD("set_slot_type_right", "slot_index", "type"), &GraphNode::set_slot_type_right); + ClassDB::bind_method(D_METHOD("get_slot_type_right", "slot_index"), &GraphNode::get_slot_type_right); - ClassDB::bind_method(D_METHOD("set_slot_color_right", "idx", "color_right"), &GraphNode::set_slot_color_right); - ClassDB::bind_method(D_METHOD("get_slot_color_right", "idx"), &GraphNode::get_slot_color_right); + ClassDB::bind_method(D_METHOD("set_slot_color_right", "slot_index", "color"), &GraphNode::set_slot_color_right); + ClassDB::bind_method(D_METHOD("get_slot_color_right", "slot_index"), &GraphNode::get_slot_color_right); - ClassDB::bind_method(D_METHOD("is_slot_draw_stylebox", "idx"), &GraphNode::is_slot_draw_stylebox); - ClassDB::bind_method(D_METHOD("set_slot_draw_stylebox", "idx", "draw_stylebox"), &GraphNode::set_slot_draw_stylebox); + ClassDB::bind_method(D_METHOD("is_slot_draw_stylebox", "slot_index"), &GraphNode::is_slot_draw_stylebox); + ClassDB::bind_method(D_METHOD("set_slot_draw_stylebox", "slot_index", "enable"), &GraphNode::set_slot_draw_stylebox); ClassDB::bind_method(D_METHOD("set_position_offset", "offset"), &GraphNode::set_position_offset); ClassDB::bind_method(D_METHOD("get_position_offset"), &GraphNode::get_position_offset); @@ -1019,20 +1108,28 @@ void GraphNode::_bind_methods() { ClassDB::bind_method(D_METHOD("set_resizable", "resizable"), &GraphNode::set_resizable); ClassDB::bind_method(D_METHOD("is_resizable"), &GraphNode::is_resizable); + ClassDB::bind_method(D_METHOD("set_draggable", "draggable"), &GraphNode::set_draggable); + ClassDB::bind_method(D_METHOD("is_draggable"), &GraphNode::is_draggable); + + ClassDB::bind_method(D_METHOD("set_selectable", "selectable"), &GraphNode::set_selectable); + ClassDB::bind_method(D_METHOD("is_selectable"), &GraphNode::is_selectable); + ClassDB::bind_method(D_METHOD("set_selected", "selected"), &GraphNode::set_selected); ClassDB::bind_method(D_METHOD("is_selected"), &GraphNode::is_selected); ClassDB::bind_method(D_METHOD("get_connection_input_count"), &GraphNode::get_connection_input_count); - ClassDB::bind_method(D_METHOD("get_connection_input_height", "idx"), &GraphNode::get_connection_input_height); - ClassDB::bind_method(D_METHOD("get_connection_input_position", "idx"), &GraphNode::get_connection_input_position); - ClassDB::bind_method(D_METHOD("get_connection_input_type", "idx"), &GraphNode::get_connection_input_type); - ClassDB::bind_method(D_METHOD("get_connection_input_color", "idx"), &GraphNode::get_connection_input_color); + ClassDB::bind_method(D_METHOD("get_connection_input_height", "port"), &GraphNode::get_connection_input_height); + ClassDB::bind_method(D_METHOD("get_connection_input_position", "port"), &GraphNode::get_connection_input_position); + ClassDB::bind_method(D_METHOD("get_connection_input_type", "port"), &GraphNode::get_connection_input_type); + ClassDB::bind_method(D_METHOD("get_connection_input_color", "port"), &GraphNode::get_connection_input_color); + ClassDB::bind_method(D_METHOD("get_connection_input_slot", "port"), &GraphNode::get_connection_input_slot); ClassDB::bind_method(D_METHOD("get_connection_output_count"), &GraphNode::get_connection_output_count); - ClassDB::bind_method(D_METHOD("get_connection_output_height", "idx"), &GraphNode::get_connection_output_height); - ClassDB::bind_method(D_METHOD("get_connection_output_position", "idx"), &GraphNode::get_connection_output_position); - ClassDB::bind_method(D_METHOD("get_connection_output_type", "idx"), &GraphNode::get_connection_output_type); - ClassDB::bind_method(D_METHOD("get_connection_output_color", "idx"), &GraphNode::get_connection_output_color); + ClassDB::bind_method(D_METHOD("get_connection_output_height", "port"), &GraphNode::get_connection_output_height); + ClassDB::bind_method(D_METHOD("get_connection_output_position", "port"), &GraphNode::get_connection_output_position); + ClassDB::bind_method(D_METHOD("get_connection_output_type", "port"), &GraphNode::get_connection_output_type); + ClassDB::bind_method(D_METHOD("get_connection_output_color", "port"), &GraphNode::get_connection_output_color); + ClassDB::bind_method(D_METHOD("get_connection_output_slot", "port"), &GraphNode::get_connection_output_slot); ClassDB::bind_method(D_METHOD("set_show_close_button", "show"), &GraphNode::set_show_close_button); ClassDB::bind_method(D_METHOD("is_close_button_visible"), &GraphNode::is_close_button_visible); @@ -1044,6 +1141,8 @@ void GraphNode::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_position_offset", "get_position_offset"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_close"), "set_show_close_button", "is_close_button_visible"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "resizable"), "set_resizable", "is_resizable"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draggable"), "set_draggable", "is_draggable"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selectable"), "set_selectable", "is_selectable"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selected"), "set_selected", "is_selected"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "comment"), "set_comment", "is_comment"); ADD_PROPERTY(PropertyInfo(Variant::INT, "overlay", PROPERTY_HINT_ENUM, "Disabled,Breakpoint,Position"), "set_overlay", "get_overlay"); @@ -1051,8 +1150,11 @@ void GraphNode::_bind_methods() { ADD_GROUP("BiDi", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language"); + ADD_GROUP("", ""); ADD_SIGNAL(MethodInfo("position_offset_changed")); + ADD_SIGNAL(MethodInfo("selected")); + ADD_SIGNAL(MethodInfo("deselected")); ADD_SIGNAL(MethodInfo("slot_updated", PropertyInfo(Variant::INT, "idx"))); ADD_SIGNAL(MethodInfo("dragged", PropertyInfo(Variant::VECTOR2, "from"), PropertyInfo(Variant::VECTOR2, "to"))); ADD_SIGNAL(MethodInfo("raise_request")); diff --git a/scene/gui/graph_node.h b/scene/gui/graph_node.h index 0651eb5cc9..e66b0cfc20 100644 --- a/scene/gui/graph_node.h +++ b/scene/gui/graph_node.h @@ -67,6 +67,8 @@ private: Vector2 position_offset; bool comment = false; bool resizable = false; + bool draggable = true; + bool selectable = true; bool resizing = false; Vector2 resizing_from; @@ -76,15 +78,17 @@ private: Vector<int> cache_y; - struct ConnCache { - Vector2 pos; + struct PortCache { + Vector2 position; + int height; + + int slot_idx; int type = 0; Color color; - int height; }; - Vector<ConnCache> conn_input_cache; - Vector<ConnCache> conn_output_cache; + Vector<PortCache> left_port_cache; + Vector<PortCache> right_port_cache; HashMap<int, Slot> slot_info; @@ -101,7 +105,6 @@ private: #ifdef TOOLS_ENABLED void _edit_set_position(const Point2 &p_position) override; - void _validate_property(PropertyInfo &property) const override; #endif protected: @@ -112,6 +115,7 @@ protected: bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; + void _validate_property(PropertyInfo &p_property) const; public: bool has_point(const Point2 &p_point) const override; @@ -163,16 +167,18 @@ public: bool is_close_button_visible() const; int get_connection_input_count(); - int get_connection_input_height(int p_idx); - Vector2 get_connection_input_position(int p_idx); - int get_connection_input_type(int p_idx); - Color get_connection_input_color(int p_idx); + int get_connection_input_height(int p_port); + Vector2 get_connection_input_position(int p_port); + int get_connection_input_type(int p_port); + Color get_connection_input_color(int p_port); + int get_connection_input_slot(int p_port); int get_connection_output_count(); - int get_connection_output_height(int p_idx); - Vector2 get_connection_output_position(int p_idx); - int get_connection_output_type(int p_idx); - Color get_connection_output_color(int p_idx); + int get_connection_output_height(int p_port); + Vector2 get_connection_output_position(int p_port); + int get_connection_output_type(int p_port); + Color get_connection_output_color(int p_port); + int get_connection_output_slot(int p_port); void set_overlay(Overlay p_overlay); Overlay get_overlay() const; @@ -183,6 +189,12 @@ public: void set_resizable(bool p_enable); bool is_resizable() const; + void set_draggable(bool p_draggable); + bool is_draggable(); + + void set_selectable(bool p_selectable); + bool is_selectable(); + virtual Size2 get_minimum_size() const override; virtual Vector<int> get_allowed_size_flags_horizontal() const override; diff --git a/scene/gui/grid_container.cpp b/scene/gui/grid_container.cpp index 6f8518a7b0..8dc890eccb 100644 --- a/scene/gui/grid_container.cpp +++ b/scene/gui/grid_container.cpp @@ -31,6 +31,13 @@ #include "grid_container.h" #include "core/templates/rb_set.h" +void GridContainer::_update_theme_item_cache() { + Container::_update_theme_item_cache(); + + theme_cache.h_separation = get_theme_constant(SNAME("h_separation")); + theme_cache.v_separation = get_theme_constant(SNAME("v_separation")); +} + void GridContainer::_notification(int p_what) { switch (p_what) { case NOTIFICATION_SORT_CHILDREN: { @@ -39,11 +46,6 @@ void GridContainer::_notification(int p_what) { RBSet<int> col_expanded; // Columns which have the SIZE_EXPAND flag set. RBSet<int> row_expanded; // Rows which have the SIZE_EXPAND flag set. - int hsep = get_theme_constant(SNAME("h_separation")); - int vsep = get_theme_constant(SNAME("v_separation")); - int max_col = MIN(get_child_count(), columns); - int max_row = ceil((float)get_child_count() / (float)columns); - // Compute the per-column/per-row data. int valid_controls_index = 0; for (int i = 0; i < get_child_count(); i++) { @@ -79,6 +81,9 @@ void GridContainer::_notification(int p_what) { } } + int max_col = MIN(valid_controls_index, columns); + int max_row = ceil((float)valid_controls_index / (float)columns); + // Consider all empty columns expanded. for (int i = valid_controls_index; i < columns; i++) { col_expanded.insert(i); @@ -97,8 +102,8 @@ void GridContainer::_notification(int p_what) { remaining_space.height -= E.value; } } - remaining_space.height -= vsep * MAX(max_row - 1, 0); - remaining_space.width -= hsep * MAX(max_col - 1, 0); + remaining_space.height -= theme_cache.v_separation * MAX(max_row - 1, 0); + remaining_space.width -= theme_cache.h_separation * MAX(max_col - 1, 0); bool can_fit = false; while (!can_fit && col_expanded.size() > 0) { @@ -201,7 +206,7 @@ void GridContainer::_notification(int p_what) { col_ofs = 0; } if (row > 0) { - row_ofs += (row_expanded.has(row - 1) ? row_expand : row_minh[row - 1]) + vsep; + row_ofs += (row_expanded.has(row - 1) ? row_expand : row_minh[row - 1]) + theme_cache.v_separation; if (row_expanded.has(row - 1) && row - 1 < row_remaining_pixel_index) { // Apply the remaining pixel of the previous row. @@ -223,11 +228,11 @@ void GridContainer::_notification(int p_what) { if (rtl) { Point2 p(col_ofs - s.width, row_ofs); fit_child_in_rect(c, Rect2(p, s)); - col_ofs -= s.width + hsep; + col_ofs -= s.width + theme_cache.h_separation; } else { Point2 p(col_ofs, row_ofs); fit_child_in_rect(c, Rect2(p, s)); - col_ofs += s.width + hsep; + col_ofs += s.width + theme_cache.h_separation; } } } break; @@ -245,6 +250,11 @@ void GridContainer::_notification(int p_what) { void GridContainer::set_columns(int p_columns) { ERR_FAIL_COND(p_columns < 1); + + if (columns == p_columns) { + return; + } + columns = p_columns; queue_sort(); update_minimum_size(); @@ -265,9 +275,6 @@ Size2 GridContainer::get_minimum_size() const { RBMap<int, int> col_minw; RBMap<int, int> row_minh; - int hsep = get_theme_constant(SNAME("h_separation")); - int vsep = get_theme_constant(SNAME("v_separation")); - int max_row = 0; int max_col = 0; @@ -307,8 +314,8 @@ Size2 GridContainer::get_minimum_size() const { ms.height += E.value; } - ms.height += vsep * max_row; - ms.width += hsep * max_col; + ms.height += theme_cache.v_separation * max_row; + ms.width += theme_cache.h_separation * max_col; return ms; } diff --git a/scene/gui/grid_container.h b/scene/gui/grid_container.h index 9d77f90ab3..522046f694 100644 --- a/scene/gui/grid_container.h +++ b/scene/gui/grid_container.h @@ -38,7 +38,14 @@ class GridContainer : public Container { int columns = 1; + struct ThemeCache { + int h_separation = 0; + int v_separation = 0; + } theme_cache; + protected: + virtual void _update_theme_item_cache() override; + void _notification(int p_what); static void _bind_methods(); diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index 1d4ca4d196..357f2480bd 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -43,11 +43,11 @@ void ItemList::_shape(int p_idx) { } else { item.text_buf->set_direction((TextServer::Direction)item.text_direction); } - item.text_buf->add_string(item.text, get_theme_font(SNAME("font")), get_theme_font_size(SNAME("font_size")), item.language); + item.text_buf->add_string(item.text, theme_cache.font, theme_cache.font_size, item.language); if (icon_mode == ICON_MODE_TOP && max_text_lines > 0) { - item.text_buf->set_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND); + item.text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_TRIM_EDGE_SPACES); } else { - item.text_buf->set_flags(TextServer::BREAK_NONE); + item.text_buf->set_break_flags(TextServer::BREAK_NONE); } item.text_buf->set_text_overrun_behavior(text_overrun_behavior); item.text_buf->set_max_lines_visible(max_text_lines); @@ -63,7 +63,7 @@ int ItemList::add_item(const String &p_item, const Ref<Texture2D> &p_texture, bo _shape(items.size() - 1); - update(); + queue_redraw(); shape_changed = true; notify_property_list_changed(); return item_id; @@ -76,7 +76,7 @@ int ItemList::add_icon_item(const Ref<Texture2D> &p_item, bool p_selectable) { items.push_back(item); int item_id = items.size() - 1; - update(); + queue_redraw(); shape_changed = true; notify_property_list_changed(); return item_id; @@ -88,9 +88,13 @@ void ItemList::set_item_text(int p_idx, const String &p_text) { } ERR_FAIL_INDEX(p_idx, items.size()); + if (items[p_idx].text == p_text) { + return; + } + items.write[p_idx].text = p_text; _shape(p_idx); - update(); + queue_redraw(); shape_changed = true; } @@ -108,7 +112,7 @@ void ItemList::set_item_text_direction(int p_idx, Control::TextDirection p_text_ if (items[p_idx].text_direction != p_text_direction) { items.write[p_idx].text_direction = p_text_direction; _shape(p_idx); - update(); + queue_redraw(); } } @@ -125,7 +129,7 @@ void ItemList::set_item_language(int p_idx, const String &p_language) { if (items[p_idx].language != p_language) { items.write[p_idx].language = p_language; _shape(p_idx); - update(); + queue_redraw(); } } @@ -153,8 +157,12 @@ void ItemList::set_item_tooltip(int p_idx, const String &p_tooltip) { } ERR_FAIL_INDEX(p_idx, items.size()); + if (items[p_idx].tooltip == p_tooltip) { + return; + } + items.write[p_idx].tooltip = p_tooltip; - update(); + queue_redraw(); shape_changed = true; } @@ -169,8 +177,12 @@ void ItemList::set_item_icon(int p_idx, const Ref<Texture2D> &p_icon) { } ERR_FAIL_INDEX(p_idx, items.size()); + if (items[p_idx].icon == p_icon) { + return; + } + items.write[p_idx].icon = p_icon; - update(); + queue_redraw(); shape_changed = true; } @@ -186,8 +198,12 @@ void ItemList::set_item_icon_transposed(int p_idx, const bool p_transposed) { } ERR_FAIL_INDEX(p_idx, items.size()); + if (items[p_idx].icon_transposed == p_transposed) { + return; + } + items.write[p_idx].icon_transposed = p_transposed; - update(); + queue_redraw(); shape_changed = true; } @@ -203,8 +219,12 @@ void ItemList::set_item_icon_region(int p_idx, const Rect2 &p_region) { } ERR_FAIL_INDEX(p_idx, items.size()); + if (items[p_idx].icon_region == p_region) { + return; + } + items.write[p_idx].icon_region = p_region; - update(); + queue_redraw(); shape_changed = true; } @@ -220,8 +240,12 @@ void ItemList::set_item_icon_modulate(int p_idx, const Color &p_modulate) { } ERR_FAIL_INDEX(p_idx, items.size()); + if (items[p_idx].icon_modulate == p_modulate) { + return; + } + items.write[p_idx].icon_modulate = p_modulate; - update(); + queue_redraw(); } Color ItemList::get_item_icon_modulate(int p_idx) const { @@ -236,8 +260,12 @@ void ItemList::set_item_custom_bg_color(int p_idx, const Color &p_custom_bg_colo } ERR_FAIL_INDEX(p_idx, items.size()); + if (items[p_idx].custom_bg == p_custom_bg_color) { + return; + } + items.write[p_idx].custom_bg = p_custom_bg_color; - update(); + queue_redraw(); } Color ItemList::get_item_custom_bg_color(int p_idx) const { @@ -252,8 +280,12 @@ void ItemList::set_item_custom_fg_color(int p_idx, const Color &p_custom_fg_colo } ERR_FAIL_INDEX(p_idx, items.size()); + if (items[p_idx].custom_fg == p_custom_fg_color) { + return; + } + items.write[p_idx].custom_fg = p_custom_fg_color; - update(); + queue_redraw(); } Color ItemList::get_item_custom_fg_color(int p_idx) const { @@ -268,8 +300,12 @@ void ItemList::set_item_tag_icon(int p_idx, const Ref<Texture2D> &p_tag_icon) { } ERR_FAIL_INDEX(p_idx, items.size()); + if (items[p_idx].tag_icon == p_tag_icon) { + return; + } + items.write[p_idx].tag_icon = p_tag_icon; - update(); + queue_redraw(); shape_changed = true; } @@ -299,8 +335,12 @@ void ItemList::set_item_disabled(int p_idx, bool p_disabled) { } ERR_FAIL_INDEX(p_idx, items.size()); + if (items[p_idx].disabled == p_disabled) { + return; + } + items.write[p_idx].disabled = p_disabled; - update(); + queue_redraw(); } bool ItemList::is_item_disabled(int p_idx) const { @@ -314,8 +354,12 @@ void ItemList::set_item_metadata(int p_idx, const Variant &p_metadata) { } ERR_FAIL_INDEX(p_idx, items.size()); + if (items[p_idx].metadata == p_metadata) { + return; + } + items.write[p_idx].metadata = p_metadata; - update(); + queue_redraw(); shape_changed = true; } @@ -343,7 +387,7 @@ void ItemList::select(int p_idx, bool p_single) { items.write[p_idx].selected = true; } } - update(); + queue_redraw(); } void ItemList::deselect(int p_idx) { @@ -355,7 +399,7 @@ void ItemList::deselect(int p_idx) { } else { items.write[p_idx].selected = false; } - update(); + queue_redraw(); } void ItemList::deselect_all() { @@ -367,7 +411,7 @@ void ItemList::deselect_all() { items.write[i].selected = false; } current = -1; - update(); + queue_redraw(); } bool ItemList::is_selected(int p_idx) const { @@ -379,11 +423,15 @@ bool ItemList::is_selected(int p_idx) const { void ItemList::set_current(int p_current) { ERR_FAIL_INDEX(p_current, items.size()); + if (current == p_current) { + return; + } + if (select_mode == SELECT_SINGLE) { select(p_current, true); } else { current = p_current; - update(); + queue_redraw(); } } @@ -403,15 +451,20 @@ void ItemList::move_item(int p_from_idx, int p_to_idx) { items.remove_at(p_from_idx); items.insert(p_to_idx, item); - update(); + queue_redraw(); shape_changed = true; notify_property_list_changed(); } void ItemList::set_item_count(int p_count) { ERR_FAIL_COND(p_count < 0); + + if (items.size() == p_count) { + return; + } + items.resize(p_count); - update(); + queue_redraw(); shape_changed = true; notify_property_list_changed(); } @@ -427,7 +480,7 @@ void ItemList::remove_item(int p_idx) { if (current == p_idx) { current = -1; } - update(); + queue_redraw(); shape_changed = true; defer_select_single = -1; notify_property_list_changed(); @@ -437,7 +490,7 @@ void ItemList::clear() { items.clear(); current = -1; ensure_selected_visible = false; - update(); + queue_redraw(); shape_changed = true; defer_select_single = -1; notify_property_list_changed(); @@ -445,8 +498,13 @@ void ItemList::clear() { void ItemList::set_fixed_column_width(int p_size) { ERR_FAIL_COND(p_size < 0); + + if (fixed_column_width == p_size) { + return; + } + fixed_column_width = p_size; - update(); + queue_redraw(); shape_changed = true; } @@ -455,8 +513,12 @@ int ItemList::get_fixed_column_width() const { } void ItemList::set_same_column_width(bool p_enable) { + if (same_column_width == p_enable) { + return; + } + same_column_width = p_enable; - update(); + queue_redraw(); shape_changed = true; } @@ -470,14 +532,14 @@ void ItemList::set_max_text_lines(int p_lines) { max_text_lines = p_lines; for (int i = 0; i < items.size(); i++) { if (icon_mode == ICON_MODE_TOP && max_text_lines > 0) { - items.write[i].text_buf->set_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND); + items.write[i].text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_TRIM_EDGE_SPACES); items.write[i].text_buf->set_max_lines_visible(p_lines); } else { - items.write[i].text_buf->set_flags(TextServer::BREAK_NONE); + items.write[i].text_buf->set_break_flags(TextServer::BREAK_NONE); } } shape_changed = true; - update(); + queue_redraw(); } } @@ -487,8 +549,13 @@ int ItemList::get_max_text_lines() const { void ItemList::set_max_columns(int p_amount) { ERR_FAIL_COND(p_amount < 0); + + if (max_columns == p_amount) { + return; + } + max_columns = p_amount; - update(); + queue_redraw(); shape_changed = true; } @@ -497,8 +564,12 @@ int ItemList::get_max_columns() const { } void ItemList::set_select_mode(SelectMode p_mode) { + if (select_mode == p_mode) { + return; + } + select_mode = p_mode; - update(); + queue_redraw(); } ItemList::SelectMode ItemList::get_select_mode() const { @@ -511,13 +582,13 @@ void ItemList::set_icon_mode(IconMode p_mode) { icon_mode = p_mode; for (int i = 0; i < items.size(); i++) { if (icon_mode == ICON_MODE_TOP && max_text_lines > 0) { - items.write[i].text_buf->set_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND); + items.write[i].text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_TRIM_EDGE_SPACES); } else { - items.write[i].text_buf->set_flags(TextServer::BREAK_NONE); + items.write[i].text_buf->set_break_flags(TextServer::BREAK_NONE); } } shape_changed = true; - update(); + queue_redraw(); } } @@ -525,12 +596,16 @@ ItemList::IconMode ItemList::get_icon_mode() const { return icon_mode; } -void ItemList::set_fixed_icon_size(const Size2 &p_size) { +void ItemList::set_fixed_icon_size(const Size2i &p_size) { + if (fixed_icon_size == p_size) { + return; + } + fixed_icon_size = p_size; - update(); + queue_redraw(); } -Size2 ItemList::get_fixed_icon_size() const { +Size2i ItemList::get_fixed_icon_size() const { return fixed_icon_size; } @@ -580,32 +655,19 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) { if (mb.is_valid() && mb->is_pressed()) { search_string = ""; //any mousepress cancels Vector2 pos = mb->get_position(); - Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg")); - pos -= bg->get_offset(); + pos -= theme_cache.panel_style->get_offset(); pos.y += scroll_bar->get_value(); if (is_layout_rtl()) { pos.x = get_size().width - pos.x; } - int closest = -1; - - for (int i = 0; i < items.size(); i++) { - Rect2 rc = items[i].rect_cache; - if (i % current_columns == current_columns - 1) { - rc.size.width = get_size().width; //not right but works - } - - if (rc.has_point(pos)) { - closest = i; - break; - } - } + int closest = get_item_at_position(mb->get_position(), true); if (closest != -1 && (mb->get_button_index() == MouseButton::LEFT || (allow_rmb_select && mb->get_button_index() == MouseButton::RIGHT))) { int i = closest; - if (select_mode == SELECT_MULTI && items[i].selected && mb->is_command_pressed()) { + if (select_mode == SELECT_MULTI && items[i].selected && mb->is_command_or_control_pressed()) { deselect(i); emit_signal(SNAME("multi_selected"), i, false); @@ -628,13 +690,13 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) { emit_signal(SNAME("item_clicked"), i, get_local_mouse_position(), mb->get_button_index()); } else { - if (!mb->is_double_click() && !mb->is_command_pressed() && select_mode == SELECT_MULTI && items[i].selectable && !items[i].disabled && items[i].selected && mb->get_button_index() == MouseButton::LEFT) { + if (!mb->is_double_click() && !mb->is_command_or_control_pressed() && select_mode == SELECT_MULTI && items[i].selectable && !items[i].disabled && items[i].selected && mb->get_button_index() == MouseButton::LEFT) { defer_select_single = i; return; } if (!items[i].selected || allow_reselect) { - select(i, select_mode == SELECT_SINGLE || !mb->is_command_pressed()); + select(i, select_mode == SELECT_SINGLE || !mb->is_command_or_control_pressed()); if (select_mode == SELECT_SINGLE) { emit_signal(SNAME("item_selected"), i); @@ -886,7 +948,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) { void ItemList::ensure_current_is_visible() { ensure_selected_visible = true; - update(); + queue_redraw(); } static Rect2 _adjust_to_max_size(Size2 p_size, Size2 p_max_size) { @@ -905,11 +967,36 @@ static Rect2 _adjust_to_max_size(Size2 p_size, Size2 p_max_size) { return Rect2(ofs_x, ofs_y, tex_width, tex_height); } +void ItemList::_update_theme_item_cache() { + Control::_update_theme_item_cache(); + + theme_cache.h_separation = get_theme_constant(SNAME("h_separation")); + theme_cache.v_separation = get_theme_constant(SNAME("v_separation")); + + theme_cache.panel_style = get_theme_stylebox(SNAME("panel")); + theme_cache.focus_style = get_theme_stylebox(SNAME("focus")); + + theme_cache.font = get_theme_font(SNAME("font")); + theme_cache.font_size = get_theme_font_size(SNAME("font_size")); + theme_cache.font_color = get_theme_color(SNAME("font_color")); + theme_cache.font_selected_color = get_theme_color(SNAME("font_selected_color")); + theme_cache.font_outline_size = get_theme_constant(SNAME("outline_size")); + theme_cache.font_outline_color = get_theme_color(SNAME("font_outline_color")); + + theme_cache.line_separation = get_theme_constant(SNAME("line_separation")); + theme_cache.icon_margin = get_theme_constant(SNAME("icon_margin")); + theme_cache.selected_style = get_theme_stylebox(SNAME("selected")); + theme_cache.selected_focus_style = get_theme_stylebox(SNAME("selected_focus")); + theme_cache.cursor_style = get_theme_stylebox(SNAME("cursor_unfocused")); + theme_cache.cursor_focus_style = get_theme_stylebox(SNAME("cursor")); + theme_cache.guide_color = get_theme_color(SNAME("guide_color")); +} + void ItemList::_notification(int p_what) { switch (p_what) { case NOTIFICATION_RESIZED: { shape_changed = true; - update(); + queue_redraw(); } break; case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: @@ -919,45 +1006,36 @@ void ItemList::_notification(int p_what) { _shape(i); } shape_changed = true; - update(); + queue_redraw(); } break; case NOTIFICATION_DRAW: { - Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg")); - int mw = scroll_bar->get_minimum_size().x; scroll_bar->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -mw); scroll_bar->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0); - scroll_bar->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, bg->get_margin(SIDE_TOP)); - scroll_bar->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, -bg->get_margin(SIDE_BOTTOM)); + scroll_bar->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, theme_cache.panel_style->get_margin(SIDE_TOP)); + scroll_bar->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, -theme_cache.panel_style->get_margin(SIDE_BOTTOM)); Size2 size = get_size(); + int width = size.width - theme_cache.panel_style->get_minimum_size().width; - int width = size.width - bg->get_minimum_size().width; - if (scroll_bar->is_visible()) { - width -= mw; - } + draw_style_box(theme_cache.panel_style, Rect2(Point2(), size)); - draw_style_box(bg, Rect2(Point2(), size)); + Ref<StyleBox> sbsel; + Ref<StyleBox> cursor; - int hseparation = get_theme_constant(SNAME("h_separation")); - int vseparation = get_theme_constant(SNAME("v_separation")); - int icon_margin = get_theme_constant(SNAME("icon_margin")); - int line_separation = get_theme_constant(SNAME("line_separation")); - Color font_outline_color = get_theme_color(SNAME("font_outline_color")); - int outline_size = get_theme_constant(SNAME("outline_size")); - - Ref<StyleBox> sbsel = has_focus() ? get_theme_stylebox(SNAME("selected_focus")) : get_theme_stylebox(SNAME("selected")); - Ref<StyleBox> cursor = has_focus() ? get_theme_stylebox(SNAME("cursor")) : get_theme_stylebox(SNAME("cursor_unfocused")); + if (has_focus()) { + sbsel = theme_cache.selected_focus_style; + cursor = theme_cache.cursor_focus_style; + } else { + sbsel = theme_cache.selected_style; + cursor = theme_cache.cursor_style; + } bool rtl = is_layout_rtl(); - Color guide_color = get_theme_color(SNAME("guide_color")); - Color font_color = get_theme_color(SNAME("font_color")); - Color font_selected_color = get_theme_color(SNAME("font_selected_color")); - if (has_focus()) { RenderingServer::get_singleton()->canvas_item_add_clip_ignore(get_canvas_item(), true); - draw_style_box(get_theme_stylebox(SNAME("bg_focus")), Rect2(Point2(), size)); + draw_style_box(theme_cache.focus_style, Rect2(Point2(), size)); RenderingServer::get_singleton()->canvas_item_add_clip_ignore(get_canvas_item(), false); } @@ -976,9 +1054,9 @@ void ItemList::_notification(int p_what) { if (!items[i].text.is_empty()) { if (icon_mode == ICON_MODE_TOP) { - minsize.y += icon_margin; + minsize.y += theme_cache.icon_margin; } else { - minsize.x += icon_margin; + minsize.x += theme_cache.icon_margin; } } } @@ -996,7 +1074,7 @@ void ItemList::_notification(int p_what) { if (icon_mode == ICON_MODE_TOP) { minsize.x = MAX(minsize.x, s.width); if (max_text_lines > 0) { - minsize.y += s.height + line_separation * max_text_lines; + minsize.y += s.height + theme_cache.line_separation * max_text_lines; } else { minsize.y += s.height; } @@ -1013,13 +1091,13 @@ void ItemList::_notification(int p_what) { max_column_width = MAX(max_column_width, minsize.x); // elements need to adapt to the selected size - minsize.y += vseparation; - minsize.x += hseparation; + minsize.y += theme_cache.v_separation; + minsize.x += theme_cache.h_separation; items.write[i].rect_cache.size = minsize; items.write[i].min_rect_cache.size = minsize; } - int fit_size = size.x - bg->get_minimum_size().width - mw; + int fit_size = size.x - theme_cache.panel_style->get_minimum_size().width - mw; //2-attempt best fit current_columns = 0x7FFFFFFF; @@ -1047,11 +1125,11 @@ void ItemList::_notification(int p_what) { } items.write[i].rect_cache.position = ofs; max_h = MAX(max_h, items[i].rect_cache.size.y); - ofs.x += items[i].rect_cache.size.x + hseparation; + ofs.x += items[i].rect_cache.size.x + theme_cache.h_separation; col++; if (col == current_columns) { if (i < items.size() - 1) { - separators.push_back(ofs.y + max_h + vseparation / 2); + separators.push_back(ofs.y + max_h + theme_cache.v_separation / 2); } for (int j = i; j >= 0 && col > 0; j--, col--) { @@ -1059,7 +1137,7 @@ void ItemList::_notification(int p_what) { } ofs.x = 0; - ofs.y += max_h + vseparation; + ofs.y += max_h + theme_cache.v_separation; col = 0; max_h = 0; } @@ -1070,10 +1148,10 @@ void ItemList::_notification(int p_what) { } if (all_fit) { - float page = MAX(0, size.height - bg->get_minimum_size().height); + float page = MAX(0, size.height - theme_cache.panel_style->get_minimum_size().height); float max = MAX(page, ofs.y + max_h); if (auto_height) { - auto_height_value = ofs.y + max_h + bg->get_minimum_size().height; + auto_height_value = ofs.y + max_h + theme_cache.panel_style->get_minimum_size().height; } scroll_bar->set_max(max); scroll_bar->set_page(page); @@ -1095,6 +1173,10 @@ void ItemList::_notification(int p_what) { shape_changed = false; } + if (scroll_bar->is_visible()) { + width -= mw; + } + //ensure_selected_visible needs to be checked before we draw the list. if (ensure_selected_visible && current >= 0 && current < items.size()) { Rect2 r = items[current].rect_cache; @@ -1110,7 +1192,7 @@ void ItemList::_notification(int p_what) { ensure_selected_visible = false; - Vector2 base_ofs = bg->get_offset(); + Vector2 base_ofs = theme_cache.panel_style->get_offset(); base_ofs.y -= int(scroll_bar->get_value()); const Rect2 clip(-base_ofs, size); // visible frame, don't need to draw outside of there @@ -1154,10 +1236,10 @@ void ItemList::_notification(int p_what) { if (items[i].selected) { Rect2 r = rcache; r.position += base_ofs; - r.position.y -= vseparation / 2; - r.size.y += vseparation; - r.position.x -= hseparation / 2; - r.size.x += hseparation; + r.position.y -= theme_cache.v_separation / 2; + r.size.y += theme_cache.v_separation; + r.position.x -= theme_cache.h_separation / 2; + r.size.x += theme_cache.h_separation; if (rtl) { r.position.x = size.width - r.position.x - r.size.x; @@ -1170,10 +1252,10 @@ void ItemList::_notification(int p_what) { r.position += base_ofs; // Size rect to make the align the temperature colors - r.position.y -= vseparation / 2; - r.size.y += vseparation; - r.position.x -= hseparation / 2; - r.size.x += hseparation; + r.position.y -= theme_cache.v_separation / 2; + r.size.y += theme_cache.v_separation; + r.position.x -= theme_cache.h_separation / 2; + r.size.x += theme_cache.h_separation; if (rtl) { r.position.x = size.width - r.position.x - r.size.x; @@ -1199,11 +1281,11 @@ void ItemList::_notification(int p_what) { if (icon_mode == ICON_MODE_TOP) { pos.x += Math::floor((items[i].rect_cache.size.width - icon_size.width) / 2); - pos.y += icon_margin; - text_ofs.y = icon_size.height + icon_margin * 2; + pos.y += theme_cache.icon_margin; + text_ofs.y = icon_size.height + theme_cache.icon_margin * 2; } else { pos.y += Math::floor((items[i].rect_cache.size.height - icon_size.height) / 2); - text_ofs.x = icon_size.width + icon_margin; + text_ofs.x = icon_size.width + theme_cache.icon_margin; } Rect2 draw_rect = Rect2(pos, icon_size); @@ -1254,7 +1336,7 @@ void ItemList::_notification(int p_what) { max_len = size2.x; } - Color modulate = items[i].selected ? font_selected_color : (items[i].custom_fg != Color() ? items[i].custom_fg : font_color); + Color modulate = items[i].selected ? theme_cache.font_selected_color : (items[i].custom_fg != Color() ? items[i].custom_fg : theme_cache.font_color); if (items[i].disabled) { modulate.a *= 0.5; } @@ -1269,8 +1351,8 @@ void ItemList::_notification(int p_what) { items.write[i].text_buf->set_alignment(HORIZONTAL_ALIGNMENT_CENTER); - if (outline_size > 0 && font_outline_color.a > 0) { - items[i].text_buf->draw_outline(get_canvas_item(), text_ofs, outline_size, font_outline_color); + if (theme_cache.font_outline_size > 0 && theme_cache.font_outline_color.a > 0) { + items[i].text_buf->draw_outline(get_canvas_item(), text_ofs, theme_cache.font_outline_size, theme_cache.font_outline_color); } items[i].text_buf->draw(get_canvas_item(), text_ofs, modulate); @@ -1300,8 +1382,8 @@ void ItemList::_notification(int p_what) { items.write[i].text_buf->set_alignment(HORIZONTAL_ALIGNMENT_LEFT); } - if (outline_size > 0 && font_outline_color.a > 0) { - items[i].text_buf->draw_outline(get_canvas_item(), text_ofs, outline_size, font_outline_color); + if (theme_cache.font_outline_size > 0 && theme_cache.font_outline_color.a > 0) { + items[i].text_buf->draw_outline(get_canvas_item(), text_ofs, theme_cache.font_outline_size, theme_cache.font_outline_color); } if (width - text_ofs.x > 0) { @@ -1313,10 +1395,10 @@ void ItemList::_notification(int p_what) { if (select_mode == SELECT_MULTI && i == current) { Rect2 r = rcache; r.position += base_ofs; - r.position.y -= vseparation / 2; - r.size.y += vseparation; - r.position.x -= hseparation / 2; - r.size.x += hseparation; + r.position.y -= theme_cache.v_separation / 2; + r.size.y += theme_cache.v_separation; + r.position.x -= theme_cache.h_separation / 2; + r.size.x += theme_cache.h_separation; if (rtl) { r.position.x = size.width - r.position.x - r.size.x; @@ -1348,20 +1430,19 @@ void ItemList::_notification(int p_what) { } const int y = base_ofs.y + separators[i]; - draw_line(Vector2(bg->get_margin(SIDE_LEFT), y), Vector2(width, y), guide_color); + draw_line(Vector2(theme_cache.panel_style->get_margin(SIDE_LEFT), y), Vector2(width, y), theme_cache.guide_color); } } break; } } void ItemList::_scroll_changed(double) { - update(); + queue_redraw(); } int ItemList::get_item_at_position(const Point2 &p_pos, bool p_exact) const { Vector2 pos = p_pos; - Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg")); - pos -= bg->get_offset(); + pos -= theme_cache.panel_style->get_offset(); pos.y += scroll_bar->get_value(); if (is_layout_rtl()) { @@ -1374,7 +1455,7 @@ int ItemList::get_item_at_position(const Point2 &p_pos, bool p_exact) const { for (int i = 0; i < items.size(); i++) { Rect2 rc = items[i].rect_cache; if (i % current_columns == current_columns - 1) { - rc.size.width = get_size().width - rc.position.x; //make sure you can still select the last item when clicking past the column + rc.size.width = get_size().width - rc.position.x; // Make sure you can still select the last item when clicking past the column. } if (rc.has_point(pos)) { @@ -1398,8 +1479,7 @@ bool ItemList::is_pos_at_end_of_items(const Point2 &p_pos) const { } Vector2 pos = p_pos; - Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg")); - pos -= bg->get_offset(); + pos -= theme_cache.panel_style->get_offset(); pos.y += scroll_bar->get_value(); if (is_layout_rtl()) { @@ -1430,7 +1510,7 @@ String ItemList::get_tooltip(const Point2 &p_pos) const { void ItemList::sort_items_by_text() { items.sort(); - update(); + queue_redraw(); shape_changed = true; if (select_mode == SELECT_SINGLE) { @@ -1512,9 +1592,13 @@ void ItemList::set_autoscroll_to_bottom(const bool p_enable) { } void ItemList::set_auto_height(bool p_enable) { + if (auto_height == p_enable) { + return; + } + auto_height = p_enable; shape_changed = true; - update(); + queue_redraw(); } bool ItemList::has_auto_height() const { @@ -1528,7 +1612,7 @@ void ItemList::set_text_overrun_behavior(TextServer::OverrunBehavior p_behavior) items.write[i].text_buf->set_text_overrun_behavior(p_behavior); } shape_changed = true; - update(); + queue_redraw(); } } @@ -1734,7 +1818,7 @@ void ItemList::_bind_methods() { ADD_GROUP("Icon", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "icon_mode", PROPERTY_HINT_ENUM, "Top,Left"), "set_icon_mode", "get_icon_mode"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "icon_scale"), "set_icon_scale", "get_icon_scale"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "fixed_icon_size", PROPERTY_HINT_NONE, "suffix:px"), "set_fixed_icon_size", "get_fixed_icon_size"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "fixed_icon_size", PROPERTY_HINT_NONE, "suffix:px"), "set_fixed_icon_size", "get_fixed_icon_size"); BIND_ENUM_CONSTANT(ICON_MODE_TOP); BIND_ENUM_CONSTANT(ICON_MODE_LEFT); diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h index c7d87da0b5..4b1b9d9282 100644 --- a/scene/gui/item_list.h +++ b/scene/gui/item_list.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef ITEMLIST_H -#define ITEMLIST_H +#ifndef ITEM_LIST_H +#define ITEM_LIST_H #include "scene/gui/control.h" #include "scene/gui/scroll_bar.h" @@ -109,23 +109,45 @@ private: int max_columns = 1; Size2 fixed_icon_size; - Size2 max_item_size_cache; int defer_select_single = -1; - bool allow_rmb_select = false; - bool allow_reselect = false; real_t icon_scale = 1.0; bool do_autoscroll_to_bottom = false; + struct ThemeCache { + int h_separation = 0; + int v_separation = 0; + + Ref<StyleBox> panel_style; + Ref<StyleBox> focus_style; + + Ref<Font> font; + int font_size = 0; + Color font_color; + Color font_selected_color; + int font_outline_size = 0; + Color font_outline_color; + + int line_separation = 0; + int icon_margin = 0; + Ref<StyleBox> selected_style; + Ref<StyleBox> selected_focus_style; + Ref<StyleBox> cursor_style; + Ref<StyleBox> cursor_focus_style; + Color guide_color; + } theme_cache; + void _scroll_changed(double); void _shape(int p_idx); protected: + virtual void _update_theme_item_cache() override; + void _notification(int p_what); bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; @@ -222,8 +244,8 @@ public: void set_icon_mode(IconMode p_mode); IconMode get_icon_mode() const; - void set_fixed_icon_size(const Size2 &p_size); - Size2 get_fixed_icon_size() const; + void set_fixed_icon_size(const Size2i &p_size); + Size2i get_fixed_icon_size() const; void set_allow_rmb_select(bool p_allow); bool get_allow_rmb_select() const; @@ -259,4 +281,4 @@ public: VARIANT_ENUM_CAST(ItemList::SelectMode); VARIANT_ENUM_CAST(ItemList::IconMode); -#endif // ITEMLIST_H +#endif // ITEM_LIST_H diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index 5dec1df4a5..306ca3d340 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -31,17 +31,20 @@ #include "label.h" #include "core/config/project_settings.h" +#include "core/core_string_names.h" #include "core/string/print_string.h" #include "core/string/translation.h" #include "servers/text_server.h" void Label::set_autowrap_mode(TextServer::AutowrapMode p_mode) { - if (autowrap_mode != p_mode) { - autowrap_mode = p_mode; - lines_dirty = true; + if (autowrap_mode == p_mode) { + return; } - update(); + + autowrap_mode = p_mode; + lines_dirty = true; + queue_redraw(); if (clip || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) { update_minimum_size(); @@ -53,10 +56,14 @@ TextServer::AutowrapMode Label::get_autowrap_mode() const { } void Label::set_uppercase(bool p_uppercase) { + if (uppercase == p_uppercase) { + return; + } + uppercase = p_uppercase; dirty = true; - update(); + queue_redraw(); } bool Label::is_uppercase() const { @@ -64,7 +71,7 @@ bool Label::is_uppercase() const { } int Label::get_line_height(int p_line) const { - Ref<Font> font = get_theme_font(SNAME("font")); + Ref<Font> font = (settings.is_valid() && settings->get_font().is_valid()) ? settings->get_font() : theme_cache.font; if (p_line >= 0 && p_line < lines_rid.size()) { return TS->shaped_text_get_size(lines_rid[p_line]).y; } else if (lines_rid.size() > 0) { @@ -74,12 +81,13 @@ int Label::get_line_height(int p_line) const { } return h; } else { - return font->get_height(get_theme_font_size(SNAME("font_size"))); + int font_size = settings.is_valid() ? settings->get_font_size() : theme_cache.font_size; + return font->get_height(font_size); } } void Label::_shape() { - Ref<StyleBox> style = get_theme_stylebox(SNAME("normal"), SNAME("Label")); + Ref<StyleBox> style = theme_cache.normal_style; int width = (get_size().width - style->get_minimum_size().width); if (dirty || font_dirty) { @@ -91,8 +99,8 @@ void Label::_shape() { } else { TS->shaped_text_set_direction(text_rid, (TextServer::Direction)text_direction); } - const Ref<Font> &font = get_theme_font(SNAME("font")); - int font_size = get_theme_font_size(SNAME("font_size")); + const Ref<Font> &font = (settings.is_valid() && settings->get_font().is_valid()) ? settings->get_font() : theme_cache.font; + int font_size = settings.is_valid() ? settings->get_font_size() : theme_cache.font_size; ERR_FAIL_COND(font.is_null()); String text = (uppercase) ? TS->string_to_upper(xl_text, language) : xl_text; if (visible_chars >= 0 && visible_chars_behavior == TextServer::VC_CHARS_BEFORE_SHAPING) { @@ -121,10 +129,10 @@ void Label::_shape() { } lines_rid.clear(); - uint16_t autowrap_flags = TextServer::BREAK_MANDATORY; + BitField<TextServer::LineBreakFlag> autowrap_flags = TextServer::BREAK_MANDATORY; switch (autowrap_mode) { case TextServer::AUTOWRAP_WORD_SMART: - autowrap_flags = TextServer::BREAK_WORD_BOUND_ADAPTIVE | TextServer::BREAK_MANDATORY; + autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_ADAPTIVE | TextServer::BREAK_MANDATORY; break; case TextServer::AUTOWRAP_WORD: autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_MANDATORY; @@ -135,8 +143,9 @@ void Label::_shape() { case TextServer::AUTOWRAP_OFF: break; } - PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(text_rid, width, 0, autowrap_flags); + autowrap_flags = autowrap_flags | TextServer::BREAK_TRIM_EDGE_SPACES; + PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(text_rid, width, 0, autowrap_flags); for (int i = 0; i < line_breaks.size(); i = i + 2) { RID line = TS->shaped_text_substr(text_rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]); lines_rid.push_back(line); @@ -158,23 +167,23 @@ void Label::_shape() { } if (lines_dirty) { - uint16_t overrun_flags = TextServer::OVERRUN_NO_TRIM; + BitField<TextServer::TextOverrunFlag> overrun_flags = TextServer::OVERRUN_NO_TRIM; switch (overrun_behavior) { case TextServer::OVERRUN_TRIM_WORD_ELLIPSIS: - overrun_flags |= TextServer::OVERRUN_TRIM; - overrun_flags |= TextServer::OVERRUN_TRIM_WORD_ONLY; - overrun_flags |= TextServer::OVERRUN_ADD_ELLIPSIS; + overrun_flags.set_flag(TextServer::OVERRUN_TRIM); + overrun_flags.set_flag(TextServer::OVERRUN_TRIM_WORD_ONLY); + overrun_flags.set_flag(TextServer::OVERRUN_ADD_ELLIPSIS); break; case TextServer::OVERRUN_TRIM_ELLIPSIS: - overrun_flags |= TextServer::OVERRUN_TRIM; - overrun_flags |= TextServer::OVERRUN_ADD_ELLIPSIS; + overrun_flags.set_flag(TextServer::OVERRUN_TRIM); + overrun_flags.set_flag(TextServer::OVERRUN_ADD_ELLIPSIS); break; case TextServer::OVERRUN_TRIM_WORD: - overrun_flags |= TextServer::OVERRUN_TRIM; - overrun_flags |= TextServer::OVERRUN_TRIM_WORD_ONLY; + overrun_flags.set_flag(TextServer::OVERRUN_TRIM); + overrun_flags.set_flag(TextServer::OVERRUN_TRIM_WORD_ONLY); break; case TextServer::OVERRUN_TRIM_CHAR: - overrun_flags |= TextServer::OVERRUN_TRIM; + overrun_flags.set_flag(TextServer::OVERRUN_TRIM); break; case TextServer::OVERRUN_NO_TRIMMING: break; @@ -186,7 +195,7 @@ void Label::_shape() { int visible_lines = get_visible_line_count(); bool lines_hidden = visible_lines > 0 && visible_lines < lines_rid.size(); if (lines_hidden) { - overrun_flags |= TextServer::OVERRUN_ENFORCE_ELLIPSIS; + overrun_flags.set_flag(TextServer::OVERRUN_ENFORCE_ELLIPSIS); } if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) { for (int i = 0; i < lines_rid.size(); i++) { @@ -204,7 +213,7 @@ void Label::_shape() { for (int i = 0; i < lines_rid.size(); i++) { if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) { TS->shaped_text_fit_to_width(lines_rid[i], width); - overrun_flags |= TextServer::OVERRUN_JUSTIFICATION_AWARE; + overrun_flags.set_flag(TextServer::OVERRUN_JUSTIFICATION_AWARE); TS->shaped_text_overrun_trim_to_width(lines_rid[i], width, overrun_flags); TS->shaped_text_fit_to_width(lines_rid[i], width, TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_CONSTRAIN_ELLIPSIS); } else { @@ -223,9 +232,8 @@ void Label::_shape() { } void Label::_update_visible() { - int line_spacing = get_theme_constant(SNAME("line_spacing"), SNAME("Label")); - Ref<StyleBox> style = get_theme_stylebox(SNAME("normal"), SNAME("Label")); - Ref<Font> font = get_theme_font(SNAME("font")); + int line_spacing = settings.is_valid() ? settings->get_line_spacing() : theme_cache.line_spacing; + Ref<StyleBox> style = theme_cache.normal_style; int lines_visible = lines_rid.size(); if (max_lines_visible >= 0 && lines_visible > max_lines_visible) { @@ -264,6 +272,22 @@ inline void draw_glyph_outline(const Glyph &p_gl, const RID &p_canvas, const Col } } +void Label::_update_theme_item_cache() { + Control::_update_theme_item_cache(); + + theme_cache.normal_style = get_theme_stylebox(SNAME("normal")); + theme_cache.font = get_theme_font(SNAME("font")); + + theme_cache.font_size = get_theme_font_size(SNAME("font_size")); + theme_cache.line_spacing = get_theme_constant(SNAME("line_spacing")); + theme_cache.font_color = get_theme_color(SNAME("font_color")); + theme_cache.font_shadow_color = get_theme_color(SNAME("font_shadow_color")); + theme_cache.font_shadow_offset = Point2(get_theme_constant(SNAME("shadow_offset_x")), get_theme_constant(SNAME("shadow_offset_y"))); + theme_cache.font_outline_color = get_theme_color(SNAME("font_outline_color")); + theme_cache.font_outline_size = get_theme_constant(SNAME("outline_size")); + theme_cache.font_shadow_outline_size = get_theme_constant(SNAME("shadow_outline_size")); +} + void Label::_notification(int p_what) { switch (p_what) { case NOTIFICATION_TRANSLATION_CHANGED: { @@ -272,16 +296,16 @@ void Label::_notification(int p_what) { return; // Nothing new. } xl_text = new_text; - if (percent_visible < 1) { - visible_chars = get_total_character_count() * percent_visible; + if (visible_ratio < 1) { + visible_chars = get_total_character_count() * visible_ratio; } dirty = true; - update(); + queue_redraw(); } break; case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { - update(); + queue_redraw(); } break; case NOTIFICATION_DRAW: { @@ -295,17 +319,19 @@ void Label::_notification(int p_what) { RID ci = get_canvas_item(); + bool has_settings = settings.is_valid(); + Size2 string_size; Size2 size = get_size(); - Ref<StyleBox> style = get_theme_stylebox(SNAME("normal")); - Ref<Font> font = get_theme_font(SNAME("font")); - Color font_color = get_theme_color(SNAME("font_color")); - Color font_shadow_color = get_theme_color(SNAME("font_shadow_color")); - Point2 shadow_ofs(get_theme_constant(SNAME("shadow_offset_x")), get_theme_constant(SNAME("shadow_offset_y"))); - int line_spacing = get_theme_constant(SNAME("line_spacing")); - Color font_outline_color = get_theme_color(SNAME("font_outline_color")); - int outline_size = get_theme_constant(SNAME("outline_size")); - int shadow_outline_size = get_theme_constant(SNAME("shadow_outline_size")); + Ref<StyleBox> style = theme_cache.normal_style; + Ref<Font> font = (has_settings && settings->get_font().is_valid()) ? settings->get_font() : theme_cache.font; + Color font_color = has_settings ? settings->get_font_color() : theme_cache.font_color; + Color font_shadow_color = has_settings ? settings->get_shadow_color() : theme_cache.font_shadow_color; + Point2 shadow_ofs = has_settings ? settings->get_shadow_offset() : theme_cache.font_shadow_offset; + int line_spacing = has_settings ? settings->get_line_spacing() : theme_cache.line_spacing; + Color font_outline_color = has_settings ? settings->get_outline_color() : theme_cache.font_outline_color; + int outline_size = has_settings ? settings->get_outline_size() : theme_cache.font_outline_size; + int shadow_outline_size = has_settings ? settings->get_shadow_size() : theme_cache.font_shadow_outline_size; bool rtl = (TS->shaped_text_get_inferred_direction(text_rid) == TextServer::DIRECTION_RTL); bool rtl_layout = is_layout_rtl(); @@ -339,7 +365,7 @@ void Label::_notification(int p_what) { total_h += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing; total_glyphs += TS->shaped_text_get_glyph_count(lines_rid[i]) + TS->shaped_text_get_ellipsis_glyph_count(lines_rid[i]); } - int visible_glyphs = total_glyphs * percent_visible; + int visible_glyphs = total_glyphs * visible_ratio; int processed_glyphs = 0; total_h += style->get_margin(SIDE_TOP) + style->get_margin(SIDE_BOTTOM); @@ -535,7 +561,7 @@ void Label::_notification(int p_what) { case NOTIFICATION_THEME_CHANGED: { font_dirty = true; - update(); + queue_redraw(); } break; case NOTIFICATION_RESIZED: { @@ -552,10 +578,12 @@ Size2 Label::get_minimum_size() const { Size2 min_size = minsize; - Ref<Font> font = get_theme_font(SNAME("font")); - min_size.height = MAX(min_size.height, font->get_height(get_theme_font_size(SNAME("font_size")))); + const Ref<Font> &font = (settings.is_valid() && settings->get_font().is_valid()) ? settings->get_font() : theme_cache.font; + int font_size = settings.is_valid() ? settings->get_font_size() : theme_cache.font_size; - Size2 min_style = get_theme_stylebox(SNAME("normal"))->get_minimum_size(); + min_size.height = MAX(min_size.height, font->get_height(font_size) + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM)); + + Size2 min_style = theme_cache.normal_style->get_minimum_size(); if (autowrap_mode != TextServer::AUTOWRAP_OFF) { return Size2(1, (clip || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) ? 1 : min_size.height) + min_style; } else { @@ -578,9 +606,8 @@ int Label::get_line_count() const { } int Label::get_visible_line_count() const { - Ref<Font> font = get_theme_font(SNAME("font")); - Ref<StyleBox> style = get_theme_stylebox(SNAME("normal")); - int line_spacing = get_theme_constant(SNAME("line_spacing")); + Ref<StyleBox> style = theme_cache.normal_style; + int line_spacing = settings.is_valid() ? settings->get_line_spacing() : theme_cache.line_spacing; int lines_visible = 0; float total_h = 0.0; for (int64_t i = lines_skipped; i < lines_rid.size(); i++) { @@ -604,13 +631,16 @@ int Label::get_visible_line_count() const { void Label::set_horizontal_alignment(HorizontalAlignment p_alignment) { ERR_FAIL_INDEX((int)p_alignment, 4); - if (horizontal_alignment != p_alignment) { - if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL || p_alignment == HORIZONTAL_ALIGNMENT_FILL) { - lines_dirty = true; // Reshape lines. - } - horizontal_alignment = p_alignment; + if (horizontal_alignment == p_alignment) { + return; } - update(); + + if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL || p_alignment == HORIZONTAL_ALIGNMENT_FILL) { + lines_dirty = true; // Reshape lines. + } + horizontal_alignment = p_alignment; + + queue_redraw(); } HorizontalAlignment Label::get_horizontal_alignment() const { @@ -619,8 +649,13 @@ HorizontalAlignment Label::get_horizontal_alignment() const { void Label::set_vertical_alignment(VerticalAlignment p_alignment) { ERR_FAIL_INDEX((int)p_alignment, 4); + + if (vertical_alignment == p_alignment) { + return; + } + vertical_alignment = p_alignment; - update(); + queue_redraw(); } VerticalAlignment Label::get_vertical_alignment() const { @@ -634,19 +669,41 @@ void Label::set_text(const String &p_string) { text = p_string; xl_text = atr(p_string); dirty = true; - if (percent_visible < 1) { - visible_chars = get_total_character_count() * percent_visible; + if (visible_ratio < 1) { + visible_chars = get_total_character_count() * visible_ratio; } - update(); + queue_redraw(); update_minimum_size(); } +void Label::_invalidate() { + font_dirty = true; + queue_redraw(); +} + +void Label::set_label_settings(const Ref<LabelSettings> &p_settings) { + if (settings != p_settings) { + if (settings.is_valid()) { + settings->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Label::_invalidate)); + } + settings = p_settings; + if (settings.is_valid()) { + settings->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Label::_invalidate), CONNECT_REFERENCE_COUNTED); + } + _invalidate(); + } +} + +Ref<LabelSettings> Label::get_label_settings() const { + return settings; +} + void Label::set_text_direction(Control::TextDirection p_text_direction) { ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3); if (text_direction != p_text_direction) { text_direction = p_text_direction; font_dirty = true; - update(); + queue_redraw(); } } @@ -654,7 +711,7 @@ void Label::set_structured_text_bidi_override(TextServer::StructuredTextParser p if (st_parser != p_parser) { st_parser = p_parser; dirty = true; - update(); + queue_redraw(); } } @@ -663,9 +720,13 @@ TextServer::StructuredTextParser Label::get_structured_text_bidi_override() cons } void Label::set_structured_text_bidi_override_options(Array p_args) { + if (st_args == p_args) { + return; + } + st_args = p_args; dirty = true; - update(); + queue_redraw(); } Array Label::get_structured_text_bidi_override_options() const { @@ -680,7 +741,7 @@ void Label::set_language(const String &p_language) { if (language != p_language) { language = p_language; dirty = true; - update(); + queue_redraw(); } } @@ -689,8 +750,12 @@ String Label::get_language() const { } void Label::set_clip_text(bool p_clip) { + if (clip == p_clip) { + return; + } + clip = p_clip; - update(); + queue_redraw(); update_minimum_size(); } @@ -699,11 +764,13 @@ bool Label::is_clipping_text() const { } void Label::set_text_overrun_behavior(TextServer::OverrunBehavior p_behavior) { - if (overrun_behavior != p_behavior) { - overrun_behavior = p_behavior; - lines_dirty = true; + if (overrun_behavior == p_behavior) { + return; } - update(); + + overrun_behavior = p_behavior; + lines_dirty = true; + queue_redraw(); if (clip || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) { update_minimum_size(); } @@ -721,14 +788,14 @@ void Label::set_visible_characters(int p_amount) { if (visible_chars != p_amount) { visible_chars = p_amount; if (get_total_character_count() > 0) { - percent_visible = (float)p_amount / (float)get_total_character_count(); + visible_ratio = (float)p_amount / (float)get_total_character_count(); } else { - percent_visible = 1.0; + visible_ratio = 1.0; } if (visible_chars_behavior == TextServer::VC_CHARS_BEFORE_SHAPING) { dirty = true; } - update(); + queue_redraw(); } } @@ -736,24 +803,28 @@ int Label::get_visible_characters() const { return visible_chars; } -void Label::set_percent_visible(float p_percent) { - if (percent_visible != p_percent) { - if (p_percent < 0 || p_percent >= 1) { +void Label::set_visible_ratio(float p_ratio) { + if (visible_ratio != p_ratio) { + if (p_ratio >= 1.0) { visible_chars = -1; - percent_visible = 1; + visible_ratio = 1.0; + } else if (p_ratio < 0.0) { + visible_chars = 0; + visible_ratio = 0.0; } else { - visible_chars = get_total_character_count() * p_percent; - percent_visible = p_percent; + visible_chars = get_total_character_count() * p_ratio; + visible_ratio = p_ratio; } + if (visible_chars_behavior == TextServer::VC_CHARS_BEFORE_SHAPING) { dirty = true; } - update(); + queue_redraw(); } } -float Label::get_percent_visible() const { - return percent_visible; +float Label::get_visible_ratio() const { + return visible_ratio; } TextServer::VisibleCharactersBehavior Label::get_visible_characters_behavior() const { @@ -764,15 +835,20 @@ void Label::set_visible_characters_behavior(TextServer::VisibleCharactersBehavio if (visible_chars_behavior != p_behavior) { visible_chars_behavior = p_behavior; dirty = true; - update(); + queue_redraw(); } } void Label::set_lines_skipped(int p_lines) { ERR_FAIL_COND(p_lines < 0); + + if (lines_skipped == p_lines) { + return; + } + lines_skipped = p_lines; _update_visible(); - update(); + queue_redraw(); } int Label::get_lines_skipped() const { @@ -780,9 +856,13 @@ int Label::get_lines_skipped() const { } void Label::set_max_lines_visible(int p_lines) { + if (max_lines_visible == p_lines) { + return; + } + max_lines_visible = p_lines; _update_visible(); - update(); + queue_redraw(); } int Label::get_max_lines_visible() const { @@ -804,6 +884,8 @@ void Label::_bind_methods() { ClassDB::bind_method(D_METHOD("get_vertical_alignment"), &Label::get_vertical_alignment); ClassDB::bind_method(D_METHOD("set_text", "text"), &Label::set_text); ClassDB::bind_method(D_METHOD("get_text"), &Label::get_text); + ClassDB::bind_method(D_METHOD("set_label_settings", "settings"), &Label::set_label_settings); + ClassDB::bind_method(D_METHOD("get_label_settings"), &Label::get_label_settings); ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &Label::set_text_direction); ClassDB::bind_method(D_METHOD("get_text_direction"), &Label::get_text_direction); ClassDB::bind_method(D_METHOD("set_language", "language"), &Label::set_language); @@ -824,8 +906,8 @@ void Label::_bind_methods() { ClassDB::bind_method(D_METHOD("get_visible_characters"), &Label::get_visible_characters); ClassDB::bind_method(D_METHOD("get_visible_characters_behavior"), &Label::get_visible_characters_behavior); ClassDB::bind_method(D_METHOD("set_visible_characters_behavior", "behavior"), &Label::set_visible_characters_behavior); - ClassDB::bind_method(D_METHOD("set_percent_visible", "percent_visible"), &Label::set_percent_visible); - ClassDB::bind_method(D_METHOD("get_percent_visible"), &Label::get_percent_visible); + ClassDB::bind_method(D_METHOD("set_visible_ratio", "ratio"), &Label::set_visible_ratio); + ClassDB::bind_method(D_METHOD("get_visible_ratio"), &Label::get_visible_ratio); ClassDB::bind_method(D_METHOD("set_lines_skipped", "lines_skipped"), &Label::set_lines_skipped); ClassDB::bind_method(D_METHOD("get_lines_skipped"), &Label::get_lines_skipped); ClassDB::bind_method(D_METHOD("set_max_lines_visible", "lines_visible"), &Label::set_max_lines_visible); @@ -836,19 +918,21 @@ void Label::_bind_methods() { ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override_options"), &Label::get_structured_text_bidi_override_options); ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT_INTL), "set_text", "get_text"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "label_settings", PROPERTY_HINT_RESOURCE_TYPE, "LabelSettings"), "set_label_settings", "get_label_settings"); ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment"); ADD_PROPERTY(PropertyInfo(Variant::INT, "vertical_alignment", PROPERTY_HINT_ENUM, "Top,Center,Bottom,Fill"), "set_vertical_alignment", "get_vertical_alignment"); ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Off,Arbitrary,Word,Word (Smart)"), "set_autowrap_mode", "get_autowrap_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "is_clipping_text"); ADD_PROPERTY(PropertyInfo(Variant::INT, "text_overrun_behavior", PROPERTY_HINT_ENUM, "Trim Nothing,Trim Characters,Trim Words,Ellipsis,Word Ellipsis"), "set_text_overrun_behavior", "get_text_overrun_behavior"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uppercase"), "set_uppercase", "is_uppercase"); + + ADD_GROUP("Displayed Text", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "lines_skipped", PROPERTY_HINT_RANGE, "0,999,1"), "set_lines_skipped", "get_lines_skipped"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_lines_visible", PROPERTY_HINT_RANGE, "-1,999,1"), "set_max_lines_visible", "get_max_lines_visible"); - - // Note: "visible_characters" and "percent_visible" should be set after "text" to be correctly applied. + // Note: "visible_characters" and "visible_ratio" should be set after "text" to be correctly applied. ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters", PROPERTY_HINT_RANGE, "-1,128000,1"), "set_visible_characters", "get_visible_characters"); ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters_behavior", PROPERTY_HINT_ENUM, "Characters Before Shaping,Characters After Shaping,Glyphs (Layout Direction),Glyphs (Left-to-Right),Glyphs (Right-to-Left)"), "set_visible_characters_behavior", "get_visible_characters_behavior"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "percent_visible", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_percent_visible", "get_percent_visible"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visible_ratio", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_visible_ratio", "get_visible_ratio"); ADD_GROUP("BiDi", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction"); diff --git a/scene/gui/label.h b/scene/gui/label.h index a59d35950d..b79c94a5ba 100644 --- a/scene/gui/label.h +++ b/scene/gui/label.h @@ -32,6 +32,7 @@ #define LABEL_H #include "scene/gui/control.h" +#include "scene/resources/label_settings.h" class Label : public Control { GDCLASS(Label, Control); @@ -58,19 +59,36 @@ private: TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT; Array st_args; - float percent_visible = 1.0; - TextServer::VisibleCharactersBehavior visible_chars_behavior = TextServer::VC_CHARS_BEFORE_SHAPING; int visible_chars = -1; + float visible_ratio = 1.0; int lines_skipped = 0; int max_lines_visible = -1; + Ref<LabelSettings> settings; + + struct ThemeCache { + Ref<StyleBox> normal_style; + Ref<Font> font; + + int font_size = 0; + int line_spacing = 0; + Color font_color; + Color font_shadow_color; + Point2 font_shadow_offset; + Color font_outline_color; + int font_outline_size; + int font_shadow_outline_size; + } theme_cache; + void _update_visible(); void _shape(); + void _invalidate(); protected: - void _notification(int p_what); + virtual void _update_theme_item_cache() override; + void _notification(int p_what); static void _bind_methods(); public: @@ -85,6 +103,9 @@ public: void set_text(const String &p_string); String get_text() const; + void set_label_settings(const Ref<LabelSettings> &p_settings); + Ref<LabelSettings> get_label_settings() const; + void set_text_direction(TextDirection p_text_direction); TextDirection get_text_direction() const; @@ -103,22 +124,22 @@ public: void set_uppercase(bool p_uppercase); bool is_uppercase() const; - TextServer::VisibleCharactersBehavior get_visible_characters_behavior() const; void set_visible_characters_behavior(TextServer::VisibleCharactersBehavior p_behavior); + TextServer::VisibleCharactersBehavior get_visible_characters_behavior() const; void set_visible_characters(int p_amount); int get_visible_characters() const; int get_total_character_count() const; + void set_visible_ratio(float p_ratio); + float get_visible_ratio() const; + void set_clip_text(bool p_clip); bool is_clipping_text() const; void set_text_overrun_behavior(TextServer::OverrunBehavior p_behavior); TextServer::OverrunBehavior get_text_overrun_behavior() const; - void set_percent_visible(float p_percent); - float get_percent_visible() const; - void set_lines_skipped(int p_lines); int get_lines_skipped() const; @@ -133,4 +154,4 @@ public: ~Label(); }; -#endif +#endif // LABEL_H diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 39f8f23cd8..be94337c89 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -51,7 +51,7 @@ void LineEdit::_swap_current_input_direction() { input_direction = TEXT_DIRECTION_LTR; } set_caret_column(get_caret_column()); - update(); + queue_redraw(); } void LineEdit::_move_caret_left(bool p_select, bool p_move_by_word) { @@ -285,7 +285,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { if (!text.is_empty() && is_editable() && _is_over_clear_button(b->get_position())) { clear_button_status.press_attempt = true; clear_button_status.pressing_inside = true; - update(); + queue_redraw(); return; } @@ -348,7 +348,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { } } - update(); + queue_redraw(); } else { if (selection.enabled && !pass && b->get_button_index() == MouseButton::LEFT && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) { @@ -375,7 +375,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { show_virtual_keyboard(); } - update(); + queue_redraw(); } Ref<InputEventMouseMotion> m = p_event; @@ -385,7 +385,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { bool last_press_inside = clear_button_status.pressing_inside; clear_button_status.pressing_inside = clear_button_status.press_attempt && _is_over_clear_button(m->get_position()); if (last_press_inside != clear_button_status.pressing_inside) { - update(); + queue_redraw(); } } @@ -448,7 +448,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { if (context_menu_enabled) { if (k->is_action("ui_menu", true)) { _ensure_menu(); - Point2 pos = Point2(get_caret_pixel_pos().x, (get_size().y + get_theme_font(SNAME("font"))->get_height(get_theme_font_size(SNAME("font_size")))) / 2); + Point2 pos = Point2(get_caret_pixel_pos().x, (get_size().y + theme_cache.font->get_height(theme_cache.font_size)) / 2); menu->set_position(get_screen_position() + pos); menu->reset_size(); menu->popup(); @@ -589,7 +589,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { // Allow unicode handling if: // * No Modifiers are pressed (except shift) - bool allow_unicode_handling = !(k->is_command_pressed() || k->is_ctrl_pressed() || k->is_alt_pressed() || k->is_meta_pressed()); + bool allow_unicode_handling = !(k->is_command_or_control_pressed() || k->is_ctrl_pressed() || k->is_alt_pressed() || k->is_meta_pressed()); if (allow_unicode_handling && editable && k->get_unicode() >= 32) { // Handle Unicode (if no modifiers active) @@ -607,11 +607,13 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { void LineEdit::set_horizontal_alignment(HorizontalAlignment p_alignment) { ERR_FAIL_INDEX((int)p_alignment, 4); - if (alignment != p_alignment) { - alignment = p_alignment; - _shape(); + if (alignment == p_alignment) { + return; } - update(); + + alignment = p_alignment; + _shape(); + queue_redraw(); } HorizontalAlignment LineEdit::get_horizontal_alignment() const { @@ -679,7 +681,7 @@ void LineEdit::drop_data(const Point2 &p_point, const Variant &p_data) { } text_changed_dirty = true; } - update(); + queue_redraw(); } } @@ -694,18 +696,45 @@ bool LineEdit::_is_over_clear_button(const Point2 &p_pos) const { if (!clear_button_enabled || !has_point(p_pos)) { return false; } - Ref<Texture2D> icon = Control::get_theme_icon(SNAME("clear")); - int x_ofs = get_theme_stylebox(SNAME("normal"))->get_margin(SIDE_RIGHT); + Ref<Texture2D> icon = theme_cache.clear_icon; + int x_ofs = theme_cache.normal->get_margin(SIDE_RIGHT); return p_pos.x > get_size().width - icon->get_width() - x_ofs; } +void LineEdit::_update_theme_item_cache() { + Control::_update_theme_item_cache(); + + theme_cache.normal = get_theme_stylebox(SNAME("normal")); + theme_cache.read_only = get_theme_stylebox(SNAME("read_only")); + theme_cache.focus = get_theme_stylebox(SNAME("focus")); + + theme_cache.font = get_theme_font(SNAME("font")); + theme_cache.font_size = get_theme_font_size(SNAME("font_size")); + theme_cache.font_color = get_theme_color(SNAME("font_color")); + theme_cache.font_uneditable_color = get_theme_color(SNAME("font_uneditable_color")); + theme_cache.font_selected_color = get_theme_color(SNAME("font_selected_color")); + theme_cache.font_outline_size = get_theme_constant(SNAME("outline_size")); + theme_cache.font_outline_color = get_theme_color(SNAME("font_outline_color")); + theme_cache.font_placeholder_color = get_theme_color(SNAME("font_placeholder_color")); + theme_cache.caret_width = get_theme_constant(SNAME("caret_width")); + theme_cache.caret_color = get_theme_color(SNAME("caret_color")); + theme_cache.minimum_character_width = get_theme_constant(SNAME("minimum_character_width")); + theme_cache.selection_color = get_theme_color(SNAME("selection_color")); + + theme_cache.clear_icon = get_theme_icon(SNAME("clear")); + theme_cache.clear_button_color = get_theme_color(SNAME("clear_button_color")); + theme_cache.clear_button_color_pressed = get_theme_color(SNAME("clear_button_color_pressed")); + + theme_cache.base_scale = get_theme_default_base_scale(); +} + void LineEdit::_notification(int p_what) { switch (p_what) { #ifdef TOOLS_ENABLED case NOTIFICATION_ENTER_TREE: { if (Engine::get_singleton()->is_editor_hint() && !get_tree()->is_node_being_edited(this)) { set_caret_blink_enabled(EDITOR_GET("text_editor/appearance/caret/caret_blink")); - set_caret_blink_speed(EDITOR_GET("text_editor/appearance/caret/caret_blink_speed")); + set_caret_blink_interval(EDITOR_GET("text_editor/appearance/caret/caret_blink_interval")); if (!EditorSettings::get_singleton()->is_connected("settings_changed", callable_mp(this, &LineEdit::_editor_settings_changed))) { EditorSettings::get_singleton()->connect("settings_changed", callable_mp(this, &LineEdit::_editor_settings_changed)); @@ -716,39 +745,39 @@ void LineEdit::_notification(int p_what) { case NOTIFICATION_RESIZED: { _fit_to_width(); - scroll_offset = 0; + scroll_offset = 0.0; set_caret_column(get_caret_column()); } break; case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: case NOTIFICATION_THEME_CHANGED: { _shape(); - update(); + queue_redraw(); } break; case NOTIFICATION_TRANSLATION_CHANGED: { placeholder_translated = atr(placeholder); _shape(); - update(); + queue_redraw(); } break; case NOTIFICATION_WM_WINDOW_FOCUS_IN: { window_has_focus = true; draw_caret = true; - update(); + queue_redraw(); } break; case NOTIFICATION_WM_WINDOW_FOCUS_OUT: { window_has_focus = false; draw_caret = false; - update(); + queue_redraw(); } break; case NOTIFICATION_INTERNAL_PROCESS: { if (caret_blinking) { caret_blink_timer += get_process_delta_time(); - if (caret_blink_timer >= caret_blink_speed) { + if (caret_blink_timer >= caret_blink_interval) { caret_blink_timer = 0.0; _toggle_draw_caret(); } @@ -769,19 +798,19 @@ void LineEdit::_notification(int p_what) { RID ci = get_canvas_item(); - Ref<StyleBox> style = get_theme_stylebox(SNAME("normal")); + Ref<StyleBox> style = theme_cache.normal; if (!is_editable()) { - style = get_theme_stylebox(SNAME("read_only")); + style = theme_cache.read_only; draw_caret = false; } - Ref<Font> font = get_theme_font(SNAME("font")); + Ref<Font> font = theme_cache.font; if (!flat) { style->draw(ci, Rect2(Point2(), size)); } if (has_focus()) { - get_theme_stylebox(SNAME("focus"))->draw(ci, Rect2(Point2(), size)); + theme_cache.focus->draw(ci, Rect2(Point2(), size)); } int x_ofs = 0; @@ -799,7 +828,7 @@ void LineEdit::_notification(int p_what) { } } break; case HORIZONTAL_ALIGNMENT_CENTER: { - if (scroll_offset != 0) { + if (!Math::is_zero_approx(scroll_offset)) { x_ofs = style->get_offset().x; } else { x_ofs = MAX(style->get_margin(SIDE_LEFT), int(size.width - (text_width)) / 2); @@ -819,32 +848,37 @@ void LineEdit::_notification(int p_what) { int y_area = height - style->get_minimum_size().height; int y_ofs = style->get_offset().y + (y_area - text_height) / 2; - Color selection_color = get_theme_color(SNAME("selection_color")); - Color font_color = get_theme_color(is_editable() ? SNAME("font_color") : SNAME("font_uneditable_color")); - Color font_selected_color = get_theme_color(SNAME("font_selected_color")); - Color caret_color = get_theme_color(SNAME("caret_color")); + Color selection_color = theme_cache.selection_color; + Color font_color; + if (is_editable()) { + font_color = theme_cache.font_color; + } else { + font_color = theme_cache.font_uneditable_color; + } + Color font_selected_color = theme_cache.font_selected_color; + Color caret_color = theme_cache.caret_color; // Draw placeholder color. if (using_placeholder) { - font_color = get_theme_color(SNAME("font_placeholder_color")); + font_color = theme_cache.font_placeholder_color; } bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled; if (right_icon.is_valid() || display_clear_icon) { - Ref<Texture2D> r_icon = display_clear_icon ? Control::get_theme_icon(SNAME("clear")) : right_icon; + Ref<Texture2D> r_icon = display_clear_icon ? theme_cache.clear_icon : right_icon; Color color_icon(1, 1, 1, !is_editable() ? .5 * .9 : .9); if (display_clear_icon) { if (clear_button_status.press_attempt && clear_button_status.pressing_inside) { - color_icon = get_theme_color(SNAME("clear_button_color_pressed")); + color_icon = theme_cache.clear_button_color_pressed; } else { - color_icon = get_theme_color(SNAME("clear_button_color")); + color_icon = theme_cache.clear_button_color; } } r_icon->draw(ci, Point2(width - r_icon->get_width() - style->get_margin(SIDE_RIGHT), height / 2 - r_icon->get_height() / 2), color_icon); if (alignment == HORIZONTAL_ALIGNMENT_CENTER) { - if (scroll_offset == 0) { + if (Math::is_zero_approx(scroll_offset)) { x_ofs = MAX(style->get_margin(SIDE_LEFT), int(size.width - text_width - r_icon->get_width() - style->get_margin(SIDE_RIGHT) * 2) / 2); } } else { @@ -877,8 +911,8 @@ void LineEdit::_notification(int p_what) { // Draw text. ofs.y += TS->shaped_text_get_ascent(text_rid); - Color font_outline_color = get_theme_color(SNAME("font_outline_color")); - int outline_size = get_theme_constant(SNAME("outline_size")); + Color font_outline_color = theme_cache.font_outline_color; + int outline_size = theme_cache.font_outline_size; if (outline_size > 0 && font_outline_color.a > 0) { Vector2 oofs = ofs; for (int i = 0; i < gl_size; i++) { @@ -916,7 +950,7 @@ void LineEdit::_notification(int p_what) { ofs.x = x_ofs + scroll_offset; if (draw_caret || drag_caret_force_displayed) { // Prevent carets from disappearing at theme scales below 1.0 (if the caret width is 1). - const int caret_width = get_theme_constant(SNAME("caret_width")) * MAX(1, get_theme_default_base_scale()); + const int caret_width = theme_cache.caret_width * MAX(1, theme_cache.base_scale); if (ime_text.length() == 0) { // Normal caret. @@ -924,7 +958,7 @@ void LineEdit::_notification(int p_what) { if (caret.l_caret == Rect2() && caret.t_caret == Rect2()) { // No carets, add one at the start. - int h = get_theme_font(SNAME("font"))->get_height(get_theme_font_size(SNAME("font_size"))); + int h = theme_cache.font->get_height(theme_cache.font_size); int y = style->get_offset().y + (y_area - h) / 2; if (rtl) { caret.l_dir = TextServer::DIRECTION_RTL; @@ -1050,7 +1084,7 @@ void LineEdit::_notification(int p_what) { _shape(); set_caret_column(caret_column); // Update scroll_offset - update(); + queue_redraw(); } } break; @@ -1191,7 +1225,7 @@ void LineEdit::shift_selection_check_post(bool p_shift) { } void LineEdit::set_caret_at_pixel_pos(int p_x) { - Ref<StyleBox> style = get_theme_stylebox(SNAME("normal")); + Ref<StyleBox> style = theme_cache.normal; bool rtl = is_layout_rtl(); int x_ofs = 0; @@ -1206,7 +1240,7 @@ void LineEdit::set_caret_at_pixel_pos(int p_x) { } } break; case HORIZONTAL_ALIGNMENT_CENTER: { - if (scroll_offset != 0) { + if (!Math::is_zero_approx(scroll_offset)) { x_ofs = style->get_offset().x; } else { x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - (text_width)) / 2); @@ -1224,9 +1258,9 @@ void LineEdit::set_caret_at_pixel_pos(int p_x) { bool using_placeholder = text.is_empty() && ime_text.is_empty(); bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled; if (right_icon.is_valid() || display_clear_icon) { - Ref<Texture2D> r_icon = display_clear_icon ? Control::get_theme_icon(SNAME("clear")) : right_icon; + Ref<Texture2D> r_icon = display_clear_icon ? theme_cache.clear_icon : right_icon; if (alignment == HORIZONTAL_ALIGNMENT_CENTER) { - if (scroll_offset == 0) { + if (Math::is_zero_approx(scroll_offset)) { x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - text_width - r_icon->get_width() - style->get_margin(SIDE_RIGHT) * 2) / 2); } } else { @@ -1234,12 +1268,12 @@ void LineEdit::set_caret_at_pixel_pos(int p_x) { } } - int ofs = TS->shaped_text_hit_test_position(text_rid, p_x - x_ofs - scroll_offset); + int ofs = ceil(TS->shaped_text_hit_test_position(text_rid, p_x - x_ofs - scroll_offset)); set_caret_column(ofs); } -Vector2i LineEdit::get_caret_pixel_pos() { - Ref<StyleBox> style = get_theme_stylebox(SNAME("normal")); +Vector2 LineEdit::get_caret_pixel_pos() { + Ref<StyleBox> style = theme_cache.normal; bool rtl = is_layout_rtl(); int x_ofs = 0; @@ -1254,7 +1288,7 @@ Vector2i LineEdit::get_caret_pixel_pos() { } } break; case HORIZONTAL_ALIGNMENT_CENTER: { - if (scroll_offset != 0) { + if (!Math::is_zero_approx(scroll_offset)) { x_ofs = style->get_offset().x; } else { x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - (text_width)) / 2); @@ -1272,9 +1306,9 @@ Vector2i LineEdit::get_caret_pixel_pos() { bool using_placeholder = text.is_empty() && ime_text.is_empty(); bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled; if (right_icon.is_valid() || display_clear_icon) { - Ref<Texture2D> r_icon = display_clear_icon ? Control::get_theme_icon(SNAME("clear")) : right_icon; + Ref<Texture2D> r_icon = display_clear_icon ? theme_cache.clear_icon : right_icon; if (alignment == HORIZONTAL_ALIGNMENT_CENTER) { - if (scroll_offset == 0) { + if (Math::is_zero_approx(scroll_offset)) { x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - text_width - r_icon->get_width() - style->get_margin(SIDE_RIGHT) * 2) / 2); } } else { @@ -1282,7 +1316,7 @@ Vector2i LineEdit::get_caret_pixel_pos() { } } - Vector2i ret; + Vector2 ret; CaretInfo caret; // Get position of the start of caret. if (ime_text.length() != 0 && ime_selection.x != 0) { @@ -1355,16 +1389,16 @@ bool LineEdit::is_caret_force_displayed() const { void LineEdit::set_caret_force_displayed(const bool p_enabled) { caret_force_displayed = p_enabled; set_caret_blink_enabled(caret_blink_enabled); - update(); + queue_redraw(); } -float LineEdit::get_caret_blink_speed() const { - return caret_blink_speed; +float LineEdit::get_caret_blink_interval() const { + return caret_blink_interval; } -void LineEdit::set_caret_blink_speed(const float p_speed) { - ERR_FAIL_COND(p_speed <= 0); - caret_blink_speed = p_speed; +void LineEdit::set_caret_blink_interval(const float p_interval) { + ERR_FAIL_COND(p_interval <= 0); + caret_blink_interval = p_interval; } void LineEdit::_reset_caret_blink_timer() { @@ -1372,7 +1406,7 @@ void LineEdit::_reset_caret_blink_timer() { draw_caret = true; if (has_focus()) { caret_blink_timer = 0.0; - update(); + queue_redraw(); } } } @@ -1380,7 +1414,7 @@ void LineEdit::_reset_caret_blink_timer() { void LineEdit::_toggle_draw_caret() { draw_caret = !draw_caret; if (is_visible_in_tree() && ((has_focus() && window_has_focus) || caret_force_displayed)) { - update(); + queue_redraw(); } } @@ -1423,9 +1457,9 @@ void LineEdit::set_text(String p_text) { insert_text_at_caret(p_text); _create_undo_state(); - update(); + queue_redraw(); caret_column = 0; - scroll_offset = 0; + scroll_offset = 0.0; } void LineEdit::set_text_direction(Control::TextDirection p_text_direction) { @@ -1443,7 +1477,7 @@ void LineEdit::set_text_direction(Control::TextDirection p_text_direction) { menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_LTR), text_direction == TEXT_DIRECTION_LTR); menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_RTL), text_direction == TEXT_DIRECTION_RTL); } - update(); + queue_redraw(); } } @@ -1455,7 +1489,7 @@ void LineEdit::set_language(const String &p_language) { if (language != p_language) { language = p_language; _shape(); - update(); + queue_redraw(); } } @@ -1470,7 +1504,7 @@ void LineEdit::set_draw_control_chars(bool p_draw_control_chars) { menu->set_item_checked(menu->get_item_index(MENU_DISPLAY_UCC), draw_control_chars); } _shape(); - update(); + queue_redraw(); } } @@ -1482,7 +1516,7 @@ void LineEdit::set_structured_text_bidi_override(TextServer::StructuredTextParse if (st_parser != p_parser) { st_parser = p_parser; _shape(); - update(); + queue_redraw(); } } @@ -1493,7 +1527,7 @@ TextServer::StructuredTextParser LineEdit::get_structured_text_bidi_override() c void LineEdit::set_structured_text_bidi_override_options(Array p_args) { st_args = p_args; _shape(); - update(); + queue_redraw(); } Array LineEdit::get_structured_text_bidi_override_options() const { @@ -1513,9 +1547,9 @@ void LineEdit::clear() { void LineEdit::show_virtual_keyboard() { if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) { if (selection.enabled) { - DisplayServer::get_singleton()->virtual_keyboard_show(text, get_global_rect(), false, max_length, selection.begin, selection.end); + DisplayServer::get_singleton()->virtual_keyboard_show(text, get_global_rect(), DisplayServer::VirtualKeyboardType(virtual_keyboard_type), max_length, selection.begin, selection.end); } else { - DisplayServer::get_singleton()->virtual_keyboard_show(text, get_global_rect(), false, max_length, caret_column); + DisplayServer::get_singleton()->virtual_keyboard_show(text, get_global_rect(), DisplayServer::VirtualKeyboardType(virtual_keyboard_type), max_length, caret_column); } } } @@ -1525,10 +1559,14 @@ String LineEdit::get_text() const { } void LineEdit::set_placeholder(String p_text) { + if (placeholder == p_text) { + return; + } + placeholder = p_text; placeholder_translated = atr(placeholder); _shape(); - update(); + queue_redraw(); } String LineEdit::get_placeholder() const { @@ -1549,11 +1587,11 @@ void LineEdit::set_caret_column(int p_column) { // Fit to window. if (!is_inside_tree()) { - scroll_offset = 0; + scroll_offset = 0.0; return; } - Ref<StyleBox> style = get_theme_stylebox(SNAME("normal")); + Ref<StyleBox> style = theme_cache.normal; bool rtl = is_layout_rtl(); int x_ofs = 0; @@ -1568,7 +1606,7 @@ void LineEdit::set_caret_column(int p_column) { } } break; case HORIZONTAL_ALIGNMENT_CENTER: { - if (scroll_offset != 0) { + if (!Math::is_zero_approx(scroll_offset)) { x_ofs = style->get_offset().x; } else { x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - (text_width)) / 2); @@ -1587,9 +1625,9 @@ void LineEdit::set_caret_column(int p_column) { bool using_placeholder = text.is_empty() && ime_text.is_empty(); bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled; if (right_icon.is_valid() || display_clear_icon) { - Ref<Texture2D> r_icon = display_clear_icon ? Control::get_theme_icon(SNAME("clear")) : right_icon; + Ref<Texture2D> r_icon = display_clear_icon ? theme_cache.clear_icon : right_icon; if (alignment == HORIZONTAL_ALIGNMENT_CENTER) { - if (scroll_offset == 0) { + if (Math::is_zero_approx(scroll_offset)) { x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - text_width - r_icon->get_width() - style->get_margin(SIDE_RIGHT) * 2) / 2); } } else { @@ -1599,30 +1637,30 @@ void LineEdit::set_caret_column(int p_column) { } // Note: Use two coordinates to fit IME input range. - Vector2i primary_catret_offset = get_caret_pixel_pos(); + Vector2 primary_caret_offset = get_caret_pixel_pos(); - if (MIN(primary_catret_offset.x, primary_catret_offset.y) <= x_ofs) { - scroll_offset += (x_ofs - MIN(primary_catret_offset.x, primary_catret_offset.y)); - } else if (MAX(primary_catret_offset.x, primary_catret_offset.y) >= ofs_max) { - scroll_offset += (ofs_max - MAX(primary_catret_offset.x, primary_catret_offset.y)); + if (MIN(primary_caret_offset.x, primary_caret_offset.y) <= x_ofs) { + scroll_offset += x_ofs - MIN(primary_caret_offset.x, primary_caret_offset.y); + } else if (MAX(primary_caret_offset.x, primary_caret_offset.y) >= ofs_max) { + scroll_offset += ofs_max - MAX(primary_caret_offset.x, primary_caret_offset.y); } scroll_offset = MIN(0, scroll_offset); - update(); + queue_redraw(); } int LineEdit::get_caret_column() const { return caret_column; } -void LineEdit::set_scroll_offset(int p_pos) { +void LineEdit::set_scroll_offset(float p_pos) { scroll_offset = p_pos; - if (scroll_offset < 0) { - scroll_offset = 0; + if (scroll_offset < 0.0) { + scroll_offset = 0.0; } } -int LineEdit::get_scroll_offset() const { +float LineEdit::get_scroll_offset() const { return scroll_offset; } @@ -1650,23 +1688,23 @@ void LineEdit::clear_internal() { deselect(); _clear_undo_stack(); caret_column = 0; - scroll_offset = 0; + scroll_offset = 0.0; undo_text = ""; text = ""; _shape(); - update(); + queue_redraw(); } Size2 LineEdit::get_minimum_size() const { - Ref<StyleBox> style = get_theme_stylebox(SNAME("normal")); - Ref<Font> font = get_theme_font(SNAME("font")); - int font_size = get_theme_font_size(SNAME("font_size")); + Ref<StyleBox> style = theme_cache.normal; + Ref<Font> font = theme_cache.font; + int font_size = theme_cache.font_size; Size2 min_size; // Minimum size of text. float em_space_size = font->get_char_size('M', font_size).x; - min_size.width = get_theme_constant(SNAME("minimum_character_width")) * em_space_size; + min_size.width = theme_cache.minimum_character_width * em_space_size; if (expand_to_text_length) { // Add a space because some fonts are too exact, and because caret needs a bit more when at the end. @@ -1682,9 +1720,8 @@ Size2 LineEdit::get_minimum_size() const { icon_max_width = right_icon->get_width(); } if (clear_button_enabled) { - Ref<Texture2D> clear_icon = Control::get_theme_icon(SNAME("clear")); - min_size.height = MAX(min_size.height, clear_icon->get_height()); - icon_max_width = MAX(icon_max_width, clear_icon->get_width()); + min_size.height = MAX(min_size.height, theme_cache.clear_icon->get_height()); + icon_max_width = MAX(icon_max_width, theme_cache.clear_icon->get_width()); } min_size.width += icon_max_width; @@ -1698,7 +1735,7 @@ void LineEdit::deselect() { selection.enabled = false; selection.creating = false; selection.double_click = false; - update(); + queue_redraw(); } bool LineEdit::has_selection() const { @@ -1762,7 +1799,7 @@ void LineEdit::select_all() { selection.begin = 0; selection.end = text.length(); selection.enabled = true; - update(); + queue_redraw(); } void LineEdit::set_editable(bool p_editable) { @@ -1773,7 +1810,7 @@ void LineEdit::set_editable(bool p_editable) { editable = p_editable; update_minimum_size(); - update(); + queue_redraw(); } bool LineEdit::is_editable() const { @@ -1781,11 +1818,13 @@ bool LineEdit::is_editable() const { } void LineEdit::set_secret(bool p_secret) { - if (pass != p_secret) { - pass = p_secret; - _shape(); + if (pass == p_secret) { + return; } - update(); + + pass = p_secret; + _shape(); + queue_redraw(); } bool LineEdit::is_secret() const { @@ -1797,11 +1836,13 @@ void LineEdit::set_secret_character(const String &p_string) { // It also wouldn't make sense to use multiple characters as the secret character. ERR_FAIL_COND_MSG(p_string.length() != 1, "Secret character must be exactly one character long (" + itos(p_string.length()) + " characters given)."); - if (secret_character != p_string) { - secret_character = p_string; - _shape(); + if (secret_character == p_string) { + return; } - update(); + + secret_character = p_string; + _shape(); + queue_redraw(); } String LineEdit::get_secret_character() const { @@ -1838,7 +1879,7 @@ void LineEdit::select(int p_from, int p_to) { selection.end = p_to; selection.creating = false; selection.double_click = false; - update(); + queue_redraw(); } bool LineEdit::is_text_field() const { @@ -1996,7 +2037,7 @@ PopupMenu *LineEdit::get_menu() const { void LineEdit::_editor_settings_changed() { #ifdef TOOLS_ENABLED set_caret_blink_enabled(EDITOR_GET("text_editor/appearance/caret/caret_blink")); - set_caret_blink_speed(EDITOR_GET("text_editor/appearance/caret/caret_blink_speed")); + set_caret_blink_interval(EDITOR_GET("text_editor/appearance/caret/caret_blink_interval")); #endif } @@ -2017,7 +2058,7 @@ void LineEdit::set_clear_button_enabled(bool p_enabled) { clear_button_enabled = p_enabled; _fit_to_width(); update_minimum_size(); - update(); + queue_redraw(); } bool LineEdit::is_clear_button_enabled() const { @@ -2040,6 +2081,14 @@ bool LineEdit::is_virtual_keyboard_enabled() const { return virtual_keyboard_enabled; } +void LineEdit::set_virtual_keyboard_type(VirtualKeyboardType p_type) { + virtual_keyboard_type = p_type; +} + +LineEdit::VirtualKeyboardType LineEdit::get_virtual_keyboard_type() const { + return virtual_keyboard_type; +} + void LineEdit::set_middle_mouse_paste_enabled(bool p_enabled) { middle_mouse_paste_enabled = p_enabled; } @@ -2049,6 +2098,10 @@ bool LineEdit::is_middle_mouse_paste_enabled() const { } void LineEdit::set_selecting_enabled(bool p_enabled) { + if (selecting_enabled == p_enabled) { + return; + } + selecting_enabled = p_enabled; if (!selecting_enabled) { @@ -2061,6 +2114,10 @@ bool LineEdit::is_selecting_enabled() const { } void LineEdit::set_deselect_on_focus_loss_enabled(const bool p_enabled) { + if (deselect_on_focus_loss_enabled == p_enabled) { + return; + } + deselect_on_focus_loss_enabled = p_enabled; if (p_enabled && selection.enabled && !has_focus()) { deselect(); @@ -2078,7 +2135,7 @@ void LineEdit::set_right_icon(const Ref<Texture2D> &p_icon) { right_icon = p_icon; _fit_to_width(); update_minimum_size(); - update(); + queue_redraw(); } Ref<Texture2D> LineEdit::get_right_icon() { @@ -2088,7 +2145,7 @@ Ref<Texture2D> LineEdit::get_right_icon() { void LineEdit::set_flat(bool p_enabled) { if (flat != p_enabled) { flat = p_enabled; - update(); + queue_redraw(); } } @@ -2129,8 +2186,8 @@ void LineEdit::_shape() { } TS->shaped_text_set_preserve_control(text_rid, draw_control_chars); - const Ref<Font> &font = get_theme_font(SNAME("font")); - int font_size = get_theme_font_size(SNAME("font_size")); + const Ref<Font> &font = theme_cache.font; + int font_size = theme_cache.font_size; ERR_FAIL_COND(font.is_null()); TS->shaped_text_add_string(text_rid, t, font->get_rids(), font_size, font->get_opentype_features(), language); for (int i = 0; i < TextServer::SPACING_MAX; i++) { @@ -2150,12 +2207,12 @@ void LineEdit::_shape() { void LineEdit::_fit_to_width() { if (alignment == HORIZONTAL_ALIGNMENT_FILL) { - Ref<StyleBox> style = get_theme_stylebox(SNAME("normal")); + Ref<StyleBox> style = theme_cache.normal; int t_width = get_size().width - style->get_margin(SIDE_RIGHT) - style->get_margin(SIDE_LEFT); bool using_placeholder = text.is_empty() && ime_text.is_empty(); bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled; if (right_icon.is_valid() || display_clear_icon) { - Ref<Texture2D> r_icon = display_clear_icon ? Control::get_theme_icon(SNAME("clear")) : right_icon; + Ref<Texture2D> r_icon = display_clear_icon ? theme_cache.clear_icon : right_icon; t_width -= r_icon->get_width(); } TS->shaped_text_fit_to_width(text_rid, MAX(t_width, full_width)); @@ -2216,9 +2273,9 @@ Key LineEdit::_get_menu_action_accelerator(const String &p_action) { } } -void LineEdit::_validate_property(PropertyInfo &property) const { - if (!caret_blink_enabled && property.name == "caret_blink_speed") { - property.usage = PROPERTY_USAGE_NO_EDITOR; +void LineEdit::_validate_property(PropertyInfo &p_property) const { + if (!caret_blink_enabled && p_property.name == "caret_blink_interval") { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } } @@ -2260,8 +2317,8 @@ void LineEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("is_caret_mid_grapheme_enabled"), &LineEdit::is_caret_mid_grapheme_enabled); ClassDB::bind_method(D_METHOD("set_caret_force_displayed", "enabled"), &LineEdit::set_caret_force_displayed); ClassDB::bind_method(D_METHOD("is_caret_force_displayed"), &LineEdit::is_caret_force_displayed); - ClassDB::bind_method(D_METHOD("set_caret_blink_speed", "blink_speed"), &LineEdit::set_caret_blink_speed); - ClassDB::bind_method(D_METHOD("get_caret_blink_speed"), &LineEdit::get_caret_blink_speed); + ClassDB::bind_method(D_METHOD("set_caret_blink_interval", "interval"), &LineEdit::set_caret_blink_interval); + ClassDB::bind_method(D_METHOD("get_caret_blink_interval"), &LineEdit::get_caret_blink_interval); ClassDB::bind_method(D_METHOD("set_max_length", "chars"), &LineEdit::set_max_length); ClassDB::bind_method(D_METHOD("get_max_length"), &LineEdit::get_max_length); ClassDB::bind_method(D_METHOD("insert_text_at_caret", "text"), &LineEdit::insert_text_at_caret); @@ -2280,6 +2337,8 @@ void LineEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("is_context_menu_enabled"), &LineEdit::is_context_menu_enabled); ClassDB::bind_method(D_METHOD("set_virtual_keyboard_enabled", "enable"), &LineEdit::set_virtual_keyboard_enabled); ClassDB::bind_method(D_METHOD("is_virtual_keyboard_enabled"), &LineEdit::is_virtual_keyboard_enabled); + ClassDB::bind_method(D_METHOD("set_virtual_keyboard_type", "type"), &LineEdit::set_virtual_keyboard_type); + ClassDB::bind_method(D_METHOD("get_virtual_keyboard_type"), &LineEdit::get_virtual_keyboard_type); ClassDB::bind_method(D_METHOD("set_clear_button_enabled", "enable"), &LineEdit::set_clear_button_enabled); ClassDB::bind_method(D_METHOD("is_clear_button_enabled"), &LineEdit::is_clear_button_enabled); ClassDB::bind_method(D_METHOD("set_shortcut_keys_enabled", "enable"), &LineEdit::set_shortcut_keys_enabled); @@ -2329,6 +2388,15 @@ void LineEdit::_bind_methods() { BIND_ENUM_CONSTANT(MENU_INSERT_SHY); BIND_ENUM_CONSTANT(MENU_MAX); + BIND_ENUM_CONSTANT(KEYBOARD_TYPE_DEFAULT); + BIND_ENUM_CONSTANT(KEYBOARD_TYPE_MULTILINE); + BIND_ENUM_CONSTANT(KEYBOARD_TYPE_NUMBER); + BIND_ENUM_CONSTANT(KEYBOARD_TYPE_NUMBER_DECIMAL); + BIND_ENUM_CONSTANT(KEYBOARD_TYPE_PHONE); + BIND_ENUM_CONSTANT(KEYBOARD_TYPE_EMAIL_ADDRESS); + BIND_ENUM_CONSTANT(KEYBOARD_TYPE_PASSWORD); + BIND_ENUM_CONSTANT(KEYBOARD_TYPE_URL); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "text"), "set_text", "get_text"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "placeholder_text"), "set_placeholder", "get_placeholder"); ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment"); @@ -2339,6 +2407,7 @@ void LineEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand_to_text_length"), "set_expand_to_text_length_enabled", "is_expand_to_text_length_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "virtual_keyboard_enabled"), "set_virtual_keyboard_enabled", "is_virtual_keyboard_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "virtual_keyboard_type", PROPERTY_HINT_ENUM, "Default,Multiline,Number,Decimal,Phone,Email,Password,URL"), "set_virtual_keyboard_type", "get_virtual_keyboard_type"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clear_button_enabled"), "set_clear_button_enabled", "is_clear_button_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_keys_enabled"), "set_shortcut_keys_enabled", "is_shortcut_keys_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "middle_mouse_paste_enabled"), "set_middle_mouse_paste_enabled", "is_middle_mouse_paste_enabled"); @@ -2350,7 +2419,7 @@ void LineEdit::_bind_methods() { ADD_GROUP("Caret", "caret_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_blink"), "set_caret_blink_enabled", "is_caret_blink_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.01"), "set_caret_blink_speed", "get_caret_blink_speed"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "caret_blink_interval", PROPERTY_HINT_RANGE, "0.1,10,0.01"), "set_caret_blink_interval", "get_caret_blink_interval"); ADD_PROPERTY(PropertyInfo(Variant::INT, "caret_column", PROPERTY_HINT_RANGE, "0,1000,1,or_greater"), "set_caret_column", "get_caret_column"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_force_displayed"), "set_caret_force_displayed", "is_caret_force_displayed"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_mid_grapheme"), "set_caret_mid_grapheme_enabled", "is_caret_mid_grapheme_enabled"); diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index 557da35bfd..a4d5205f81 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -70,6 +70,17 @@ public: MENU_MAX }; + enum VirtualKeyboardType { + KEYBOARD_TYPE_DEFAULT, + KEYBOARD_TYPE_MULTILINE, + KEYBOARD_TYPE_NUMBER, + KEYBOARD_TYPE_NUMBER_DECIMAL, + KEYBOARD_TYPE_PHONE, + KEYBOARD_TYPE_EMAIL_ADDRESS, + KEYBOARD_TYPE_PASSWORD, + KEYBOARD_TYPE_URL + }; + private: HorizontalAlignment alignment = HORIZONTAL_ALIGNMENT_LEFT; @@ -84,7 +95,7 @@ private: String text; String placeholder; String placeholder_translated; - String secret_character = "*"; + String secret_character = U"•"; String ime_text; Point2 ime_selection; @@ -102,7 +113,7 @@ private: bool caret_mid_grapheme_enabled = true; int caret_column = 0; - int scroll_offset = 0; + float scroll_offset = 0.0; int max_length = 0; // 0 for no maximum. String language; @@ -120,6 +131,7 @@ private: bool shortcut_keys_enabled = true; bool virtual_keyboard_enabled = true; + VirtualKeyboardType virtual_keyboard_type = KEYBOARD_TYPE_DEFAULT; bool middle_mouse_paste_enabled = true; @@ -141,8 +153,7 @@ private: struct TextOperation { int caret_column = 0; - int scroll_offset = 0; - int cached_width = 0; + float scroll_offset = 0.0; String text; }; List<TextOperation> undo_stack; @@ -159,10 +170,35 @@ private: bool caret_blink_enabled = false; bool caret_force_displayed = false; bool draw_caret = true; - float caret_blink_speed = 0.65; + float caret_blink_interval = 0.65; double caret_blink_timer = 0.0; bool caret_blinking = false; + struct ThemeCache { + Ref<StyleBox> normal; + Ref<StyleBox> read_only; + Ref<StyleBox> focus; + + Ref<Font> font; + int font_size = 0; + Color font_color; + Color font_uneditable_color; + Color font_selected_color; + int font_outline_size; + Color font_outline_color; + Color font_placeholder_color; + int caret_width = 0; + Color caret_color; + int minimum_character_width = 0; + Color selection_color; + + Ref<Texture2D> clear_icon; + Color clear_button_color; + Color clear_button_color_pressed; + + float base_scale = 1.0; + } theme_cache; + bool _is_over_clear_button(const Point2 &p_pos) const; void _clear_undo_stack(); @@ -180,11 +216,11 @@ private: void shift_selection_check_post(bool); void selection_fill_at_caret(); - void set_scroll_offset(int p_pos); - int get_scroll_offset() const; + void set_scroll_offset(float p_pos); + float get_scroll_offset() const; void set_caret_at_pixel_pos(int p_x); - Vector2i get_caret_pixel_pos(); + Vector2 get_caret_pixel_pos(); void _reset_caret_blink_timer(); void _toggle_draw_caret(); @@ -204,12 +240,13 @@ private: void _ensure_menu(); protected: + virtual void _update_theme_item_cache() override; void _notification(int p_what); static void _bind_methods(); virtual void unhandled_key_input(const Ref<InputEvent> &p_event) override; virtual void gui_input(const Ref<InputEvent> &p_event) override; - void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; public: void set_horizontal_alignment(HorizontalAlignment p_alignment); @@ -274,8 +311,8 @@ public: bool is_caret_blink_enabled() const; void set_caret_blink_enabled(const bool p_enabled); - float get_caret_blink_speed() const; - void set_caret_blink_speed(const float p_speed); + float get_caret_blink_interval() const; + void set_caret_blink_interval(const float p_interval); void set_caret_force_displayed(const bool p_enabled); bool is_caret_force_displayed() const; @@ -311,6 +348,9 @@ public: void set_virtual_keyboard_enabled(bool p_enable); bool is_virtual_keyboard_enabled() const; + void set_virtual_keyboard_type(VirtualKeyboardType p_type); + VirtualKeyboardType get_virtual_keyboard_type() const; + void set_middle_mouse_paste_enabled(bool p_enabled); bool is_middle_mouse_paste_enabled() const; @@ -335,5 +375,6 @@ public: }; VARIANT_ENUM_CAST(LineEdit::MenuItems); +VARIANT_ENUM_CAST(LineEdit::VirtualKeyboardType); -#endif +#endif // LINE_EDIT_H diff --git a/scene/gui/link_button.cpp b/scene/gui/link_button.cpp index 30c0bb3321..7219e86f52 100644 --- a/scene/gui/link_button.cpp +++ b/scene/gui/link_button.cpp @@ -33,8 +33,8 @@ #include "core/string/translation.h" void LinkButton::_shape() { - Ref<Font> font = get_theme_font(SNAME("font")); - int font_size = get_theme_font_size(SNAME("font_size")); + Ref<Font> font = theme_cache.font; + int font_size = theme_cache.font_size; text_buf->clear(); if (text_direction == Control::TEXT_DIRECTION_INHERITED) { @@ -54,7 +54,7 @@ void LinkButton::set_text(const String &p_text) { xl_text = atr(text); _shape(); update_minimum_size(); - update(); + queue_redraw(); } String LinkButton::get_text() const { @@ -65,7 +65,7 @@ void LinkButton::set_structured_text_bidi_override(TextServer::StructuredTextPar if (st_parser != p_parser) { st_parser = p_parser; _shape(); - update(); + queue_redraw(); } } @@ -76,7 +76,7 @@ TextServer::StructuredTextParser LinkButton::get_structured_text_bidi_override() void LinkButton::set_structured_text_bidi_override_options(Array p_args) { st_args = p_args; _shape(); - update(); + queue_redraw(); } Array LinkButton::get_structured_text_bidi_override_options() const { @@ -88,7 +88,7 @@ void LinkButton::set_text_direction(Control::TextDirection p_text_direction) { if (text_direction != p_text_direction) { text_direction = p_text_direction; _shape(); - update(); + queue_redraw(); } } @@ -100,7 +100,7 @@ void LinkButton::set_language(const String &p_language) { if (language != p_language) { language = p_language; _shape(); - update(); + queue_redraw(); } } @@ -109,8 +109,12 @@ String LinkButton::get_language() const { } void LinkButton::set_underline_mode(UnderlineMode p_underline_mode) { + if (underline_mode == p_underline_mode) { + return; + } + underline_mode = p_underline_mode; - update(); + queue_redraw(); } LinkButton::UnderlineMode LinkButton::get_underline_mode() const { @@ -121,23 +125,43 @@ Size2 LinkButton::get_minimum_size() const { return text_buf->get_size(); } +void LinkButton::_update_theme_item_cache() { + BaseButton::_update_theme_item_cache(); + + theme_cache.focus = get_theme_stylebox(SNAME("focus")); + + theme_cache.font_color = get_theme_color(SNAME("font_color")); + theme_cache.font_focus_color = get_theme_color(SNAME("font_focus_color")); + theme_cache.font_pressed_color = get_theme_color(SNAME("font_pressed_color")); + theme_cache.font_hover_color = get_theme_color(SNAME("font_hover_color")); + theme_cache.font_hover_pressed_color = get_theme_color(SNAME("font_hover_pressed_color")); + theme_cache.font_disabled_color = get_theme_color(SNAME("font_disabled_color")); + + theme_cache.font = get_theme_font(SNAME("font")); + theme_cache.font_size = get_theme_font_size(SNAME("font_size")); + theme_cache.outline_size = get_theme_constant(SNAME("outline_size")); + theme_cache.font_outline_color = get_theme_color(SNAME("font_outline_color")); + + theme_cache.underline_spacing = get_theme_constant(SNAME("underline_spacing")); +} + void LinkButton::_notification(int p_what) { switch (p_what) { case NOTIFICATION_TRANSLATION_CHANGED: { xl_text = atr(text); _shape(); update_minimum_size(); - update(); + queue_redraw(); } break; case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { - update(); + queue_redraw(); } break; case NOTIFICATION_THEME_CHANGED: { _shape(); update_minimum_size(); - update(); + queue_redraw(); } break; case NOTIFICATION_DRAW: { @@ -149,9 +173,9 @@ void LinkButton::_notification(int p_what) { switch (get_draw_mode()) { case DRAW_NORMAL: { if (has_focus()) { - color = get_theme_color(SNAME("font_focus_color")); + color = theme_cache.font_focus_color; } else { - color = get_theme_color(SNAME("font_color")); + color = theme_cache.font_color; } do_underline = underline_mode == UNDERLINE_MODE_ALWAYS; @@ -159,35 +183,35 @@ void LinkButton::_notification(int p_what) { case DRAW_HOVER_PRESSED: case DRAW_PRESSED: { if (has_theme_color(SNAME("font_pressed_color"))) { - color = get_theme_color(SNAME("font_pressed_color")); + color = theme_cache.font_pressed_color; } else { - color = get_theme_color(SNAME("font_color")); + color = theme_cache.font_color; } do_underline = underline_mode != UNDERLINE_MODE_NEVER; } break; case DRAW_HOVER: { - color = get_theme_color(SNAME("font_hover_color")); + color = theme_cache.font_hover_color; do_underline = underline_mode != UNDERLINE_MODE_NEVER; } break; case DRAW_DISABLED: { - color = get_theme_color(SNAME("font_disabled_color")); + color = theme_cache.font_disabled_color; do_underline = underline_mode == UNDERLINE_MODE_ALWAYS; } break; } if (has_focus()) { - Ref<StyleBox> style = get_theme_stylebox(SNAME("focus")); + Ref<StyleBox> style = theme_cache.focus; style->draw(ci, Rect2(Point2(), size)); } int width = text_buf->get_line_width(); - Color font_outline_color = get_theme_color(SNAME("font_outline_color")); - int outline_size = get_theme_constant(SNAME("outline_size")); + Color font_outline_color = theme_cache.font_outline_color; + int outline_size = theme_cache.outline_size; if (is_layout_rtl()) { if (outline_size > 0 && font_outline_color.a > 0) { text_buf->draw_outline(get_canvas_item(), Vector2(size.width - width, 0), outline_size, font_outline_color); @@ -201,7 +225,7 @@ void LinkButton::_notification(int p_what) { } if (do_underline) { - int underline_spacing = get_theme_constant(SNAME("underline_spacing")) + text_buf->get_line_underline_position(); + int underline_spacing = theme_cache.underline_spacing + text_buf->get_line_underline_position(); int y = text_buf->get_line_ascent() + underline_spacing; if (is_layout_rtl()) { diff --git a/scene/gui/link_button.h b/scene/gui/link_button.h index 54a31f06ce..accd848163 100644 --- a/scene/gui/link_button.h +++ b/scene/gui/link_button.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef LINKBUTTON_H -#define LINKBUTTON_H +#ifndef LINK_BUTTON_H +#define LINK_BUTTON_H #include "scene/gui/base_button.h" #include "scene/resources/text_line.h" @@ -55,10 +55,29 @@ private: TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT; Array st_args; + struct ThemeCache { + Ref<StyleBox> focus; + + Color font_color; + Color font_focus_color; + Color font_pressed_color; + Color font_hover_color; + Color font_hover_pressed_color; + Color font_disabled_color; + + Ref<Font> font; + int font_size = 0; + int outline_size = 0; + Color font_outline_color; + + int underline_spacing = 0; + } theme_cache; + void _shape(); protected: virtual Size2 get_minimum_size() const override; + virtual void _update_theme_item_cache() override; void _notification(int p_what); static void _bind_methods(); @@ -86,4 +105,4 @@ public: VARIANT_ENUM_CAST(LinkButton::UnderlineMode); -#endif // LINKBUTTON_H +#endif // LINK_BUTTON_H diff --git a/scene/gui/margin_container.cpp b/scene/gui/margin_container.cpp index fac37a8634..60fe681824 100644 --- a/scene/gui/margin_container.cpp +++ b/scene/gui/margin_container.cpp @@ -30,12 +30,16 @@ #include "margin_container.h" -Size2 MarginContainer::get_minimum_size() const { - int margin_left = get_theme_constant(SNAME("margin_left")); - int margin_top = get_theme_constant(SNAME("margin_top")); - int margin_right = get_theme_constant(SNAME("margin_right")); - int margin_bottom = get_theme_constant(SNAME("margin_bottom")); +void MarginContainer::_update_theme_item_cache() { + Container::_update_theme_item_cache(); + + theme_cache.margin_left = get_theme_constant(SNAME("margin_left")); + theme_cache.margin_top = get_theme_constant(SNAME("margin_top")); + theme_cache.margin_right = get_theme_constant(SNAME("margin_right")); + theme_cache.margin_bottom = get_theme_constant(SNAME("margin_bottom")); +} +Size2 MarginContainer::get_minimum_size() const { Size2 max; for (int i = 0; i < get_child_count(); i++) { @@ -59,8 +63,8 @@ Size2 MarginContainer::get_minimum_size() const { } } - max.width += (margin_left + margin_right); - max.height += (margin_top + margin_bottom); + max.width += (theme_cache.margin_left + theme_cache.margin_right); + max.height += (theme_cache.margin_top + theme_cache.margin_bottom); return max; } @@ -86,11 +90,6 @@ Vector<int> MarginContainer::get_allowed_size_flags_vertical() const { void MarginContainer::_notification(int p_what) { switch (p_what) { case NOTIFICATION_SORT_CHILDREN: { - int margin_left = get_theme_constant(SNAME("margin_left")); - int margin_top = get_theme_constant(SNAME("margin_top")); - int margin_right = get_theme_constant(SNAME("margin_right")); - int margin_bottom = get_theme_constant(SNAME("margin_bottom")); - Size2 s = get_size(); for (int i = 0; i < get_child_count(); i++) { @@ -102,9 +101,9 @@ void MarginContainer::_notification(int p_what) { continue; } - int w = s.width - margin_left - margin_right; - int h = s.height - margin_top - margin_bottom; - fit_child_in_rect(c, Rect2(margin_left, margin_top, w, h)); + int w = s.width - theme_cache.margin_left - theme_cache.margin_right; + int h = s.height - theme_cache.margin_top - theme_cache.margin_bottom; + fit_child_in_rect(c, Rect2(theme_cache.margin_left, theme_cache.margin_top, w, h)); } } break; diff --git a/scene/gui/margin_container.h b/scene/gui/margin_container.h index f8a3c5bb11..5c33785170 100644 --- a/scene/gui/margin_container.h +++ b/scene/gui/margin_container.h @@ -36,7 +36,16 @@ class MarginContainer : public Container { GDCLASS(MarginContainer, Container); + struct ThemeCache { + int margin_left = 0; + int margin_top = 0; + int margin_right = 0; + int margin_bottom = 0; + } theme_cache; + protected: + virtual void _update_theme_item_cache() override; + void _notification(int p_what); public: diff --git a/scene/gui/menu_bar.cpp b/scene/gui/menu_bar.cpp new file mode 100644 index 0000000000..75592a1b99 --- /dev/null +++ b/scene/gui/menu_bar.cpp @@ -0,0 +1,906 @@ +/*************************************************************************/ +/* menu_bar.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "menu_bar.h" + +#include "core/os/keyboard.h" +#include "scene/main/window.h" + +void MenuBar::gui_input(const Ref<InputEvent> &p_event) { + ERR_FAIL_COND(p_event.is_null()); + if (is_native_menu()) { + // Handled by OS. + return; + } + + MutexLock lock(mutex); + if (p_event->is_action("ui_left") && p_event->is_pressed()) { + int new_sel = selected_menu; + int old_sel = (selected_menu < 0) ? 0 : selected_menu; + do { + new_sel--; + if (new_sel < 0) { + new_sel = menu_cache.size() - 1; + } + if (old_sel == new_sel) { + return; + } + } while (menu_cache[new_sel].hidden || menu_cache[new_sel].disabled); + + if (selected_menu != new_sel) { + selected_menu = new_sel; + focused_menu = selected_menu; + if (active_menu >= 0) { + get_menu_popup(active_menu)->hide(); + } + _open_popup(selected_menu, true); + } + return; + } else if (p_event->is_action("ui_right") && p_event->is_pressed()) { + int new_sel = selected_menu; + int old_sel = (selected_menu < 0) ? menu_cache.size() - 1 : selected_menu; + do { + new_sel++; + if (new_sel >= menu_cache.size()) { + new_sel = 0; + } + if (old_sel == new_sel) { + return; + } + } while (menu_cache[new_sel].hidden || menu_cache[new_sel].disabled); + + if (selected_menu != new_sel) { + selected_menu = new_sel; + focused_menu = selected_menu; + if (active_menu >= 0) { + get_menu_popup(active_menu)->hide(); + } + _open_popup(selected_menu, true); + } + return; + } + + Ref<InputEventMouseMotion> mm = p_event; + if (mm.is_valid()) { + int old_sel = selected_menu; + focused_menu = _get_index_at_point(mm->get_position()); + if (focused_menu >= 0) { + selected_menu = focused_menu; + } + if (selected_menu != old_sel) { + queue_redraw(); + } + } + + Ref<InputEventMouseButton> mb = p_event; + if (mb.is_valid()) { + if (mb->is_pressed() && (mb->get_button_index() == MouseButton::LEFT || mb->get_button_index() == MouseButton::RIGHT)) { + int index = _get_index_at_point(mb->get_position()); + if (index >= 0) { + _open_popup(index); + } + } + } +} + +void MenuBar::_open_popup(int p_index, bool p_focus_item) { + ERR_FAIL_INDEX(p_index, menu_cache.size()); + + PopupMenu *pm = get_menu_popup(p_index); + if (pm->is_visible()) { + pm->hide(); + return; + } + + Rect2 item_rect = _get_menu_item_rect(p_index); + Point2 screen_pos = get_screen_position() + item_rect.position * get_viewport()->get_canvas_transform().get_scale(); + Size2 screen_size = item_rect.size * get_viewport()->get_canvas_transform().get_scale(); + + active_menu = p_index; + + pm->set_size(Size2(screen_size.x, 0)); + screen_pos.y += screen_size.y; + if (is_layout_rtl()) { + screen_pos.x += screen_size.x - pm->get_size().width; + } + pm->set_position(screen_pos); + pm->set_parent_rect(Rect2(Point2(screen_pos - pm->get_position()), Size2(screen_size.x, screen_pos.y))); + pm->popup(); + + if (p_focus_item) { + for (int i = 0; i < pm->get_item_count(); i++) { + if (!pm->is_item_disabled(i)) { + pm->set_focused_item(i); + break; + } + } + } + + queue_redraw(); +} + +void MenuBar::shortcut_input(const Ref<InputEvent> &p_event) { + ERR_FAIL_COND(p_event.is_null()); + + if (!_is_focus_owner_in_shortcut_context()) { + return; + } + + if (disable_shortcuts) { + return; + } + + if (p_event->is_pressed() && !p_event->is_echo() && (Object::cast_to<InputEventKey>(p_event.ptr()) || Object::cast_to<InputEventJoypadButton>(p_event.ptr()) || Object::cast_to<InputEventAction>(*p_event) || Object::cast_to<InputEventShortcut>(*p_event))) { + if (!get_parent() || !is_visible_in_tree()) { + return; + } + + Vector<PopupMenu *> popups = _get_popups(); + for (int i = 0; i < popups.size(); i++) { + if (menu_cache[i].hidden || menu_cache[i].disabled) { + continue; + } + if (popups[i]->activate_item_by_event(p_event, false)) { + accept_event(); + return; + } + } + } +} + +void MenuBar::set_shortcut_context(Node *p_node) { + if (p_node != nullptr) { + shortcut_context = p_node->get_instance_id(); + } else { + shortcut_context = ObjectID(); + } +} + +Node *MenuBar::get_shortcut_context() const { + Object *ctx_obj = ObjectDB::get_instance(shortcut_context); + Node *ctx_node = Object::cast_to<Node>(ctx_obj); + + return ctx_node; +} + +bool MenuBar::_is_focus_owner_in_shortcut_context() const { + if (shortcut_context == ObjectID()) { + // No context, therefore global - always "in" context. + return true; + } + + Node *ctx_node = get_shortcut_context(); + Control *vp_focus = get_viewport() ? get_viewport()->gui_get_focus_owner() : nullptr; + + // If the context is valid and the viewport focus is valid, check if the context is the focus or is a parent of it. + return ctx_node && vp_focus && (ctx_node == vp_focus || ctx_node->is_ancestor_of(vp_focus)); +} + +void MenuBar::_popup_visibility_changed(bool p_visible) { + if (!p_visible) { + active_menu = -1; + focused_menu = -1; + set_process_internal(false); + queue_redraw(); + return; + } + + if (switch_on_hover) { + Window *window = Object::cast_to<Window>(get_viewport()); + if (window) { + mouse_pos_adjusted = window->get_position(); + + if (window->is_embedded()) { + Window *window_parent = Object::cast_to<Window>(window->get_parent()->get_viewport()); + while (window_parent) { + if (!window_parent->is_embedded()) { + mouse_pos_adjusted += window_parent->get_position(); + break; + } + + window_parent = Object::cast_to<Window>(window_parent->get_parent()->get_viewport()); + } + } + + set_process_internal(true); + } + } +} + +void MenuBar::_update_submenu(const String &p_menu_name, PopupMenu *p_child) { + int count = p_child->get_item_count(); + global_menus.insert(p_menu_name); + for (int i = 0; i < count; i++) { + if (p_child->is_item_separator(i)) { + DisplayServer::get_singleton()->global_menu_add_separator(p_menu_name); + } else if (!p_child->get_item_submenu(i).is_empty()) { + Node *n = p_child->get_node(p_child->get_item_submenu(i)); + ERR_FAIL_COND_MSG(!n, "Item subnode does not exist: " + p_child->get_item_submenu(i) + "."); + PopupMenu *pm = Object::cast_to<PopupMenu>(n); + ERR_FAIL_COND_MSG(!pm, "Item subnode is not a PopupMenu: " + p_child->get_item_submenu(i) + "."); + + DisplayServer::get_singleton()->global_menu_add_submenu_item(p_menu_name, p_child->get_item_text(i), p_menu_name + "/" + itos(i)); + _update_submenu(p_menu_name + "/" + itos(i), pm); + } else { + int index = DisplayServer::get_singleton()->global_menu_add_item(p_menu_name, p_child->get_item_text(i), callable_mp(p_child, &PopupMenu::activate_item), Callable(), i); + + if (p_child->is_item_checkable(i)) { + DisplayServer::get_singleton()->global_menu_set_item_checkable(p_menu_name, index, true); + } + if (p_child->is_item_radio_checkable(i)) { + DisplayServer::get_singleton()->global_menu_set_item_radio_checkable(p_menu_name, index, true); + } + DisplayServer::get_singleton()->global_menu_set_item_checked(p_menu_name, index, p_child->is_item_checked(i)); + DisplayServer::get_singleton()->global_menu_set_item_disabled(p_menu_name, index, p_child->is_item_disabled(i)); + DisplayServer::get_singleton()->global_menu_set_item_max_states(p_menu_name, index, p_child->get_item_max_states(i)); + DisplayServer::get_singleton()->global_menu_set_item_icon(p_menu_name, index, p_child->get_item_icon(i)); + DisplayServer::get_singleton()->global_menu_set_item_state(p_menu_name, index, p_child->get_item_state(i)); + DisplayServer::get_singleton()->global_menu_set_item_indentation_level(p_menu_name, index, p_child->get_item_indent(i)); + DisplayServer::get_singleton()->global_menu_set_item_tooltip(p_menu_name, index, p_child->get_item_tooltip(i)); + if (!p_child->is_item_shortcut_disabled(i) && p_child->get_item_shortcut(i).is_valid() && p_child->get_item_shortcut(i)->has_valid_event()) { + Array events = p_child->get_item_shortcut(i)->get_events(); + for (int j = 0; j < events.size(); j++) { + Ref<InputEventKey> ie = events[j]; + if (ie.is_valid()) { + DisplayServer::get_singleton()->global_menu_set_item_accelerator(p_menu_name, index, ie->get_keycode_with_modifiers()); + break; + } + } + } else if (p_child->get_item_accelerator(i) != Key::NONE) { + DisplayServer::get_singleton()->global_menu_set_item_accelerator(p_menu_name, i, p_child->get_item_accelerator(i)); + } + } + } +} + +bool MenuBar::is_native_menu() const { + if (Engine::get_singleton()->is_editor_hint() && is_inside_tree() && get_tree()->get_edited_scene_root() && (get_tree()->get_edited_scene_root()->is_ancestor_of(this) || get_tree()->get_edited_scene_root() == this)) { + return false; + } + + return (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_GLOBAL_MENU) && is_native); +} + +void MenuBar::_clear_menu() { + if (!DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_GLOBAL_MENU)) { + return; + } + + // Remove root menu items. + int count = DisplayServer::get_singleton()->global_menu_get_item_count("_main"); + for (int i = count - 1; i >= 0; i--) { + if (global_menus.has(DisplayServer::get_singleton()->global_menu_get_item_submenu("_main", i))) { + DisplayServer::get_singleton()->global_menu_remove_item("_main", i); + } + } + // Erase submenu contents. + for (const String &E : global_menus) { + DisplayServer::get_singleton()->global_menu_clear(E); + } + global_menus.clear(); +} + +void MenuBar::_update_menu() { + _clear_menu(); + + if (!is_visible_in_tree()) { + return; + } + + int index = start_index; + if (is_native_menu()) { + Vector<PopupMenu *> popups = _get_popups(); + String root_name = "MenuBar<" + String::num_int64((uint64_t)this, 16) + ">"; + for (int i = 0; i < popups.size(); i++) { + if (menu_cache[i].hidden) { + continue; + } + String menu_name = String(popups[i]->get_meta("_menu_name", popups[i]->get_name())); + + index = DisplayServer::get_singleton()->global_menu_add_submenu_item("_main", menu_name, root_name + "/" + itos(i), index); + if (menu_cache[i].disabled) { + DisplayServer::get_singleton()->global_menu_set_item_disabled("_main", index, true); + } + _update_submenu(root_name + "/" + itos(i), popups[i]); + index++; + } + } + update_minimum_size(); + queue_redraw(); +} + +void MenuBar::_update_theme_item_cache() { + Control::_update_theme_item_cache(); + + theme_cache.normal = get_theme_stylebox(SNAME("normal")); + theme_cache.normal_mirrored = get_theme_stylebox(SNAME("normal_mirrored")); + theme_cache.disabled = get_theme_stylebox(SNAME("disabled")); + theme_cache.disabled_mirrored = get_theme_stylebox(SNAME("disabled_mirrored")); + theme_cache.pressed = get_theme_stylebox(SNAME("pressed")); + theme_cache.pressed_mirrored = get_theme_stylebox(SNAME("pressed_mirrored")); + theme_cache.hover = get_theme_stylebox(SNAME("hover")); + theme_cache.hover_mirrored = get_theme_stylebox(SNAME("hover_mirrored")); + theme_cache.hover_pressed = get_theme_stylebox(SNAME("hover_pressed")); + theme_cache.hover_pressed_mirrored = get_theme_stylebox(SNAME("hover_pressed_mirrored")); + + theme_cache.font = get_theme_font(SNAME("font")); + theme_cache.font_size = get_theme_font_size(SNAME("font_size")); + theme_cache.outline_size = get_theme_constant(SNAME("outline_size")); + theme_cache.font_outline_color = get_theme_color(SNAME("font_outline_color")); + + theme_cache.font_color = get_theme_color(SNAME("font_color")); + theme_cache.font_disabled_color = get_theme_color(SNAME("font_disabled_color")); + theme_cache.font_pressed_color = get_theme_color(SNAME("font_pressed_color")); + theme_cache.font_hover_color = get_theme_color(SNAME("font_hover_color")); + theme_cache.font_hover_pressed_color = get_theme_color(SNAME("font_hover_pressed_color")); + theme_cache.font_focus_color = get_theme_color(SNAME("font_focus_color")); + + theme_cache.h_separation = get_theme_constant(SNAME("h_separation")); +} + +void MenuBar::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + if (get_menu_count() > 0) { + _refresh_menu_names(); + } + } break; + case NOTIFICATION_EXIT_TREE: { + _clear_menu(); + } break; + case NOTIFICATION_MOUSE_EXIT: { + focused_menu = -1; + queue_redraw(); + } break; + case NOTIFICATION_TRANSLATION_CHANGED: + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: + case NOTIFICATION_THEME_CHANGED: { + for (int i = 0; i < menu_cache.size(); i++) { + shape(menu_cache.write[i]); + } + _update_menu(); + } break; + case NOTIFICATION_VISIBILITY_CHANGED: { + _update_menu(); + } break; + case NOTIFICATION_DRAW: { + if (is_native_menu()) { + return; + } + for (int i = 0; i < menu_cache.size(); i++) { + _draw_menu_item(i); + } + } break; + case NOTIFICATION_INTERNAL_PROCESS: { + MutexLock lock(mutex); + + if (is_native_menu()) { + // Handled by OS. + return; + } + + Vector2 pos = DisplayServer::get_singleton()->mouse_get_position() - mouse_pos_adjusted - get_global_position(); + if (pos == old_mouse_pos) { + return; + } + old_mouse_pos = pos; + + int index = _get_index_at_point(pos); + if (index >= 0 && index != active_menu) { + selected_menu = index; + focused_menu = selected_menu; + if (active_menu >= 0) { + get_menu_popup(active_menu)->hide(); + } + _open_popup(index); + } + } break; + } +} + +int MenuBar::_get_index_at_point(const Point2 &p_point) const { + Ref<StyleBox> style = theme_cache.normal; + int offset = 0; + for (int i = 0; i < menu_cache.size(); i++) { + if (menu_cache[i].hidden) { + continue; + } + Size2 size = menu_cache[i].text_buf->get_size() + style->get_minimum_size(); + if (p_point.x > offset && p_point.x < offset + size.x) { + if (p_point.y > 0 && p_point.y < size.y) { + return i; + } + } + offset += size.x + theme_cache.h_separation; + } + return -1; +} + +Rect2 MenuBar::_get_menu_item_rect(int p_index) const { + ERR_FAIL_INDEX_V(p_index, menu_cache.size(), Rect2()); + + Ref<StyleBox> style = theme_cache.normal; + + int offset = 0; + for (int i = 0; i < p_index; i++) { + if (menu_cache[i].hidden) { + continue; + } + Size2 size = menu_cache[i].text_buf->get_size() + style->get_minimum_size(); + offset += size.x + theme_cache.h_separation; + } + + return Rect2(Point2(offset, 0), menu_cache[p_index].text_buf->get_size() + style->get_minimum_size()); +} + +void MenuBar::_draw_menu_item(int p_index) { + ERR_FAIL_INDEX(p_index, menu_cache.size()); + + RID ci = get_canvas_item(); + bool hovered = (focused_menu == p_index); + bool pressed = (active_menu == p_index); + bool rtl = is_layout_rtl(); + + if (menu_cache[p_index].hidden) { + return; + } + + Color color; + Ref<StyleBox> style = theme_cache.normal; + Rect2 item_rect = _get_menu_item_rect(p_index); + + if (menu_cache[p_index].disabled) { + if (rtl && has_theme_stylebox(SNAME("disabled_mirrored"))) { + style = theme_cache.disabled_mirrored; + } else { + style = theme_cache.disabled; + } + if (!flat) { + style->draw(ci, item_rect); + } + color = theme_cache.font_disabled_color; + } else if (hovered && pressed && has_theme_stylebox("hover_pressed")) { + if (rtl && has_theme_stylebox(SNAME("hover_pressed_mirrored"))) { + style = theme_cache.hover_pressed_mirrored; + } else { + style = theme_cache.hover_pressed; + } + if (!flat) { + style->draw(ci, item_rect); + } + if (has_theme_color(SNAME("font_hover_pressed_color"))) { + color = theme_cache.font_hover_pressed_color; + } + } else if (pressed) { + if (rtl && has_theme_stylebox(SNAME("pressed_mirrored"))) { + style = theme_cache.pressed_mirrored; + } else { + style = theme_cache.pressed; + } + if (!flat) { + style->draw(ci, item_rect); + } + if (has_theme_color(SNAME("font_pressed_color"))) { + color = theme_cache.font_pressed_color; + } else { + color = theme_cache.font_color; + } + } else if (hovered) { + if (rtl && has_theme_stylebox(SNAME("hover_mirrored"))) { + style = theme_cache.hover_mirrored; + } else { + style = theme_cache.hover; + } + if (!flat) { + style->draw(ci, item_rect); + } + color = theme_cache.font_hover_color; + } else { + if (rtl && has_theme_stylebox(SNAME("normal_mirrored"))) { + style = theme_cache.normal_mirrored; + } else { + style = theme_cache.normal; + } + if (!flat) { + style->draw(ci, item_rect); + } + // Focus colors only take precedence over normal state. + if (has_focus()) { + color = theme_cache.font_focus_color; + } else { + color = theme_cache.font_color; + } + } + + Point2 text_ofs = item_rect.position + Point2(style->get_margin(SIDE_LEFT), style->get_margin(SIDE_TOP)); + + Color font_outline_color = theme_cache.font_outline_color; + int outline_size = theme_cache.outline_size; + if (outline_size > 0 && font_outline_color.a > 0) { + menu_cache[p_index].text_buf->draw_outline(ci, text_ofs, outline_size, font_outline_color); + } + menu_cache[p_index].text_buf->draw(ci, text_ofs, color); +} + +void MenuBar::shape(Menu &p_menu) { + p_menu.text_buf->clear(); + if (text_direction == Control::TEXT_DIRECTION_INHERITED) { + p_menu.text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); + } else { + p_menu.text_buf->set_direction((TextServer::Direction)text_direction); + } + p_menu.text_buf->add_string(p_menu.name, theme_cache.font, theme_cache.font_size, language); +} + +void MenuBar::_refresh_menu_names() { + Vector<PopupMenu *> popups = _get_popups(); + for (int i = 0; i < popups.size(); i++) { + if (!popups[i]->has_meta("_menu_name") && String(popups[i]->get_name()) != get_menu_title(i)) { + menu_cache.write[i].name = popups[i]->get_name(); + shape(menu_cache.write[i]); + } + } + _update_menu(); +} + +Vector<PopupMenu *> MenuBar::_get_popups() const { + Vector<PopupMenu *> popups; + for (int i = 0; i < get_child_count(); i++) { + PopupMenu *pm = Object::cast_to<PopupMenu>(get_child(i)); + if (!pm) { + continue; + } + popups.push_back(pm); + } + return popups; +} + +int MenuBar::get_menu_idx_from_control(PopupMenu *p_child) const { + ERR_FAIL_NULL_V(p_child, -1); + ERR_FAIL_COND_V(p_child->get_parent() != this, -1); + + Vector<PopupMenu *> popups = _get_popups(); + for (int i = 0; i < popups.size(); i++) { + if (popups[i] == p_child) { + return i; + } + } + + return -1; +} + +void MenuBar::add_child_notify(Node *p_child) { + Control::add_child_notify(p_child); + + PopupMenu *pm = Object::cast_to<PopupMenu>(p_child); + if (!pm) { + return; + } + Menu menu = Menu(p_child->get_name()); + shape(menu); + + menu_cache.push_back(menu); + p_child->connect("renamed", callable_mp(this, &MenuBar::_refresh_menu_names)); + p_child->connect("menu_changed", callable_mp(this, &MenuBar::_update_menu)); + p_child->connect("about_to_popup", callable_mp(this, &MenuBar::_popup_visibility_changed).bind(true)); + p_child->connect("popup_hide", callable_mp(this, &MenuBar::_popup_visibility_changed).bind(false)); + + _update_menu(); +} + +void MenuBar::move_child_notify(Node *p_child) { + Control::move_child_notify(p_child); + + PopupMenu *pm = Object::cast_to<PopupMenu>(p_child); + if (!pm) { + return; + } + + int old_idx = -1; + String menu_name = String(pm->get_meta("_menu_name", pm->get_name())); + // Find the previous menu index of the control. + for (int i = 0; i < get_menu_count(); i++) { + if (get_menu_title(i) == menu_name) { + old_idx = i; + break; + } + } + Menu menu = menu_cache[old_idx]; + menu_cache.remove_at(old_idx); + menu_cache.insert(get_menu_idx_from_control(pm), menu); + + _update_menu(); +} + +void MenuBar::remove_child_notify(Node *p_child) { + Control::remove_child_notify(p_child); + + PopupMenu *pm = Object::cast_to<PopupMenu>(p_child); + if (!pm) { + return; + } + + int idx = get_menu_idx_from_control(pm); + + menu_cache.remove_at(idx); + + p_child->remove_meta("_menu_name"); + p_child->remove_meta("_menu_tooltip"); + + p_child->disconnect("renamed", callable_mp(this, &MenuBar::_refresh_menu_names)); + p_child->disconnect("menu_changed", callable_mp(this, &MenuBar::_update_menu)); + p_child->disconnect("about_to_popup", callable_mp(this, &MenuBar::_popup_visibility_changed)); + p_child->disconnect("popup_hide", callable_mp(this, &MenuBar::_popup_visibility_changed)); + + _update_menu(); +} + +void MenuBar::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_switch_on_hover", "enable"), &MenuBar::set_switch_on_hover); + ClassDB::bind_method(D_METHOD("is_switch_on_hover"), &MenuBar::is_switch_on_hover); + ClassDB::bind_method(D_METHOD("set_disable_shortcuts", "disabled"), &MenuBar::set_disable_shortcuts); + + ClassDB::bind_method(D_METHOD("set_prefer_global_menu", "enabled"), &MenuBar::set_prefer_global_menu); + ClassDB::bind_method(D_METHOD("is_prefer_global_menu"), &MenuBar::is_prefer_global_menu); + ClassDB::bind_method(D_METHOD("is_native_menu"), &MenuBar::is_native_menu); + + ClassDB::bind_method(D_METHOD("get_menu_count"), &MenuBar::get_menu_count); + + ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &MenuBar::set_text_direction); + ClassDB::bind_method(D_METHOD("get_text_direction"), &MenuBar::get_text_direction); + ClassDB::bind_method(D_METHOD("set_language", "language"), &MenuBar::set_language); + ClassDB::bind_method(D_METHOD("get_language"), &MenuBar::get_language); + ClassDB::bind_method(D_METHOD("set_flat", "enabled"), &MenuBar::set_flat); + ClassDB::bind_method(D_METHOD("is_flat"), &MenuBar::is_flat); + ClassDB::bind_method(D_METHOD("set_start_index", "enabled"), &MenuBar::set_start_index); + ClassDB::bind_method(D_METHOD("get_start_index"), &MenuBar::get_start_index); + + ClassDB::bind_method(D_METHOD("set_menu_title", "menu", "title"), &MenuBar::set_menu_title); + ClassDB::bind_method(D_METHOD("get_menu_title", "menu"), &MenuBar::get_menu_title); + + ClassDB::bind_method(D_METHOD("set_menu_tooltip", "menu", "tooltip"), &MenuBar::set_menu_tooltip); + ClassDB::bind_method(D_METHOD("get_menu_tooltip", "menu"), &MenuBar::get_menu_tooltip); + + ClassDB::bind_method(D_METHOD("set_menu_disabled", "menu", "disabled"), &MenuBar::set_menu_disabled); + ClassDB::bind_method(D_METHOD("is_menu_disabled", "menu"), &MenuBar::is_menu_disabled); + + ClassDB::bind_method(D_METHOD("set_menu_hidden", "menu", "hidden"), &MenuBar::set_menu_hidden); + ClassDB::bind_method(D_METHOD("is_menu_hidden", "menu"), &MenuBar::is_menu_hidden); + + ClassDB::bind_method(D_METHOD("set_shortcut_context", "node"), &MenuBar::set_shortcut_context); + ClassDB::bind_method(D_METHOD("get_shortcut_context"), &MenuBar::get_shortcut_context); + + ClassDB::bind_method(D_METHOD("get_menu_popup", "menu"), &MenuBar::get_menu_popup); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "start_index"), "set_start_index", "get_start_index"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "switch_on_hover"), "set_switch_on_hover", "is_switch_on_hover"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "prefer_global_menu"), "set_prefer_global_menu", "is_prefer_global_menu"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shortcut_context", PROPERTY_HINT_NODE_TYPE, "Node"), "set_shortcut_context", "get_shortcut_context"); + + ADD_GROUP("BiDi", ""); + ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language"); +} + +void MenuBar::set_switch_on_hover(bool p_enabled) { + switch_on_hover = p_enabled; +} + +bool MenuBar::is_switch_on_hover() { + return switch_on_hover; +} + +void MenuBar::set_disable_shortcuts(bool p_disabled) { + disable_shortcuts = p_disabled; +} + +void MenuBar::set_text_direction(Control::TextDirection p_text_direction) { + ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3); + if (text_direction != p_text_direction) { + text_direction = p_text_direction; + _update_menu(); + } +} + +Control::TextDirection MenuBar::get_text_direction() const { + return text_direction; +} + +void MenuBar::set_language(const String &p_language) { + if (language != p_language) { + language = p_language; + _update_menu(); + } +} + +String MenuBar::get_language() const { + return language; +} + +void MenuBar::set_flat(bool p_enabled) { + if (flat != p_enabled) { + flat = p_enabled; + queue_redraw(); + } +} + +bool MenuBar::is_flat() const { + return flat; +} + +void MenuBar::set_start_index(int p_index) { + if (start_index != p_index) { + start_index = p_index; + _update_menu(); + } +} + +int MenuBar::get_start_index() const { + return start_index; +} + +void MenuBar::set_prefer_global_menu(bool p_enabled) { + if (is_native != p_enabled) { + if (is_native) { + _clear_menu(); + } + is_native = p_enabled; + _update_menu(); + } +} + +bool MenuBar::is_prefer_global_menu() const { + return is_native; +} + +Size2 MenuBar::get_minimum_size() const { + if (is_native_menu()) { + return Size2(); + } + + Ref<StyleBox> style = theme_cache.normal; + + Vector2 size; + for (int i = 0; i < menu_cache.size(); i++) { + if (menu_cache[i].hidden) { + continue; + } + Size2 sz = menu_cache[i].text_buf->get_size() + style->get_minimum_size(); + size.y = MAX(size.y, sz.y); + size.x += sz.x; + } + if (menu_cache.size() > 1) { + size.x += theme_cache.h_separation * (menu_cache.size() - 1); + } + return size; +} + +int MenuBar::get_menu_count() const { + return menu_cache.size(); +} + +void MenuBar::set_menu_title(int p_menu, const String &p_title) { + ERR_FAIL_INDEX(p_menu, menu_cache.size()); + PopupMenu *pm = get_menu_popup(p_menu); + if (p_title == pm->get_name()) { + pm->remove_meta("_menu_name"); + } else { + pm->set_meta("_menu_name", p_title); + } + menu_cache.write[p_menu].name = p_title; + shape(menu_cache.write[p_menu]); + _update_menu(); +} + +String MenuBar::get_menu_title(int p_menu) const { + ERR_FAIL_INDEX_V(p_menu, menu_cache.size(), String()); + return menu_cache[p_menu].name; +} + +void MenuBar::set_menu_tooltip(int p_menu, const String &p_tooltip) { + ERR_FAIL_INDEX(p_menu, menu_cache.size()); + PopupMenu *pm = get_menu_popup(p_menu); + pm->set_meta("_menu_tooltip", p_tooltip); + menu_cache.write[p_menu].name = p_tooltip; +} + +String MenuBar::get_menu_tooltip(int p_menu) const { + ERR_FAIL_INDEX_V(p_menu, menu_cache.size(), String()); + return menu_cache[p_menu].tooltip; +} + +void MenuBar::set_menu_disabled(int p_menu, bool p_disabled) { + ERR_FAIL_INDEX(p_menu, menu_cache.size()); + menu_cache.write[p_menu].disabled = p_disabled; + _update_menu(); +} + +bool MenuBar::is_menu_disabled(int p_menu) const { + ERR_FAIL_INDEX_V(p_menu, menu_cache.size(), false); + return menu_cache[p_menu].disabled; +} + +void MenuBar::set_menu_hidden(int p_menu, bool p_hidden) { + ERR_FAIL_INDEX(p_menu, menu_cache.size()); + menu_cache.write[p_menu].hidden = p_hidden; + _update_menu(); +} + +bool MenuBar::is_menu_hidden(int p_menu) const { + ERR_FAIL_INDEX_V(p_menu, menu_cache.size(), false); + return menu_cache[p_menu].hidden; +} + +PopupMenu *MenuBar::get_menu_popup(int p_idx) const { + Vector<PopupMenu *> controls = _get_popups(); + if (p_idx >= 0 && p_idx < controls.size()) { + return controls[p_idx]; + } else { + return nullptr; + } +} + +String MenuBar::get_tooltip(const Point2 &p_pos) const { + int index = _get_index_at_point(p_pos); + if (index >= 0 && index < menu_cache.size()) { + return menu_cache[index].tooltip; + } else { + return String(); + } +} + +void MenuBar::get_translatable_strings(List<String> *p_strings) const { + Vector<PopupMenu *> popups = _get_popups(); + for (int i = 0; i < popups.size(); i++) { + PopupMenu *pm = popups[i]; + + if (!pm->has_meta("_menu_name") && !pm->has_meta("_menu_tooltip")) { + continue; + } + + String name = pm->get_meta("_menu_name"); + if (!name.is_empty()) { + p_strings->push_back(name); + } + + String tooltip = pm->get_meta("_menu_tooltip"); + if (!tooltip.is_empty()) { + p_strings->push_back(tooltip); + } + } +} + +MenuBar::MenuBar() { + set_process_shortcut_input(true); +} + +MenuBar::~MenuBar() { +} diff --git a/scene/gui/menu_bar.h b/scene/gui/menu_bar.h new file mode 100644 index 0000000000..f7ef19e98b --- /dev/null +++ b/scene/gui/menu_bar.h @@ -0,0 +1,185 @@ +/*************************************************************************/ +/* menu_bar.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef MENU_BAR_H +#define MENU_BAR_H + +#include "scene/gui/button.h" +#include "scene/gui/popup_menu.h" + +class MenuBar : public Control { + GDCLASS(MenuBar, Control); + + Mutex mutex; + + bool switch_on_hover = true; + bool disable_shortcuts = false; + bool is_native = true; + bool flat = false; + int start_index = -1; + + String language; + TextDirection text_direction = TEXT_DIRECTION_AUTO; + + struct Menu { + String name; + String tooltip; + + Ref<TextLine> text_buf; + bool hidden = false; + bool disabled = false; + + Menu(const String &p_name) { + name = p_name; + text_buf.instantiate(); + } + + Menu() { + text_buf.instantiate(); + } + }; + Vector<Menu> menu_cache; + HashSet<String> global_menus; + + int focused_menu = -1; + int selected_menu = -1; + int active_menu = -1; + + Vector2i mouse_pos_adjusted; + Vector2i old_mouse_pos; + ObjectID shortcut_context; + + struct ThemeCache { + Ref<StyleBox> normal; + Ref<StyleBox> normal_mirrored; + Ref<StyleBox> disabled; + Ref<StyleBox> disabled_mirrored; + Ref<StyleBox> pressed; + Ref<StyleBox> pressed_mirrored; + Ref<StyleBox> hover; + Ref<StyleBox> hover_mirrored; + Ref<StyleBox> hover_pressed; + Ref<StyleBox> hover_pressed_mirrored; + + Ref<Font> font; + int font_size = 0; + int outline_size = 0; + Color font_outline_color; + + Color font_color; + Color font_disabled_color; + Color font_pressed_color; + Color font_hover_color; + Color font_hover_pressed_color; + Color font_focus_color; + + int h_separation = 0; + } theme_cache; + + int _get_index_at_point(const Point2 &p_point) const; + Rect2 _get_menu_item_rect(int p_index) const; + void _draw_menu_item(int p_index); + + void shape(Menu &p_menu); + void _refresh_menu_names(); + Vector<PopupMenu *> _get_popups() const; + int get_menu_idx_from_control(PopupMenu *p_child) const; + + void _open_popup(int p_index, bool p_focus_item = false); + void _popup_visibility_changed(bool p_visible); + void _update_submenu(const String &p_menu_name, PopupMenu *p_child); + void _clear_menu(); + void _update_menu(); + + bool _is_focus_owner_in_shortcut_context() const; + +protected: + virtual void shortcut_input(const Ref<InputEvent> &p_event) override; + + virtual void _update_theme_item_cache() override; + void _notification(int p_what); + virtual void add_child_notify(Node *p_child) override; + virtual void move_child_notify(Node *p_child) override; + virtual void remove_child_notify(Node *p_child) override; + static void _bind_methods(); + +public: + virtual void gui_input(const Ref<InputEvent> &p_event) override; + + void set_switch_on_hover(bool p_enabled); + bool is_switch_on_hover(); + void set_disable_shortcuts(bool p_disabled); + + void set_prefer_global_menu(bool p_enabled); + bool is_prefer_global_menu() const; + + bool is_native_menu() const; + + virtual Size2 get_minimum_size() const override; + + int get_menu_count() const; + + void set_text_direction(TextDirection p_text_direction); + TextDirection get_text_direction() const; + + void set_language(const String &p_language); + String get_language() const; + + void set_start_index(int p_index); + int get_start_index() const; + + void set_flat(bool p_enabled); + bool is_flat() const; + + void set_menu_title(int p_menu, const String &p_title); + String get_menu_title(int p_menu) const; + + void set_menu_tooltip(int p_menu, const String &p_tooltip); + String get_menu_tooltip(int p_menu) const; + + void set_menu_disabled(int p_menu, bool p_disabled); + bool is_menu_disabled(int p_menu) const; + + void set_menu_hidden(int p_menu, bool p_hidden); + bool is_menu_hidden(int p_menu) const; + + void set_shortcut_context(Node *p_node); + Node *get_shortcut_context() const; + + PopupMenu *get_menu_popup(int p_menu) const; + + virtual void get_translatable_strings(List<String> *p_strings) const override; + virtual String get_tooltip(const Point2 &p_pos) const override; + + MenuBar(); + ~MenuBar(); +}; + +#endif // MENU_BAR_H diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp index 316fee53fe..fa8df48412 100644 --- a/scene/gui/menu_button.cpp +++ b/scene/gui/menu_button.cpp @@ -44,15 +44,12 @@ void MenuButton::shortcut_input(const Ref<InputEvent> &p_event) { return; } - if (p_event->is_pressed() && !p_event->is_echo() && (Object::cast_to<InputEventKey>(p_event.ptr()) || Object::cast_to<InputEventJoypadButton>(p_event.ptr()) || Object::cast_to<InputEventAction>(*p_event) || Object::cast_to<InputEventShortcut>(*p_event))) { - if (!get_parent() || !is_visible_in_tree() || is_disabled()) { - return; - } - - if (popup->activate_item_by_event(p_event, false)) { - accept_event(); - } + if (p_event->is_pressed() && !p_event->is_echo() && !is_disabled() && is_visible_in_tree() && popup->activate_item_by_event(p_event, false)) { + accept_event(); + return; } + + Button::shortcut_input(p_event); } void MenuButton::_popup_visibility_changed(bool p_visible) { @@ -86,6 +83,23 @@ void MenuButton::_popup_visibility_changed(bool p_visible) { } void MenuButton::pressed() { + if (popup->is_visible()) { + popup->hide(); + return; + } + + show_popup(); +} + +PopupMenu *MenuButton::get_popup() const { + return popup; +} + +void MenuButton::show_popup() { + if (!get_viewport()) { + return; + } + emit_signal(SNAME("about_to_popup")); Size2 size = get_size() * get_viewport()->get_canvas_transform().get_scale(); @@ -98,24 +112,19 @@ void MenuButton::pressed() { popup->set_position(gp); popup->set_parent_rect(Rect2(Point2(gp - popup->get_position()), size)); - // If not triggered by the mouse, start the popup with its first item selected. - if (popup->get_item_count() > 0 && - ((get_action_mode() == ActionMode::ACTION_MODE_BUTTON_PRESS && Input::get_singleton()->is_action_just_pressed("ui_accept")) || - (get_action_mode() == ActionMode::ACTION_MODE_BUTTON_RELEASE && Input::get_singleton()->is_action_just_released("ui_accept")))) { - popup->set_current_index(0); + // If not triggered by the mouse, start the popup with its first enabled item focused. + if (!_was_pressed_by_mouse()) { + for (int i = 0; i < popup->get_item_count(); i++) { + if (!popup->is_item_disabled(i)) { + popup->set_focused_item(i); + break; + } + } } popup->popup(); } -void MenuButton::gui_input(const Ref<InputEvent> &p_event) { - BaseButton::gui_input(p_event); -} - -PopupMenu *MenuButton::get_popup() const { - return popup; -} - void MenuButton::set_switch_on_hover(bool p_enabled) { switch_on_hover = p_enabled; } @@ -126,6 +135,11 @@ bool MenuButton::is_switch_on_hover() { void MenuButton::set_item_count(int p_count) { ERR_FAIL_COND(p_count < 0); + + if (popup->get_item_count() == p_count) { + return; + } + popup->set_item_count(p_count); notify_property_list_changed(); } @@ -153,7 +167,10 @@ void MenuButton::_notification(int p_what) { if (menu_btn_other && menu_btn_other != this && menu_btn_other->is_switch_on_hover() && !menu_btn_other->is_disabled() && (get_parent()->is_ancestor_of(menu_btn_other) || menu_btn_other->get_parent()->is_ancestor_of(popup))) { popup->hide(); + menu_btn_other->pressed(); + // As the popup wasn't triggered by a mouse click, the item focus needs to be removed manually. + menu_btn_other->get_popup()->set_focused_item(-1); } } break; } @@ -210,6 +227,7 @@ void MenuButton::_get_property_list(List<PropertyInfo> *p_list) const { void MenuButton::_bind_methods() { ClassDB::bind_method(D_METHOD("get_popup"), &MenuButton::get_popup); + ClassDB::bind_method(D_METHOD("show_popup"), &MenuButton::show_popup); ClassDB::bind_method(D_METHOD("set_switch_on_hover", "enable"), &MenuButton::set_switch_on_hover); ClassDB::bind_method(D_METHOD("is_switch_on_hover"), &MenuButton::is_switch_on_hover); ClassDB::bind_method(D_METHOD("set_disable_shortcuts", "disabled"), &MenuButton::set_disable_shortcuts); @@ -239,8 +257,8 @@ MenuButton::MenuButton(const String &p_text) : popup = memnew(PopupMenu); popup->hide(); add_child(popup, false, INTERNAL_MODE_FRONT); - popup->connect("about_to_popup", callable_mp(this, &MenuButton::_popup_visibility_changed), varray(true)); - popup->connect("popup_hide", callable_mp(this, &MenuButton::_popup_visibility_changed), varray(false)); + popup->connect("about_to_popup", callable_mp(this, &MenuButton::_popup_visibility_changed).bind(true)); + popup->connect("popup_hide", callable_mp(this, &MenuButton::_popup_visibility_changed).bind(false)); } MenuButton::~MenuButton() { diff --git a/scene/gui/menu_button.h b/scene/gui/menu_button.h index 0a6b46c796..3aacbca3a8 100644 --- a/scene/gui/menu_button.h +++ b/scene/gui/menu_button.h @@ -44,8 +44,6 @@ class MenuButton : public Button { Vector2i mouse_pos_adjusted; - virtual void gui_input(const Ref<InputEvent> &p_event) override; - void _popup_visibility_changed(bool p_visible); protected: @@ -60,6 +58,8 @@ public: virtual void pressed() override; PopupMenu *get_popup() const; + void show_popup(); + void set_switch_on_hover(bool p_enabled); bool is_switch_on_hover(); void set_disable_shortcuts(bool p_disabled); @@ -71,4 +71,4 @@ public: ~MenuButton(); }; -#endif +#endif // MENU_BUTTON_H diff --git a/scene/gui/nine_patch_rect.cpp b/scene/gui/nine_patch_rect.cpp index 8fee10b19a..6048916d7d 100644 --- a/scene/gui/nine_patch_rect.cpp +++ b/scene/gui/nine_patch_rect.cpp @@ -94,7 +94,7 @@ void NinePatchRect::set_texture(const Ref<Texture2D> &p_tex) { return; } texture = p_tex; - update(); + queue_redraw(); update_minimum_size(); emit_signal(SceneStringNames::get_singleton()->texture_changed); } @@ -105,8 +105,13 @@ Ref<Texture2D> NinePatchRect::get_texture() const { void NinePatchRect::set_patch_margin(Side p_side, int p_size) { ERR_FAIL_INDEX((int)p_side, 4); + + if (margin[p_side] == p_size) { + return; + } + margin[p_side] = p_size; - update(); + queue_redraw(); update_minimum_size(); } @@ -130,8 +135,12 @@ Rect2 NinePatchRect::get_region_rect() const { } void NinePatchRect::set_draw_center(bool p_enabled) { + if (draw_center == p_enabled) { + return; + } + draw_center = p_enabled; - update(); + queue_redraw(); } bool NinePatchRect::is_draw_center_enabled() const { @@ -139,8 +148,12 @@ bool NinePatchRect::is_draw_center_enabled() const { } void NinePatchRect::set_h_axis_stretch_mode(AxisStretchMode p_mode) { + if (axis_h == p_mode) { + return; + } + axis_h = p_mode; - update(); + queue_redraw(); } NinePatchRect::AxisStretchMode NinePatchRect::get_h_axis_stretch_mode() const { @@ -148,8 +161,12 @@ NinePatchRect::AxisStretchMode NinePatchRect::get_h_axis_stretch_mode() const { } void NinePatchRect::set_v_axis_stretch_mode(AxisStretchMode p_mode) { + if (axis_v == p_mode) { + return; + } + axis_v = p_mode; - update(); + queue_redraw(); } NinePatchRect::AxisStretchMode NinePatchRect::get_v_axis_stretch_mode() const { diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index a86f2bdbc1..2cbece69f2 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -35,14 +35,19 @@ static const int NONE_SELECTED = -1; Size2 OptionButton::get_minimum_size() const { - Size2 minsize = Button::get_minimum_size(); + Size2 minsize; + if (fit_to_longest_item) { + minsize = _cached_size; + } else { + minsize = Button::get_minimum_size(); + } if (has_theme_icon(SNAME("arrow"))) { - const Size2 padding = get_theme_stylebox(SNAME("normal"))->get_minimum_size(); - const Size2 arrow_size = Control::get_theme_icon(SNAME("arrow"))->get_size(); + const Size2 padding = theme_cache.normal->get_minimum_size(); + const Size2 arrow_size = theme_cache.arrow_icon->get_size(); Size2 content_size = minsize - padding; - content_size.width += arrow_size.width + get_theme_constant(SNAME("h_separation")); + content_size.width += arrow_size.width + MAX(0, theme_cache.h_separation); content_size.height = MAX(content_size.height, arrow_size.height); minsize = content_size + padding; @@ -51,32 +56,63 @@ Size2 OptionButton::get_minimum_size() const { return minsize; } +void OptionButton::_update_theme_item_cache() { + Button::_update_theme_item_cache(); + + theme_cache.normal = get_theme_stylebox(SNAME("normal")); + + theme_cache.font_color = get_theme_color(SNAME("font_color")); + theme_cache.font_focus_color = get_theme_color(SNAME("font_focus_color")); + theme_cache.font_pressed_color = get_theme_color(SNAME("font_pressed_color")); + theme_cache.font_hover_color = get_theme_color(SNAME("font_hover_color")); + theme_cache.font_hover_pressed_color = get_theme_color(SNAME("font_hover_pressed_color")); + theme_cache.font_disabled_color = get_theme_color(SNAME("font_disabled_color")); + + theme_cache.h_separation = get_theme_constant(SNAME("h_separation")); + + theme_cache.arrow_icon = get_theme_icon(SNAME("arrow")); + theme_cache.arrow_margin = get_theme_constant(SNAME("arrow_margin")); + theme_cache.modulate_arrow = get_theme_constant(SNAME("modulate_arrow")); +} + void OptionButton::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_POSTINITIALIZE: { + if (has_theme_icon(SNAME("arrow"))) { + if (is_layout_rtl()) { + _set_internal_margin(SIDE_LEFT, theme_cache.arrow_icon->get_width()); + } else { + _set_internal_margin(SIDE_RIGHT, theme_cache.arrow_icon->get_width()); + } + } + } break; + case NOTIFICATION_DRAW: { if (!has_theme_icon(SNAME("arrow"))) { return; } RID ci = get_canvas_item(); - Ref<Texture2D> arrow = Control::get_theme_icon(SNAME("arrow")); Color clr = Color(1, 1, 1); - if (get_theme_constant(SNAME("modulate_arrow"))) { + if (theme_cache.modulate_arrow) { switch (get_draw_mode()) { case DRAW_PRESSED: - clr = get_theme_color(SNAME("font_pressed_color")); + clr = theme_cache.font_pressed_color; break; case DRAW_HOVER: - clr = get_theme_color(SNAME("font_hover_color")); + clr = theme_cache.font_hover_color; + break; + case DRAW_HOVER_PRESSED: + clr = theme_cache.font_hover_pressed_color; break; case DRAW_DISABLED: - clr = get_theme_color(SNAME("font_disabled_color")); + clr = theme_cache.font_disabled_color; break; default: if (has_focus()) { - clr = get_theme_color(SNAME("font_focus_color")); + clr = theme_cache.font_focus_color; } else { - clr = get_theme_color(SNAME("font_color")); + clr = theme_cache.font_color; } } } @@ -85,11 +121,11 @@ void OptionButton::_notification(int p_what) { Point2 ofs; if (is_layout_rtl()) { - ofs = Point2(get_theme_constant(SNAME("arrow_margin")), int(Math::abs((size.height - arrow->get_height()) / 2))); + ofs = Point2(theme_cache.arrow_margin, int(Math::abs((size.height - theme_cache.arrow_icon->get_height()) / 2))); } else { - ofs = Point2(size.width - arrow->get_width() - get_theme_constant(SNAME("arrow_margin")), int(Math::abs((size.height - arrow->get_height()) / 2))); + ofs = Point2(size.width - theme_cache.arrow_icon->get_width() - theme_cache.arrow_margin, int(Math::abs((size.height - theme_cache.arrow_icon->get_height()) / 2))); } - arrow->draw(ci, ofs, clr); + theme_cache.arrow_icon->draw(ci, ofs, clr); } break; case NOTIFICATION_TRANSLATION_CHANGED: @@ -100,13 +136,14 @@ void OptionButton::_notification(int p_what) { case NOTIFICATION_THEME_CHANGED: { if (has_theme_icon(SNAME("arrow"))) { if (is_layout_rtl()) { - _set_internal_margin(SIDE_LEFT, Control::get_theme_icon(SNAME("arrow"))->get_width()); + _set_internal_margin(SIDE_LEFT, theme_cache.arrow_icon->get_width()); _set_internal_margin(SIDE_RIGHT, 0.f); } else { _set_internal_margin(SIDE_LEFT, 0.f); - _set_internal_margin(SIDE_RIGHT, Control::get_theme_icon(SNAME("arrow"))->get_width()); + _set_internal_margin(SIDE_RIGHT, theme_cache.arrow_icon->get_width()); } } + _refresh_size_cache(); } break; case NOTIFICATION_VISIBILITY_CHANGED: { @@ -135,6 +172,10 @@ bool OptionButton::_set(const StringName &p_name, const Variant &p_value) { _select(idx, false); } + if (property == "text" || property == "icon") { + _queue_refresh_cache(); + } + return valid; } return false; @@ -185,21 +226,12 @@ void OptionButton::_selected(int p_which) { } void OptionButton::pressed() { - Size2 size = get_size() * get_viewport()->get_canvas_transform().get_scale(); - popup->set_position(get_screen_position() + Size2(0, size.height * get_global_transform().get_scale().y)); - popup->set_size(Size2(size.width, 0)); - - // If not triggered by the mouse, start the popup with the checked item selected. - if (popup->get_item_count() > 0) { - if ((get_action_mode() == ActionMode::ACTION_MODE_BUTTON_PRESS && Input::get_singleton()->is_action_just_pressed("ui_accept")) || - (get_action_mode() == ActionMode::ACTION_MODE_BUTTON_RELEASE && Input::get_singleton()->is_action_just_released("ui_accept"))) { - popup->set_current_index(current > -1 ? current : 0); - } else { - popup->scroll_to_item(current > -1 ? current : 0); - } + if (popup->is_visible()) { + popup->hide(); + return; } - popup->popup(); + show_popup(); } void OptionButton::add_icon_item(const Ref<Texture2D> &p_icon, const String &p_label, int p_id) { @@ -208,6 +240,7 @@ void OptionButton::add_icon_item(const Ref<Texture2D> &p_icon, const String &p_l if (first_selectable) { select(get_item_count() - 1); } + _queue_refresh_cache(); } void OptionButton::add_item(const String &p_label, int p_id) { @@ -216,6 +249,7 @@ void OptionButton::add_item(const String &p_label, int p_id) { if (first_selectable) { select(get_item_count() - 1); } + _queue_refresh_cache(); } void OptionButton::set_item_text(int p_idx, const String &p_text) { @@ -224,6 +258,7 @@ void OptionButton::set_item_text(int p_idx, const String &p_text) { if (current == p_idx) { set_text(p_text); } + _queue_refresh_cache(); } void OptionButton::set_item_icon(int p_idx, const Ref<Texture2D> &p_icon) { @@ -232,6 +267,7 @@ void OptionButton::set_item_icon(int p_idx, const Ref<Texture2D> &p_icon) { if (current == p_idx) { set_icon(p_icon); } + _queue_refresh_cache(); } void OptionButton::set_item_id(int p_idx, int p_id) { @@ -301,6 +337,7 @@ void OptionButton::set_item_count(int p_count) { } } + _refresh_size_cache(); notify_property_list_changed(); } @@ -333,6 +370,19 @@ int OptionButton::get_item_count() const { return popup->get_item_count(); } +void OptionButton::set_fit_to_longest_item(bool p_fit) { + if (p_fit == fit_to_longest_item) { + return; + } + fit_to_longest_item = p_fit; + + _refresh_size_cache(); +} + +bool OptionButton::is_fit_to_longest_item() const { + return fit_to_longest_item; +} + void OptionButton::add_separator(const String &p_text) { popup->add_separator(p_text); } @@ -341,6 +391,7 @@ void OptionButton::clear() { popup->clear(); set_text(""); current = NONE_SELECTED; + _refresh_size_cache(); } void OptionButton::_select(int p_which, bool p_emit) { @@ -380,6 +431,29 @@ void OptionButton::_select_int(int p_which) { _select(p_which, false); } +void OptionButton::_refresh_size_cache() { + cache_refresh_pending = false; + + if (!fit_to_longest_item) { + return; + } + + _cached_size = Vector2(); + for (int i = 0; i < get_item_count(); i++) { + _cached_size = _cached_size.max(get_minimum_size_for_text_and_icon(get_item_text(i), get_item_icon(i))); + } + update_minimum_size(); +} + +void OptionButton::_queue_refresh_cache() { + if (cache_refresh_pending) { + return; + } + cache_refresh_pending = true; + + callable_mp(this, &OptionButton::_refresh_size_cache).call_deferredp(nullptr, 0); +} + void OptionButton::select(int p_idx) { _select(p_idx, false); } @@ -405,19 +479,53 @@ void OptionButton::remove_item(int p_idx) { if (current == p_idx) { _select(NONE_SELECTED); } + _queue_refresh_cache(); } PopupMenu *OptionButton::get_popup() const { return popup; } +void OptionButton::show_popup() { + if (!get_viewport()) { + return; + } + + Size2 size = get_size() * get_viewport()->get_canvas_transform().get_scale(); + popup->set_position(get_screen_position() + Size2(0, size.height * get_global_transform().get_scale().y)); + popup->set_size(Size2(size.width, 0)); + + // If not triggered by the mouse, start the popup with the checked item (or the first enabled one) focused. + if (current != NONE_SELECTED && !popup->is_item_disabled(current)) { + if (!_was_pressed_by_mouse()) { + popup->set_focused_item(current); + } else { + popup->scroll_to_item(current); + } + } else { + for (int i = 0; i < popup->get_item_count(); i++) { + if (!popup->is_item_disabled(i)) { + if (!_was_pressed_by_mouse()) { + popup->set_focused_item(i); + } else { + popup->scroll_to_item(i); + } + + break; + } + } + } + + popup->popup(); +} + void OptionButton::get_translatable_strings(List<String> *p_strings) const { popup->get_translatable_strings(p_strings); } -void OptionButton::_validate_property(PropertyInfo &property) const { - if (property.name == "text" || property.name == "icon") { - property.usage = PROPERTY_USAGE_NONE; +void OptionButton::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "text" || p_property.name == "icon") { + p_property.usage = PROPERTY_USAGE_NONE; } } @@ -445,18 +553,22 @@ void OptionButton::_bind_methods() { ClassDB::bind_method(D_METHOD("get_selected_id"), &OptionButton::get_selected_id); ClassDB::bind_method(D_METHOD("get_selected_metadata"), &OptionButton::get_selected_metadata); ClassDB::bind_method(D_METHOD("remove_item", "idx"), &OptionButton::remove_item); - ClassDB::bind_method(D_METHOD("_select_int"), &OptionButton::_select_int); + ClassDB::bind_method(D_METHOD("_select_int", "idx"), &OptionButton::_select_int); ClassDB::bind_method(D_METHOD("get_popup"), &OptionButton::get_popup); + ClassDB::bind_method(D_METHOD("show_popup"), &OptionButton::show_popup); ClassDB::bind_method(D_METHOD("set_item_count", "count"), &OptionButton::set_item_count); ClassDB::bind_method(D_METHOD("get_item_count"), &OptionButton::get_item_count); ClassDB::bind_method(D_METHOD("has_selectable_items"), &OptionButton::has_selectable_items); ClassDB::bind_method(D_METHOD("get_selectable_item", "from_last"), &OptionButton::get_selectable_item, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("set_fit_to_longest_item", "fit"), &OptionButton::set_fit_to_longest_item); + ClassDB::bind_method(D_METHOD("is_fit_to_longest_item"), &OptionButton::is_fit_to_longest_item); // "selected" property must come after "item_count", otherwise GH-10213 occurs. ADD_ARRAY_COUNT("Items", "item_count", "set_item_count", "get_item_count", "popup/item_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "selected"), "_select_int", "get_selected"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fit_to_longest_item"), "set_fit_to_longest_item", "is_fit_to_longest_item"); ADD_SIGNAL(MethodInfo("item_selected", PropertyInfo(Variant::INT, "index"))); ADD_SIGNAL(MethodInfo("item_focused", PropertyInfo(Variant::INT, "index"))); } @@ -465,15 +577,6 @@ OptionButton::OptionButton(const String &p_text) : Button(p_text) { set_toggle_mode(true); set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT); - if (is_layout_rtl()) { - if (has_theme_icon(SNAME("arrow"))) { - _set_internal_margin(SIDE_LEFT, Control::get_theme_icon(SNAME("arrow"))->get_width()); - } - } else { - if (has_theme_icon(SNAME("arrow"))) { - _set_internal_margin(SIDE_RIGHT, Control::get_theme_icon(SNAME("arrow"))->get_width()); - } - } set_action_mode(ACTION_MODE_BUTTON_PRESS); popup = memnew(PopupMenu); @@ -481,7 +584,8 @@ OptionButton::OptionButton(const String &p_text) : add_child(popup, false, INTERNAL_MODE_FRONT); popup->connect("index_pressed", callable_mp(this, &OptionButton::_selected)); popup->connect("id_focused", callable_mp(this, &OptionButton::_focused)); - popup->connect("popup_hide", callable_mp((BaseButton *)this, &BaseButton::set_pressed), varray(false)); + popup->connect("popup_hide", callable_mp((BaseButton *)this, &BaseButton::set_pressed).bind(false)); + _refresh_size_cache(); } OptionButton::~OptionButton() { diff --git a/scene/gui/option_button.h b/scene/gui/option_button.h index 7896132626..b76a31d37e 100644 --- a/scene/gui/option_button.h +++ b/scene/gui/option_button.h @@ -39,21 +39,44 @@ class OptionButton : public Button { PopupMenu *popup = nullptr; int current = -1; + bool fit_to_longest_item = true; + Vector2 _cached_size; + bool cache_refresh_pending = false; + + struct ThemeCache { + Ref<StyleBox> normal; + + Color font_color; + Color font_focus_color; + Color font_pressed_color; + Color font_hover_color; + Color font_hover_pressed_color; + Color font_disabled_color; + + int h_separation = 0; + + Ref<Texture2D> arrow_icon; + int arrow_margin = 0; + int modulate_arrow = 0; + } theme_cache; void _focused(int p_which); void _selected(int p_which); void _select(int p_which, bool p_emit = false); void _select_int(int p_which); + void _refresh_size_cache(); + void _queue_refresh_cache(); virtual void pressed() override; protected: Size2 get_minimum_size() const override; + virtual void _update_theme_item_cache() override; void _notification(int p_what); bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; - virtual void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; static void _bind_methods(); public: @@ -85,6 +108,8 @@ public: void set_item_count(int p_count); int get_item_count() const; + void set_fit_to_longest_item(bool p_fit); + bool is_fit_to_longest_item() const; void add_separator(const String &p_text = ""); @@ -98,6 +123,7 @@ public: void remove_item(int p_idx); PopupMenu *get_popup() const; + void show_popup(); virtual void get_translatable_strings(List<String> *p_strings) const override; @@ -105,4 +131,4 @@ public: ~OptionButton(); }; -#endif +#endif // OPTION_BUTTON_H diff --git a/scene/gui/panel.cpp b/scene/gui/panel.cpp index 1ac6cf57ab..09bc295513 100644 --- a/scene/gui/panel.cpp +++ b/scene/gui/panel.cpp @@ -30,12 +30,17 @@ #include "panel.h" +void Panel::_update_theme_item_cache() { + Control::_update_theme_item_cache(); + + theme_cache.panel_style = get_theme_stylebox(SNAME("panel")); +} + void Panel::_notification(int p_what) { switch (p_what) { case NOTIFICATION_DRAW: { RID ci = get_canvas_item(); - Ref<StyleBox> style = get_theme_stylebox(SNAME("panel")); - style->draw(ci, Rect2(Point2(), get_size())); + theme_cache.panel_style->draw(ci, Rect2(Point2(), get_size())); } break; } } diff --git a/scene/gui/panel.h b/scene/gui/panel.h index 5d2e912680..f9bd721681 100644 --- a/scene/gui/panel.h +++ b/scene/gui/panel.h @@ -36,7 +36,13 @@ class Panel : public Control { GDCLASS(Panel, Control); + struct ThemeCache { + Ref<StyleBox> panel_style; + } theme_cache; + protected: + virtual void _update_theme_item_cache() override; + void _notification(int p_what); public: diff --git a/scene/gui/panel_container.cpp b/scene/gui/panel_container.cpp index fe01712a89..eeaded1788 100644 --- a/scene/gui/panel_container.cpp +++ b/scene/gui/panel_container.cpp @@ -31,14 +31,6 @@ #include "panel_container.h" Size2 PanelContainer::get_minimum_size() const { - Ref<StyleBox> style; - - if (has_theme_stylebox(SNAME("panel"))) { - style = get_theme_stylebox(SNAME("panel")); - } else { - style = get_theme_stylebox(SNAME("panel"), SNAME("PanelContainer")); - } - Size2 ms; for (int i = 0; i < get_child_count(); i++) { Control *c = Object::cast_to<Control>(get_child(i)); @@ -54,8 +46,8 @@ Size2 PanelContainer::get_minimum_size() const { ms.height = MAX(ms.height, minsize.height); } - if (style.is_valid()) { - ms += style->get_minimum_size(); + if (theme_cache.panel_style.is_valid()) { + ms += theme_cache.panel_style->get_minimum_size(); } return ms; } @@ -78,35 +70,25 @@ Vector<int> PanelContainer::get_allowed_size_flags_vertical() const { return flags; } +void PanelContainer::_update_theme_item_cache() { + Container::_update_theme_item_cache(); + + theme_cache.panel_style = get_theme_stylebox(SNAME("panel")); +} + void PanelContainer::_notification(int p_what) { switch (p_what) { case NOTIFICATION_DRAW: { RID ci = get_canvas_item(); - Ref<StyleBox> style; - - if (has_theme_stylebox(SNAME("panel"))) { - style = get_theme_stylebox(SNAME("panel")); - } else { - style = get_theme_stylebox(SNAME("panel"), SNAME("PanelContainer")); - } - - style->draw(ci, Rect2(Point2(), get_size())); + theme_cache.panel_style->draw(ci, Rect2(Point2(), get_size())); } break; case NOTIFICATION_SORT_CHILDREN: { - Ref<StyleBox> style; - - if (has_theme_stylebox(SNAME("panel"))) { - style = get_theme_stylebox(SNAME("panel")); - } else { - style = get_theme_stylebox(SNAME("panel"), SNAME("PanelContainer")); - } - Size2 size = get_size(); Point2 ofs; - if (style.is_valid()) { - size -= style->get_minimum_size(); - ofs += style->get_offset(); + if (theme_cache.panel_style.is_valid()) { + size -= theme_cache.panel_style->get_minimum_size(); + ofs += theme_cache.panel_style->get_offset(); } for (int i = 0; i < get_child_count(); i++) { diff --git a/scene/gui/panel_container.h b/scene/gui/panel_container.h index 8f07ce38eb..97d0cdc872 100644 --- a/scene/gui/panel_container.h +++ b/scene/gui/panel_container.h @@ -36,7 +36,12 @@ class PanelContainer : public Container { GDCLASS(PanelContainer, Container); + struct ThemeCache { + Ref<StyleBox> panel_style; + } theme_cache; + protected: + virtual void _update_theme_item_cache() override; void _notification(int p_what); public: diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp index c4396f636a..ceae3791f3 100644 --- a/scene/gui/popup.cpp +++ b/scene/gui/popup.cpp @@ -68,6 +68,12 @@ void Popup::_deinitialize_visible_parents() { } } +void Popup::_update_theme_item_cache() { + Window::_update_theme_item_cache(); + + theme_cache.panel_style = get_theme_stylebox(SNAME("panel")); +} + void Popup::_notification(int p_what) { switch (p_what) { case NOTIFICATION_VISIBILITY_CHANGED: { @@ -186,8 +192,6 @@ Popup::~Popup() { } Size2 PopupPanel::_get_contents_minimum_size() const { - Ref<StyleBox> p = get_theme_stylebox(SNAME("panel"), get_class_name()); - Size2 ms; for (int i = 0; i < get_child_count(); i++) { @@ -205,14 +209,12 @@ Size2 PopupPanel::_get_contents_minimum_size() const { ms.y = MAX(cms.y, ms.y); } - return ms + p->get_minimum_size(); + return ms + theme_cache.panel_style->get_minimum_size(); } void PopupPanel::_update_child_rects() { - Ref<StyleBox> p = get_theme_stylebox(SNAME("panel"), get_class_name()); - - Vector2 cpos(p->get_offset()); - Vector2 csize(get_size() - p->get_minimum_size()); + Vector2 cpos(theme_cache.panel_style->get_offset()); + Vector2 csize(get_size() - theme_cache.panel_style->get_minimum_size()); for (int i = 0; i < get_child_count(); i++) { Control *c = Object::cast_to<Control>(get_child(i)); @@ -234,15 +236,17 @@ void PopupPanel::_update_child_rects() { } } +void PopupPanel::_update_theme_item_cache() { + Popup::_update_theme_item_cache(); + + theme_cache.panel_style = get_theme_stylebox(SNAME("panel")); +} + void PopupPanel::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_READY: case NOTIFICATION_THEME_CHANGED: { - panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), get_class_name())); - } break; - - case NOTIFICATION_ENTER_TREE: - case NOTIFICATION_READY: { - panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), get_class_name())); + panel->add_theme_style_override("panel", theme_cache.panel_style); _update_child_rects(); } break; diff --git a/scene/gui/popup.h b/scene/gui/popup.h index b53c8be50f..0d6ca25c18 100644 --- a/scene/gui/popup.h +++ b/scene/gui/popup.h @@ -43,6 +43,10 @@ class Popup : public Window { LocalVector<Window *> visible_parents; bool popped_up = false; + struct ThemeCache { + Ref<StyleBox> panel_style; + } theme_cache; + void _input_from_window(const Ref<InputEvent> &p_event); void _initialize_visible_parents(); @@ -52,6 +56,7 @@ protected: void _close_pressed(); virtual Rect2i _popup_adjust_rect() const override; + virtual void _update_theme_item_cache() override; void _notification(int p_what); static void _bind_methods(); @@ -69,8 +74,14 @@ class PopupPanel : public Popup { Panel *panel = nullptr; + struct ThemeCache { + Ref<StyleBox> panel_style; + } theme_cache; + protected: void _update_child_rects(); + + virtual void _update_theme_item_cache() override; void _notification(int p_what); virtual Size2 _get_contents_minimum_size() const override; @@ -79,4 +90,4 @@ public: PopupPanel(); }; -#endif +#endif // POPUP_H diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index 3a31246b17..10e13042a7 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -36,6 +36,7 @@ #include "core/os/os.h" #include "core/string/print_string.h" #include "core/string/translation.h" +#include "scene/gui/menu_bar.h" String PopupMenu::_get_accel_text(const Item &p_item) const { if (p_item.shortcut.is_valid()) { @@ -47,15 +48,12 @@ String PopupMenu::_get_accel_text(const Item &p_item) const { } Size2 PopupMenu::_get_contents_minimum_size() const { - int vseparation = get_theme_constant(SNAME("v_separation")); - int hseparation = get_theme_constant(SNAME("h_separation")); - - Size2 minsize = get_theme_stylebox(SNAME("panel"))->get_minimum_size(); // Accounts for margin in the margin container + Size2 minsize = theme_cache.panel_style->get_minimum_size(); // Accounts for margin in the margin container minsize.x += scroll_container->get_v_scroll_bar()->get_size().width * 2; // Adds a buffer so that the scrollbar does not render over the top of content float max_w = 0.0; float icon_w = 0.0; - int check_w = MAX(get_theme_icon(SNAME("checked"))->get_width(), get_theme_icon(SNAME("radio_checked"))->get_width()) + hseparation; + int check_w = MAX(theme_cache.checked->get_width(), theme_cache.radio_checked->get_width()) + theme_cache.h_separation; int accel_max_w = 0; bool has_check = false; @@ -66,23 +64,23 @@ Size2 PopupMenu::_get_contents_minimum_size() const { size.height = _get_item_height(i); icon_w = MAX(icon_size.width, icon_w); - size.width += items[i].h_ofs; + size.width += items[i].indent * theme_cache.indent; if (items[i].checkable_type && !items[i].separator) { has_check = true; } size.width += items[i].text_buf->get_size().x; - size.height += vseparation; + size.height += theme_cache.v_separation; if (items[i].accel != Key::NONE || (items[i].shortcut.is_valid() && items[i].shortcut->has_valid_event())) { - int accel_w = hseparation * 2; + int accel_w = theme_cache.h_separation * 2; accel_w += items[i].accel_text_buf->get_size().x; accel_max_w = MAX(accel_w, accel_max_w); } if (!items[i].submenu.is_empty()) { - size.width += get_theme_icon(SNAME("submenu"))->get_width(); + size.width += theme_cache.submenu->get_width(); } max_w = MAX(max_w, size.width); @@ -90,7 +88,7 @@ Size2 PopupMenu::_get_contents_minimum_size() const { minsize.height += size.height; } - int item_side_padding = get_theme_constant(SNAME("item_start_padding")) + get_theme_constant(SNAME("item_end_padding")); + int item_side_padding = theme_cache.item_start_padding + theme_cache.item_end_padding; minsize.width += max_w + icon_w + accel_max_w + item_side_padding; if (has_check) { @@ -112,33 +110,31 @@ int PopupMenu::_get_item_height(int p_item) const { int icon_height = items[p_item].get_icon_size().height; if (items[p_item].checkable_type && !items[p_item].separator) { - icon_height = MAX(icon_height, MAX(get_theme_icon(SNAME("checked"))->get_height(), get_theme_icon(SNAME("radio_checked"))->get_height())); + icon_height = MAX(icon_height, MAX(theme_cache.checked->get_height(), theme_cache.radio_checked->get_height())); } int text_height = items[p_item].text_buf->get_size().height; if (text_height == 0 && !items[p_item].separator) { - text_height = get_theme_font(SNAME("font"))->get_height(get_theme_font_size(SNAME("font_size"))); + text_height = theme_cache.font->get_height(theme_cache.font_size); } int separator_height = 0; if (items[p_item].separator) { - separator_height = MAX(get_theme_stylebox(SNAME("separator"))->get_minimum_size().height, MAX(get_theme_stylebox(SNAME("labeled_separator_left"))->get_minimum_size().height, get_theme_stylebox(SNAME("labeled_separator_right"))->get_minimum_size().height)); + separator_height = MAX(theme_cache.separator_style->get_minimum_size().height, MAX(theme_cache.labeled_separator_left->get_minimum_size().height, theme_cache.labeled_separator_right->get_minimum_size().height)); } return MAX(separator_height, MAX(text_height, icon_height)); } int PopupMenu::_get_items_total_height() const { - int vsep = get_theme_constant(SNAME("v_separation")); - // Get total height of all items by taking max of icon height and font height int items_total_height = 0; for (int i = 0; i < items.size(); i++) { - items_total_height += _get_item_height(i) + vsep; + items_total_height += _get_item_height(i) + theme_cache.v_separation; } // Subtract a separator which is not needed for the last item. - return items_total_height - vsep; + return items_total_height - theme_cache.v_separation; } int PopupMenu::_get_mouse_over(const Point2 &p_over) const { @@ -146,18 +142,15 @@ int PopupMenu::_get_mouse_over(const Point2 &p_over) const { return -1; } - Ref<StyleBox> style = get_theme_stylebox(SNAME("panel")); // Accounts for margin in the margin container - - int vseparation = get_theme_constant(SNAME("v_separation")); - - Point2 ofs = style->get_offset() + Point2(0, vseparation / 2); + // Accounts for margin in the margin container + Point2 ofs = theme_cache.panel_style->get_offset() + Point2(0, theme_cache.v_separation / 2); if (ofs.y > p_over.y) { return -1; } for (int i = 0; i < items.size(); i++) { - ofs.y += i > 0 ? vseparation : (float)vseparation / 2; + ofs.y += i > 0 ? theme_cache.v_separation : (float)theme_cache.v_separation / 2; ofs.y += _get_item_height(i); @@ -178,9 +171,6 @@ void PopupMenu::_activate_submenu(int p_over, bool p_by_keyboard) { return; // Already visible. } - Ref<StyleBox> style = get_theme_stylebox(SNAME("panel")); - int vsep = get_theme_constant(SNAME("v_separation")); - Point2 this_pos = get_position(); Rect2 this_rect(this_pos, get_size()); @@ -215,9 +205,14 @@ void PopupMenu::_activate_submenu(int p_over, bool p_by_keyboard) { submenu_pum->activated_by_keyboard = p_by_keyboard; - // If not triggered by the mouse, start the popup with its first item selected. - if (submenu_pum->get_item_count() > 0 && p_by_keyboard) { - submenu_pum->set_current_index(0); + // If not triggered by the mouse, start the popup with its first enabled item focused. + if (p_by_keyboard) { + for (int i = 0; i < submenu_pum->get_item_count(); i++) { + if (!submenu_pum->is_item_disabled(i)) { + submenu_pum->set_focused_item(i); + break; + } + } } submenu_pum->popup(); @@ -225,7 +220,7 @@ void PopupMenu::_activate_submenu(int p_over, bool p_by_keyboard) { // Set autohide areas. Rect2 safe_area = this_rect; - safe_area.position.y += items[p_over]._ofs_cache + scroll_offset + style->get_offset().height - vsep / 2; + safe_area.position.y += items[p_over]._ofs_cache + scroll_offset + theme_cache.panel_style->get_offset().height - theme_cache.v_separation / 2; safe_area.size.y = items[p_over]._height_cache; DisplayServer::get_singleton()->window_set_popup_safe_rect(submenu_popup->get_window_id(), safe_area); @@ -234,11 +229,11 @@ void PopupMenu::_activate_submenu(int p_over, bool p_by_keyboard) { // Autohide area above the submenu item. submenu_pum->clear_autohide_areas(); - submenu_pum->add_autohide_area(Rect2(this_rect.position.x, this_rect.position.y, this_rect.size.x, items[p_over]._ofs_cache + scroll_offset + style->get_offset().height - vsep / 2)); + submenu_pum->add_autohide_area(Rect2(this_rect.position.x, this_rect.position.y, this_rect.size.x, items[p_over]._ofs_cache + scroll_offset + theme_cache.panel_style->get_offset().height - theme_cache.v_separation / 2)); // If there is an area below the submenu item, add an autohide area there. if (items[p_over]._ofs_cache + items[p_over]._height_cache + scroll_offset <= control->get_size().height) { - int from = items[p_over]._ofs_cache + items[p_over]._height_cache + scroll_offset + vsep / 2 + style->get_offset().height; + int from = items[p_over]._ofs_cache + items[p_over]._height_cache + scroll_offset + theme_cache.v_separation / 2 + theme_cache.panel_style->get_offset().height; submenu_pum->add_autohide_area(Rect2(this_rect.position.x, this_rect.position.y + from, this_rect.size.x, this_rect.size.y - from)); } } @@ -277,89 +272,104 @@ void PopupMenu::_submenu_timeout() { void PopupMenu::gui_input(const Ref<InputEvent> &p_event) { ERR_FAIL_COND(p_event.is_null()); - if (p_event->is_action("ui_down") && p_event->is_pressed()) { - int search_from = mouse_over + 1; - if (search_from >= items.size()) { - search_from = 0; - } - - bool match_found = false; - for (int i = search_from; i < items.size(); i++) { - if (!items[i].separator && !items[i].disabled) { - mouse_over = i; - emit_signal(SNAME("id_focused"), i); - scroll_to_item(i); - control->update(); - set_input_as_handled(); - match_found = true; - break; + if (!items.is_empty()) { + if (p_event->is_action("ui_down") && p_event->is_pressed()) { + int search_from = mouse_over + 1; + if (search_from >= items.size()) { + search_from = 0; } - } - if (!match_found) { - // If the last item is not selectable, try re-searching from the start. - for (int i = 0; i < search_from; i++) { + bool match_found = false; + for (int i = search_from; i < items.size(); i++) { if (!items[i].separator && !items[i].disabled) { mouse_over = i; emit_signal(SNAME("id_focused"), i); scroll_to_item(i); - control->update(); + control->queue_redraw(); set_input_as_handled(); + match_found = true; break; } } - } - } else if (p_event->is_action("ui_up") && p_event->is_pressed()) { - int search_from = mouse_over - 1; - if (search_from < 0) { - search_from = items.size() - 1; - } - bool match_found = false; - for (int i = search_from; i >= 0; i--) { - if (!items[i].separator && !items[i].disabled) { - mouse_over = i; - emit_signal(SNAME("id_focused"), i); - scroll_to_item(i); - control->update(); - set_input_as_handled(); - match_found = true; - break; + if (!match_found) { + // If the last item is not selectable, try re-searching from the start. + for (int i = 0; i < search_from; i++) { + if (!items[i].separator && !items[i].disabled) { + mouse_over = i; + emit_signal(SNAME("id_focused"), i); + scroll_to_item(i); + control->queue_redraw(); + set_input_as_handled(); + break; + } + } + } + } else if (p_event->is_action("ui_up") && p_event->is_pressed()) { + int search_from = mouse_over - 1; + if (search_from < 0) { + search_from = items.size() - 1; } - } - if (!match_found) { - // If the first item is not selectable, try re-searching from the end. - for (int i = items.size() - 1; i >= search_from; i--) { + bool match_found = false; + for (int i = search_from; i >= 0; i--) { if (!items[i].separator && !items[i].disabled) { mouse_over = i; emit_signal(SNAME("id_focused"), i); scroll_to_item(i); - control->update(); + control->queue_redraw(); set_input_as_handled(); + match_found = true; break; } } - } - } else if (p_event->is_action("ui_left") && p_event->is_pressed()) { - Node *n = get_parent(); - if (n && Object::cast_to<PopupMenu>(n)) { - hide(); - set_input_as_handled(); - } - } else if (p_event->is_action("ui_right") && p_event->is_pressed()) { - if (mouse_over >= 0 && mouse_over < items.size() && !items[mouse_over].separator && !items[mouse_over].submenu.is_empty() && submenu_over != mouse_over) { - _activate_submenu(mouse_over, true); - set_input_as_handled(); - } - } else if (p_event->is_action("ui_accept") && p_event->is_pressed()) { - if (mouse_over >= 0 && mouse_over < items.size() && !items[mouse_over].separator) { - if (!items[mouse_over].submenu.is_empty() && submenu_over != mouse_over) { + + if (!match_found) { + // If the first item is not selectable, try re-searching from the end. + for (int i = items.size() - 1; i >= search_from; i--) { + if (!items[i].separator && !items[i].disabled) { + mouse_over = i; + emit_signal(SNAME("id_focused"), i); + scroll_to_item(i); + control->queue_redraw(); + set_input_as_handled(); + break; + } + } + } + } else if (p_event->is_action("ui_left") && p_event->is_pressed()) { + Node *n = get_parent(); + if (n) { + if (Object::cast_to<PopupMenu>(n)) { + hide(); + set_input_as_handled(); + } else if (Object::cast_to<MenuBar>(n)) { + Object::cast_to<MenuBar>(n)->gui_input(p_event); + set_input_as_handled(); + return; + } + } + } else if (p_event->is_action("ui_right") && p_event->is_pressed()) { + if (mouse_over >= 0 && mouse_over < items.size() && !items[mouse_over].separator && !items[mouse_over].submenu.is_empty() && submenu_over != mouse_over) { _activate_submenu(mouse_over, true); + set_input_as_handled(); } else { - activate_item(mouse_over); + Node *n = get_parent(); + if (n && Object::cast_to<MenuBar>(n)) { + Object::cast_to<MenuBar>(n)->gui_input(p_event); + set_input_as_handled(); + return; + } + } + } else if (p_event->is_action("ui_accept") && p_event->is_pressed()) { + if (mouse_over >= 0 && mouse_over < items.size() && !items[mouse_over].separator) { + if (!items[mouse_over].submenu.is_empty() && submenu_over != mouse_over) { + _activate_submenu(mouse_over, true); + } else { + activate_item(mouse_over); + } + set_input_as_handled(); } - set_input_as_handled(); } } @@ -421,7 +431,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseMotion> m = p_event; if (m.is_valid()) { - if (m->get_velocity().is_equal_approx(Vector2())) { + if (m->get_velocity().is_zero_approx()) { return; } activated_by_keyboard = false; @@ -442,7 +452,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) { if (id < 0) { mouse_over = -1; - control->update(); + control->queue_redraw(); return; } @@ -453,7 +463,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) { if (over != mouse_over) { mouse_over = over; - control->update(); + control->queue_redraw(); } } @@ -490,7 +500,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) { mouse_over = i; emit_signal(SNAME("id_focused"), i); scroll_to_item(i); - control->update(); + control->queue_redraw(); set_input_as_handled(); break; } @@ -507,34 +517,17 @@ void PopupMenu::_draw_items() { margin_size.height = margin_container->get_theme_constant(SNAME("margin_top")) + margin_container->get_theme_constant(SNAME("margin_bottom")); // Space between the item content and the sides of popup menu. - int item_start_padding = get_theme_constant(SNAME("item_start_padding")); - int item_end_padding = get_theme_constant(SNAME("item_end_padding")); - bool rtl = control->is_layout_rtl(); - Ref<StyleBox> style = get_theme_stylebox(SNAME("panel")); - Ref<StyleBox> hover = get_theme_stylebox(SNAME("hover")); - // In Item::checkable_type enum order (less the non-checkable member). - Ref<Texture2D> check[] = { get_theme_icon(SNAME("checked")), get_theme_icon(SNAME("radio_checked")) }; - Ref<Texture2D> uncheck[] = { get_theme_icon(SNAME("unchecked")), get_theme_icon(SNAME("radio_unchecked")) }; + // In Item::checkable_type enum order (less the non-checkable member), with disabled repeated at the end. + Ref<Texture2D> check[] = { theme_cache.checked, theme_cache.radio_checked, theme_cache.checked_disabled, theme_cache.radio_checked_disabled }; + Ref<Texture2D> uncheck[] = { theme_cache.unchecked, theme_cache.radio_unchecked, theme_cache.unchecked_disabled, theme_cache.radio_unchecked_disabled }; Ref<Texture2D> submenu; if (rtl) { - submenu = get_theme_icon(SNAME("submenu_mirrored")); + submenu = theme_cache.submenu_mirrored; } else { - submenu = get_theme_icon(SNAME("submenu")); + submenu = theme_cache.submenu; } - Ref<StyleBox> separator = get_theme_stylebox(SNAME("separator")); - Ref<StyleBox> labeled_separator_left = get_theme_stylebox(SNAME("labeled_separator_left")); - Ref<StyleBox> labeled_separator_right = get_theme_stylebox(SNAME("labeled_separator_right")); - - int vseparation = get_theme_constant(SNAME("v_separation")); - int hseparation = get_theme_constant(SNAME("h_separation")); - Color font_color = get_theme_color(SNAME("font_color")); - Color font_disabled_color = get_theme_color(SNAME("font_disabled_color")); - Color font_accelerator_color = get_theme_color(SNAME("font_accelerator_color")); - Color font_hover_color = get_theme_color(SNAME("font_hover_color")); - Color font_separator_color = get_theme_color(SNAME("font_separator_color")); - float scroll_width = scroll_container->get_v_scroll_bar()->is_visible_in_tree() ? scroll_container->get_v_scroll_bar()->get_size().width : 0; float display_width = control->get_size().width - scroll_width; @@ -553,12 +546,16 @@ void PopupMenu::_draw_items() { } } if (icon_ofs > 0.0) { - icon_ofs += hseparation; + icon_ofs += theme_cache.h_separation; } float check_ofs = 0.0; if (has_check) { - check_ofs = MAX(get_theme_icon(SNAME("checked"))->get_width(), get_theme_icon(SNAME("radio_checked"))->get_width()) + hseparation; + for (int i = 0; i < 4; i++) { + check_ofs = MAX(check_ofs, check[i]->get_width()); + check_ofs = MAX(check_ofs, uncheck[i]->get_width()); + } + check_ofs += theme_cache.h_separation; } Point2 ofs = Point2(); @@ -566,7 +563,7 @@ void PopupMenu::_draw_items() { // Loop through all items and draw each. for (int i = 0; i < items.size(); i++) { // For the first item only add half a separation. For all other items, add a whole separation to the offset. - ofs.y += i > 0 ? vseparation : (float)vseparation / 2; + ofs.y += i > 0 ? theme_cache.v_separation : (float)theme_cache.v_separation / 2; _shape_item(i); @@ -576,51 +573,52 @@ void PopupMenu::_draw_items() { if (i == mouse_over) { if (rtl) { - hover->draw(ci, Rect2(item_ofs + Point2(scroll_width, -vseparation / 2), Size2(display_width, h + vseparation))); + theme_cache.hover_style->draw(ci, Rect2(item_ofs + Point2(scroll_width, -theme_cache.v_separation / 2), Size2(display_width, h + theme_cache.v_separation))); } else { - hover->draw(ci, Rect2(item_ofs + Point2(0, -vseparation / 2), Size2(display_width, h + vseparation))); + theme_cache.hover_style->draw(ci, Rect2(item_ofs + Point2(0, -theme_cache.v_separation / 2), Size2(display_width, h + theme_cache.v_separation))); } } String text = items[i].xl_text; // Separator - item_ofs.x += items[i].h_ofs; + item_ofs.x += items[i].indent * theme_cache.indent; if (items[i].separator) { if (!text.is_empty() || !items[i].icon.is_null()) { - int content_size = items[i].text_buf->get_size().width + hseparation * 2; + int content_size = items[i].text_buf->get_size().width + theme_cache.h_separation * 2; if (!items[i].icon.is_null()) { - content_size += icon_size.width + hseparation; + content_size += icon_size.width + theme_cache.h_separation; } int content_center = display_width / 2; int content_left = content_center - content_size / 2; int content_right = content_center + content_size / 2; if (content_left > item_ofs.x) { - int sep_h = labeled_separator_left->get_center_size().height + labeled_separator_left->get_minimum_size().height; + int sep_h = theme_cache.labeled_separator_left->get_center_size().height + theme_cache.labeled_separator_left->get_minimum_size().height; int sep_ofs = Math::floor((h - sep_h) / 2.0); - labeled_separator_left->draw(ci, Rect2(item_ofs + Point2(0, sep_ofs), Size2(MAX(0, content_left - item_ofs.x), sep_h))); + theme_cache.labeled_separator_left->draw(ci, Rect2(item_ofs + Point2(0, sep_ofs), Size2(MAX(0, content_left - item_ofs.x), sep_h))); } if (content_right < display_width) { - int sep_h = labeled_separator_right->get_center_size().height + labeled_separator_right->get_minimum_size().height; + int sep_h = theme_cache.labeled_separator_right->get_center_size().height + theme_cache.labeled_separator_right->get_minimum_size().height; int sep_ofs = Math::floor((h - sep_h) / 2.0); - labeled_separator_right->draw(ci, Rect2(Point2(content_right, item_ofs.y + sep_ofs), Size2(MAX(0, display_width - content_right), sep_h))); + theme_cache.labeled_separator_right->draw(ci, Rect2(Point2(content_right, item_ofs.y + sep_ofs), Size2(MAX(0, display_width - content_right), sep_h))); } } else { - int sep_h = separator->get_center_size().height + separator->get_minimum_size().height; + int sep_h = theme_cache.separator_style->get_center_size().height + theme_cache.separator_style->get_minimum_size().height; int sep_ofs = Math::floor((h - sep_h) / 2.0); - separator->draw(ci, Rect2(item_ofs + Point2(0, sep_ofs), Size2(display_width, sep_h))); + theme_cache.separator_style->draw(ci, Rect2(item_ofs + Point2(0, sep_ofs), Size2(display_width, sep_h))); } } Color icon_color(1, 1, 1, items[i].disabled && !items[i].separator ? 0.5 : 1); // For non-separator items, add some padding for the content. - item_ofs.x += item_start_padding; + item_ofs.x += theme_cache.item_start_padding; // Checkboxes if (items[i].checkable_type && !items[i].separator) { - Texture2D *icon = (items[i].checked ? check[items[i].checkable_type - 1] : uncheck[items[i].checkable_type - 1]).ptr(); + int disabled = int(items[i].disabled) * 2; + Texture2D *icon = (items[i].checked ? check[items[i].checkable_type - 1 + disabled] : uncheck[items[i].checkable_type - 1 + disabled]).ptr(); if (rtl) { icon->draw(ci, Size2(control->get_size().width - item_ofs.x - icon->get_width(), item_ofs.y) + Point2(0, Math::floor((h - icon->get_height()) / 2.0)), icon_color); } else { @@ -633,7 +631,7 @@ void PopupMenu::_draw_items() { // Icon if (!items[i].icon.is_null()) { if (items[i].separator) { - separator_ofs -= (icon_size.width + hseparation) / 2; + separator_ofs -= (icon_size.width + theme_cache.h_separation) / 2; if (rtl) { items[i].icon->draw(ci, Size2(control->get_size().width - item_ofs.x - separator_ofs - icon_size.width, item_ofs.y) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color); @@ -652,61 +650,55 @@ void PopupMenu::_draw_items() { // Submenu arrow on right hand side. if (!items[i].submenu.is_empty()) { if (rtl) { - submenu->draw(ci, Point2(scroll_width + style->get_margin(SIDE_LEFT) + item_end_padding, item_ofs.y + Math::floor(h - submenu->get_height()) / 2), icon_color); + submenu->draw(ci, Point2(scroll_width + theme_cache.panel_style->get_margin(SIDE_LEFT) + theme_cache.item_end_padding, item_ofs.y + Math::floor(h - submenu->get_height()) / 2), icon_color); } else { - submenu->draw(ci, Point2(display_width - style->get_margin(SIDE_RIGHT) - submenu->get_width() - item_end_padding, item_ofs.y + Math::floor(h - submenu->get_height()) / 2), icon_color); + submenu->draw(ci, Point2(display_width - theme_cache.panel_style->get_margin(SIDE_RIGHT) - submenu->get_width() - theme_cache.item_end_padding, item_ofs.y + Math::floor(h - submenu->get_height()) / 2), icon_color); } } - Color font_outline_color = get_theme_color(SNAME("font_outline_color")); - int outline_size = get_theme_constant(SNAME("outline_size")); - // Text if (items[i].separator) { - Color font_separator_outline_color = get_theme_color(SNAME("font_separator_outline_color")); - int separator_outline_size = get_theme_constant(SNAME("separator_outline_size")); - if (!text.is_empty()) { Vector2 text_pos = Point2(separator_ofs, item_ofs.y + Math::floor((h - items[i].text_buf->get_size().y) / 2.0)); if (!rtl && !items[i].icon.is_null()) { - text_pos.x += icon_size.width + hseparation; + text_pos.x += icon_size.width + theme_cache.h_separation; } - if (separator_outline_size > 0 && font_separator_outline_color.a > 0) { - items[i].text_buf->draw_outline(ci, text_pos, separator_outline_size, font_separator_outline_color); + if (theme_cache.font_separator_outline_size > 0 && theme_cache.font_separator_outline_color.a > 0) { + items[i].text_buf->draw_outline(ci, text_pos, theme_cache.font_separator_outline_size, theme_cache.font_separator_outline_color); } - items[i].text_buf->draw(ci, text_pos, font_separator_color); + items[i].text_buf->draw(ci, text_pos, theme_cache.font_separator_color); } } else { item_ofs.x += icon_ofs + check_ofs; if (rtl) { Vector2 text_pos = Size2(control->get_size().width - items[i].text_buf->get_size().width - item_ofs.x, item_ofs.y) + Point2(0, Math::floor((h - items[i].text_buf->get_size().y) / 2.0)); - if (outline_size > 0 && font_outline_color.a > 0) { - items[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color); + if (theme_cache.font_outline_size > 0 && theme_cache.font_outline_color.a > 0) { + items[i].text_buf->draw_outline(ci, text_pos, theme_cache.font_outline_size, theme_cache.font_outline_color); } - items[i].text_buf->draw(ci, text_pos, items[i].disabled ? font_disabled_color : (i == mouse_over ? font_hover_color : font_color)); + items[i].text_buf->draw(ci, text_pos, items[i].disabled ? theme_cache.font_disabled_color : (i == mouse_over ? theme_cache.font_hover_color : theme_cache.font_color)); } else { Vector2 text_pos = item_ofs + Point2(0, Math::floor((h - items[i].text_buf->get_size().y) / 2.0)); - if (outline_size > 0 && font_outline_color.a > 0) { - items[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color); + if (theme_cache.font_outline_size > 0 && theme_cache.font_outline_color.a > 0) { + items[i].text_buf->draw_outline(ci, text_pos, theme_cache.font_outline_size, theme_cache.font_outline_color); } - items[i].text_buf->draw(ci, text_pos, items[i].disabled ? font_disabled_color : (i == mouse_over ? font_hover_color : font_color)); + items[i].text_buf->draw(ci, text_pos, items[i].disabled ? theme_cache.font_disabled_color : (i == mouse_over ? theme_cache.font_hover_color : theme_cache.font_color)); } } // Accelerator / Shortcut if (items[i].accel != Key::NONE || (items[i].shortcut.is_valid() && items[i].shortcut->has_valid_event())) { if (rtl) { - item_ofs.x = scroll_width + style->get_margin(SIDE_LEFT) + item_end_padding; + item_ofs.x = scroll_width + theme_cache.panel_style->get_margin(SIDE_LEFT) + theme_cache.item_end_padding; } else { - item_ofs.x = display_width - style->get_margin(SIDE_RIGHT) - items[i].accel_text_buf->get_size().x - item_end_padding; + item_ofs.x = display_width - theme_cache.panel_style->get_margin(SIDE_RIGHT) - items[i].accel_text_buf->get_size().x - theme_cache.item_end_padding; } Vector2 text_pos = item_ofs + Point2(0, Math::floor((h - items[i].text_buf->get_size().y) / 2.0)); - if (outline_size > 0 && font_outline_color.a > 0) { - items[i].accel_text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color); + if (theme_cache.font_outline_size > 0 && theme_cache.font_outline_color.a > 0) { + items[i].accel_text_buf->draw_outline(ci, text_pos, theme_cache.font_outline_size, theme_cache.font_outline_color); } - items[i].accel_text_buf->draw(ci, text_pos, i == mouse_over ? font_hover_color : font_accelerator_color); + items[i].accel_text_buf->draw(ci, text_pos, i == mouse_over ? theme_cache.font_hover_color : theme_cache.font_accelerator_color); } // Cache the item vertical offset from the first item and the height. @@ -718,9 +710,8 @@ void PopupMenu::_draw_items() { } void PopupMenu::_draw_background() { - Ref<StyleBox> style = get_theme_stylebox(SNAME("panel")); RID ci2 = margin_container->get_canvas_item(); - style->draw(ci2, Rect2(Point2(), margin_container->get_size())); + theme_cache.panel_style->draw(ci2, Rect2(Point2(), margin_container->get_size())); } void PopupMenu::_minimum_lifetime_timeout() { @@ -752,8 +743,8 @@ void PopupMenu::_shape_item(int p_item) { if (items.write[p_item].dirty) { items.write[p_item].text_buf->clear(); - Ref<Font> font = get_theme_font(items[p_item].separator ? SNAME("font_separator") : SNAME("font")); - int font_size = get_theme_font_size(items[p_item].separator ? SNAME("font_separator_size") : SNAME("font_size")); + Ref<Font> font = items[p_item].separator ? theme_cache.font_separator : theme_cache.font; + int font_size = items[p_item].separator ? theme_cache.font_separator_size : theme_cache.font_size; if (items[p_item].text_direction == Control::TEXT_DIRECTION_INHERITED) { items.write[p_item].text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); @@ -769,6 +760,77 @@ void PopupMenu::_shape_item(int p_item) { } } +void PopupMenu::_menu_changed() { + emit_signal(SNAME("menu_changed")); +} + +void PopupMenu::add_child_notify(Node *p_child) { + Window::add_child_notify(p_child); + + PopupMenu *pm = Object::cast_to<PopupMenu>(p_child); + if (!pm) { + return; + } + p_child->connect("menu_changed", callable_mp(this, &PopupMenu::_menu_changed)); + _menu_changed(); +} + +void PopupMenu::remove_child_notify(Node *p_child) { + Window::remove_child_notify(p_child); + + PopupMenu *pm = Object::cast_to<PopupMenu>(p_child); + if (!pm) { + return; + } + p_child->disconnect("menu_changed", callable_mp(this, &PopupMenu::_menu_changed)); + _menu_changed(); +} + +void PopupMenu::_update_theme_item_cache() { + Popup::_update_theme_item_cache(); + + theme_cache.panel_style = get_theme_stylebox(SNAME("panel")); + theme_cache.hover_style = get_theme_stylebox(SNAME("hover")); + + theme_cache.separator_style = get_theme_stylebox(SNAME("separator")); + theme_cache.labeled_separator_left = get_theme_stylebox(SNAME("labeled_separator_left")); + theme_cache.labeled_separator_right = get_theme_stylebox(SNAME("labeled_separator_right")); + + theme_cache.v_separation = get_theme_constant(SNAME("v_separation")); + theme_cache.h_separation = get_theme_constant(SNAME("h_separation")); + theme_cache.indent = get_theme_constant(SNAME("indent")); + theme_cache.item_start_padding = get_theme_constant(SNAME("item_start_padding")); + theme_cache.item_end_padding = get_theme_constant(SNAME("item_end_padding")); + + theme_cache.checked = get_theme_icon(SNAME("checked")); + theme_cache.checked_disabled = get_theme_icon(SNAME("checked_disabled")); + theme_cache.unchecked = get_theme_icon(SNAME("unchecked")); + theme_cache.unchecked_disabled = get_theme_icon(SNAME("unchecked_disabled")); + theme_cache.radio_checked = get_theme_icon(SNAME("radio_checked")); + theme_cache.radio_checked_disabled = get_theme_icon(SNAME("radio_checked_disabled")); + theme_cache.radio_unchecked = get_theme_icon(SNAME("radio_unchecked")); + theme_cache.radio_unchecked_disabled = get_theme_icon(SNAME("radio_unchecked_disabled")); + + theme_cache.submenu = get_theme_icon(SNAME("submenu")); + theme_cache.submenu_mirrored = get_theme_icon(SNAME("submenu_mirrored")); + + theme_cache.font = get_theme_font(SNAME("font")); + theme_cache.font_size = get_theme_font_size(SNAME("font_size")); + theme_cache.font_separator = get_theme_font(SNAME("font_separator")); + theme_cache.font_separator_size = get_theme_font_size(SNAME("font_separator_size")); + + theme_cache.font_color = get_theme_color(SNAME("font_color")); + theme_cache.font_hover_color = get_theme_color(SNAME("font_hover_color")); + theme_cache.font_disabled_color = get_theme_color(SNAME("font_disabled_color")); + theme_cache.font_accelerator_color = get_theme_color(SNAME("font_accelerator_color")); + theme_cache.font_outline_size = get_theme_constant(SNAME("outline_size")); + theme_cache.font_outline_color = get_theme_color(SNAME("font_outline_color")); + + theme_cache.font_separator_color = get_theme_color(SNAME("font_separator_color")); + theme_cache.font_separator_outline_size = get_theme_constant(SNAME("separator_outline_size")); + theme_cache.font_separator_outline_color = get_theme_color(SNAME("font_separator_outline_color")); +} + void PopupMenu::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { @@ -790,7 +852,8 @@ void PopupMenu::_notification(int p_what) { } child_controls_changed(); - control->update(); + _menu_changed(); + control->queue_redraw(); } break; case NOTIFICATION_WM_MOUSE_ENTER: { @@ -800,7 +863,7 @@ void PopupMenu::_notification(int p_what) { case NOTIFICATION_WM_MOUSE_EXIT: { if (mouse_over >= 0 && (items[mouse_over].submenu.is_empty() || submenu_over != -1)) { mouse_over = -1; - control->update(); + control->queue_redraw(); } } break; @@ -828,7 +891,7 @@ void PopupMenu::_notification(int p_what) { if (!is_visible()) { if (mouse_over >= 0) { mouse_over = -1; - control->update(); + control->queue_redraw(); } for (int i = 0; i < items.size(); i++) { @@ -856,11 +919,10 @@ void PopupMenu::_notification(int p_what) { } // Set margin on the margin container - Ref<StyleBox> panel_style = get_theme_stylebox(SNAME("panel")); - margin_container->add_theme_constant_override("margin_left", panel_style->get_margin(Side::SIDE_LEFT)); - margin_container->add_theme_constant_override("margin_top", panel_style->get_margin(Side::SIDE_TOP)); - margin_container->add_theme_constant_override("margin_right", panel_style->get_margin(Side::SIDE_RIGHT)); - margin_container->add_theme_constant_override("margin_bottom", panel_style->get_margin(Side::SIDE_BOTTOM)); + margin_container->add_theme_constant_override("margin_left", theme_cache.panel_style->get_margin(Side::SIDE_LEFT)); + margin_container->add_theme_constant_override("margin_top", theme_cache.panel_style->get_margin(Side::SIDE_TOP)); + margin_container->add_theme_constant_override("margin_right", theme_cache.panel_style->get_margin(Side::SIDE_RIGHT)); + margin_container->add_theme_constant_override("margin_bottom", theme_cache.panel_style->get_margin(Side::SIDE_BOTTOM)); } } break; } @@ -881,9 +943,10 @@ void PopupMenu::add_item(const String &p_label, int p_id, Key p_accel) { ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel); items.push_back(item); _shape_item(items.size() - 1); - control->update(); + control->queue_redraw(); child_controls_changed(); notify_property_list_changed(); + _menu_changed(); } void PopupMenu::add_icon_item(const Ref<Texture2D> &p_icon, const String &p_label, int p_id, Key p_accel) { @@ -892,9 +955,10 @@ void PopupMenu::add_icon_item(const Ref<Texture2D> &p_icon, const String &p_labe item.icon = p_icon; items.push_back(item); _shape_item(items.size() - 1); - control->update(); + control->queue_redraw(); child_controls_changed(); notify_property_list_changed(); + _menu_changed(); } void PopupMenu::add_check_item(const String &p_label, int p_id, Key p_accel) { @@ -903,8 +967,9 @@ void PopupMenu::add_check_item(const String &p_label, int p_id, Key p_accel) { item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; items.push_back(item); _shape_item(items.size() - 1); - control->update(); + control->queue_redraw(); child_controls_changed(); + _menu_changed(); } void PopupMenu::add_icon_check_item(const Ref<Texture2D> &p_icon, const String &p_label, int p_id, Key p_accel) { @@ -914,7 +979,7 @@ void PopupMenu::add_icon_check_item(const Ref<Texture2D> &p_icon, const String & item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; items.push_back(item); _shape_item(items.size() - 1); - control->update(); + control->queue_redraw(); child_controls_changed(); } @@ -924,8 +989,9 @@ void PopupMenu::add_radio_check_item(const String &p_label, int p_id, Key p_acce item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; items.push_back(item); _shape_item(items.size() - 1); - control->update(); + control->queue_redraw(); child_controls_changed(); + _menu_changed(); } void PopupMenu::add_icon_radio_check_item(const Ref<Texture2D> &p_icon, const String &p_label, int p_id, Key p_accel) { @@ -935,8 +1001,9 @@ void PopupMenu::add_icon_radio_check_item(const Ref<Texture2D> &p_icon, const St item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; items.push_back(item); _shape_item(items.size() - 1); - control->update(); + control->queue_redraw(); child_controls_changed(); + _menu_changed(); } void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int p_default_state, int p_id, Key p_accel) { @@ -946,8 +1013,9 @@ void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int item.state = p_default_state; items.push_back(item); _shape_item(items.size() - 1); - control->update(); + control->queue_redraw(); child_controls_changed(); + _menu_changed(); } #define ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global) \ @@ -964,8 +1032,9 @@ void PopupMenu::add_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bool p_g ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global); items.push_back(item); _shape_item(items.size() - 1); - control->update(); + control->queue_redraw(); child_controls_changed(); + _menu_changed(); } void PopupMenu::add_icon_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) { @@ -974,8 +1043,9 @@ void PopupMenu::add_icon_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortc item.icon = p_icon; items.push_back(item); _shape_item(items.size() - 1); - control->update(); + control->queue_redraw(); child_controls_changed(); + _menu_changed(); } void PopupMenu::add_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) { @@ -984,8 +1054,9 @@ void PopupMenu::add_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bo item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; items.push_back(item); _shape_item(items.size() - 1); - control->update(); + control->queue_redraw(); child_controls_changed(); + _menu_changed(); } void PopupMenu::add_icon_check_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) { @@ -995,8 +1066,9 @@ void PopupMenu::add_icon_check_shortcut(const Ref<Texture2D> &p_icon, const Ref< item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; items.push_back(item); _shape_item(items.size() - 1); - control->update(); + control->queue_redraw(); child_controls_changed(); + _menu_changed(); } void PopupMenu::add_radio_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) { @@ -1005,8 +1077,9 @@ void PopupMenu::add_radio_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_ item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; items.push_back(item); _shape_item(items.size() - 1); - control->update(); + control->queue_redraw(); child_controls_changed(); + _menu_changed(); } void PopupMenu::add_icon_radio_check_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) { @@ -1016,8 +1089,9 @@ void PopupMenu::add_icon_radio_check_shortcut(const Ref<Texture2D> &p_icon, cons item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; items.push_back(item); _shape_item(items.size() - 1); - control->update(); + control->queue_redraw(); child_controls_changed(); + _menu_changed(); } void PopupMenu::add_submenu_item(const String &p_label, const String &p_submenu, int p_id) { @@ -1028,8 +1102,9 @@ void PopupMenu::add_submenu_item(const String &p_label, const String &p_submenu, item.submenu = p_submenu; items.push_back(item); _shape_item(items.size() - 1); - control->update(); + control->queue_redraw(); child_controls_changed(); + _menu_changed(); } #undef ITEM_SETUP_WITH_ACCEL @@ -1050,8 +1125,9 @@ void PopupMenu::set_item_text(int p_idx, const String &p_text) { items.write[p_idx].dirty = true; _shape_item(p_idx); - control->update(); + control->queue_redraw(); child_controls_changed(); + _menu_changed(); } void PopupMenu::set_item_text_direction(int p_item, Control::TextDirection p_text_direction) { @@ -1063,7 +1139,7 @@ void PopupMenu::set_item_text_direction(int p_item, Control::TextDirection p_tex if (items[p_item].text_direction != p_text_direction) { items.write[p_item].text_direction = p_text_direction; items.write[p_item].dirty = true; - control->update(); + control->queue_redraw(); } } @@ -1075,7 +1151,7 @@ void PopupMenu::set_item_language(int p_item, const String &p_language) { if (items[p_item].language != p_language) { items.write[p_item].language = p_language; items.write[p_item].dirty = true; - control->update(); + control->queue_redraw(); } } @@ -1084,10 +1160,16 @@ void PopupMenu::set_item_icon(int p_idx, const Ref<Texture2D> &p_icon) { p_idx += get_item_count(); } ERR_FAIL_INDEX(p_idx, items.size()); + + if (items[p_idx].icon == p_icon) { + return; + } + items.write[p_idx].icon = p_icon; - control->update(); + control->queue_redraw(); child_controls_changed(); + _menu_changed(); } void PopupMenu::set_item_checked(int p_idx, bool p_checked) { @@ -1096,10 +1178,15 @@ void PopupMenu::set_item_checked(int p_idx, bool p_checked) { } ERR_FAIL_INDEX(p_idx, items.size()); + if (items[p_idx].checked == p_checked) { + return; + } + items.write[p_idx].checked = p_checked; - control->update(); + control->queue_redraw(); child_controls_changed(); + _menu_changed(); } void PopupMenu::set_item_id(int p_idx, int p_id) { @@ -1107,10 +1194,16 @@ void PopupMenu::set_item_id(int p_idx, int p_id) { p_idx += get_item_count(); } ERR_FAIL_INDEX(p_idx, items.size()); + + if (items[p_idx].id == p_id) { + return; + } + items.write[p_idx].id = p_id; - control->update(); + control->queue_redraw(); child_controls_changed(); + _menu_changed(); } void PopupMenu::set_item_accelerator(int p_idx, Key p_accel) { @@ -1118,11 +1211,17 @@ void PopupMenu::set_item_accelerator(int p_idx, Key p_accel) { p_idx += get_item_count(); } ERR_FAIL_INDEX(p_idx, items.size()); + + if (items[p_idx].accel == p_accel) { + return; + } + items.write[p_idx].accel = p_accel; items.write[p_idx].dirty = true; - control->update(); + control->queue_redraw(); child_controls_changed(); + _menu_changed(); } void PopupMenu::set_item_metadata(int p_idx, const Variant &p_meta) { @@ -1130,9 +1229,15 @@ void PopupMenu::set_item_metadata(int p_idx, const Variant &p_meta) { p_idx += get_item_count(); } ERR_FAIL_INDEX(p_idx, items.size()); + + if (items[p_idx].metadata == p_meta) { + return; + } + items.write[p_idx].metadata = p_meta; - control->update(); + control->queue_redraw(); child_controls_changed(); + _menu_changed(); } void PopupMenu::set_item_disabled(int p_idx, bool p_disabled) { @@ -1140,9 +1245,15 @@ void PopupMenu::set_item_disabled(int p_idx, bool p_disabled) { p_idx += get_item_count(); } ERR_FAIL_INDEX(p_idx, items.size()); + + if (items[p_idx].disabled == p_disabled) { + return; + } + items.write[p_idx].disabled = p_disabled; - control->update(); + control->queue_redraw(); child_controls_changed(); + _menu_changed(); } void PopupMenu::set_item_submenu(int p_idx, const String &p_submenu) { @@ -1150,16 +1261,23 @@ void PopupMenu::set_item_submenu(int p_idx, const String &p_submenu) { p_idx += get_item_count(); } ERR_FAIL_INDEX(p_idx, items.size()); + + if (items[p_idx].submenu == p_submenu) { + return; + } + items.write[p_idx].submenu = p_submenu; - control->update(); + control->queue_redraw(); child_controls_changed(); + _menu_changed(); } void PopupMenu::toggle_item_checked(int p_idx) { ERR_FAIL_INDEX(p_idx, items.size()); items.write[p_idx].checked = !items[p_idx].checked; - control->update(); + control->queue_redraw(); child_controls_changed(); + _menu_changed(); } String PopupMenu::get_item_text(int p_idx) const { @@ -1242,9 +1360,14 @@ Ref<Shortcut> PopupMenu::get_item_shortcut(int p_idx) const { return items[p_idx].shortcut; } -int PopupMenu::get_item_horizontal_offset(int p_idx) const { +int PopupMenu::get_item_indent(int p_idx) const { ERR_FAIL_INDEX_V(p_idx, items.size(), 0); - return items[p_idx].h_ofs; + return items[p_idx].indent; +} + +int PopupMenu::get_item_max_states(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx, items.size(), -1); + return items[p_idx].max_states; } int PopupMenu::get_item_state(int p_idx) const { @@ -1257,8 +1380,13 @@ void PopupMenu::set_item_as_separator(int p_idx, bool p_separator) { p_idx += get_item_count(); } ERR_FAIL_INDEX(p_idx, items.size()); + + if (items[p_idx].separator == p_separator) { + return; + } + items.write[p_idx].separator = p_separator; - control->update(); + control->queue_redraw(); } bool PopupMenu::is_item_separator(int p_idx) const { @@ -1271,8 +1399,15 @@ void PopupMenu::set_item_as_checkable(int p_idx, bool p_checkable) { p_idx += get_item_count(); } ERR_FAIL_INDEX(p_idx, items.size()); + + int type = (int)(p_checkable ? Item::CHECKABLE_TYPE_CHECK_BOX : Item::CHECKABLE_TYPE_NONE); + if (type == items[p_idx].checkable_type) { + return; + } + items.write[p_idx].checkable_type = p_checkable ? Item::CHECKABLE_TYPE_CHECK_BOX : Item::CHECKABLE_TYPE_NONE; - control->update(); + control->queue_redraw(); + _menu_changed(); } void PopupMenu::set_item_as_radio_checkable(int p_idx, bool p_radio_checkable) { @@ -1280,8 +1415,15 @@ void PopupMenu::set_item_as_radio_checkable(int p_idx, bool p_radio_checkable) { p_idx += get_item_count(); } ERR_FAIL_INDEX(p_idx, items.size()); + + int type = (int)(p_radio_checkable ? Item::CHECKABLE_TYPE_RADIO_BUTTON : Item::CHECKABLE_TYPE_NONE); + if (type == items[p_idx].checkable_type) { + return; + } + items.write[p_idx].checkable_type = p_radio_checkable ? Item::CHECKABLE_TYPE_RADIO_BUTTON : Item::CHECKABLE_TYPE_NONE; - control->update(); + control->queue_redraw(); + _menu_changed(); } void PopupMenu::set_item_tooltip(int p_idx, const String &p_tooltip) { @@ -1289,8 +1431,14 @@ void PopupMenu::set_item_tooltip(int p_idx, const String &p_tooltip) { p_idx += get_item_count(); } ERR_FAIL_INDEX(p_idx, items.size()); + + if (items[p_idx].tooltip == p_tooltip) { + return; + } + items.write[p_idx].tooltip = p_tooltip; - control->update(); + control->queue_redraw(); + _menu_changed(); } void PopupMenu::set_item_shortcut(int p_idx, const Ref<Shortcut> &p_shortcut, bool p_global) { @@ -1298,6 +1446,11 @@ void PopupMenu::set_item_shortcut(int p_idx, const Ref<Shortcut> &p_shortcut, bo p_idx += get_item_count(); } ERR_FAIL_INDEX(p_idx, items.size()); + + if (items[p_idx].shortcut == p_shortcut && items[p_idx].shortcut_is_global == p_global && items[p_idx].shortcut.is_valid() == p_shortcut.is_valid()) { + return; + } + if (items[p_idx].shortcut.is_valid()) { _unref_shortcut(items[p_idx].shortcut); } @@ -1309,17 +1462,24 @@ void PopupMenu::set_item_shortcut(int p_idx, const Ref<Shortcut> &p_shortcut, bo _ref_shortcut(items[p_idx].shortcut); } - control->update(); + control->queue_redraw(); + _menu_changed(); } -void PopupMenu::set_item_horizontal_offset(int p_idx, int p_offset) { +void PopupMenu::set_item_indent(int p_idx, int p_indent) { if (p_idx < 0) { p_idx += get_item_count(); } ERR_FAIL_INDEX(p_idx, items.size()); - items.write[p_idx].h_ofs = p_offset; - control->update(); + + if (items.write[p_idx].indent == p_indent) { + return; + } + items.write[p_idx].indent = p_indent; + + control->queue_redraw(); child_controls_changed(); + _menu_changed(); } void PopupMenu::set_item_multistate(int p_idx, int p_state) { @@ -1327,8 +1487,14 @@ void PopupMenu::set_item_multistate(int p_idx, int p_state) { p_idx += get_item_count(); } ERR_FAIL_INDEX(p_idx, items.size()); + + if (items[p_idx].state == p_state) { + return; + } + items.write[p_idx].state = p_state; - control->update(); + control->queue_redraw(); + _menu_changed(); } void PopupMenu::set_item_shortcut_disabled(int p_idx, bool p_disabled) { @@ -1336,8 +1502,14 @@ void PopupMenu::set_item_shortcut_disabled(int p_idx, bool p_disabled) { p_idx += get_item_count(); } ERR_FAIL_INDEX(p_idx, items.size()); + + if (items[p_idx].shortcut_is_disabled == p_disabled) { + return; + } + items.write[p_idx].shortcut_is_disabled = p_disabled; - control->update(); + control->queue_redraw(); + _menu_changed(); } void PopupMenu::toggle_item_multistate(int p_idx) { @@ -1351,7 +1523,8 @@ void PopupMenu::toggle_item_multistate(int p_idx) { items.write[p_idx].state = 0; } - control->update(); + control->queue_redraw(); + _menu_changed(); } bool PopupMenu::is_item_checkable(int p_idx) const { @@ -1364,25 +1537,45 @@ bool PopupMenu::is_item_radio_checkable(int p_idx) const { return items[p_idx].checkable_type == Item::CHECKABLE_TYPE_RADIO_BUTTON; } +bool PopupMenu::is_item_shortcut_global(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx, items.size(), false); + return items[p_idx].shortcut_is_global; +} + bool PopupMenu::is_item_shortcut_disabled(int p_idx) const { ERR_FAIL_INDEX_V(p_idx, items.size(), false); return items[p_idx].shortcut_is_disabled; } -void PopupMenu::set_current_index(int p_idx) { - ERR_FAIL_INDEX(p_idx, items.size()); +void PopupMenu::set_focused_item(int p_idx) { + if (p_idx != -1) { + ERR_FAIL_INDEX(p_idx, items.size()); + } + + if (mouse_over == p_idx) { + return; + } + mouse_over = p_idx; - scroll_to_item(mouse_over); - control->update(); + if (mouse_over != -1) { + scroll_to_item(mouse_over); + } + + control->queue_redraw(); } -int PopupMenu::get_current_index() const { +int PopupMenu::get_focused_item() const { return mouse_over; } void PopupMenu::set_item_count(int p_count) { ERR_FAIL_COND(p_count < 0); int prev_size = items.size(); + + if (prev_size == p_count) { + return; + } + items.resize(p_count); if (prev_size < p_count) { @@ -1391,9 +1584,10 @@ void PopupMenu::set_item_count(int p_count) { } } - control->update(); + control->queue_redraw(); child_controls_changed(); notify_property_list_changed(); + _menu_changed(); } int PopupMenu::get_item_count() const { @@ -1533,8 +1727,9 @@ void PopupMenu::remove_item(int p_idx) { } items.remove_at(p_idx); - control->update(); + control->queue_redraw(); child_controls_changed(); + _menu_changed(); } void PopupMenu::add_separator(const String &p_text, int p_id) { @@ -1546,7 +1741,8 @@ void PopupMenu::add_separator(const String &p_text, int p_id) { sep.xl_text = atr(p_text); } items.push_back(sep); - control->update(); + control->queue_redraw(); + _menu_changed(); } void PopupMenu::clear() { @@ -1557,15 +1753,16 @@ void PopupMenu::clear() { } items.clear(); mouse_over = -1; - control->update(); + control->queue_redraw(); child_controls_changed(); notify_property_list_changed(); + _menu_changed(); } void PopupMenu::_ref_shortcut(Ref<Shortcut> p_sc) { if (!shortcut_refcount.has(p_sc)) { shortcut_refcount[p_sc] = 1; - p_sc->connect("changed", callable_mp((CanvasItem *)this, &CanvasItem::update)); + p_sc->connect("changed", callable_mp(this, &PopupMenu::_shortcut_changed)); } else { shortcut_refcount[p_sc] += 1; } @@ -1575,11 +1772,18 @@ void PopupMenu::_unref_shortcut(Ref<Shortcut> p_sc) { ERR_FAIL_COND(!shortcut_refcount.has(p_sc)); shortcut_refcount[p_sc]--; if (shortcut_refcount[p_sc] == 0) { - p_sc->disconnect("changed", callable_mp((CanvasItem *)this, &CanvasItem::update)); + p_sc->disconnect("changed", callable_mp(this, &PopupMenu::_shortcut_changed)); shortcut_refcount.erase(p_sc); } } +void PopupMenu::_shortcut_changed() { + for (int i = 0; i < items.size(); i++) { + items.write[i].dirty = true; + } + control->queue_redraw(); +} + // Hide on item selection determines whether or not the popup will close after item selection void PopupMenu::set_hide_on_item_selection(bool p_enabled) { hide_on_item_selection = p_enabled; @@ -1834,7 +2038,7 @@ void PopupMenu::_bind_methods() { ClassDB::bind_method(D_METHOD("set_item_as_radio_checkable", "index", "enable"), &PopupMenu::set_item_as_radio_checkable); ClassDB::bind_method(D_METHOD("set_item_tooltip", "index", "tooltip"), &PopupMenu::set_item_tooltip); ClassDB::bind_method(D_METHOD("set_item_shortcut", "index", "shortcut", "global"), &PopupMenu::set_item_shortcut, DEFVAL(false)); - ClassDB::bind_method(D_METHOD("set_item_horizontal_offset", "index", "offset"), &PopupMenu::set_item_horizontal_offset); + ClassDB::bind_method(D_METHOD("set_item_indent", "index", "indent"), &PopupMenu::set_item_indent); ClassDB::bind_method(D_METHOD("set_item_multistate", "index", "state"), &PopupMenu::set_item_multistate); ClassDB::bind_method(D_METHOD("set_item_shortcut_disabled", "index", "disabled"), &PopupMenu::set_item_shortcut_disabled); @@ -1858,10 +2062,10 @@ void PopupMenu::_bind_methods() { ClassDB::bind_method(D_METHOD("is_item_shortcut_disabled", "index"), &PopupMenu::is_item_shortcut_disabled); ClassDB::bind_method(D_METHOD("get_item_tooltip", "index"), &PopupMenu::get_item_tooltip); ClassDB::bind_method(D_METHOD("get_item_shortcut", "index"), &PopupMenu::get_item_shortcut); - ClassDB::bind_method(D_METHOD("get_item_horizontal_offset", "index"), &PopupMenu::get_item_horizontal_offset); + ClassDB::bind_method(D_METHOD("get_item_indent", "index"), &PopupMenu::get_item_indent); - ClassDB::bind_method(D_METHOD("set_current_index", "index"), &PopupMenu::set_current_index); - ClassDB::bind_method(D_METHOD("get_current_index"), &PopupMenu::get_current_index); + ClassDB::bind_method(D_METHOD("set_focused_item", "index"), &PopupMenu::set_focused_item); + ClassDB::bind_method(D_METHOD("get_focused_item"), &PopupMenu::get_focused_item); ClassDB::bind_method(D_METHOD("set_item_count", "count"), &PopupMenu::set_item_count); ClassDB::bind_method(D_METHOD("get_item_count"), &PopupMenu::get_item_count); @@ -1898,6 +2102,7 @@ void PopupMenu::_bind_methods() { ADD_SIGNAL(MethodInfo("id_pressed", PropertyInfo(Variant::INT, "id"))); ADD_SIGNAL(MethodInfo("id_focused", PropertyInfo(Variant::INT, "id"))); ADD_SIGNAL(MethodInfo("index_pressed", PropertyInfo(Variant::INT, "index"))); + ADD_SIGNAL(MethodInfo("menu_changed")); } void PopupMenu::popup(const Rect2 &p_bounds) { @@ -1909,7 +2114,7 @@ void PopupMenu::popup(const Rect2 &p_bounds) { PopupMenu::PopupMenu() { // Margin Container margin_container = memnew(MarginContainer); - margin_container->set_anchors_and_offsets_preset(Control::PRESET_WIDE); + margin_container->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); add_child(margin_container, false, INTERNAL_MODE_FRONT); margin_container->connect("draw", callable_mp(this, &PopupMenu::_draw_background)); @@ -1921,7 +2126,7 @@ PopupMenu::PopupMenu() { // The control which will display the items control = memnew(Control); control->set_clip_contents(false); - control->set_anchors_and_offsets_preset(Control::PRESET_WIDE); + control->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); control->set_h_size_flags(Control::SIZE_EXPAND_FILL); control->set_v_size_flags(Control::SIZE_EXPAND_FILL); scroll_container->add_child(control, false, INTERNAL_MODE_FRONT); diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h index daa38b0e6d..89d3904456 100644 --- a/scene/gui/popup_menu.h +++ b/scene/gui/popup_menu.h @@ -68,7 +68,7 @@ class PopupMenu : public Popup { Key accel = Key::NONE; int _ofs_cache = 0; int _height_cache = 0; - int h_ofs = 0; + int indent = 0; Ref<Shortcut> shortcut; bool shortcut_is_global = false; bool shortcut_is_disabled = false; @@ -121,6 +121,8 @@ class PopupMenu : public Popup { void _ref_shortcut(Ref<Shortcut> p_sc); void _unref_shortcut(Ref<Shortcut> p_sc); + void _shortcut_changed(); + bool allow_search = true; uint64_t search_time_msec = 0; String search_string = ""; @@ -129,13 +131,61 @@ class PopupMenu : public Popup { ScrollContainer *scroll_container = nullptr; Control *control = nullptr; + struct ThemeCache { + Ref<StyleBox> panel_style; + Ref<StyleBox> hover_style; + + Ref<StyleBox> separator_style; + Ref<StyleBox> labeled_separator_left; + Ref<StyleBox> labeled_separator_right; + + int v_separation = 0; + int h_separation = 0; + int indent = 0; + int item_start_padding = 0; + int item_end_padding = 0; + + Ref<Texture2D> checked; + Ref<Texture2D> checked_disabled; + Ref<Texture2D> unchecked; + Ref<Texture2D> unchecked_disabled; + Ref<Texture2D> radio_checked; + Ref<Texture2D> radio_checked_disabled; + Ref<Texture2D> radio_unchecked; + Ref<Texture2D> radio_unchecked_disabled; + + Ref<Texture2D> submenu; + Ref<Texture2D> submenu_mirrored; + + Ref<Font> font; + int font_size = 0; + Ref<Font> font_separator; + int font_separator_size = 0; + + Color font_color; + Color font_hover_color; + Color font_disabled_color; + Color font_accelerator_color; + int font_outline_size = 0; + Color font_outline_color; + + Color font_separator_color; + int font_separator_outline_size = 0; + Color font_separator_outline_color; + } theme_cache; + void _draw_items(); void _draw_background(); void _minimum_lifetime_timeout(); void _close_pressed(); + void _menu_changed(); protected: + virtual void _update_theme_item_cache() override; + + virtual void add_child_notify(Node *p_child) override; + virtual void remove_child_notify(Node *p_child) override; void _notification(int p_what); bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; @@ -183,7 +233,7 @@ public: void set_item_as_radio_checkable(int p_idx, bool p_radio_checkable); void set_item_tooltip(int p_idx, const String &p_tooltip); void set_item_shortcut(int p_idx, const Ref<Shortcut> &p_shortcut, bool p_global = false); - void set_item_horizontal_offset(int p_idx, int p_offset); + void set_item_indent(int p_idx, int p_indent); void set_item_multistate(int p_idx, int p_state); void toggle_item_multistate(int p_idx); void set_item_shortcut_disabled(int p_idx, bool p_disabled); @@ -206,13 +256,15 @@ public: bool is_item_checkable(int p_idx) const; bool is_item_radio_checkable(int p_idx) const; bool is_item_shortcut_disabled(int p_idx) const; + bool is_item_shortcut_global(int p_idx) const; String get_item_tooltip(int p_idx) const; Ref<Shortcut> get_item_shortcut(int p_idx) const; - int get_item_horizontal_offset(int p_idx) const; + int get_item_indent(int p_idx) const; + int get_item_max_states(int p_idx) const; int get_item_state(int p_idx) const; - void set_current_index(int p_idx); - int get_current_index() const; + void set_focused_item(int p_idx); + int get_focused_item() const; void set_item_count(int p_count); int get_item_count() const; @@ -260,4 +312,4 @@ public: ~PopupMenu(); }; -#endif +#endif // POPUP_MENU_H diff --git a/scene/gui/progress_bar.cpp b/scene/gui/progress_bar.cpp index 80859e8eb9..8369eaa227 100644 --- a/scene/gui/progress_bar.cpp +++ b/scene/gui/progress_bar.cpp @@ -33,18 +33,13 @@ #include "scene/resources/text_line.h" Size2 ProgressBar::get_minimum_size() const { - Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg")); - Ref<StyleBox> fg = get_theme_stylebox(SNAME("fg")); - Ref<Font> font = get_theme_font(SNAME("font")); - int font_size = get_theme_font_size(SNAME("font_size")); - - Size2 minimum_size = bg->get_minimum_size(); - minimum_size.height = MAX(minimum_size.height, fg->get_minimum_size().height); - minimum_size.width = MAX(minimum_size.width, fg->get_minimum_size().width); - if (percent_visible) { + Size2 minimum_size = theme_cache.background_style->get_minimum_size(); + minimum_size.height = MAX(minimum_size.height, theme_cache.fill_style->get_minimum_size().height); + minimum_size.width = MAX(minimum_size.width, theme_cache.fill_style->get_minimum_size().width); + if (show_percentage) { String txt = "100%"; - TextLine tl = TextLine(txt, font, font_size); - minimum_size.height = MAX(minimum_size.height, bg->get_minimum_size().height + tl.get_size().y); + TextLine tl = TextLine(txt, theme_cache.font, theme_cache.font_size); + minimum_size.height = MAX(minimum_size.height, theme_cache.background_style->get_minimum_size().height + tl.get_size().y); } else { // this is needed, else the progressbar will collapse minimum_size.width = MAX(minimum_size.width, 1); minimum_size.height = MAX(minimum_size.height, 1); @@ -52,23 +47,30 @@ Size2 ProgressBar::get_minimum_size() const { return minimum_size; } +void ProgressBar::_update_theme_item_cache() { + Range::_update_theme_item_cache(); + + theme_cache.background_style = get_theme_stylebox(SNAME("background")); + theme_cache.fill_style = get_theme_stylebox(SNAME("fill")); + + theme_cache.font = get_theme_font(SNAME("font")); + theme_cache.font_size = get_theme_font_size(SNAME("font_size")); + theme_cache.font_color = get_theme_color(SNAME("font_color")); + theme_cache.font_outline_size = get_theme_constant(SNAME("outline_size")); + theme_cache.font_outline_color = get_theme_color(SNAME("font_outline_color")); +} + void ProgressBar::_notification(int p_what) { switch (p_what) { case NOTIFICATION_DRAW: { - Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg")); - Ref<StyleBox> fg = get_theme_stylebox(SNAME("fg")); - Ref<Font> font = get_theme_font(SNAME("font")); - int font_size = get_theme_font_size(SNAME("font_size")); - Color font_color = get_theme_color(SNAME("font_color")); - - draw_style_box(bg, Rect2(Point2(), get_size())); + draw_style_box(theme_cache.background_style, Rect2(Point2(), get_size())); float r = get_as_ratio(); switch (mode) { case FILL_BEGIN_TO_END: case FILL_END_TO_BEGIN: { - int mp = fg->get_minimum_size().width; + int mp = theme_cache.fill_style->get_minimum_size().width; int p = round(r * (get_size().width - mp)); // We want FILL_BEGIN_TO_END to map to right to left when UI layout is RTL, // and left to right otherwise. And likewise for FILL_END_TO_BEGIN. @@ -76,23 +78,23 @@ void ProgressBar::_notification(int p_what) { if (p > 0) { if (right_to_left) { int p_remaining = round((1.0 - r) * (get_size().width - mp)); - draw_style_box(fg, Rect2(Point2(p_remaining, 0), Size2(p + fg->get_minimum_size().width, get_size().height))); + draw_style_box(theme_cache.fill_style, Rect2(Point2(p_remaining, 0), Size2(p + theme_cache.fill_style->get_minimum_size().width, get_size().height))); } else { - draw_style_box(fg, Rect2(Point2(0, 0), Size2(p + fg->get_minimum_size().width, get_size().height))); + draw_style_box(theme_cache.fill_style, Rect2(Point2(0, 0), Size2(p + theme_cache.fill_style->get_minimum_size().width, get_size().height))); } } } break; case FILL_TOP_TO_BOTTOM: case FILL_BOTTOM_TO_TOP: { - int mp = fg->get_minimum_size().height; + int mp = theme_cache.fill_style->get_minimum_size().height; int p = round(r * (get_size().height - mp)); if (p > 0) { if (mode == FILL_TOP_TO_BOTTOM) { - draw_style_box(fg, Rect2(Point2(0, 0), Size2(get_size().width, p + fg->get_minimum_size().height))); + draw_style_box(theme_cache.fill_style, Rect2(Point2(0, 0), Size2(get_size().width, p + theme_cache.fill_style->get_minimum_size().height))); } else { int p_remaining = round((1.0 - r) * (get_size().height - mp)); - draw_style_box(fg, Rect2(Point2(0, p_remaining), Size2(get_size().width, p + fg->get_minimum_size().height))); + draw_style_box(theme_cache.fill_style, Rect2(Point2(0, p_remaining), Size2(get_size().width, p + theme_cache.fill_style->get_minimum_size().height))); } } } break; @@ -100,16 +102,16 @@ void ProgressBar::_notification(int p_what) { break; } - if (percent_visible) { + if (show_percentage) { String txt = TS->format_number(itos(int(get_as_ratio() * 100))) + TS->percent_sign(); - TextLine tl = TextLine(txt, font, font_size); + TextLine tl = TextLine(txt, theme_cache.font, theme_cache.font_size); Vector2 text_pos = (Point2(get_size().width - tl.get_size().x, get_size().height - tl.get_size().y) / 2).round(); - Color font_outline_color = get_theme_color(SNAME("font_outline_color")); - int outline_size = get_theme_constant(SNAME("outline_size")); - if (outline_size > 0 && font_outline_color.a > 0) { - tl.draw_outline(get_canvas_item(), text_pos, outline_size, font_outline_color); + + if (theme_cache.font_outline_size > 0 && theme_cache.font_outline_color.a > 0) { + tl.draw_outline(get_canvas_item(), text_pos, theme_cache.font_outline_size, theme_cache.font_outline_color); } - tl.draw(get_canvas_item(), text_pos, font_color); + + tl.draw(get_canvas_item(), text_pos, theme_cache.font_color); } } break; } @@ -118,34 +120,34 @@ void ProgressBar::_notification(int p_what) { void ProgressBar::set_fill_mode(int p_fill) { ERR_FAIL_INDEX(p_fill, FILL_MODE_MAX); mode = (FillMode)p_fill; - update(); + queue_redraw(); } int ProgressBar::get_fill_mode() { return mode; } -void ProgressBar::set_percent_visible(bool p_visible) { - if (percent_visible == p_visible) { +void ProgressBar::set_show_percentage(bool p_visible) { + if (show_percentage == p_visible) { return; } - percent_visible = p_visible; + show_percentage = p_visible; update_minimum_size(); - update(); + queue_redraw(); } -bool ProgressBar::is_percent_visible() const { - return percent_visible; +bool ProgressBar::is_percentage_shown() const { + return show_percentage; } void ProgressBar::_bind_methods() { ClassDB::bind_method(D_METHOD("set_fill_mode", "mode"), &ProgressBar::set_fill_mode); ClassDB::bind_method(D_METHOD("get_fill_mode"), &ProgressBar::get_fill_mode); - ClassDB::bind_method(D_METHOD("set_percent_visible", "visible"), &ProgressBar::set_percent_visible); - ClassDB::bind_method(D_METHOD("is_percent_visible"), &ProgressBar::is_percent_visible); + ClassDB::bind_method(D_METHOD("set_show_percentage", "visible"), &ProgressBar::set_show_percentage); + ClassDB::bind_method(D_METHOD("is_percentage_shown"), &ProgressBar::is_percentage_shown); ADD_PROPERTY(PropertyInfo(Variant::INT, "fill_mode", PROPERTY_HINT_ENUM, "Begin to End,End to Begin,Top to Bottom,Bottom to Top"), "set_fill_mode", "get_fill_mode"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "percent_visible"), "set_percent_visible", "is_percent_visible"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_percentage"), "set_show_percentage", "is_percentage_shown"); BIND_ENUM_CONSTANT(FILL_BEGIN_TO_END); BIND_ENUM_CONSTANT(FILL_END_TO_BEGIN); diff --git a/scene/gui/progress_bar.h b/scene/gui/progress_bar.h index 5ba21ad7d5..b6d7d2c7cf 100644 --- a/scene/gui/progress_bar.h +++ b/scene/gui/progress_bar.h @@ -36,9 +36,22 @@ class ProgressBar : public Range { GDCLASS(ProgressBar, Range); - bool percent_visible = true; + bool show_percentage = true; + + struct ThemeCache { + Ref<StyleBox> background_style; + Ref<StyleBox> fill_style; + + Ref<Font> font; + int font_size = 0; + Color font_color; + int font_outline_size = 0; + Color font_outline_color; + } theme_cache; protected: + virtual void _update_theme_item_cache() override; + void _notification(int p_what); static void _bind_methods(); @@ -54,8 +67,8 @@ public: void set_fill_mode(int p_fill); int get_fill_mode(); - void set_percent_visible(bool p_visible); - bool is_percent_visible() const; + void set_show_percentage(bool p_visible); + bool is_percentage_shown() const; Size2 get_minimum_size() const override; ProgressBar(); diff --git a/scene/gui/range.cpp b/scene/gui/range.cpp index fae6688452..2d2b3e413d 100644 --- a/scene/gui/range.cpp +++ b/scene/gui/range.cpp @@ -30,8 +30,8 @@ #include "range.h" -TypedArray<String> Range::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray Range::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (shared->exp_ratio && shared->min <= 0) { warnings.push_back(RTR("If \"Exp Edit\" is enabled, \"Min Value\" must be greater than 0.")); @@ -46,7 +46,7 @@ void Range::_value_changed(double p_value) { void Range::_value_changed_notify() { _value_changed(shared->val); emit_signal(SNAME("value_changed"), shared->val); - update(); + queue_redraw(); } void Range::Shared::emit_value_changed() { @@ -61,7 +61,7 @@ void Range::Shared::emit_value_changed() { void Range::_changed_notify(const char *p_what) { emit_signal(SNAME("changed")); - update(); + queue_redraw(); } void Range::_validate_values() { @@ -106,6 +106,10 @@ void Range::set_value(double p_val) { } void Range::set_min(double p_min) { + if (shared->min == p_min) { + return; + } + shared->min = p_min; set_value(shared->val); _validate_values(); @@ -116,6 +120,10 @@ void Range::set_min(double p_min) { } void Range::set_max(double p_max) { + if (shared->max == p_max) { + return; + } + shared->max = p_max; set_value(shared->val); _validate_values(); @@ -124,11 +132,19 @@ void Range::set_max(double p_max) { } void Range::set_step(double p_step) { + if (shared->step == p_step) { + return; + } + shared->step = p_step; shared->emit_changed("step"); } void Range::set_page(double p_page) { + if (shared->page == p_page) { + return; + } + shared->page = p_page; set_value(shared->val); _validate_values(); @@ -300,6 +316,10 @@ bool Range::is_using_rounded_values() const { } void Range::set_exp_ratio(bool p_enable) { + if (shared->exp_ratio == p_enable) { + return; + } + shared->exp_ratio = p_enable; update_configuration_warnings(); diff --git a/scene/gui/range.h b/scene/gui/range.h index 1274821bd1..19452243cf 100644 --- a/scene/gui/range.h +++ b/scene/gui/range.h @@ -100,10 +100,10 @@ public: void share(Range *p_range); void unshare(); - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; Range(); ~Range(); }; -#endif +#endif // RANGE_H diff --git a/scene/gui/reference_rect.cpp b/scene/gui/reference_rect.cpp index 5190a5a7d2..fa5ac5b864 100644 --- a/scene/gui/reference_rect.cpp +++ b/scene/gui/reference_rect.cpp @@ -46,8 +46,12 @@ void ReferenceRect::_notification(int p_what) { } void ReferenceRect::set_border_color(const Color &p_color) { + if (border_color == p_color) { + return; + } + border_color = p_color; - update(); + queue_redraw(); } Color ReferenceRect::get_border_color() const { @@ -55,8 +59,13 @@ Color ReferenceRect::get_border_color() const { } void ReferenceRect::set_border_width(float p_width) { - border_width = MAX(0.0, p_width); - update(); + float width_max = MAX(0.0, p_width); + if (border_width == width_max) { + return; + } + + border_width = width_max; + queue_redraw(); } float ReferenceRect::get_border_width() const { @@ -64,8 +73,12 @@ float ReferenceRect::get_border_width() const { } void ReferenceRect::set_editor_only(const bool &p_enabled) { + if (editor_only == p_enabled) { + return; + } + editor_only = p_enabled; - update(); + queue_redraw(); } bool ReferenceRect::get_editor_only() const { diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 05824d54f1..7ea46a0b4f 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -52,7 +52,7 @@ RichTextLabel::Item *RichTextLabel::_get_next_item(Item *p_item, bool p_free) co } else if (p_item->E->next()) { return p_item->E->next()->get(); } else { - //go up until something with a next is found + // Go up until something with a next is found. while (p_item->parent && !p_item->E->next()) { p_item = p_item->parent; } @@ -72,7 +72,7 @@ RichTextLabel::Item *RichTextLabel::_get_next_item(Item *p_item, bool p_free) co } else if (p_item->E->next()) { return p_item->E->next()->get(); } else { - //go up until something with a next is found + // Go up until something with a next is found. while (p_item->type != ITEM_FRAME && !p_item->E->next()) { p_item = p_item->parent; } @@ -84,8 +84,6 @@ RichTextLabel::Item *RichTextLabel::_get_next_item(Item *p_item, bool p_free) co } } } - - return nullptr; } RichTextLabel::Item *RichTextLabel::_get_prev_item(Item *p_item, bool p_free) const { @@ -97,7 +95,7 @@ RichTextLabel::Item *RichTextLabel::_get_prev_item(Item *p_item, bool p_free) co } else if (p_item->E->prev()) { return p_item->E->prev()->get(); } else { - //go back until something with a prev is found + // Go back until something with a prev is found. while (p_item->parent && !p_item->E->prev()) { p_item = p_item->parent; } @@ -117,7 +115,7 @@ RichTextLabel::Item *RichTextLabel::_get_prev_item(Item *p_item, bool p_free) co } else if (p_item->E->prev()) { return p_item->E->prev()->get(); } else { - //go back until something with a prev is found + // Go back until something with a prev is found. while (p_item->type != ITEM_FRAME && !p_item->E->prev()) { p_item = p_item->parent; } @@ -129,13 +127,10 @@ RichTextLabel::Item *RichTextLabel::_get_prev_item(Item *p_item, bool p_free) co } } } - - return nullptr; } Rect2 RichTextLabel::_get_text_rect() { - Ref<StyleBox> style = get_theme_stylebox(SNAME("normal")); - return Rect2(style->get_offset(), get_size() - style->get_minimum_size()); + return Rect2(theme_cache.normal_style->get_offset(), get_size() - theme_cache.normal_style->get_minimum_size()); } RichTextLabel::Item *RichTextLabel::_get_item_at_pos(RichTextLabel::Item *p_item_from, RichTextLabel::Item *p_item_to, int p_position) { @@ -149,7 +144,12 @@ RichTextLabel::Item *RichTextLabel::_get_item_at_pos(RichTextLabel::Item *p_item return it; } } break; - case ITEM_NEWLINE: + case ITEM_NEWLINE: { + offset += 1; + if (offset == p_position) { + return it; + } + } break; case ITEM_IMAGE: case ITEM_TABLE: { offset += 1; @@ -219,13 +219,21 @@ void RichTextLabel::_update_line_font(ItemFrame *p_frame, int p_line, const Ref< for (int i = 0; i < spans; i++) { ItemText *it = reinterpret_cast<ItemText *>((uint64_t)TS->shaped_get_span_meta(t, i)); if (it) { - Ref<Font> font = _find_font(it); - if (font.is_null()) { - font = p_base_font; + Ref<Font> font = p_base_font; + int font_size = p_base_font_size; + + ItemFont *font_it = _find_font(it); + if (font_it) { + if (font_it->font.is_valid()) { + font = font_it->font; + } + if (font_it->font_size > 0) { + font_size = font_it->font_size; + } } - int font_size = _find_font_size(it); - if (font_size == -1) { - font_size = p_base_font_size; + ItemFontSize *font_size_it = _find_font_size(it); + if (font_size_it && font_size_it->font_size > 0) { + font_size = font_size_it->font_size; } TS->shaped_set_span_update_font(t, i, font->get_rids(), font_size, font->get_opentype_features()); for (int j = 0; j < TextServer::SPACING_MAX; j++) { @@ -274,8 +282,6 @@ float RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font switch (it->type) { case ITEM_TABLE: { ItemTable *table = static_cast<ItemTable *>(it); - int hseparation = get_theme_constant(SNAME("table_h_separation")); - int vseparation = get_theme_constant(SNAME("table_v_separation")); int col_count = table->columns.size(); for (int i = 0; i < col_count; i++) { @@ -296,12 +302,12 @@ float RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font } // Compute minimum width for each cell. - const int available_width = p_width - hseparation * (col_count - 1); + const int available_width = p_width - theme_cache.table_h_separation * (col_count - 1); // Compute available width and total ratio (for expanders). int total_ratio = 0; int remaining_width = available_width; - table->total_width = hseparation; + table->total_width = theme_cache.table_h_separation; for (int i = 0; i < col_count; i++) { remaining_width -= table->columns[i].min_width; @@ -319,7 +325,7 @@ float RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font if (table->columns[i].expand && total_ratio > 0 && remaining_width > 0) { table->columns[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio; } - table->total_width += table->columns[i].width + hseparation; + table->total_width += table->columns[i].width + theme_cache.table_h_separation; } // Resize to max_width if needed and distribute the remaining space. @@ -381,9 +387,9 @@ float RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font frame->lines[i].offset.y = prev_h; frame->lines[i].offset += offset; - float h = frame->lines[i].text_buf->get_size().y + (frame->lines[i].text_buf->get_line_count() - 1) * get_theme_constant(SNAME("line_separation")); + float h = frame->lines[i].text_buf->get_size().y + (frame->lines[i].text_buf->get_line_count() - 1) * theme_cache.line_separation; if (i > 0) { - h += get_theme_constant(SNAME("line_separation")); + h += theme_cache.line_separation; } if (frame->min_size_over.y > 0) { h = MAX(h, frame->min_size_over.y); @@ -392,15 +398,15 @@ float RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font h = MIN(h, frame->max_size_over.y); } yofs += h; - prev_h = frame->lines[i].offset.y + frame->lines[i].text_buf->get_size().y + frame->lines[i].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")); + prev_h = frame->lines[i].offset.y + frame->lines[i].text_buf->get_size().y + frame->lines[i].text_buf->get_line_count() * theme_cache.line_separation; } yofs += frame->padding.size.y; - offset.x += table->columns[column].width + hseparation + frame->padding.size.x; + offset.x += table->columns[column].width + theme_cache.table_h_separation + frame->padding.size.x; row_height = MAX(yofs, row_height); if (column == col_count - 1) { offset.x = 0; - row_height += vseparation; + row_height += theme_cache.table_v_separation; table->total_height += row_height; offset.y += row_height; table->rows.push_back(row_height); @@ -426,10 +432,10 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> Line &l = p_frame->lines[p_line]; MutexLock lock(l.text_buf->get_mutex()); - uint16_t autowrap_flags = TextServer::BREAK_MANDATORY; + BitField<TextServer::LineBreakFlag> autowrap_flags = TextServer::BREAK_MANDATORY; switch (autowrap_mode) { case TextServer::AUTOWRAP_WORD_SMART: - autowrap_flags = TextServer::BREAK_WORD_BOUND_ADAPTIVE | TextServer::BREAK_MANDATORY; + autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_ADAPTIVE | TextServer::BREAK_MANDATORY; break; case TextServer::AUTOWRAP_WORD: autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_MANDATORY; @@ -440,10 +446,12 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> case TextServer::AUTOWRAP_OFF: break; } + autowrap_flags = autowrap_flags | TextServer::BREAK_TRIM_EDGE_SPACES; // Clear cache. l.text_buf->clear(); - l.text_buf->set_flags(autowrap_flags | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_TRIM_EDGE_SPACES); + l.text_buf->set_break_flags(autowrap_flags); + l.text_buf->set_justification_flags(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_TRIM_EDGE_SPACES); l.char_offset = *r_char_offset; l.char_count = 0; @@ -477,13 +485,21 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> l.dc_ol_color = dc->ol_color; } break; case ITEM_NEWLINE: { - Ref<Font> font = _find_font(it); - if (font.is_null()) { - font = p_base_font; + Ref<Font> font = p_base_font; + int font_size = p_base_font_size; + + ItemFont *font_it = _find_font(it); + if (font_it) { + if (font_it->font.is_valid()) { + font = font_it->font; + } + if (font_it->font_size > 0) { + font_size = font_it->font_size; + } } - int font_size = _find_font_size(it); - if (font_size == -1) { - font_size = p_base_font_size; + ItemFontSize *font_size_it = _find_font_size(it); + if (font_size_it && font_size_it->font_size > 0) { + font_size = font_size_it->font_size; } l.text_buf->add_string("\n", font, font_size); text += "\n"; @@ -492,13 +508,21 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> } break; case ITEM_TEXT: { ItemText *t = static_cast<ItemText *>(it); - Ref<Font> font = _find_font(it); - if (font.is_null()) { - font = p_base_font; + Ref<Font> font = p_base_font; + int font_size = p_base_font_size; + + ItemFont *font_it = _find_font(it); + if (font_it) { + if (font_it->font.is_valid()) { + font = font_it->font; + } + if (font_it->font_size > 0) { + font_size = font_it->font_size; + } } - int font_size = _find_font_size(it); - if (font_size == -1) { - font_size = p_base_font_size; + ItemFontSize *font_size_it = _find_font_size(it); + if (font_size_it && font_size_it->font_size > 0) { + font_size = font_size_it->font_size; } String lang = _find_language(it); String tx = t->text; @@ -520,8 +544,6 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> } break; case ITEM_TABLE: { ItemTable *table = static_cast<ItemTable *>(it); - int hseparation = get_theme_constant(SNAME("table_h_separation")); - int vseparation = get_theme_constant(SNAME("table_v_separation")); int col_count = table->columns.size(); int t_char_count = 0; // Set minimums to zero. @@ -531,7 +553,7 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> table->columns[i].width = 0; } // Compute minimum width for each cell. - const int available_width = p_width - hseparation * (col_count - 1); + const int available_width = p_width - theme_cache.table_h_separation * (col_count - 1); int idx = 0; for (Item *E : table->subitems) { @@ -560,7 +582,7 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> // Compute available width and total ratio (for expanders). int total_ratio = 0; int remaining_width = available_width; - table->total_width = hseparation; + table->total_width = theme_cache.table_h_separation; for (int i = 0; i < col_count; i++) { remaining_width -= table->columns[i].min_width; @@ -578,7 +600,7 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> if (table->columns[i].expand && total_ratio > 0 && remaining_width > 0) { table->columns[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio; } - table->total_width += table->columns[i].width + hseparation; + table->total_width += table->columns[i].width + theme_cache.table_h_separation; } // Resize to max_width if needed and distribute the remaining space. @@ -641,9 +663,9 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> frame->lines[i].offset.y = prev_h; frame->lines[i].offset += offset; - float h = frame->lines[i].text_buf->get_size().y + (frame->lines[i].text_buf->get_line_count() - 1) * get_theme_constant(SNAME("line_separation")); + float h = frame->lines[i].text_buf->get_size().y + (frame->lines[i].text_buf->get_line_count() - 1) * theme_cache.line_separation; if (i > 0) { - h += get_theme_constant(SNAME("line_separation")); + h += theme_cache.line_separation; } if (frame->min_size_over.y > 0) { h = MAX(h, frame->min_size_over.y); @@ -652,16 +674,16 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> h = MIN(h, frame->max_size_over.y); } yofs += h; - prev_h = frame->lines[i].offset.y + frame->lines[i].text_buf->get_size().y + frame->lines[i].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")); + prev_h = frame->lines[i].offset.y + frame->lines[i].text_buf->get_size().y + frame->lines[i].text_buf->get_line_count() * theme_cache.line_separation; } yofs += frame->padding.size.y; - offset.x += table->columns[column].width + hseparation + frame->padding.size.x; + offset.x += table->columns[column].width + theme_cache.table_h_separation + frame->padding.size.x; row_height = MAX(yofs, row_height); // Add row height after last column of the row or last cell of the table. if (column == col_count - 1 || E->next() == nullptr) { offset.x = 0; - row_height += vseparation; + row_height += theme_cache.table_v_separation; table->total_height += row_height; offset.y += row_height; table->rows.push_back(row_height); @@ -692,7 +714,6 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)p_frame->lines.size(), 0); Vector2 off; - int line_spacing = get_theme_constant(SNAME("line_separation")); Line &l = p_frame->lines[p_line]; MutexLock lock(l.text_buf->get_mutex()); @@ -712,7 +733,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o bool trim_glyphs_ltr = (visible_characters >= 0) && ((visible_chars_behavior == TextServer::VC_GLYPHS_LTR) || ((visible_chars_behavior == TextServer::VC_GLYPHS_AUTO) && !lrtl)); bool trim_glyphs_rtl = (visible_characters >= 0) && ((visible_chars_behavior == TextServer::VC_GLYPHS_RTL) || ((visible_chars_behavior == TextServer::VC_GLYPHS_AUTO) && lrtl)); int total_glyphs = (trim_glyphs_ltr || trim_glyphs_rtl) ? get_total_glyph_count() : 0; - int visible_glyphs = total_glyphs * percent_visible; + int visible_glyphs = total_glyphs * visible_ratio; Vector<int> list_index; Vector<ItemList *> list_items; @@ -744,13 +765,21 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o } } if (!prefix.is_empty()) { - Ref<Font> font = _find_font(l.from); - if (font.is_null()) { - font = get_theme_font(SNAME("normal_font")); + Ref<Font> font = theme_cache.normal_font; + int font_size = theme_cache.normal_font_size; + + ItemFont *font_it = _find_font(l.from); + if (font_it) { + if (font_it->font.is_valid()) { + font = font_it->font; + } + if (font_it->font_size > 0) { + font_size = font_it->font_size; + } } - int font_size = _find_font_size(l.from); - if (font_size == -1) { - font_size = get_theme_font_size(SNAME("normal_font_size")); + ItemFontSize *font_size_it = _find_font_size(l.from); + if (font_size_it && font_size_it->font_size > 0) { + font_size = font_size_it->font_size; } if (rtl) { float offx = 0.0f; @@ -780,20 +809,21 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o // Draw text. for (int line = 0; line < l.text_buf->get_line_count(); line++) { if (line > 0) { - off.y += line_spacing; + off.y += theme_cache.line_separation; } - RID rid = l.text_buf->get_line_rid(line); if (p_ofs.y + off.y >= ctrl_size.height) { break; } - if (p_ofs.y + off.y + TS->shaped_text_get_size(rid).y <= 0) { - off.y += TS->shaped_text_get_size(rid).y; + + const Size2 line_size = l.text_buf->get_line_size(line); + if (p_ofs.y + off.y + line_size.y <= 0) { + off.y += line_size.y; continue; } float width = l.text_buf->get_width(); - float length = TS->shaped_text_get_width(rid); + float length = line_size.x; // Draw line. line_count++; @@ -836,6 +866,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o } } + RID rid = l.text_buf->get_line_rid(line); //draw_rect(Rect2(p_ofs + off, TS->shaped_text_get_size(rid)), Color(1,0,0), false, 2); //DEBUG_RECTS off.y += TS->shaped_text_get_ascent(rid); @@ -853,10 +884,11 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o } break; case ITEM_TABLE: { ItemTable *table = static_cast<ItemTable *>(it); - Color odd_row_bg = get_theme_color(SNAME("table_odd_row_bg")); - Color even_row_bg = get_theme_color(SNAME("table_even_row_bg")); - Color border = get_theme_color(SNAME("table_border")); - int hseparation = get_theme_constant(SNAME("table_h_separation")); + Color odd_row_bg = theme_cache.table_odd_row_bg; + Color even_row_bg = theme_cache.table_even_row_bg; + Color border = theme_cache.table_border; + int hseparation = theme_cache.table_h_separation; + int col_count = table->columns.size(); int row_count = table->rows.size(); @@ -929,17 +961,18 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o uint32_t gl = glyphs[i].index; uint16_t gl_fl = glyphs[i].flags; uint8_t gl_cn = glyphs[i].count; - bool cprev = false; + bool cprev_cluster = false; + bool cprev_conn = false; if (gl_cn == 0) { // Parts of the same cluster, always connected. - cprev = true; + cprev_cluster = true; } if (gl_fl & TextServer::GRAPHEME_IS_RTL) { // Check if previous grapheme cluster is connected. if (i > 0 && (glyphs[i - 1].flags & TextServer::GRAPHEME_IS_CONNECTED)) { - cprev = true; + cprev_conn = true; } } else { if (glyphs[i].flags & TextServer::GRAPHEME_IS_CONNECTED) { - cprev = true; + cprev_conn = true; } } @@ -958,6 +991,8 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o for (int j = 0; j < fx_stack.size(); j++) { ItemFX *item_fx = fx_stack[j]; + bool cn = cprev_cluster || (cprev_conn && item_fx->connected); + if (item_fx->type == ITEM_CUSTOMFX && custom_fx_ok) { ItemCustomFX *item_custom = static_cast<ItemCustomFX *>(item_fx); @@ -988,12 +1023,12 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o } else if (item_fx->type == ITEM_SHAKE) { ItemShake *item_shake = static_cast<ItemShake *>(item_fx); - if (!cprev) { + if (!cn) { uint64_t char_current_rand = item_shake->offset_random(glyphs[i].start); uint64_t char_previous_rand = item_shake->offset_previous_random(glyphs[i].start); uint64_t max_rand = 2147483647; - double current_offset = Math::range_lerp(char_current_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI); - double previous_offset = Math::range_lerp(char_previous_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI); + double current_offset = Math::remap(char_current_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI); + double previous_offset = Math::remap(char_previous_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI); double n_time = (double)(item_shake->elapsed_time / (0.5f / item_shake->rate)); n_time = (n_time > 1.0) ? 1.0 : n_time; item_shake->prev_off = Point2(Math::lerp(Math::sin(previous_offset), Math::sin(current_offset), n_time), Math::lerp(Math::cos(previous_offset), Math::cos(current_offset), n_time)) * (float)item_shake->strength / 10.0f; @@ -1002,7 +1037,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o } else if (item_fx->type == ITEM_WAVE) { ItemWave *item_wave = static_cast<ItemWave *>(item_fx); - if (!cprev) { + if (!cn) { double value = Math::sin(item_wave->frequency * item_wave->elapsed_time + ((p_ofs.x + gloff.x) / 50)) * (item_wave->amplitude / 10.0f); item_wave->prev_off = Point2(0, 1) * value; } @@ -1010,7 +1045,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o } else if (item_fx->type == ITEM_TORNADO) { ItemTornado *item_tornado = static_cast<ItemTornado *>(item_fx); - if (!cprev) { + if (!cn) { double torn_x = Math::sin(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + gloff.x) / 50)) * (item_tornado->radius); double torn_y = Math::cos(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + gloff.x) / 50)) * (item_tornado->radius); item_tornado->prev_off = Point2(torn_x, torn_y); @@ -1052,8 +1087,8 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o _draw_fbg_boxes(ci, rid, fbg_line_off, it_from, it_to, chr_range.x, chr_range.y, 0); // Draw main text. - Color selection_fg = get_theme_color(SNAME("font_selected_color")); - Color selection_bg = get_theme_color(SNAME("selection_color")); + Color selection_fg = theme_cache.font_selected_color; + Color selection_bg = theme_cache.selection_color; int sel_start = -1; int sel_end = -1; @@ -1095,7 +1130,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o } else if (ul_started) { ul_started = false; float y_off = TS->shaped_text_get_underline_position(rid); - float underline_width = TS->shaped_text_get_underline_thickness(rid) * get_theme_default_base_scale(); + float underline_width = TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale; draw_line(ul_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), ul_color, underline_width); } if (_find_hint(it, nullptr) && underline_hint) { @@ -1108,7 +1143,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o } else if (dot_ul_started) { dot_ul_started = false; float y_off = TS->shaped_text_get_underline_position(rid); - float underline_width = TS->shaped_text_get_underline_thickness(rid) * get_theme_default_base_scale(); + float underline_width = TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale; draw_dashed_line(dot_ul_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), dot_ul_color, underline_width, underline_width * 2); } if (_find_strikethrough(it)) { @@ -1121,7 +1156,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o } else if (st_started) { st_started = false; float y_off = -TS->shaped_text_get_ascent(rid) + TS->shaped_text_get_size(rid).y / 2; - float underline_width = TS->shaped_text_get_underline_thickness(rid) * get_theme_default_base_scale(); + float underline_width = TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale; draw_line(st_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), st_color, underline_width); } @@ -1145,17 +1180,18 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o uint32_t gl = glyphs[i].index; uint16_t gl_fl = glyphs[i].flags; uint8_t gl_cn = glyphs[i].count; - bool cprev = false; + bool cprev_cluster = false; + bool cprev_conn = false; if (gl_cn == 0) { // Parts of the same grapheme cluster, always connected. - cprev = true; + cprev_cluster = true; } if (gl_fl & TextServer::GRAPHEME_IS_RTL) { // Check if previous grapheme cluster is connected. if (i > 0 && (glyphs[i - 1].flags & TextServer::GRAPHEME_IS_CONNECTED)) { - cprev = true; + cprev_conn = true; } } else { if (glyphs[i].flags & TextServer::GRAPHEME_IS_CONNECTED) { - cprev = true; + cprev_conn = true; } } @@ -1173,6 +1209,8 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o for (int j = 0; j < fx_stack.size(); j++) { ItemFX *item_fx = fx_stack[j]; + bool cn = cprev_cluster || (cprev_conn && item_fx->connected); + if (item_fx->type == ITEM_CUSTOMFX && custom_fx_ok) { ItemCustomFX *item_custom = static_cast<ItemCustomFX *>(item_fx); @@ -1203,12 +1241,12 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o } else if (item_fx->type == ITEM_SHAKE) { ItemShake *item_shake = static_cast<ItemShake *>(item_fx); - if (!cprev) { + if (!cn) { uint64_t char_current_rand = item_shake->offset_random(glyphs[i].start); uint64_t char_previous_rand = item_shake->offset_previous_random(glyphs[i].start); uint64_t max_rand = 2147483647; - double current_offset = Math::range_lerp(char_current_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI); - double previous_offset = Math::range_lerp(char_previous_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI); + double current_offset = Math::remap(char_current_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI); + double previous_offset = Math::remap(char_previous_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI); double n_time = (double)(item_shake->elapsed_time / (0.5f / item_shake->rate)); n_time = (n_time > 1.0) ? 1.0 : n_time; item_shake->prev_off = Point2(Math::lerp(Math::sin(previous_offset), Math::sin(current_offset), n_time), Math::lerp(Math::cos(previous_offset), Math::cos(current_offset), n_time)) * (float)item_shake->strength / 10.0f; @@ -1217,7 +1255,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o } else if (item_fx->type == ITEM_WAVE) { ItemWave *item_wave = static_cast<ItemWave *>(item_fx); - if (!cprev) { + if (!cn) { double value = Math::sin(item_wave->frequency * item_wave->elapsed_time + ((p_ofs.x + off.x) / 50)) * (item_wave->amplitude / 10.0f); item_wave->prev_off = Point2(0, 1) * value; } @@ -1225,7 +1263,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o } else if (item_fx->type == ITEM_TORNADO) { ItemTornado *item_tornado = static_cast<ItemTornado *>(item_fx); - if (!cprev) { + if (!cn) { double torn_x = Math::sin(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + off.x) / 50)) * (item_tornado->radius); double torn_y = Math::cos(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + off.x) / 50)) * (item_tornado->radius); item_tornado->prev_off = Point2(torn_x, torn_y); @@ -1260,19 +1298,19 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o if (ul_started) { ul_started = false; float y_off = TS->shaped_text_get_underline_position(rid); - float underline_width = TS->shaped_text_get_underline_thickness(rid) * get_theme_default_base_scale(); + float underline_width = TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale; draw_line(ul_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), ul_color, underline_width); } if (dot_ul_started) { dot_ul_started = false; float y_off = TS->shaped_text_get_underline_position(rid); - float underline_width = TS->shaped_text_get_underline_thickness(rid) * get_theme_default_base_scale(); + float underline_width = TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale; draw_dashed_line(dot_ul_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), dot_ul_color, underline_width, underline_width * 2); } if (st_started) { st_started = false; float y_off = -TS->shaped_text_get_ascent(rid) + TS->shaped_text_get_size(rid).y / 2; - float underline_width = TS->shaped_text_get_underline_thickness(rid) * get_theme_default_base_scale(); + float underline_width = TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale; draw_line(st_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), st_color, underline_width); } } @@ -1282,19 +1320,19 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o if (ul_started) { ul_started = false; float y_off = TS->shaped_text_get_underline_position(rid); - float underline_width = TS->shaped_text_get_underline_thickness(rid) * get_theme_default_base_scale(); + float underline_width = TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale; draw_line(ul_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), ul_color, underline_width); } if (dot_ul_started) { dot_ul_started = false; float y_off = TS->shaped_text_get_underline_position(rid); - float underline_width = TS->shaped_text_get_underline_thickness(rid) * get_theme_default_base_scale(); + float underline_width = TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale; draw_dashed_line(dot_ul_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), dot_ul_color, underline_width, underline_width * 2); } if (st_started) { st_started = false; float y_off = -TS->shaped_text_get_ascent(rid) + TS->shaped_text_get_size(rid).y / 2; - float underline_width = TS->shaped_text_get_underline_thickness(rid) * get_theme_default_base_scale(); + float underline_width = TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale; draw_line(st_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), st_color, underline_width); } // Draw foreground color box @@ -1306,7 +1344,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o return line_count; } -void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, ItemFrame **r_click_frame, int *r_click_line, Item **r_click_item, int *r_click_char, bool *r_outside) { +void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, ItemFrame **r_click_frame, int *r_click_line, Item **r_click_item, int *r_click_char, bool *r_outside, bool p_meta) { if (r_click_item) { *r_click_item = nullptr; } @@ -1329,8 +1367,8 @@ void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item Point2 ofs = text_rect.get_position() + Vector2(0, main->lines[from_line].offset.y - vofs); while (ofs.y < size.height && from_line < to_line) { MutexLock lock(main->lines[from_line].text_buf->get_mutex()); - _find_click_in_line(p_frame, from_line, ofs, text_rect.size.x, p_click, r_click_frame, r_click_line, r_click_item, r_click_char); - ofs.y += main->lines[from_line].text_buf->get_size().y + main->lines[from_line].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")); + _find_click_in_line(p_frame, from_line, ofs, text_rect.size.x, p_click, r_click_frame, r_click_line, r_click_item, r_click_char, false, p_meta); + ofs.y += main->lines[from_line].text_buf->get_size().y + main->lines[from_line].text_buf->get_line_count() * theme_cache.line_separation; if (((r_click_item != nullptr) && ((*r_click_item) != nullptr)) || ((r_click_frame != nullptr) && ((*r_click_frame) != nullptr))) { if (r_outside != nullptr) { *r_outside = false; @@ -1341,9 +1379,11 @@ void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item } } -float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Point2i &p_click, ItemFrame **r_click_frame, int *r_click_line, Item **r_click_item, int *r_click_char, bool p_table) { +float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Point2i &p_click, ItemFrame **r_click_frame, int *r_click_line, Item **r_click_item, int *r_click_char, bool p_table, bool p_meta) { Vector2 off; + bool line_clicked = false; + float text_rect_begin = 0.0; int char_pos = -1; Line &l = p_frame->lines[p_line]; MutexLock lock(l.text_buf->get_mutex()); @@ -1406,9 +1446,6 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V if (p_click.y >= rect.position.y && p_click.y <= rect.position.y + rect.size.y) { switch (it->type) { case ITEM_TABLE: { - int hseparation = get_theme_constant(SNAME("table_h_separation")); - int vseparation = get_theme_constant(SNAME("table_v_separation")); - ItemTable *table = static_cast<ItemTable *>(it); int idx = 0; @@ -1426,7 +1463,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V if (rtl) { coff.x = rect.size.width - table->columns[col].width - coff.x; } - Rect2 crect = Rect2(rect.position + coff - frame->padding.position, Size2(table->columns[col].width + hseparation, table->rows[row] + vseparation) + frame->padding.position + frame->padding.size); + Rect2 crect = Rect2(rect.position + coff - frame->padding.position, Size2(table->columns[col].width + theme_cache.table_h_separation, table->rows[row] + theme_cache.table_v_separation) + frame->padding.position + frame->padding.size); if (col == col_count - 1) { if (rtl) { crect.size.x = crect.position.x + crect.size.x; @@ -1437,7 +1474,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V } if (crect.has_point(p_click)) { for (int j = 0; j < (int)frame->lines.size(); j++) { - _find_click_in_line(frame, j, rect.position + Vector2(0, frame->lines[j].offset.y), rect.size.x, p_click, &table_click_frame, &table_click_line, &table_click_item, &table_click_char, true); + _find_click_in_line(frame, j, rect.position + Vector2(0, frame->lines[j].offset.y), rect.size.x, p_click, &table_click_frame, &table_click_line, &table_click_item, &table_click_char, true, p_meta); if (table_click_frame && table_click_item) { // Save cell detected cell hit data. table_range = Vector2i(INT32_MAX, 0); @@ -1465,11 +1502,23 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V } Rect2 rect = Rect2(p_ofs + off - Vector2(0, TS->shaped_text_get_ascent(rid)) - p_frame->padding.position, TS->shaped_text_get_size(rid) + p_frame->padding.position + p_frame->padding.size); if (p_table) { - rect.size.y += get_theme_constant(SNAME("table_v_separation")); + rect.size.y += theme_cache.table_v_separation; } if (p_click.y >= rect.position.y && p_click.y <= rect.position.y + rect.size.y) { - char_pos = TS->shaped_text_hit_test_position(rid, p_click.x - rect.position.x); + if ((!rtl && p_click.x >= rect.position.x) || (rtl && p_click.x <= rect.position.x + rect.size.x)) { + if (p_meta) { + int64_t glyph_idx = TS->shaped_text_hit_test_grapheme(rid, p_click.x - rect.position.x); + if (glyph_idx >= 0) { + const Glyph *glyphs = TS->shaped_text_get_glyphs(rid); + char_pos = glyphs[glyph_idx].start; + } + } else { + char_pos = TS->shaped_text_hit_test_position(rid, p_click.x - rect.position.x); + } + } + line_clicked = true; + text_rect_begin = rtl ? rect.position.x + rect.size.x : rect.position.x; } // If table hit was detected, and line hit is in the table bounds use table hit. @@ -1492,27 +1541,49 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V return table_offy; } - off.y += TS->shaped_text_get_descent(rid) + get_theme_constant(SNAME("line_separation")); + off.y += TS->shaped_text_get_descent(rid) + theme_cache.line_separation; } // Text line hit. - if (char_pos >= 0) { + if (line_clicked) { // Find item. if (r_click_item != nullptr) { Item *it = p_frame->lines[p_line].from; Item *it_to = (p_line + 1 < (int)p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr; - if (char_pos == p_frame->lines[p_line].char_count) { - // Selection after the end of line, select last item. - if (it_to != nullptr) { - *r_click_item = _get_prev_item(it_to); - } else { - for (Item *i = it; i; i = _get_next_item(i)) { - *r_click_item = i; + if (char_pos >= 0) { + *r_click_item = _get_item_at_pos(it, it_to, char_pos); + } else { + int stop = text_rect_begin; + *r_click_item = _find_indentable(it); + while (*r_click_item) { + Ref<Font> font = theme_cache.normal_font; + int font_size = theme_cache.normal_font_size; + ItemFont *font_it = _find_font(*r_click_item); + if (font_it) { + if (font_it->font.is_valid()) { + font = font_it->font; + } + if (font_it->font_size > 0) { + font_size = font_it->font_size; + } } + ItemFontSize *font_size_it = _find_font_size(*r_click_item); + if (font_size_it && font_size_it->font_size > 0) { + font_size = font_size_it->font_size; + } + if (rtl) { + stop += tab_size * font->get_char_size(' ', font_size).width; + if (stop > p_click.x) { + break; + } + } else { + stop -= tab_size * font->get_char_size(' ', font_size).width; + if (stop < p_click.x) { + break; + } + } + *r_click_item = _find_indentable((*r_click_item)->parent); } - } else { - // Selection in the line. - *r_click_item = _get_item_at_pos(it, it_to, char_pos); } } @@ -1545,7 +1616,7 @@ void RichTextLabel::_scroll_changed(double) { scroll_updated = true; - update(); + queue_redraw(); } void RichTextLabel::_update_fx(RichTextLabel::ItemFrame *p_frame, double p_delta_time) { @@ -1595,11 +1666,52 @@ int RichTextLabel::_find_first_line(int p_from, int p_to, int p_vofs) const { r = m; } } - return l; + return MIN(l, (int)main->lines.size() - 1); } _FORCE_INLINE_ float RichTextLabel::_calculate_line_vertical_offset(const RichTextLabel::Line &line) const { - return line.get_height(get_theme_constant(SNAME("line_separation"))); + return line.get_height(theme_cache.line_separation); +} + +void RichTextLabel::_update_theme_item_cache() { + Control::_update_theme_item_cache(); + + theme_cache.normal_style = get_theme_stylebox(SNAME("normal")); + theme_cache.focus_style = get_theme_stylebox(SNAME("focus")); + theme_cache.progress_bg_style = get_theme_stylebox(SNAME("background"), SNAME("ProgressBar")); + theme_cache.progress_fg_style = get_theme_stylebox(SNAME("fill"), SNAME("ProgressBar")); + + theme_cache.line_separation = get_theme_constant(SNAME("line_separation")); + + theme_cache.normal_font = get_theme_font(SNAME("normal_font")); + theme_cache.normal_font_size = get_theme_font_size(SNAME("normal_font_size")); + + theme_cache.default_color = get_theme_color(SNAME("default_color")); + theme_cache.font_selected_color = get_theme_color(SNAME("font_selected_color")); + theme_cache.selection_color = get_theme_color(SNAME("selection_color")); + theme_cache.font_outline_color = get_theme_color(SNAME("font_outline_color")); + theme_cache.font_shadow_color = get_theme_color(SNAME("font_shadow_color")); + theme_cache.shadow_outline_size = get_theme_constant(SNAME("shadow_outline_size")); + theme_cache.shadow_offset_x = get_theme_constant(SNAME("shadow_offset_x")); + theme_cache.shadow_offset_y = get_theme_constant(SNAME("shadow_offset_y")); + theme_cache.outline_size = get_theme_constant(SNAME("outline_size")); + + theme_cache.bold_font = get_theme_font(SNAME("bold_font")); + theme_cache.bold_font_size = get_theme_font_size(SNAME("bold_font_size")); + theme_cache.bold_italics_font = get_theme_font(SNAME("bold_italics_font")); + theme_cache.bold_italics_font_size = get_theme_font_size(SNAME("bold_italics_font_size")); + theme_cache.italics_font = get_theme_font(SNAME("italics_font")); + theme_cache.italics_font_size = get_theme_font_size(SNAME("italics_font_size")); + theme_cache.mono_font = get_theme_font(SNAME("mono_font")); + theme_cache.mono_font_size = get_theme_font_size(SNAME("mono_font_size")); + + theme_cache.table_h_separation = get_theme_constant(SNAME("table_h_separation")); + theme_cache.table_v_separation = get_theme_constant(SNAME("table_v_separation")); + theme_cache.table_odd_row_bg = get_theme_color(SNAME("table_odd_row_bg")); + theme_cache.table_even_row_bg = get_theme_color(SNAME("table_even_row_bg")); + theme_cache.table_border = get_theme_color(SNAME("table_border")); + + theme_cache.base_scale = get_theme_default_base_scale(); } void RichTextLabel::_notification(int p_what) { @@ -1609,20 +1721,20 @@ void RichTextLabel::_notification(int p_what) { meta_hovering = nullptr; emit_signal(SNAME("meta_hover_ended"), current_meta); current_meta = false; - update(); + queue_redraw(); } } break; case NOTIFICATION_RESIZED: { _stop_thread(); main->first_resized_line.store(0); //invalidate ALL - update(); + queue_redraw(); } break; case NOTIFICATION_THEME_CHANGED: { _stop_thread(); main->first_invalid_font_line.store(0); //invalidate ALL - update(); + queue_redraw(); } break; case NOTIFICATION_ENTER_TREE: { @@ -1632,7 +1744,7 @@ void RichTextLabel::_notification(int p_what) { } main->first_invalid_line.store(0); //invalidate ALL - update(); + queue_redraw(); } break; case NOTIFICATION_PREDELETE: @@ -1644,22 +1756,22 @@ void RichTextLabel::_notification(int p_what) { case NOTIFICATION_TRANSLATION_CHANGED: { _stop_thread(); main->first_invalid_line.store(0); //invalidate ALL - update(); + queue_redraw(); } break; case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { - update(); + queue_redraw(); } break; case NOTIFICATION_DRAW: { RID ci = get_canvas_item(); Size2 size = get_size(); - draw_style_box(get_theme_stylebox(SNAME("normal")), Rect2(Point2(), size)); + draw_style_box(theme_cache.normal_style, Rect2(Point2(), size)); if (has_focus()) { RenderingServer::get_singleton()->canvas_item_add_clip_ignore(ci, true); - draw_style_box(get_theme_stylebox(SNAME("focus")), Rect2(Point2(), size)); + draw_style_box(theme_cache.focus_style, Rect2(Point2(), size)); RenderingServer::get_singleton()->canvas_item_add_clip_ignore(ci, false); } @@ -1669,24 +1781,20 @@ void RichTextLabel::_notification(int p_what) { } else { // Draw loading progress bar. if ((progress_delay > 0) && (OS::get_singleton()->get_ticks_msec() - loading_started >= (uint64_t)progress_delay)) { - Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg"), SNAME("ProgressBar")); - Ref<StyleBox> fg = get_theme_stylebox(SNAME("fg"), SNAME("ProgressBar")); - Ref<StyleBox> style = get_theme_stylebox(SNAME("normal")); + Vector2 p_size = Vector2(size.width - (theme_cache.normal_style->get_offset().x + vscroll->get_combined_minimum_size().width) * 2, vscroll->get_combined_minimum_size().width); + Vector2 p_pos = Vector2(theme_cache.normal_style->get_offset().x, size.height - theme_cache.normal_style->get_offset().y - vscroll->get_combined_minimum_size().width); - Vector2 p_size = Vector2(size.width - (style->get_offset().x + vscroll->get_combined_minimum_size().width) * 2, vscroll->get_combined_minimum_size().width); - Vector2 p_pos = Vector2(style->get_offset().x, size.height - style->get_offset().y - vscroll->get_combined_minimum_size().width); - - draw_style_box(bg, Rect2(p_pos, p_size)); + draw_style_box(theme_cache.progress_bg_style, Rect2(p_pos, p_size)); bool right_to_left = is_layout_rtl(); double r = loaded.load(); - int mp = fg->get_minimum_size().width; + int mp = theme_cache.progress_fg_style->get_minimum_size().width; int p = round(r * (p_size.width - mp)); if (right_to_left) { int p_remaining = round((1.0 - r) * (p_size.width - mp)); - draw_style_box(fg, Rect2(p_pos + Point2(p_remaining, 0), Size2(p + fg->get_minimum_size().width, p_size.height))); + draw_style_box(theme_cache.progress_fg_style, Rect2(p_pos + Point2(p_remaining, 0), Size2(p + theme_cache.progress_fg_style->get_minimum_size().width, p_size.height))); } else { - draw_style_box(fg, Rect2(p_pos, Size2(p + fg->get_minimum_size().width, p_size.height))); + draw_style_box(theme_cache.progress_fg_style, Rect2(p_pos, Size2(p + theme_cache.progress_fg_style->get_minimum_size().width, p_size.height))); } } } @@ -1699,13 +1807,7 @@ void RichTextLabel::_notification(int p_what) { int to_line = main->first_invalid_line.load(); int from_line = _find_first_line(0, to_line, vofs); - Ref<Font> base_font = get_theme_font(SNAME("normal_font")); - Color base_color = get_theme_color(SNAME("default_color")); - Color outline_color = get_theme_color(SNAME("font_outline_color")); - int outline_size = get_theme_constant(SNAME("outline_size")); - Color font_shadow_color = get_theme_color(SNAME("font_shadow_color")); - int shadow_outline_size = get_theme_constant(SNAME("shadow_outline_size")); - Point2 shadow_ofs(get_theme_constant(SNAME("shadow_offset_x")), get_theme_constant(SNAME("shadow_offset_y"))); + Point2 shadow_ofs(theme_cache.shadow_offset_x, theme_cache.shadow_offset_y); visible_paragraph_count = 0; visible_line_count = 0; @@ -1717,8 +1819,8 @@ void RichTextLabel::_notification(int p_what) { MutexLock lock(main->lines[from_line].text_buf->get_mutex()); visible_paragraph_count++; - visible_line_count += _draw_line(main, from_line, ofs, text_rect.size.x, base_color, outline_size, outline_color, font_shadow_color, shadow_outline_size, shadow_ofs, processed_glyphs); - ofs.y += main->lines[from_line].text_buf->get_size().y + main->lines[from_line].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")); + visible_line_count += _draw_line(main, from_line, ofs, text_rect.size.x, theme_cache.default_color, theme_cache.outline_size, theme_cache.font_outline_color, theme_cache.font_shadow_color, theme_cache.shadow_outline_size, shadow_ofs, processed_glyphs); + ofs.y += main->lines[from_line].text_buf->get_size().y + main->lines[from_line].text_buf->get_line_count() * theme_cache.line_separation; from_line++; } } break; @@ -1730,7 +1832,7 @@ void RichTextLabel::_notification(int p_what) { } double dt = get_process_delta_time(); _update_fx(main, dt); - update(); + queue_redraw(); } } break; @@ -1757,7 +1859,7 @@ Control::CursorShape RichTextLabel::get_cursor_shape(const Point2 &p_pos) const Item *item = nullptr; bool outside = true; - const_cast<RichTextLabel *>(this)->_find_click(main, p_pos, nullptr, nullptr, &item, nullptr, &outside); + const_cast<RichTextLabel *>(this)->_find_click(main, p_pos, nullptr, nullptr, &item, nullptr, &outside, true); if (item && !outside && const_cast<RichTextLabel *>(this)->_find_meta(item, nullptr)) { return CURSOR_POINTING_HAND; @@ -1782,7 +1884,7 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) { selection.drag_attempt = false; - _find_click(main, b->get_position(), &c_frame, &c_line, &c_item, &c_index, &outside); + _find_click(main, b->get_position(), &c_frame, &c_line, &c_item, &c_index, &outside, false); if (c_item != nullptr) { if (selection.enabled) { selection.click_frame = c_frame; @@ -1820,7 +1922,7 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) { selection.drag_attempt = false; - _find_click(main, b->get_position(), &c_frame, &c_line, &c_item, &c_index, &outside); + _find_click(main, b->get_position(), &c_frame, &c_line, &c_item, &c_index, &outside, false); if (c_frame) { const Line &l = c_frame->lines[c_line]; @@ -1842,7 +1944,7 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) { if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) { DisplayServer::get_singleton()->clipboard_set_primary(get_selected_text()); } - update(); + queue_redraw(); break; } } @@ -1870,7 +1972,7 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) { Item *c_item = nullptr; bool outside = true; - _find_click(main, b->get_position(), nullptr, nullptr, &c_item, nullptr, &outside); + _find_click(main, b->get_position(), nullptr, nullptr, &c_item, nullptr, &outside, true); if (c_item) { Variant meta; @@ -1926,11 +2028,11 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) { handled = true; } if (k->is_action("ui_up") && vscroll->is_visible_in_tree()) { - vscroll->set_value(vscroll->get_value() - get_theme_font(SNAME("normal_font"))->get_height(get_theme_font_size(SNAME("normal_font_size")))); + vscroll->set_value(vscroll->get_value() - theme_cache.normal_font->get_height(theme_cache.normal_font_size)); handled = true; } if (k->is_action("ui_down") && vscroll->is_visible_in_tree()) { - vscroll->set_value(vscroll->get_value() + get_theme_font(SNAME("normal_font"))->get_height(get_theme_font_size(SNAME("normal_font_size")))); + vscroll->set_value(vscroll->get_value() + theme_cache.normal_font->get_height(theme_cache.normal_font_size)); handled = true; } if (k->is_action("ui_home") && vscroll->is_visible_in_tree()) { @@ -1976,7 +2078,7 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) { int c_index = 0; bool outside; - _find_click(main, m->get_position(), &c_frame, &c_line, &c_item, &c_index, &outside); + _find_click(main, m->get_position(), &c_frame, &c_line, &c_item, &c_index, &outside, false); if (selection.click_item && c_item) { selection.from_frame = selection.click_frame; selection.from_line = selection.click_line; @@ -2008,7 +2110,7 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) { } selection.active = true; - update(); + queue_redraw(); } Variant meta; @@ -2034,7 +2136,7 @@ String RichTextLabel::get_tooltip(const Point2 &p_pos) const { Item *c_item = nullptr; bool outside; - const_cast<RichTextLabel *>(this)->_find_click(main, p_pos, nullptr, nullptr, &c_item, nullptr, &outside); + const_cast<RichTextLabel *>(this)->_find_click(main, p_pos, nullptr, nullptr, &c_item, nullptr, &outside, true); String description; if (c_item && !outside && const_cast<RichTextLabel *>(this)->_find_hint(c_item, &description)) { @@ -2069,34 +2171,116 @@ void RichTextLabel::_find_frame(Item *p_item, ItemFrame **r_frame, int *r_line) } } -Ref<Font> RichTextLabel::_find_font(Item *p_item) { +RichTextLabel::Item *RichTextLabel::_find_indentable(Item *p_item) { + Item *indentable = p_item; + + while (indentable) { + if (indentable->type == ITEM_INDENT || indentable->type == ITEM_LIST) { + return indentable; + } + indentable = indentable->parent; + } + + return indentable; +} + +RichTextLabel::ItemFont *RichTextLabel::_find_font(Item *p_item) { Item *fontitem = p_item; while (fontitem) { if (fontitem->type == ITEM_FONT) { ItemFont *fi = static_cast<ItemFont *>(fontitem); - return fi->font; + switch (fi->def_font) { + case NORMAL_FONT: { + if (fi->variation) { + Ref<FontVariation> fc = fi->font; + if (fc.is_valid()) { + fc->set_base_font(theme_cache.normal_font); + } + } else { + fi->font = theme_cache.normal_font; + } + if (fi->def_size) { + fi->font_size = theme_cache.normal_font_size; + } + } break; + case BOLD_FONT: { + if (fi->variation) { + Ref<FontVariation> fc = fi->font; + if (fc.is_valid()) { + fc->set_base_font(theme_cache.bold_font); + } + } else { + fi->font = theme_cache.bold_font; + } + if (fi->def_size) { + fi->font_size = theme_cache.bold_font_size; + } + } break; + case ITALICS_FONT: { + if (fi->variation) { + Ref<FontVariation> fc = fi->font; + if (fc.is_valid()) { + fc->set_base_font(theme_cache.italics_font); + } + } else { + fi->font = theme_cache.italics_font; + } + if (fi->def_size) { + fi->font_size = theme_cache.italics_font_size; + } + } break; + case BOLD_ITALICS_FONT: { + if (fi->variation) { + Ref<FontVariation> fc = fi->font; + if (fc.is_valid()) { + fc->set_base_font(theme_cache.bold_italics_font); + } + } else { + fi->font = theme_cache.bold_italics_font; + } + if (fi->def_size) { + fi->font_size = theme_cache.bold_italics_font_size; + } + } break; + case MONO_FONT: { + if (fi->variation) { + Ref<FontVariation> fc = fi->font; + if (fc.is_valid()) { + fc->set_base_font(theme_cache.mono_font); + } + } else { + fi->font = theme_cache.mono_font; + } + if (fi->def_size) { + fi->font_size = theme_cache.mono_font_size; + } + } break; + default: { + } break; + } + return fi; } fontitem = fontitem->parent; } - return Ref<Font>(); + return nullptr; } -int RichTextLabel::_find_font_size(Item *p_item) { +RichTextLabel::ItemFontSize *RichTextLabel::_find_font_size(Item *p_item) { Item *sizeitem = p_item; while (sizeitem) { if (sizeitem->type == ITEM_FONT_SIZE) { ItemFontSize *fi = static_cast<ItemFontSize *>(sizeitem); - return fi->font_size; + return fi; } sizeitem = sizeitem->parent; } - return -1; + return nullptr; } int RichTextLabel::_find_outline_size(Item *p_item, int p_default) { @@ -2182,24 +2366,40 @@ int RichTextLabel::_find_margin(Item *p_item, const Ref<Font> &p_base_font, int while (item) { if (item->type == ITEM_INDENT) { - Ref<Font> font = _find_font(item); - if (font.is_null()) { - font = p_base_font; + Ref<Font> font = p_base_font; + int font_size = p_base_font_size; + + ItemFont *font_it = _find_font(item); + if (font_it) { + if (font_it->font.is_valid()) { + font = font_it->font; + } + if (font_it->font_size > 0) { + font_size = font_it->font_size; + } } - int font_size = _find_font_size(item); - if (font_size == -1) { - font_size = p_base_font_size; + ItemFontSize *font_size_it = _find_font_size(item); + if (font_size_it && font_size_it->font_size > 0) { + font_size = font_size_it->font_size; } margin += tab_size * font->get_char_size(' ', font_size).width; } else if (item->type == ITEM_LIST) { - Ref<Font> font = _find_font(item); - if (font.is_null()) { - font = p_base_font; + Ref<Font> font = p_base_font; + int font_size = p_base_font_size; + + ItemFont *font_it = _find_font(item); + if (font_it) { + if (font_it->font.is_valid()) { + font = font_it->font; + } + if (font_it->font_size > 0) { + font_size = font_it->font_size; + } } - int font_size = _find_font_size(item); - if (font_size == -1) { - font_size = p_base_font_size; + ItemFontSize *font_size_it = _find_font_size(item); + if (font_size_it && font_size_it->font_size > 0) { + font_size = font_size_it->font_size; } margin += tab_size * font->get_char_size(' ', font_size).width; } @@ -2434,9 +2634,11 @@ bool RichTextLabel::_find_layout_subitem(Item *from, Item *to) { void RichTextLabel::_thread_function(void *self) { RichTextLabel *rtl = reinterpret_cast<RichTextLabel *>(self); + rtl->set_physics_process_internal(true); rtl->_process_line_caches(); + rtl->set_physics_process_internal(false); rtl->updating.store(false); - rtl->call_deferred(SNAME("update")); + rtl->call_deferred(SNAME("queue_redraw")); } void RichTextLabel::_stop_thread() { @@ -2457,7 +2659,7 @@ void RichTextLabel::set_threaded(bool p_threaded) { if (threaded != p_threaded) { _stop_thread(); threaded = p_threaded; - update(); + queue_redraw(); } } @@ -2481,14 +2683,12 @@ bool RichTextLabel::_validate_line_caches() { MutexLock data_lock(data_mutex); Rect2 text_rect = _get_text_rect(); - Ref<Font> base_font = get_theme_font(SNAME("normal_font")); - int base_font_size = get_theme_font_size(SNAME("normal_font_size")); int ctrl_height = get_size().height; // Update fonts. if (main->first_invalid_font_line.load() != (int)main->lines.size()) { for (int i = main->first_invalid_font_line.load(); i < (int)main->lines.size(); i++) { - _update_line_font(main, i, base_font, base_font_size); + _update_line_font(main, i, theme_cache.normal_font, theme_cache.normal_font_size); } main->first_resized_line.store(main->first_invalid_font_line.load()); main->first_invalid_font_line.store(main->lines.size()); @@ -2503,7 +2703,7 @@ bool RichTextLabel::_validate_line_caches() { float total_height = (fi == 0) ? 0 : _calculate_line_vertical_offset(main->lines[fi - 1]); for (int i = fi; i < (int)main->lines.size(); i++) { - total_height = _resize_line(main, i, base_font, base_font_size, text_rect.get_size().width - scroll_w, total_height); + total_height = _resize_line(main, i, theme_cache.normal_font, theme_cache.normal_font_size, text_rect.get_size().width - scroll_w, total_height); updating_scroll = true; bool exceeds = total_height > ctrl_height && scroll_active; @@ -2523,7 +2723,7 @@ bool RichTextLabel::_validate_line_caches() { total_height = 0; for (int j = 0; j <= i; j++) { - total_height = _resize_line(main, j, base_font, base_font_size, text_rect.get_size().width - scroll_w, total_height); + total_height = _resize_line(main, j, theme_cache.normal_font, theme_cache.normal_font_size, text_rect.get_size().width - scroll_w, total_height); main->first_resized_line.store(j); } @@ -2552,11 +2752,10 @@ bool RichTextLabel::_validate_line_caches() { loaded.store(true); thread.start(RichTextLabel::_thread_function, reinterpret_cast<void *>(this)); loading_started = OS::get_singleton()->get_ticks_msec(); - set_physics_process_internal(true); return false; } else { _process_line_caches(); - update(); + queue_redraw(); return true; } } @@ -2570,15 +2769,13 @@ void RichTextLabel::_process_line_caches() { MutexLock data_lock(data_mutex); Rect2 text_rect = _get_text_rect(); - int base_font_size = get_theme_font_size(SNAME("normal_font_size")); - Ref<Font> base_font = get_theme_font(SNAME("normal_font")); int ctrl_height = get_size().height; int fi = main->first_invalid_line.load(); int total_chars = (fi == 0) ? 0 : (main->lines[fi].char_offset + main->lines[fi].char_count); float total_height = (fi == 0) ? 0 : _calculate_line_vertical_offset(main->lines[fi - 1]); for (int i = fi; i < (int)main->lines.size(); i++) { - total_height = _shape_line(main, i, base_font, base_font_size, text_rect.get_size().width - scroll_w, total_height, &total_chars); + total_height = _shape_line(main, i, theme_cache.normal_font, theme_cache.normal_font_size, text_rect.get_size().width - scroll_w, total_height, &total_chars); updating_scroll = true; bool exceeds = total_height > ctrl_height && scroll_active; if (exceeds != scroll_visible) { @@ -2599,7 +2796,7 @@ void RichTextLabel::_process_line_caches() { // since scroll was added or removed we need to resize all lines total_height = 0; for (int j = 0; j <= i; j++) { - total_height = _resize_line(main, j, base_font, base_font_size, text_rect.get_size().width - scroll_w, total_height); + total_height = _resize_line(main, j, theme_cache.normal_font, theme_cache.normal_font_size, text_rect.get_size().width - scroll_w, total_height); main->first_invalid_line.store(j); main->first_resized_line.store(j); @@ -2694,7 +2891,7 @@ void RichTextLabel::add_text(const String &p_text) { pos = end + 1; } - update(); + queue_redraw(); } void RichTextLabel::_add_item(Item *p_item, bool p_enter, bool p_ensure_newline) { @@ -2732,7 +2929,7 @@ void RichTextLabel::_add_item(Item *p_item, bool p_enter, bool p_ensure_newline) if (fixed_width != -1) { update_minimum_size(); } - update(); + queue_redraw(); } void RichTextLabel::_remove_item(Item *p_item, const int p_line, const int p_subitem_line) { @@ -2813,7 +3010,7 @@ void RichTextLabel::add_newline() { _add_item(item, false); current_frame->lines.resize(current_frame->lines.size() + 1); _invalidate_current_line(current_frame); - update(); + queue_redraw(); } bool RichTextLabel::remove_line(const int p_line) { @@ -2852,7 +3049,7 @@ bool RichTextLabel::remove_line(const int p_line) { } main->first_invalid_line.store(0); - update(); + queue_redraw(); return true; } @@ -2878,7 +3075,34 @@ void RichTextLabel::push_dropcap(const String &p_string, const Ref<Font> &p_font _add_item(item, false); } -void RichTextLabel::push_font(const Ref<Font> &p_font) { +void RichTextLabel::_push_def_font_var(DefaultFont p_def_font, const Ref<Font> &p_font, int p_size) { + _stop_thread(); + MutexLock data_lock(data_mutex); + + ERR_FAIL_COND(current->type == ITEM_TABLE); + ItemFont *item = memnew(ItemFont); + + item->def_font = p_def_font; + item->variation = true; + item->font = p_font; + item->font_size = p_size; + item->def_size = (p_size <= 0); + _add_item(item, true); +} + +void RichTextLabel::_push_def_font(DefaultFont p_def_font) { + _stop_thread(); + MutexLock data_lock(data_mutex); + + ERR_FAIL_COND(current->type == ITEM_TABLE); + ItemFont *item = memnew(ItemFont); + + item->def_font = p_def_font; + item->def_size = true; + _add_item(item, true); +} + +void RichTextLabel::push_font(const Ref<Font> &p_font, int p_size) { _stop_thread(); MutexLock data_lock(data_mutex); @@ -2887,42 +3111,38 @@ void RichTextLabel::push_font(const Ref<Font> &p_font) { ItemFont *item = memnew(ItemFont); item->font = p_font; + item->font_size = p_size; _add_item(item, true); } void RichTextLabel::push_normal() { - Ref<Font> normal_font = get_theme_font(SNAME("normal_font")); - ERR_FAIL_COND(normal_font.is_null()); + ERR_FAIL_COND(theme_cache.normal_font.is_null()); - push_font(normal_font); + _push_def_font(NORMAL_FONT); } void RichTextLabel::push_bold() { - Ref<Font> bold_font = get_theme_font(SNAME("bold_font")); - ERR_FAIL_COND(bold_font.is_null()); + ERR_FAIL_COND(theme_cache.bold_font.is_null()); - push_font(bold_font); + _push_def_font(BOLD_FONT); } void RichTextLabel::push_bold_italics() { - Ref<Font> bold_italics_font = get_theme_font(SNAME("bold_italics_font")); - ERR_FAIL_COND(bold_italics_font.is_null()); + ERR_FAIL_COND(theme_cache.bold_italics_font.is_null()); - push_font(bold_italics_font); + _push_def_font(BOLD_ITALICS_FONT); } void RichTextLabel::push_italics() { - Ref<Font> italics_font = get_theme_font(SNAME("italics_font")); - ERR_FAIL_COND(italics_font.is_null()); + ERR_FAIL_COND(theme_cache.italics_font.is_null()); - push_font(italics_font); + _push_def_font(ITALICS_FONT); } void RichTextLabel::push_mono() { - Ref<Font> mono_font = get_theme_font(SNAME("mono_font")); - ERR_FAIL_COND(mono_font.is_null()); + ERR_FAIL_COND(theme_cache.mono_font.is_null()); - push_font(mono_font); + _push_def_font(MONO_FONT); } void RichTextLabel::push_font_size(int p_font_size) { @@ -3079,33 +3299,36 @@ void RichTextLabel::push_fade(int p_start_index, int p_length) { _add_item(item, true); } -void RichTextLabel::push_shake(int p_strength = 10, float p_rate = 24.0f) { +void RichTextLabel::push_shake(int p_strength = 10, float p_rate = 24.0f, bool p_connected = true) { _stop_thread(); MutexLock data_lock(data_mutex); ItemShake *item = memnew(ItemShake); item->strength = p_strength; item->rate = p_rate; + item->connected = p_connected; _add_item(item, true); } -void RichTextLabel::push_wave(float p_frequency = 1.0f, float p_amplitude = 10.0f) { +void RichTextLabel::push_wave(float p_frequency = 1.0f, float p_amplitude = 10.0f, bool p_connected = true) { _stop_thread(); MutexLock data_lock(data_mutex); ItemWave *item = memnew(ItemWave); item->frequency = p_frequency; item->amplitude = p_amplitude; + item->connected = p_connected; _add_item(item, true); } -void RichTextLabel::push_tornado(float p_frequency = 1.0f, float p_radius = 10.0f) { +void RichTextLabel::push_tornado(float p_frequency = 1.0f, float p_radius = 10.0f, bool p_connected = true) { _stop_thread(); MutexLock data_lock(data_mutex); ItemTornado *item = memnew(ItemTornado); item->frequency = p_frequency; item->radius = p_radius; + item->connected = p_connected; _add_item(item, true); } @@ -3272,11 +3495,15 @@ void RichTextLabel::clear() { } void RichTextLabel::set_tab_size(int p_spaces) { + if (tab_size == p_spaces) { + return; + } + _stop_thread(); tab_size = p_spaces; main->first_resized_line.store(0); - update(); + queue_redraw(); } int RichTextLabel::get_tab_size() const { @@ -3295,8 +3522,12 @@ bool RichTextLabel::is_fit_content_height_enabled() const { } void RichTextLabel::set_meta_underline(bool p_underline) { + if (underline_meta == p_underline) { + return; + } + underline_meta = p_underline; - update(); + queue_redraw(); } bool RichTextLabel::is_meta_underlined() const { @@ -3305,7 +3536,7 @@ bool RichTextLabel::is_meta_underlined() const { void RichTextLabel::set_hint_underline(bool p_underline) { underline_hint = p_underline; - update(); + queue_redraw(); } bool RichTextLabel::is_hint_underlined() const { @@ -3331,7 +3562,7 @@ void RichTextLabel::set_scroll_active(bool p_active) { scroll_active = p_active; vscroll->set_drag_node_enabled(p_active); - update(); + queue_redraw(); } bool RichTextLabel::is_scroll_active() const { @@ -3361,13 +3592,6 @@ void RichTextLabel::append_text(const String &p_bbcode) { int pos = 0; List<String> tag_stack; - Ref<Font> normal_font = get_theme_font(SNAME("normal_font")); - Ref<Font> bold_font = get_theme_font(SNAME("bold_font")); - Ref<Font> italics_font = get_theme_font(SNAME("italics_font")); - Ref<Font> bold_italics_font = get_theme_font(SNAME("bold_italics_font")); - Ref<Font> mono_font = get_theme_font(SNAME("mono_font")); - - Color base_color = get_theme_color(SNAME("default_color")); int indent_level = 0; @@ -3512,9 +3736,9 @@ void RichTextLabel::append_text(const String &p_bbcode) { //use bold font in_bold = true; if (in_italics) { - push_font(bold_italics_font); + _push_def_font(BOLD_ITALICS_FONT); } else { - push_font(bold_font); + _push_def_font(BOLD_FONT); } pos = brk_end + 1; tag_stack.push_front(tag); @@ -3522,15 +3746,15 @@ void RichTextLabel::append_text(const String &p_bbcode) { //use italics font in_italics = true; if (in_bold) { - push_font(bold_italics_font); + _push_def_font(BOLD_ITALICS_FONT); } else { - push_font(italics_font); + _push_def_font(ITALICS_FONT); } pos = brk_end + 1; tag_stack.push_front(tag); } else if (tag == "code") { //use monospace font - push_font(mono_font); + _push_def_font(MONO_FONT); pos = brk_end + 1; tag_stack.push_front(tag); } else if (tag.begins_with("table=")) { @@ -3815,11 +4039,11 @@ void RichTextLabel::append_text(const String &p_bbcode) { tag_stack.push_front("hint"); } else if (tag.begins_with("dropcap")) { Vector<String> subtag = tag.substr(5, tag.length()).split(" "); - int fs = get_theme_font_size(SNAME("normal_font_size")) * 3; - Ref<Font> f = get_theme_font(SNAME("normal_font")); - Color color = get_theme_color(SNAME("default_color")); - Color outline_color = get_theme_color(SNAME("outline_color")); - int outline_size = get_theme_constant(SNAME("outline_size")); + int fs = theme_cache.normal_font_size * 3; + Ref<Font> f = theme_cache.normal_font; + Color color = theme_cache.default_color; + Color outline_color = theme_cache.font_outline_color; + int outline_size = theme_cache.outline_size; Rect2 dropcap_margins = Rect2(); for (int i = 0; i < subtag.size(); i++) { @@ -3937,14 +4161,14 @@ void RichTextLabel::append_text(const String &p_bbcode) { tag_stack.push_front(bbcode_name); } else if (tag.begins_with("color=")) { String color_str = tag.substr(6, tag.length()); - Color color = Color::from_string(color_str, base_color); + Color color = Color::from_string(color_str, theme_cache.default_color); push_color(color); pos = brk_end + 1; tag_stack.push_front("color"); } else if (tag.begins_with("outline_color=")) { String color_str = tag.substr(14, tag.length()); - Color color = Color::from_string(color_str, base_color); + Color color = Color::from_string(color_str, theme_cache.default_color); push_outline_color(color); pos = brk_end + 1; tag_stack.push_front("outline_color"); @@ -3955,17 +4179,21 @@ void RichTextLabel::append_text(const String &p_bbcode) { pos = brk_end + 1; tag_stack.push_front("font_size"); - } else if (tag.begins_with("opentype_features=")) { - String fnt_ftr = tag.substr(18, tag.length()); + } else if (tag.begins_with("opentype_features=") || tag.begins_with("otf=")) { + int value_pos = tag.find("="); + String fnt_ftr = tag.substr(value_pos + 1); Vector<String> subtag = fnt_ftr.split(","); if (subtag.size() > 0) { - Ref<Font> font = _find_font(current); - if (font.is_null()) { - font = normal_font; + Ref<Font> font = theme_cache.normal_font; + DefaultFont def_font = NORMAL_FONT; + + ItemFont *font_it = _find_font(current); + if (font_it) { + if (font_it->font.is_valid()) { + font = font_it->font; + def_font = font_it->def_font; + } } - Ref<FontVariation> fc; - fc.instantiate(); - fc->set_base_font(font); Dictionary features; for (int i = 0; i < subtag.size(); i++) { Vector<String> subtag_a = subtag[i].split("="); @@ -3975,11 +4203,21 @@ void RichTextLabel::append_text(const String &p_bbcode) { features[TS->name_to_tag(subtag_a[0])] = 1; } } + + Ref<FontVariation> fc; + fc.instantiate(); + + fc->set_base_font(font); fc->set_opentype_features(features); - push_font(fc); + + if (def_font != CUSTOM_FONT) { + _push_def_font_var(def_font, fc); + } else { + push_font(fc); + } } pos = brk_end + 1; - tag_stack.push_front("opentype_features"); + tag_stack.push_front(tag.substr(0, value_pos)); } else if (tag.begins_with("font=")) { String fnt = tag.substr(5, tag.length()); @@ -3995,8 +4233,21 @@ void RichTextLabel::append_text(const String &p_bbcode) { } else if (tag.begins_with("font ")) { Vector<String> subtag = tag.substr(2, tag.length()).split(" "); + Ref<Font> font = theme_cache.normal_font; + DefaultFont def_font = NORMAL_FONT; + + ItemFont *font_it = _find_font(current); + if (font_it) { + if (font_it->font.is_valid()) { + font = font_it->font; + def_font = font_it->def_font; + } + } + Ref<FontVariation> fc; fc.instantiate(); + + int fnt_size = -1; for (int i = 1; i < subtag.size(); i++) { Vector<String> subtag_a = subtag[i].split("=", true, 2); if (subtag_a.size() == 2) { @@ -4004,13 +4255,11 @@ void RichTextLabel::append_text(const String &p_bbcode) { String fnt = subtag_a[1]; Ref<Font> font_data = ResourceLoader::load(fnt, "Font"); if (font_data.is_valid()) { - fc->set_base_font(font_data); + font = font_data; + def_font = CUSTOM_FONT; } } else if (subtag_a[0] == "size" || subtag_a[0] == "s") { - int fnt_size = subtag_a[1].to_int(); - if (fnt_size > 0) { - push_font_size(fnt_size); - } + fnt_size = subtag_a[1].to_int(); } else if (subtag_a[0] == "glyph_spacing" || subtag_a[0] == "gl") { int spacing = subtag_a[1].to_int(); fc->set_spacing(TextServer::SPACING_GLYPH, spacing); @@ -4061,7 +4310,14 @@ void RichTextLabel::append_text(const String &p_bbcode) { } } } - push_font(fc); + fc->set_base_font(font); + + if (def_font != CUSTOM_FONT) { + _push_def_font_var(def_font, fc, fnt_size); + } else { + push_font(fc, fnt_size); + } + pos = brk_end + 1; tag_stack.push_front("font"); @@ -4102,7 +4358,13 @@ void RichTextLabel::append_text(const String &p_bbcode) { rate = rate_option->value.to_float(); } - push_shake(strength, rate); + bool connected = true; + OptionMap::Iterator connected_option = bbcode_options.find("connected"); + if (connected_option) { + connected = connected_option->value.to_int(); + } + + push_shake(strength, rate, connected); pos = brk_end + 1; tag_stack.push_front("shake"); set_process_internal(true); @@ -4119,7 +4381,13 @@ void RichTextLabel::append_text(const String &p_bbcode) { period = period_option->value.to_float(); } - push_wave(period, amplitude); + bool connected = true; + OptionMap::Iterator connected_option = bbcode_options.find("connected"); + if (connected_option) { + connected = connected_option->value.to_int(); + } + + push_wave(period, amplitude, connected); pos = brk_end + 1; tag_stack.push_front("wave"); set_process_internal(true); @@ -4136,7 +4404,13 @@ void RichTextLabel::append_text(const String &p_bbcode) { frequency = frequency_option->value.to_float(); } - push_tornado(frequency, radius); + bool connected = true; + OptionMap::Iterator connected_option = bbcode_options.find("connected"); + if (connected_option) { + connected = connected_option->value.to_int(); + } + + push_tornado(frequency, radius, connected); pos = brk_end + 1; tag_stack.push_front("tornado"); set_process_internal(true); @@ -4166,7 +4440,7 @@ void RichTextLabel::append_text(const String &p_bbcode) { } else if (tag.begins_with("bgcolor=")) { String color_str = tag.substr(8, tag.length()); - Color color = Color::from_string(color_str, base_color); + Color color = Color::from_string(color_str, theme_cache.default_color); push_bgcolor(color); pos = brk_end + 1; @@ -4174,7 +4448,7 @@ void RichTextLabel::append_text(const String &p_bbcode) { } else if (tag.begins_with("fgcolor=")) { String color_str = tag.substr(8, tag.length()); - Color color = Color::from_string(color_str, base_color); + Color color = Color::from_string(color_str, theme_cache.default_color); push_fgcolor(color); pos = brk_end + 1; @@ -4217,6 +4491,8 @@ void RichTextLabel::append_text(const String &p_bbcode) { } void RichTextLabel::scroll_to_paragraph(int p_paragraph) { + _validate_line_caches(); + if (p_paragraph <= 0) { vscroll->set_value(0); } else if (p_paragraph >= main->first_invalid_line.load()) { @@ -4234,6 +4510,8 @@ int RichTextLabel::get_visible_paragraph_count() const { if (!is_visible()) { return 0; } + + const_cast<RichTextLabel *>(this)->_validate_line_caches(); return visible_paragraph_count; } @@ -4242,6 +4520,8 @@ void RichTextLabel::scroll_to_line(int p_line) { vscroll->set_value(0); return; } + _validate_line_caches(); + int line_count = 0; int to_line = main->first_invalid_line.load(); for (int i = 0; i < to_line; i++) { @@ -4249,7 +4529,7 @@ void RichTextLabel::scroll_to_line(int p_line) { if ((line_count <= p_line) && (line_count + main->lines[i].text_buf->get_line_count() >= p_line)) { float line_offset = 0.f; for (int j = 0; j < p_line - line_count; j++) { - line_offset += main->lines[i].text_buf->get_line_size(j).y + get_theme_constant(SNAME("line_separation")); + line_offset += main->lines[i].text_buf->get_line_size(j).y + theme_cache.line_separation; } vscroll->set_value(main->lines[i].offset.y + line_offset); return; @@ -4260,6 +4540,8 @@ void RichTextLabel::scroll_to_line(int p_line) { } float RichTextLabel::get_line_offset(int p_line) { + _validate_line_caches(); + int line_count = 0; int to_line = main->first_invalid_line.load(); for (int i = 0; i < to_line; i++) { @@ -4267,7 +4549,7 @@ float RichTextLabel::get_line_offset(int p_line) { if ((line_count <= p_line) && (p_line <= line_count + main->lines[i].text_buf->get_line_count())) { float line_offset = 0.f; for (int j = 0; j < p_line - line_count; j++) { - line_offset += main->lines[i].text_buf->get_line_size(j).y + get_theme_constant(SNAME("line_separation")); + line_offset += main->lines[i].text_buf->get_line_size(j).y + theme_cache.line_separation; } return main->lines[i].offset.y + line_offset; } @@ -4277,6 +4559,8 @@ float RichTextLabel::get_line_offset(int p_line) { } float RichTextLabel::get_paragraph_offset(int p_paragraph) { + _validate_line_caches(); + int to_line = main->first_invalid_line.load(); if (0 <= p_paragraph && p_paragraph < to_line) { return main->lines[p_paragraph].offset.y; @@ -4285,6 +4569,8 @@ float RichTextLabel::get_paragraph_offset(int p_paragraph) { } int RichTextLabel::get_line_count() const { + const_cast<RichTextLabel *>(this)->_validate_line_caches(); + int line_count = 0; int to_line = main->first_invalid_line.load(); for (int i = 0; i < to_line; i++) { @@ -4298,10 +4584,16 @@ int RichTextLabel::get_visible_line_count() const { if (!is_visible()) { return 0; } + const_cast<RichTextLabel *>(this)->_validate_line_caches(); + return visible_line_count; } void RichTextLabel::set_selection_enabled(bool p_enabled) { + if (selection.enabled == p_enabled) { + return; + } + selection.enabled = p_enabled; if (!p_enabled) { if (selection.active) { @@ -4314,6 +4606,10 @@ void RichTextLabel::set_selection_enabled(bool p_enabled) { } void RichTextLabel::set_deselect_on_focus_loss_enabled(const bool p_enabled) { + if (deselect_on_focus_loss_enabled == p_enabled) { + return; + } + deselect_on_focus_loss_enabled = p_enabled; if (p_enabled && selection.active && !has_focus()) { deselect(); @@ -4439,7 +4735,7 @@ bool RichTextLabel::search(const String &p_string, bool p_from_selection, bool p if (!(p_search_previous && char_idx < 0) && _search_line(selection.from_frame, selection.from_line, p_string, char_idx, p_search_previous)) { scroll_to_line(selection.from_frame->line + selection.from_line); - update(); + queue_redraw(); return true; } char_idx = p_search_previous ? -1 : 0; @@ -4464,7 +4760,7 @@ bool RichTextLabel::search(const String &p_string, bool p_from_selection, bool p // Search for next element if (_search_table(parent_table, parent_element, p_string, p_search_previous)) { scroll_to_line(selection.from_frame->line + selection.from_line); - update(); + queue_redraw(); return true; } } @@ -4488,10 +4784,13 @@ bool RichTextLabel::search(const String &p_string, bool p_from_selection, bool p if (_search_line(main, current_line, p_string, char_idx, p_search_previous)) { scroll_to_line(current_line); - update(); + queue_redraw(); return true; } - p_search_previous ? current_line-- : current_line++; + + if (current_line != ending_line) { + p_search_previous ? current_line-- : current_line++; + } } if (p_from_selection && selection.active) { @@ -4598,7 +4897,7 @@ String RichTextLabel::get_selected_text() const { void RichTextLabel::deselect() { selection.active = false; - update(); + queue_redraw(); } void RichTextLabel::selection_copy() { @@ -4653,7 +4952,7 @@ void RichTextLabel::select_all() { selection.to_char = to_frame->lines[to_line].char_count; selection.to_item = to_item; selection.active = true; - update(); + queue_redraw(); } bool RichTextLabel::is_selection_enabled() const { @@ -4681,6 +4980,10 @@ int RichTextLabel::get_selection_to() const { } void RichTextLabel::set_text(const String &p_bbcode) { + if (text == p_bbcode) { + return; + } + text = p_bbcode; if (use_bbcode) { parse_bbcode(p_bbcode); @@ -4700,7 +5003,14 @@ void RichTextLabel::set_use_bbcode(bool p_enable) { } use_bbcode = p_enable; notify_property_list_changed(); - set_text(text); + + const String current_text = text; + if (use_bbcode) { + parse_bbcode(current_text); + } else { // raw text + clear(); + add_text(current_text); + } } bool RichTextLabel::is_using_bbcode() const { @@ -4737,7 +5047,7 @@ void RichTextLabel::set_text_direction(Control::TextDirection p_text_direction) text_direction = p_text_direction; main->first_invalid_line.store(0); //invalidate ALL _validate_line_caches(); - update(); + queue_redraw(); } } @@ -4748,7 +5058,7 @@ void RichTextLabel::set_structured_text_bidi_override(TextServer::StructuredText st_parser = p_parser; main->first_invalid_line.store(0); //invalidate ALL _validate_line_caches(); - update(); + queue_redraw(); } } @@ -4763,7 +5073,7 @@ void RichTextLabel::set_structured_text_bidi_override_options(Array p_args) { st_args = p_args; main->first_invalid_line.store(0); //invalidate ALL _validate_line_caches(); - update(); + queue_redraw(); } } @@ -4782,7 +5092,7 @@ void RichTextLabel::set_language(const String &p_language) { language = p_language; main->first_invalid_line.store(0); //invalidate ALL _validate_line_caches(); - update(); + queue_redraw(); } } @@ -4797,7 +5107,7 @@ void RichTextLabel::set_autowrap_mode(TextServer::AutowrapMode p_mode) { autowrap_mode = p_mode; main->first_invalid_line = 0; //invalidate ALL _validate_line_caches(); - update(); + queue_redraw(); } } @@ -4805,27 +5115,31 @@ TextServer::AutowrapMode RichTextLabel::get_autowrap_mode() const { return autowrap_mode; } -void RichTextLabel::set_percent_visible(float p_percent) { - if (percent_visible != p_percent) { +void RichTextLabel::set_visible_ratio(float p_ratio) { + if (visible_ratio != p_ratio) { _stop_thread(); - if (p_percent < 0 || p_percent >= 1) { + if (p_ratio >= 1.0) { visible_characters = -1; - percent_visible = 1; + visible_ratio = 1.0; + } else if (p_ratio < 0.0) { + visible_characters = 0; + visible_ratio = 0.0; } else { - visible_characters = get_total_character_count() * p_percent; - percent_visible = p_percent; + visible_characters = get_total_character_count() * p_ratio; + visible_ratio = p_ratio; } + if (visible_chars_behavior == TextServer::VC_CHARS_BEFORE_SHAPING) { - main->first_invalid_line.store(0); //invalidate ALL + main->first_invalid_line.store(0); // Invalidate ALL. _validate_line_caches(); } - update(); + queue_redraw(); } } -float RichTextLabel::get_percent_visible() const { - return percent_visible; +float RichTextLabel::get_visible_ratio() const { + return visible_ratio; } void RichTextLabel::set_effects(Array p_effects) { @@ -4843,25 +5157,33 @@ void RichTextLabel::install_effect(const Variant effect) { Ref<RichTextEffect> rteffect; rteffect = effect; - if (rteffect.is_valid()) { - custom_effects.push_back(effect); - if ((!text.is_empty()) && use_bbcode) { - parse_bbcode(text); - } + ERR_FAIL_COND_MSG(rteffect.is_null(), "Invalid RichTextEffect resource."); + custom_effects.push_back(effect); + if ((!text.is_empty()) && use_bbcode) { + parse_bbcode(text); } } int RichTextLabel::get_content_height() const { + const_cast<RichTextLabel *>(this)->_validate_line_caches(); + int total_height = 0; int to_line = main->first_invalid_line.load(); if (to_line) { MutexLock lock(main->lines[to_line - 1].text_buf->get_mutex()); - total_height = main->lines[to_line - 1].offset.y + main->lines[to_line - 1].text_buf->get_size().y + main->lines[to_line - 1].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")); + if (theme_cache.line_separation < 0) { + // Do not apply to the last line to avoid cutting text. + total_height = main->lines[to_line - 1].offset.y + main->lines[to_line - 1].text_buf->get_size().y + (main->lines[to_line - 1].text_buf->get_line_count() - 1) * theme_cache.line_separation; + } else { + total_height = main->lines[to_line - 1].offset.y + main->lines[to_line - 1].text_buf->get_size().y + main->lines[to_line - 1].text_buf->get_line_count() * theme_cache.line_separation; + } } return total_height; } int RichTextLabel::get_content_width() const { + const_cast<RichTextLabel *>(this)->_validate_line_caches(); + int total_width = 0; int to_line = main->first_invalid_line.load(); for (int i = 0; i < to_line; i++) { @@ -4890,7 +5212,7 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("add_image", "image", "width", "height", "color", "inline_align"), &RichTextLabel::add_image, DEFVAL(0), DEFVAL(0), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(INLINE_ALIGNMENT_CENTER)); ClassDB::bind_method(D_METHOD("newline"), &RichTextLabel::add_newline); ClassDB::bind_method(D_METHOD("remove_line", "line"), &RichTextLabel::remove_line); - ClassDB::bind_method(D_METHOD("push_font", "font"), &RichTextLabel::push_font); + ClassDB::bind_method(D_METHOD("push_font", "font", "font_size"), &RichTextLabel::push_font); ClassDB::bind_method(D_METHOD("push_font_size", "font_size"), &RichTextLabel::push_font_size); ClassDB::bind_method(D_METHOD("push_normal"), &RichTextLabel::push_normal); ClassDB::bind_method(D_METHOD("push_bold"), &RichTextLabel::push_bold); @@ -4997,8 +5319,8 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("get_visible_characters_behavior"), &RichTextLabel::get_visible_characters_behavior); ClassDB::bind_method(D_METHOD("set_visible_characters_behavior", "behavior"), &RichTextLabel::set_visible_characters_behavior); - ClassDB::bind_method(D_METHOD("set_percent_visible", "percent_visible"), &RichTextLabel::set_percent_visible); - ClassDB::bind_method(D_METHOD("get_percent_visible"), &RichTextLabel::get_percent_visible); + ClassDB::bind_method(D_METHOD("set_visible_ratio", "ratio"), &RichTextLabel::set_visible_ratio); + ClassDB::bind_method(D_METHOD("get_visible_ratio"), &RichTextLabel::get_visible_ratio); ClassDB::bind_method(D_METHOD("get_character_line", "character"), &RichTextLabel::get_character_line); ClassDB::bind_method(D_METHOD("get_character_paragraph", "character"), &RichTextLabel::get_character_paragraph); @@ -5030,30 +5352,35 @@ void RichTextLabel::_bind_methods() { // Note: set "bbcode_enabled" first, to avoid unnecessary "text" resets. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bbcode_enabled"), "set_use_bbcode", "is_using_bbcode"); - - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "threaded"), "set_threaded", "is_threaded"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "progress_bar_delay", PROPERTY_HINT_NONE, "suffix:ms"), "set_progress_bar_delay", "get_progress_bar_delay"); - - ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_size", PROPERTY_HINT_RANGE, "0,24,1"), "set_tab_size", "get_tab_size"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fit_content_height"), "set_fit_content_height", "is_fit_content_height_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_active"), "set_scroll_active", "is_scroll_active"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_following"), "set_scroll_follow", "is_scroll_following"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selection_enabled"), "set_selection_enabled", "is_selection_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, "deselect_on_focus_loss_enabled"), "set_deselect_on_focus_loss_enabled", "is_deselect_on_focus_loss_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Off,Arbitrary,Word,Word (Smart)"), "set_autowrap_mode", "get_autowrap_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_size", PROPERTY_HINT_RANGE, "0,24,1"), "set_tab_size", "get_tab_size"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_keys_enabled"), "set_shortcut_keys_enabled", "is_shortcut_keys_enabled"); + + ADD_GROUP("Markup", ""); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "custom_effects", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "RichTextEffect"), (PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE)), "set_effects", "get_effects"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "meta_underlined"), "set_meta_underline", "is_meta_underlined"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hint_underlined"), "set_hint_underline", "is_hint_underlined"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Off,Arbitrary,Word,Word (Smart)"), "set_autowrap_mode", "get_autowrap_mode"); - // Note: "visible_characters" and "percent_visible" should be set after "text" to be correctly applied. + ADD_GROUP("Threading", ""); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "threaded"), "set_threaded", "is_threaded"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "progress_bar_delay", PROPERTY_HINT_NONE, "suffix:ms"), "set_progress_bar_delay", "get_progress_bar_delay"); + + ADD_GROUP("Text Selection", ""); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selection_enabled"), "set_selection_enabled", "is_selection_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, "deselect_on_focus_loss_enabled"), "set_deselect_on_focus_loss_enabled", "is_deselect_on_focus_loss_enabled"); + + ADD_GROUP("Displayed Text", ""); + // Note: "visible_characters" and "visible_ratio" should be set after "text" to be correctly applied. ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters", PROPERTY_HINT_RANGE, "-1,128000,1"), "set_visible_characters", "get_visible_characters"); ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters_behavior", PROPERTY_HINT_ENUM, "Characters Before Shaping,Characters After Shaping,Glyphs (Layout Direction),Glyphs (Left-to-Right),Glyphs (Right-to-Left)"), "set_visible_characters_behavior", "get_visible_characters_behavior"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "percent_visible", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_percent_visible", "get_percent_visible"); - - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_keys_enabled"), "set_shortcut_keys_enabled", "is_shortcut_keys_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visible_ratio", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_visible_ratio", "get_visible_ratio"); ADD_GROUP("BiDi", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction"); @@ -5112,7 +5439,7 @@ void RichTextLabel::set_visible_characters_behavior(TextServer::VisibleCharacter visible_chars_behavior = p_behavior; main->first_invalid_line.store(0); //invalidate ALL _validate_line_caches(); - update(); + queue_redraw(); } } @@ -5122,18 +5449,18 @@ void RichTextLabel::set_visible_characters(int p_visible) { visible_characters = p_visible; if (p_visible == -1) { - percent_visible = 1; + visible_ratio = 1; } else { int total_char_count = get_total_character_count(); if (total_char_count > 0) { - percent_visible = (float)p_visible / (float)total_char_count; + visible_ratio = (float)p_visible / (float)total_char_count; } } if (visible_chars_behavior == TextServer::VC_CHARS_BEFORE_SHAPING) { main->first_invalid_line.store(0); //invalidate ALL _validate_line_caches(); } - update(); + queue_redraw(); } } @@ -5142,6 +5469,8 @@ int RichTextLabel::get_visible_characters() const { } int RichTextLabel::get_character_line(int p_char) { + _validate_line_caches(); + int line_count = 0; int to_line = main->first_invalid_line.load(); for (int i = 0; i < to_line; i++) { @@ -5162,6 +5491,8 @@ int RichTextLabel::get_character_line(int p_char) { } int RichTextLabel::get_character_paragraph(int p_char) { + _validate_line_caches(); + int para_count = 0; int to_line = main->first_invalid_line.load(); for (int i = 0; i < to_line; i++) { @@ -5193,6 +5524,8 @@ int RichTextLabel::get_total_character_count() const { } int RichTextLabel::get_total_glyph_count() const { + const_cast<RichTextLabel *>(this)->_validate_line_caches(); + int tg = 0; Item *it = main; while (it) { @@ -5210,13 +5543,16 @@ int RichTextLabel::get_total_glyph_count() const { } void RichTextLabel::set_fixed_size_to_width(int p_width) { + if (fixed_width == p_width) { + return; + } + fixed_width = p_width; update_minimum_size(); } Size2 RichTextLabel::get_minimum_size() const { - Ref<StyleBox> style = get_theme_stylebox(SNAME("normal")); - Size2 size = style->get_minimum_size(); + Size2 size = theme_cache.normal_style->get_minimum_size(); if (fixed_width != -1) { size.x += fixed_width; diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 3b6175e9cf..71123602ad 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -82,7 +82,17 @@ public: MENU_SELECT_ALL, }; + enum DefaultFont { + NORMAL_FONT, + BOLD_FONT, + ITALICS_FONT, + BOLD_ITALICS_FONT, + MONO_FONT, + CUSTOM_FONT, + }; + protected: + virtual void _update_theme_item_cache() override; void _notification(int p_what); static void _bind_methods(); @@ -177,7 +187,11 @@ private: }; struct ItemFont : public Item { + DefaultFont def_font = CUSTOM_FONT; Ref<Font> font; + bool variation = false; + bool def_size = false; + int font_size = 0; ItemFont() { type = ITEM_FONT; } }; @@ -270,6 +284,7 @@ private: struct ItemFX : public Item { double elapsed_time = 0.f; + bool connected = true; }; struct ItemShake : public ItemFX { @@ -439,11 +454,11 @@ private: void _menu_option(int p_option); int visible_characters = -1; - float percent_visible = 1.0; + float visible_ratio = 1.0; TextServer::VisibleCharactersBehavior visible_chars_behavior = TextServer::VC_CHARS_BEFORE_SHAPING; bool _is_click_inside_selection() const; - void _find_click(ItemFrame *p_frame, const Point2i &p_click, ItemFrame **r_click_frame = nullptr, int *r_click_line = nullptr, Item **r_click_item = nullptr, int *r_click_char = nullptr, bool *r_outside = nullptr); + void _find_click(ItemFrame *p_frame, const Point2i &p_click, ItemFrame **r_click_frame = nullptr, int *r_click_line = nullptr, Item **r_click_item = nullptr, int *r_click_char = nullptr, bool *r_outside = nullptr, bool p_meta = false); String _get_line_text(ItemFrame *p_frame, int p_line, Selection p_sel) const; bool _search_line(ItemFrame *p_frame, int p_line, const String &p_string, int p_char_idx, bool p_reverse_search); @@ -454,15 +469,16 @@ private: void _update_line_font(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size); int _draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, int p_shadow_outline_size, const Point2 &p_shadow_ofs, int &r_processed_glyphs); - float _find_click_in_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Point2i &p_click, ItemFrame **r_click_frame = nullptr, int *r_click_line = nullptr, Item **r_click_item = nullptr, int *r_click_char = nullptr, bool p_table = false); + float _find_click_in_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Point2i &p_click, ItemFrame **r_click_frame = nullptr, int *r_click_line = nullptr, Item **r_click_item = nullptr, int *r_click_char = nullptr, bool p_table = false, bool p_meta = false); String _roman(int p_num, bool p_capitalize) const; String _letters(int p_num, bool p_capitalize) const; + Item *_find_indentable(Item *p_item); Item *_get_item_at_pos(Item *p_item_from, Item *p_item_to, int p_position); void _find_frame(Item *p_item, ItemFrame **r_frame, int *r_line); - int _find_font_size(Item *p_item); - Ref<Font> _find_font(Item *p_item); + ItemFontSize *_find_font_size(Item *p_item); + ItemFont *_find_font(Item *p_item); int _find_outline_size(Item *p_item, int p_default); ItemList *_find_list_item(Item *p_item); ItemDropcap *_find_dc_item(Item *p_item); @@ -510,6 +526,46 @@ private: bool fit_content_height = false; + struct ThemeCache { + Ref<StyleBox> normal_style; + Ref<StyleBox> focus_style; + Ref<StyleBox> progress_bg_style; + Ref<StyleBox> progress_fg_style; + + int line_separation; + + Ref<Font> normal_font; + int normal_font_size; + + Color default_color; + Color font_selected_color; + Color selection_color; + Color font_outline_color; + Color font_shadow_color; + int shadow_outline_size; + int shadow_offset_x; + int shadow_offset_y; + int outline_size; + Color outline_color; + + Ref<Font> bold_font; + int bold_font_size; + Ref<Font> bold_italics_font; + int bold_italics_font_size; + Ref<Font> italics_font; + int italics_font_size; + Ref<Font> mono_font; + int mono_font_size; + + int table_h_separation; + int table_v_separation; + Color table_odd_row_bg; + Color table_even_row_bg; + Color table_border; + + float base_scale = 1.0; + } theme_cache; + public: String get_parsed_text() const; void add_text(const String &p_text); @@ -517,7 +573,9 @@ public: void add_newline(); bool remove_line(const int p_line); void push_dropcap(const String &p_string, const Ref<Font> &p_font, int p_size, const Rect2 &p_dropcap_margins = Rect2(), const Color &p_color = Color(1, 1, 1), int p_ol_size = 0, const Color &p_ol_color = Color(0, 0, 0, 0)); - void push_font(const Ref<Font> &p_font); + void _push_def_font(DefaultFont p_def_font); + void _push_def_font_var(DefaultFont p_def_font, const Ref<Font> &p_font, int p_size = -1); + void push_font(const Ref<Font> &p_font, int p_size = 0); void push_font_size(int p_font_size); void push_outline_size(int p_font_size); void push_normal(); @@ -536,9 +594,9 @@ public: void push_hint(const String &p_string); void push_table(int p_columns, InlineAlignment p_alignment = INLINE_ALIGNMENT_TOP); void push_fade(int p_start_index, int p_length); - void push_shake(int p_strength, float p_rate); - void push_wave(float p_frequency, float p_amplitude); - void push_tornado(float p_frequency, float p_radius); + void push_shake(int p_strength, float p_rate, bool p_connected); + void push_wave(float p_frequency, float p_amplitude, bool p_connected); + void push_tornado(float p_frequency, float p_radius, bool p_connected); void push_rainbow(float p_saturation, float p_value, float p_frequency); void push_bgcolor(const Color &p_color); void push_fgcolor(const Color &p_color); @@ -658,8 +716,8 @@ public: int get_total_character_count() const; int get_total_glyph_count() const; - void set_percent_visible(float p_percent); - float get_percent_visible() const; + void set_visible_ratio(float p_ratio); + float get_visible_ratio() const; TextServer::VisibleCharactersBehavior get_visible_characters_behavior() const; void set_visible_characters_behavior(TextServer::VisibleCharactersBehavior p_behavior); diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp index f387f91a8b..6c05b171e3 100644 --- a/scene/gui/scroll_bar.cpp +++ b/scene/gui/scroll_bar.cpp @@ -70,8 +70,8 @@ void ScrollBar::gui_input(const Ref<InputEvent> &p_event) { if (b->is_pressed()) { double ofs = orientation == VERTICAL ? b->get_position().y : b->get_position().x; - Ref<Texture2D> decr = get_theme_icon(SNAME("decrement")); - Ref<Texture2D> incr = get_theme_icon(SNAME("increment")); + Ref<Texture2D> decr = theme_cache.decrement_icon; + Ref<Texture2D> incr = theme_cache.increment_icon; double decr_size = orientation == VERTICAL ? decr->get_height() : decr->get_width(); double incr_size = orientation == VERTICAL ? incr->get_height() : incr->get_width(); @@ -82,14 +82,14 @@ void ScrollBar::gui_input(const Ref<InputEvent> &p_event) { if (ofs < decr_size) { decr_active = true; set_value(get_value() - (custom_step >= 0 ? custom_step : get_step())); - update(); + queue_redraw(); return; } if (ofs > total - incr_size) { incr_active = true; set_value(get_value() + (custom_step >= 0 ? custom_step : get_step())); - update(); + queue_redraw(); return; } @@ -117,7 +117,7 @@ void ScrollBar::gui_input(const Ref<InputEvent> &p_event) { drag.active = true; drag.pos_at_click = grabber_ofs + ofs; drag.value_at_click = get_as_ratio(); - update(); + queue_redraw(); } else { if (scrolling) { target_scroll = CLAMP(target_scroll + get_page(), get_min(), get_max() - get_page()); @@ -137,7 +137,7 @@ void ScrollBar::gui_input(const Ref<InputEvent> &p_event) { incr_active = false; decr_active = false; drag.active = false; - update(); + queue_redraw(); } } @@ -146,7 +146,7 @@ void ScrollBar::gui_input(const Ref<InputEvent> &p_event) { if (drag.active) { double ofs = orientation == VERTICAL ? m->get_position().y : m->get_position().x; - Ref<Texture2D> decr = get_theme_icon(SNAME("decrement")); + Ref<Texture2D> decr = theme_cache.decrement_icon; double decr_size = orientation == VERTICAL ? decr->get_height() : decr->get_width(); ofs -= decr_size; @@ -156,8 +156,8 @@ void ScrollBar::gui_input(const Ref<InputEvent> &p_event) { set_as_ratio(drag.value_at_click + diff); } else { double ofs = orientation == VERTICAL ? m->get_position().y : m->get_position().x; - Ref<Texture2D> decr = get_theme_icon(SNAME("decrement")); - Ref<Texture2D> incr = get_theme_icon(SNAME("increment")); + Ref<Texture2D> decr = theme_cache.decrement_icon; + Ref<Texture2D> incr = theme_cache.increment_icon; double decr_size = orientation == VERTICAL ? decr->get_height() : decr->get_width(); double incr_size = orientation == VERTICAL ? incr->get_height() : incr->get_width(); @@ -177,7 +177,7 @@ void ScrollBar::gui_input(const Ref<InputEvent> &p_event) { if (new_hilite != highlight) { highlight = new_hilite; - update(); + queue_redraw(); } } } @@ -217,6 +217,24 @@ void ScrollBar::gui_input(const Ref<InputEvent> &p_event) { } } +void ScrollBar::_update_theme_item_cache() { + Range::_update_theme_item_cache(); + + theme_cache.scroll_style = get_theme_stylebox(SNAME("scroll")); + theme_cache.scroll_focus_style = get_theme_stylebox(SNAME("scroll_focus")); + theme_cache.scroll_offset_style = get_theme_stylebox(SNAME("hscroll")); + theme_cache.grabber_style = get_theme_stylebox(SNAME("grabber")); + theme_cache.grabber_hl_style = get_theme_stylebox(SNAME("grabber_highlight")); + theme_cache.grabber_pressed_style = get_theme_stylebox(SNAME("grabber_pressed")); + + theme_cache.increment_icon = get_theme_icon(SNAME("increment")); + theme_cache.increment_hl_icon = get_theme_icon(SNAME("increment_highlight")); + theme_cache.increment_pressed_icon = get_theme_icon(SNAME("increment_pressed")); + theme_cache.decrement_icon = get_theme_icon(SNAME("decrement")); + theme_cache.decrement_hl_icon = get_theme_icon(SNAME("decrement_highlight")); + theme_cache.decrement_pressed_icon = get_theme_icon(SNAME("decrement_pressed")); +} + void ScrollBar::_notification(int p_what) { switch (p_what) { case NOTIFICATION_DRAW: { @@ -225,30 +243,30 @@ void ScrollBar::_notification(int p_what) { Ref<Texture2D> decr, incr; if (decr_active) { - decr = get_theme_icon(SNAME("decrement_pressed")); + decr = theme_cache.decrement_pressed_icon; } else if (highlight == HIGHLIGHT_DECR) { - decr = get_theme_icon(SNAME("decrement_highlight")); + decr = theme_cache.decrement_hl_icon; } else { - decr = get_theme_icon(SNAME("decrement")); + decr = theme_cache.decrement_icon; } if (incr_active) { - incr = get_theme_icon(SNAME("increment_pressed")); + incr = theme_cache.increment_pressed_icon; } else if (highlight == HIGHLIGHT_INCR) { - incr = get_theme_icon(SNAME("increment_highlight")); + incr = theme_cache.increment_hl_icon; } else { - incr = get_theme_icon(SNAME("increment")); + incr = theme_cache.increment_icon; } - Ref<StyleBox> bg = has_focus() ? get_theme_stylebox(SNAME("scroll_focus")) : get_theme_stylebox(SNAME("scroll")); + Ref<StyleBox> bg = has_focus() ? theme_cache.scroll_focus_style : theme_cache.scroll_style; Ref<StyleBox> grabber; if (drag.active) { - grabber = get_theme_stylebox(SNAME("grabber_pressed")); + grabber = theme_cache.grabber_pressed_style; } else if (highlight == HIGHLIGHT_RANGE) { - grabber = get_theme_stylebox(SNAME("grabber_highlight")); + grabber = theme_cache.grabber_hl_style; } else { - grabber = get_theme_stylebox(SNAME("grabber")); + grabber = theme_cache.grabber_style; } Point2 ofs; @@ -303,7 +321,7 @@ void ScrollBar::_notification(int p_what) { if (drag_node) { drag_node->connect("gui_input", callable_mp(this, &ScrollBar::_drag_node_input)); - drag_node->connect("tree_exiting", callable_mp(this, &ScrollBar::_drag_node_exit), varray(), CONNECT_ONESHOT); + drag_node->connect("tree_exiting", callable_mp(this, &ScrollBar::_drag_node_exit), CONNECT_ONE_SHOT); } } break; @@ -408,13 +426,13 @@ void ScrollBar::_notification(int p_what) { case NOTIFICATION_MOUSE_EXIT: { highlight = HIGHLIGHT_NONE; - update(); + queue_redraw(); } break; } } double ScrollBar::get_grabber_min_size() const { - Ref<StyleBox> grabber = get_theme_stylebox(SNAME("grabber")); + Ref<StyleBox> grabber = theme_cache.grabber_style; Size2 gminsize = grabber->get_minimum_size() + grabber->get_center_size(); return (orientation == VERTICAL) ? gminsize.height : gminsize.width; } @@ -435,17 +453,17 @@ double ScrollBar::get_area_size() const { switch (orientation) { case VERTICAL: { double area = get_size().height; - area -= get_theme_stylebox(SNAME("scroll"))->get_minimum_size().height; - area -= get_theme_icon(SNAME("increment"))->get_height(); - area -= get_theme_icon(SNAME("decrement"))->get_height(); + area -= theme_cache.scroll_style->get_minimum_size().height; + area -= theme_cache.increment_icon->get_height(); + area -= theme_cache.decrement_icon->get_height(); area -= get_grabber_min_size(); return area; } break; case HORIZONTAL: { double area = get_size().width; - area -= get_theme_stylebox(SNAME("scroll"))->get_minimum_size().width; - area -= get_theme_icon(SNAME("increment"))->get_width(); - area -= get_theme_icon(SNAME("decrement"))->get_width(); + area -= theme_cache.scroll_style->get_minimum_size().width; + area -= theme_cache.increment_icon->get_width(); + area -= theme_cache.decrement_icon->get_width(); area -= get_grabber_min_size(); return area; } break; @@ -459,13 +477,13 @@ double ScrollBar::get_area_offset() const { double ofs = 0.0; if (orientation == VERTICAL) { - ofs += get_theme_stylebox(SNAME("hscroll"))->get_margin(SIDE_TOP); - ofs += get_theme_icon(SNAME("decrement"))->get_height(); + ofs += theme_cache.scroll_offset_style->get_margin(SIDE_TOP); + ofs += theme_cache.decrement_icon->get_height(); } if (orientation == HORIZONTAL) { - ofs += get_theme_stylebox(SNAME("hscroll"))->get_margin(SIDE_LEFT); - ofs += get_theme_icon(SNAME("decrement"))->get_width(); + ofs += theme_cache.scroll_offset_style->get_margin(SIDE_LEFT); + ofs += theme_cache.decrement_icon->get_width(); } return ofs; @@ -476,9 +494,9 @@ double ScrollBar::get_grabber_offset() const { } Size2 ScrollBar::get_minimum_size() const { - Ref<Texture2D> incr = get_theme_icon(SNAME("increment")); - Ref<Texture2D> decr = get_theme_icon(SNAME("decrement")); - Ref<StyleBox> bg = get_theme_stylebox(SNAME("scroll")); + Ref<Texture2D> incr = theme_cache.increment_icon; + Ref<Texture2D> decr = theme_cache.decrement_icon; + Ref<StyleBox> bg = theme_cache.scroll_style; Size2 minsize; if (orientation == VERTICAL) { @@ -595,7 +613,7 @@ void ScrollBar::set_drag_node(const NodePath &p_path) { if (drag_node) { drag_node->connect("gui_input", callable_mp(this, &ScrollBar::_drag_node_input)); - drag_node->connect("tree_exiting", callable_mp(this, &ScrollBar::_drag_node_exit), varray(), CONNECT_ONESHOT); + drag_node->connect("tree_exiting", callable_mp(this, &ScrollBar::_drag_node_exit), CONNECT_ONE_SHOT); } } } diff --git a/scene/gui/scroll_bar.h b/scene/gui/scroll_bar.h index 651edd1a74..13ca62d7ff 100644 --- a/scene/gui/scroll_bar.h +++ b/scene/gui/scroll_bar.h @@ -86,14 +86,31 @@ class ScrollBar : public Range { double target_scroll = 0.0; bool smooth_scroll_enabled = false; + struct ThemeCache { + Ref<StyleBox> scroll_style; + Ref<StyleBox> scroll_focus_style; + Ref<StyleBox> scroll_offset_style; + Ref<StyleBox> grabber_style; + Ref<StyleBox> grabber_hl_style; + Ref<StyleBox> grabber_pressed_style; + + Ref<Texture2D> increment_icon; + Ref<Texture2D> increment_hl_icon; + Ref<Texture2D> increment_pressed_icon; + Ref<Texture2D> decrement_icon; + Ref<Texture2D> decrement_hl_icon; + Ref<Texture2D> decrement_pressed_icon; + } theme_cache; + void _drag_node_exit(); void _drag_node_input(const Ref<InputEvent> &p_input); virtual void gui_input(const Ref<InputEvent> &p_event) override; protected: - void _notification(int p_what); + virtual void _update_theme_item_cache() override; + void _notification(int p_what); static void _bind_methods(); public: @@ -128,4 +145,4 @@ public: ScrollBar(VERTICAL) { set_h_size_flags(0); } }; -#endif +#endif // SCROLL_BAR_H diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp index 871cc520e9..761072c5bc 100644 --- a/scene/gui/scroll_container.cpp +++ b/scene/gui/scroll_container.cpp @@ -35,12 +35,15 @@ #include "scene/main/window.h" Size2 ScrollContainer::get_minimum_size() const { - Ref<StyleBox> sb = get_theme_stylebox(SNAME("bg")); Size2 min_size; + // Calculated in this function, as it needs to traverse all child controls once to calculate; + // and needs to be calculated before being used by update_scrollbars(). + largest_child_min_size = Size2(); + for (int i = 0; i < get_child_count(); i++) { Control *c = Object::cast_to<Control>(get_child(i)); - if (!c) { + if (!c || !c->is_visible()) { continue; } if (c->is_set_as_top_level()) { @@ -49,26 +52,40 @@ Size2 ScrollContainer::get_minimum_size() const { if (c == h_scroll || c == v_scroll) { continue; } - Size2 minsize = c->get_combined_minimum_size(); - if (horizontal_scroll_mode == SCROLL_MODE_DISABLED) { - min_size.x = MAX(min_size.x, minsize.x); - } - if (vertical_scroll_mode == SCROLL_MODE_DISABLED) { - min_size.y = MAX(min_size.y, minsize.y); - } + Size2 child_min_size = c->get_combined_minimum_size(); + + largest_child_min_size.x = MAX(largest_child_min_size.x, child_min_size.x); + largest_child_min_size.y = MAX(largest_child_min_size.y, child_min_size.y); } - if (h_scroll->is_visible_in_tree()) { + if (horizontal_scroll_mode == SCROLL_MODE_DISABLED) { + min_size.x = MAX(min_size.x, largest_child_min_size.x); + } + if (vertical_scroll_mode == SCROLL_MODE_DISABLED) { + min_size.y = MAX(min_size.y, largest_child_min_size.y); + } + + bool h_scroll_show = horizontal_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || (horizontal_scroll_mode == SCROLL_MODE_AUTO && largest_child_min_size.x > min_size.x); + bool v_scroll_show = vertical_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || (vertical_scroll_mode == SCROLL_MODE_AUTO && largest_child_min_size.y > min_size.y); + + if (h_scroll_show && h_scroll->get_parent() == this) { min_size.y += h_scroll->get_minimum_size().y; } - if (v_scroll->is_visible_in_tree()) { + if (v_scroll_show && v_scroll->get_parent() == this) { min_size.x += v_scroll->get_minimum_size().x; } - min_size += sb->get_minimum_size(); + + min_size += theme_cache.panel_style->get_minimum_size(); return min_size; } +void ScrollContainer::_update_theme_item_cache() { + Container::_update_theme_item_cache(); + + theme_cache.panel_style = get_theme_stylebox(SNAME("panel")); +} + void ScrollContainer::_cancel_drag() { set_physics_process_internal(false); drag_touching_deaccel = false; @@ -254,14 +271,13 @@ void ScrollContainer::ensure_control_visible(Control *p_control) { set_v_scroll(get_v_scroll() + (diff.y - global_rect.position.y)); } -void ScrollContainer::_update_dimensions() { - child_max_size = Size2(0, 0); +void ScrollContainer::_reposition_children() { + update_scrollbars(); Size2 size = get_size(); Point2 ofs; - Ref<StyleBox> sb = get_theme_stylebox(SNAME("bg")); - size -= sb->get_minimum_size(); - ofs += sb->get_offset(); + size -= theme_cache.panel_style->get_minimum_size(); + ofs += theme_cache.panel_style->get_offset(); bool rtl = is_layout_rtl(); if (h_scroll->is_visible_in_tree() && h_scroll->get_parent() == this) { //scrolls may have been moved out for reasons @@ -274,7 +290,7 @@ void ScrollContainer::_update_dimensions() { for (int i = 0; i < get_child_count(); i++) { Control *c = Object::cast_to<Control>(get_child(i)); - if (!c) { + if (!c || !c->is_visible()) { continue; } if (c->is_set_as_top_level()) { @@ -284,25 +300,13 @@ void ScrollContainer::_update_dimensions() { continue; } Size2 minsize = c->get_combined_minimum_size(); - child_max_size.x = MAX(child_max_size.x, minsize.x); - child_max_size.y = MAX(child_max_size.y, minsize.y); Rect2 r = Rect2(-Size2(get_h_scroll(), get_v_scroll()), minsize); - if (horizontal_scroll_mode == SCROLL_MODE_DISABLED || (!h_scroll->is_visible_in_tree() && c->get_h_size_flags() & SIZE_EXPAND)) { - r.position.x = 0; - if (c->get_h_size_flags() & SIZE_EXPAND) { - r.size.width = MAX(size.width, minsize.width); - } else { - r.size.width = minsize.width; - } + if (c->get_h_size_flags() & SIZE_EXPAND) { + r.size.width = MAX(size.width, minsize.width); } - if (vertical_scroll_mode == SCROLL_MODE_DISABLED || (!v_scroll->is_visible_in_tree() && c->get_v_size_flags() & SIZE_EXPAND)) { - r.position.y = 0; - if (c->get_v_size_flags() & SIZE_EXPAND) { - r.size.height = MAX(size.height, minsize.height); - } else { - r.size.height = minsize.height; - } + if (c->get_v_size_flags() & SIZE_EXPAND) { + r.size.height = MAX(size.height, minsize.height); } r.position += ofs; if (rtl && v_scroll->is_visible_in_tree() && v_scroll->get_parent() == this) { @@ -312,7 +316,7 @@ void ScrollContainer::_update_dimensions() { fit_child_in_rect(c, r); } - update(); + queue_redraw(); } void ScrollContainer::_notification(int p_what) { @@ -329,18 +333,15 @@ void ScrollContainer::_notification(int p_what) { Viewport *viewport = get_viewport(); ERR_FAIL_COND(!viewport); viewport->connect("gui_focus_changed", callable_mp(this, &ScrollContainer::_gui_focus_changed)); - _update_dimensions(); + _reposition_children(); } break; case NOTIFICATION_SORT_CHILDREN: { - _update_dimensions(); + _reposition_children(); } break; case NOTIFICATION_DRAW: { - Ref<StyleBox> sb = get_theme_stylebox(SNAME("bg")); - draw_style_box(sb, Rect2(Vector2(), get_size())); - - update_scrollbars(); + draw_style_box(theme_cache.panel_style, Rect2(Vector2(), get_size())); } break; case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { @@ -415,39 +416,27 @@ void ScrollContainer::_notification(int p_what) { void ScrollContainer::update_scrollbars() { Size2 size = get_size(); - Ref<StyleBox> sb = get_theme_stylebox(SNAME("bg")); - size -= sb->get_minimum_size(); + size -= theme_cache.panel_style->get_minimum_size(); - Size2 hmin; - Size2 vmin; - if (horizontal_scroll_mode != SCROLL_MODE_DISABLED) { - hmin = h_scroll->get_combined_minimum_size(); - } - if (vertical_scroll_mode != SCROLL_MODE_DISABLED) { - vmin = v_scroll->get_combined_minimum_size(); - } - - Size2 min = child_max_size; + Size2 hmin = h_scroll->get_combined_minimum_size(); + Size2 vmin = v_scroll->get_combined_minimum_size(); - bool hide_scroll_h = horizontal_scroll_mode != SCROLL_MODE_SHOW_ALWAYS && (horizontal_scroll_mode == SCROLL_MODE_DISABLED || horizontal_scroll_mode == SCROLL_MODE_SHOW_NEVER || (horizontal_scroll_mode == SCROLL_MODE_AUTO && min.width <= size.width)); - bool hide_scroll_v = vertical_scroll_mode != SCROLL_MODE_SHOW_ALWAYS && (vertical_scroll_mode == SCROLL_MODE_DISABLED || vertical_scroll_mode == SCROLL_MODE_SHOW_NEVER || (vertical_scroll_mode == SCROLL_MODE_AUTO && min.height <= size.height)); + h_scroll->set_visible(horizontal_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || (horizontal_scroll_mode == SCROLL_MODE_AUTO && largest_child_min_size.width > size.width)); + v_scroll->set_visible(vertical_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || (vertical_scroll_mode == SCROLL_MODE_AUTO && largest_child_min_size.height > size.height)); - h_scroll->set_max(min.width); - h_scroll->set_page(size.width - (hide_scroll_v ? 0 : vmin.width)); - h_scroll->set_visible(!hide_scroll_h); + h_scroll->set_max(largest_child_min_size.width); + h_scroll->set_page((v_scroll->is_visible() && v_scroll->get_parent() == this) ? size.width - vmin.width : size.width); - v_scroll->set_max(min.height); - v_scroll->set_page(size.height - (hide_scroll_h ? 0 : hmin.height)); - v_scroll->set_visible(!hide_scroll_v); + v_scroll->set_max(largest_child_min_size.height); + v_scroll->set_page((h_scroll->is_visible() && h_scroll->get_parent() == this) ? size.height - hmin.height : size.height); // Avoid scrollbar overlapping. - h_scroll->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, hide_scroll_v ? 0 : -vmin.width); - v_scroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, hide_scroll_h ? 0 : -hmin.height); + h_scroll->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, (v_scroll->is_visible() && v_scroll->get_parent() == this) ? -vmin.width : 0); + v_scroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, (h_scroll->is_visible() && h_scroll->get_parent() == this) ? -hmin.height : 0); } void ScrollContainer::_scroll_moved(float) { queue_sort(); - update(); }; void ScrollContainer::set_h_scroll(int p_pos) { @@ -512,8 +501,8 @@ void ScrollContainer::set_follow_focus(bool p_follow) { follow_focus = p_follow; } -TypedArray<String> ScrollContainer::get_configuration_warnings() const { - TypedArray<String> warnings = Container::get_configuration_warnings(); +PackedStringArray ScrollContainer::get_configuration_warnings() const { + PackedStringArray warnings = Container::get_configuration_warnings(); int found = 0; diff --git a/scene/gui/scroll_container.h b/scene/gui/scroll_container.h index b9fcf64db6..0079358ef7 100644 --- a/scene/gui/scroll_container.h +++ b/scene/gui/scroll_container.h @@ -50,7 +50,7 @@ private: HScrollBar *h_scroll = nullptr; VScrollBar *v_scroll = nullptr; - Size2 child_max_size; + mutable Size2 largest_child_min_size; // The largest one among the min sizes of all available child controls. void update_scrollbars(); @@ -69,13 +69,18 @@ private: int deadzone = 0; bool follow_focus = false; + struct ThemeCache { + Ref<StyleBox> panel_style; + } theme_cache; + void _cancel_drag(); protected: + virtual void _update_theme_item_cache() override; Size2 get_minimum_size() const override; void _gui_focus_changed(Control *p_control); - void _update_dimensions(); + void _reposition_children(); void _notification(int p_what); void _scroll_moved(float); @@ -109,11 +114,11 @@ public: VScrollBar *get_v_scroll_bar(); void ensure_control_visible(Control *p_control); - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; ScrollContainer(); }; VARIANT_ENUM_CAST(ScrollContainer::ScrollMode); -#endif +#endif // SCROLL_CONTAINER_H diff --git a/scene/gui/separator.cpp b/scene/gui/separator.cpp index e3400d9c8f..8177c1e469 100644 --- a/scene/gui/separator.cpp +++ b/scene/gui/separator.cpp @@ -33,24 +33,30 @@ Size2 Separator::get_minimum_size() const { Size2 ms(3, 3); if (orientation == VERTICAL) { - ms.x = get_theme_constant(SNAME("separation")); + ms.x = theme_cache.separation; } else { // HORIZONTAL - ms.y = get_theme_constant(SNAME("separation")); + ms.y = theme_cache.separation; } return ms; } +void Separator::_update_theme_item_cache() { + Control::_update_theme_item_cache(); + + theme_cache.separation = get_theme_constant(SNAME("separation")); + theme_cache.separator_style = get_theme_stylebox(SNAME("separator")); +} + void Separator::_notification(int p_what) { switch (p_what) { case NOTIFICATION_DRAW: { Size2i size = get_size(); - Ref<StyleBox> style = get_theme_stylebox(SNAME("separator")); - Size2i ssize = style->get_minimum_size() + style->get_center_size(); + Size2i ssize = theme_cache.separator_style->get_minimum_size() + theme_cache.separator_style->get_center_size(); if (orientation == VERTICAL) { - style->draw(get_canvas_item(), Rect2((size.x - ssize.x) / 2, 0, ssize.x, size.y)); + theme_cache.separator_style->draw(get_canvas_item(), Rect2((size.x - ssize.x) / 2, 0, ssize.x, size.y)); } else { - style->draw(get_canvas_item(), Rect2(0, (size.y - ssize.y) / 2, size.x, ssize.y)); + theme_cache.separator_style->draw(get_canvas_item(), Rect2(0, (size.y - ssize.y) / 2, size.x, ssize.y)); } } break; } diff --git a/scene/gui/separator.h b/scene/gui/separator.h index 1621bb3351..44e18a3f00 100644 --- a/scene/gui/separator.h +++ b/scene/gui/separator.h @@ -35,8 +35,16 @@ class Separator : public Control { GDCLASS(Separator, Control); + struct ThemeCache { + int separation = 0; + Ref<StyleBox> separator_style; + } theme_cache; + protected: Orientation orientation = Orientation::HORIZONTAL; + + virtual void _update_theme_item_cache() override; + void _notification(int p_what); public: @@ -60,4 +68,4 @@ public: HSeparator(); }; -#endif +#endif // SEPARATOR_H diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp index 4b680f72cf..ff3adfb9ac 100644 --- a/scene/gui/slider.cpp +++ b/scene/gui/slider.cpp @@ -33,11 +33,8 @@ #include "core/os/keyboard.h" Size2 Slider::get_minimum_size() const { - Ref<StyleBox> style = get_theme_stylebox(SNAME("slider")); - Size2i ss = style->get_minimum_size() + style->get_center_size(); - - Ref<Texture2D> grabber = get_theme_icon(SNAME("grabber")); - Size2i rs = grabber->get_size(); + Size2i ss = theme_cache.slider_style->get_minimum_size() + theme_cache.slider_style->get_center_size(); + Size2i rs = theme_cache.grabber_icon->get_size(); if (orientation == HORIZONTAL) { return Size2i(ss.width, MAX(ss.height, rs.height)); @@ -58,7 +55,13 @@ void Slider::gui_input(const Ref<InputEvent> &p_event) { if (mb.is_valid()) { if (mb->get_button_index() == MouseButton::LEFT) { if (mb->is_pressed()) { - Ref<Texture2D> grabber = get_theme_icon(mouse_inside || has_focus() ? "grabber_highlight" : "grabber"); + Ref<Texture2D> grabber; + if (mouse_inside || has_focus()) { + grabber = theme_cache.grabber_hl_icon; + } else { + grabber = theme_cache.grabber_icon; + } + grab.pos = orientation == VERTICAL ? mb->get_position().y : mb->get_position().x; double grab_width = (double)grabber->get_size().width; @@ -95,16 +98,16 @@ void Slider::gui_input(const Ref<InputEvent> &p_event) { if (mm.is_valid()) { if (grab.active) { Size2i size = get_size(); - Ref<Texture2D> grabber = get_theme_icon(SNAME("grabber")); - float motion = (orientation == VERTICAL ? mm->get_position().y : mm->get_position().x) - grab.pos; + Ref<Texture2D> grabber = theme_cache.grabber_icon; + double motion = (orientation == VERTICAL ? mm->get_position().y : mm->get_position().x) - grab.pos; if (orientation == VERTICAL) { motion = -motion; } - float areasize = orientation == VERTICAL ? size.height - grabber->get_size().height : size.width - grabber->get_size().width; + double areasize = orientation == VERTICAL ? size.height - grabber->get_size().height : size.width - grabber->get_size().width; if (areasize <= 0) { return; } - float umotion = motion / float(areasize); + double umotion = motion / double(areasize); set_as_ratio(grab.uvalue + umotion); } } @@ -145,21 +148,34 @@ void Slider::gui_input(const Ref<InputEvent> &p_event) { } } +void Slider::_update_theme_item_cache() { + Range::_update_theme_item_cache(); + + theme_cache.slider_style = get_theme_stylebox(SNAME("slider")); + theme_cache.grabber_area_style = get_theme_stylebox(SNAME("grabber_area")); + theme_cache.grabber_area_hl_style = get_theme_stylebox(SNAME("grabber_area_highlight")); + + theme_cache.grabber_icon = get_theme_icon(SNAME("grabber")); + theme_cache.grabber_hl_icon = get_theme_icon(SNAME("grabber_highlight")); + theme_cache.grabber_disabled_icon = get_theme_icon(SNAME("grabber_disabled")); + theme_cache.tick_icon = get_theme_icon(SNAME("tick")); +} + void Slider::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: { update_minimum_size(); - update(); + queue_redraw(); } break; case NOTIFICATION_MOUSE_ENTER: { mouse_inside = true; - update(); + queue_redraw(); } break; case NOTIFICATION_MOUSE_EXIT: { mouse_inside = false; - update(); + queue_redraw(); } break; case NOTIFICATION_VISIBILITY_CHANGED: @@ -171,16 +187,33 @@ void Slider::_notification(int p_what) { case NOTIFICATION_DRAW: { RID ci = get_canvas_item(); Size2i size = get_size(); - Ref<StyleBox> style = get_theme_stylebox(SNAME("slider")); - bool highlighted = mouse_inside || has_focus(); - Ref<StyleBox> grabber_area = get_theme_stylebox(highlighted ? "grabber_area_highlight" : "grabber_area"); - Ref<Texture2D> grabber = get_theme_icon(editable ? (highlighted ? "grabber_highlight" : "grabber") : "grabber_disabled"); - Ref<Texture2D> tick = get_theme_icon(SNAME("tick")); double ratio = Math::is_nan(get_as_ratio()) ? 0 : get_as_ratio(); + Ref<StyleBox> style = theme_cache.slider_style; + Ref<Texture2D> tick = theme_cache.tick_icon; + + bool highlighted = mouse_inside || has_focus(); + Ref<Texture2D> grabber; + if (editable) { + if (highlighted) { + grabber = theme_cache.grabber_hl_icon; + } else { + grabber = theme_cache.grabber_icon; + } + } else { + grabber = theme_cache.grabber_disabled_icon; + } + + Ref<StyleBox> grabber_area; + if (highlighted) { + grabber_area = theme_cache.grabber_area_hl_style; + } else { + grabber_area = theme_cache.grabber_area_style; + } + if (orientation == VERTICAL) { int widget_width = style->get_minimum_size().width + style->get_center_size().width; - float areasize = size.height - grabber->get_size().height; + double areasize = size.height - grabber->get_size().height; style->draw(ci, Rect2i(Point2i(size.width / 2 - widget_width / 2, 0), Size2i(widget_width, size.height))); grabber_area->draw(ci, Rect2i(Point2i((size.width - widget_width) / 2, size.height - areasize * ratio - grabber->get_size().height / 2), Size2i(widget_width, areasize * ratio + grabber->get_size().height / 2))); @@ -197,7 +230,7 @@ void Slider::_notification(int p_what) { grabber->draw(ci, Point2i(size.width / 2 - grabber->get_size().width / 2, size.height - ratio * areasize - grabber->get_size().height)); } else { int widget_height = style->get_minimum_size().height + style->get_center_size().height; - float areasize = size.width - grabber->get_size().width; + double areasize = size.width - grabber->get_size().width; style->draw(ci, Rect2i(Point2i(0, (size.height - widget_height) / 2), Size2i(size.width, widget_height))); grabber_area->draw(ci, Rect2i(Point2i(0, (size.height - widget_height) / 2), Size2i(areasize * ratio + grabber->get_size().width / 2, widget_height))); @@ -218,17 +251,21 @@ void Slider::_notification(int p_what) { } } -void Slider::set_custom_step(float p_custom_step) { +void Slider::set_custom_step(double p_custom_step) { custom_step = p_custom_step; } -float Slider::get_custom_step() const { +double Slider::get_custom_step() const { return custom_step; } void Slider::set_ticks(int p_count) { + if (ticks == p_count) { + return; + } + ticks = p_count; - update(); + queue_redraw(); } int Slider::get_ticks() const { @@ -240,13 +277,21 @@ bool Slider::get_ticks_on_borders() const { } void Slider::set_ticks_on_borders(bool _tob) { + if (ticks_on_borders == _tob) { + return; + } + ticks_on_borders = _tob; - update(); + queue_redraw(); } void Slider::set_editable(bool p_editable) { + if (editable == p_editable) { + return; + } + editable = p_editable; - update(); + queue_redraw(); } bool Slider::is_editable() const { diff --git a/scene/gui/slider.h b/scene/gui/slider.h index 5fbfee2aec..51adb354fb 100644 --- a/scene/gui/slider.h +++ b/scene/gui/slider.h @@ -38,28 +38,41 @@ class Slider : public Range { struct Grab { int pos = 0; - float uvalue = 0.0; + double uvalue = 0.0; bool active = false; } grab; int ticks = 0; bool mouse_inside = false; Orientation orientation; - float custom_step = -1.0; + double custom_step = -1.0; bool editable = true; bool scrollable = true; + struct ThemeCache { + Ref<StyleBox> slider_style; + Ref<StyleBox> grabber_area_style; + Ref<StyleBox> grabber_area_hl_style; + + Ref<Texture2D> grabber_icon; + Ref<Texture2D> grabber_hl_icon; + Ref<Texture2D> grabber_disabled_icon; + Ref<Texture2D> tick_icon; + } theme_cache; + protected: + bool ticks_on_borders = false; + virtual void gui_input(const Ref<InputEvent> &p_event) override; + virtual void _update_theme_item_cache() override; void _notification(int p_what); static void _bind_methods(); - bool ticks_on_borders = false; public: virtual Size2 get_minimum_size() const override; - void set_custom_step(float p_custom_step); - float get_custom_step() const; + void set_custom_step(double p_custom_step); + double get_custom_step() const; void set_ticks(int p_count); int get_ticks() const; diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp index 890e349afb..fe14049d93 100644 --- a/scene/gui/spin_box.cpp +++ b/scene/gui/spin_box.cpp @@ -41,12 +41,16 @@ Size2 SpinBox::get_minimum_size() const { void SpinBox::_value_changed(double p_value) { String value = TS->format_number(String::num(get_value(), Math::range_step_decimals(get_step()))); - if (!prefix.is_empty()) { - value = prefix + " " + value; - } - if (!suffix.is_empty()) { - value += " " + suffix; + + if (!line_edit->has_focus()) { + if (!prefix.is_empty()) { + value = prefix + " " + value; + } + if (!suffix.is_empty()) { + value += " " + suffix; + } } + line_edit->set_text(value); Range::_value_changed(p_value); } @@ -88,7 +92,8 @@ void SpinBox::_line_edit_input(const Ref<InputEvent> &p_event) { void SpinBox::_range_click_timeout() { if (!drag.enabled && Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT)) { bool up = get_local_mouse_position().y < (get_size().height / 2); - set_value(get_value() + (up ? get_step() : -get_step())); + double step = get_custom_arrow_step() != 0.0 ? get_custom_arrow_step() : get_step(); + set_value(get_value() + (up ? step : -step)); if (range_click_timer->is_one_shot()) { range_click_timer->set_wait_time(0.075); @@ -104,8 +109,9 @@ void SpinBox::_range_click_timeout() { void SpinBox::_release_mouse() { if (drag.enabled) { drag.enabled = false; - Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE); + Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_HIDDEN); warp_mouse(drag.capture_pos); + Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE); } } @@ -118,6 +124,8 @@ void SpinBox::gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseButton> mb = p_event; + double step = get_custom_arrow_step() != 0.0 ? get_custom_arrow_step() : get_step(); + if (mb.is_valid() && mb->is_pressed()) { bool up = mb->get_position().y < (get_size().height / 2); @@ -125,7 +133,7 @@ void SpinBox::gui_input(const Ref<InputEvent> &p_event) { case MouseButton::LEFT: { line_edit->grab_focus(); - set_value(get_value() + (up ? get_step() : -get_step())); + set_value(get_value() + (up ? step : -step)); range_click_timer->set_wait_time(0.6); range_click_timer->set_one_shot(true); @@ -140,13 +148,13 @@ void SpinBox::gui_input(const Ref<InputEvent> &p_event) { } break; case MouseButton::WHEEL_UP: { if (line_edit->has_focus()) { - set_value(get_value() + get_step() * mb->get_factor()); + set_value(get_value() + step * mb->get_factor()); accept_event(); } } break; case MouseButton::WHEEL_DOWN: { if (line_edit->has_focus()) { - set_value(get_value() - get_step() * mb->get_factor()); + set_value(get_value() - step * mb->get_factor()); accept_event(); } } break; @@ -167,8 +175,8 @@ void SpinBox::gui_input(const Ref<InputEvent> &p_event) { if (mm.is_valid() && (mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE) { if (drag.enabled) { drag.diff_y += mm->get_relative().y; - float diff_y = -0.01 * Math::pow(ABS(drag.diff_y), 1.8f) * SIGN(drag.diff_y); - set_value(CLAMP(drag.base_val + get_step() * diff_y, get_min(), get_max())); + double diff_y = -0.01 * Math::pow(ABS(drag.diff_y), 1.8) * SIGN(drag.diff_y); + set_value(CLAMP(drag.base_val + step * diff_y, get_min(), get_max())); } else if (drag.allowed && drag.capture_pos.distance_to(mm->get_position()) > 2) { Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_CAPTURED); drag.enabled = true; @@ -178,8 +186,14 @@ void SpinBox::gui_input(const Ref<InputEvent> &p_event) { } } +void SpinBox::_line_edit_focus_enter() { + int col = line_edit->get_caret_column(); + _value_changed(0); // Update the LineEdit's text. + line_edit->set_caret_column(col); +} + void SpinBox::_line_edit_focus_exit() { - // discontinue because the focus_exit was caused by right-click context menu + // Discontinue because the focus_exit was caused by right-click context menu. if (line_edit->is_menu_visible()) { return; } @@ -196,25 +210,29 @@ inline void SpinBox::_adjust_width_for_icon(const Ref<Texture2D> &icon) { } } +void SpinBox::_update_theme_item_cache() { + Range::_update_theme_item_cache(); + + theme_cache.updown_icon = get_theme_icon(SNAME("updown")); +} + void SpinBox::_notification(int p_what) { switch (p_what) { case NOTIFICATION_DRAW: { - Ref<Texture2D> updown = get_theme_icon(SNAME("updown")); - - _adjust_width_for_icon(updown); + _adjust_width_for_icon(theme_cache.updown_icon); RID ci = get_canvas_item(); Size2i size = get_size(); if (is_layout_rtl()) { - updown->draw(ci, Point2i(0, (size.height - updown->get_height()) / 2)); + theme_cache.updown_icon->draw(ci, Point2i(0, (size.height - theme_cache.updown_icon->get_height()) / 2)); } else { - updown->draw(ci, Point2i(size.width - updown->get_width(), (size.height - updown->get_height()) / 2)); + theme_cache.updown_icon->draw(ci, Point2i(size.width - theme_cache.updown_icon->get_width(), (size.height - theme_cache.updown_icon->get_height()) / 2)); } } break; case NOTIFICATION_ENTER_TREE: { - _adjust_width_for_icon(get_theme_icon(SNAME("updown"))); + _adjust_width_for_icon(theme_cache.updown_icon); _value_changed(0); } break; @@ -224,7 +242,7 @@ void SpinBox::_notification(int p_what) { case NOTIFICATION_TRANSLATION_CHANGED: { _value_changed(0); - update(); + queue_redraw(); } break; case NOTIFICATION_THEME_CHANGED: { @@ -233,7 +251,7 @@ void SpinBox::_notification(int p_what) { } break; case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { - update(); + queue_redraw(); } break; } } @@ -247,6 +265,10 @@ HorizontalAlignment SpinBox::get_horizontal_alignment() const { } void SpinBox::set_suffix(const String &p_suffix) { + if (suffix == p_suffix) { + return; + } + suffix = p_suffix; _value_changed(0); } @@ -256,6 +278,10 @@ String SpinBox::get_suffix() const { } void SpinBox::set_prefix(const String &p_prefix) { + if (prefix == p_prefix) { + return; + } + prefix = p_prefix; _value_changed(0); } @@ -272,7 +298,7 @@ void SpinBox::set_update_on_text_changed(bool p_enabled) { update_on_text_changed = p_enabled; if (p_enabled) { - line_edit->connect("text_changed", callable_mp(this, &SpinBox::_text_changed), Vector<Variant>(), CONNECT_DEFERRED); + line_edit->connect("text_changed", callable_mp(this, &SpinBox::_text_changed), CONNECT_DEFERRED); } else { line_edit->disconnect("text_changed", callable_mp(this, &SpinBox::_text_changed)); } @@ -294,6 +320,14 @@ void SpinBox::apply() { _text_submitted(line_edit->get_text()); } +void SpinBox::set_custom_arrow_step(double p_custom_arrow_step) { + custom_arrow_step = p_custom_arrow_step; +} + +double SpinBox::get_custom_arrow_step() const { + return custom_arrow_step; +} + void SpinBox::_bind_methods() { ClassDB::bind_method(D_METHOD("set_horizontal_alignment", "alignment"), &SpinBox::set_horizontal_alignment); ClassDB::bind_method(D_METHOD("get_horizontal_alignment"), &SpinBox::get_horizontal_alignment); @@ -302,6 +336,8 @@ void SpinBox::_bind_methods() { ClassDB::bind_method(D_METHOD("set_prefix", "prefix"), &SpinBox::set_prefix); ClassDB::bind_method(D_METHOD("get_prefix"), &SpinBox::get_prefix); ClassDB::bind_method(D_METHOD("set_editable", "enabled"), &SpinBox::set_editable); + ClassDB::bind_method(D_METHOD("set_custom_arrow_step", "arrow_step"), &SpinBox::set_custom_arrow_step); + ClassDB::bind_method(D_METHOD("get_custom_arrow_step"), &SpinBox::get_custom_arrow_step); ClassDB::bind_method(D_METHOD("is_editable"), &SpinBox::is_editable); ClassDB::bind_method(D_METHOD("set_update_on_text_changed", "enabled"), &SpinBox::set_update_on_text_changed); ClassDB::bind_method(D_METHOD("get_update_on_text_changed"), &SpinBox::get_update_on_text_changed); @@ -313,18 +349,20 @@ void SpinBox::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "update_on_text_changed"), "set_update_on_text_changed", "get_update_on_text_changed"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "prefix"), "set_prefix", "get_prefix"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "suffix"), "set_suffix", "get_suffix"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "custom_arrow_step"), "set_custom_arrow_step", "get_custom_arrow_step"); } SpinBox::SpinBox() { line_edit = memnew(LineEdit); add_child(line_edit, false, INTERNAL_MODE_FRONT); - line_edit->set_anchors_and_offsets_preset(Control::PRESET_WIDE); + line_edit->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); line_edit->set_mouse_filter(MOUSE_FILTER_PASS); line_edit->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_LEFT); - line_edit->connect("text_submitted", callable_mp(this, &SpinBox::_text_submitted), Vector<Variant>(), CONNECT_DEFERRED); - line_edit->connect("focus_exited", callable_mp(this, &SpinBox::_line_edit_focus_exit), Vector<Variant>(), CONNECT_DEFERRED); + line_edit->connect("text_submitted", callable_mp(this, &SpinBox::_text_submitted), CONNECT_DEFERRED); + line_edit->connect("focus_entered", callable_mp(this, &SpinBox::_line_edit_focus_enter), CONNECT_DEFERRED); + line_edit->connect("focus_exited", callable_mp(this, &SpinBox::_line_edit_focus_exit), CONNECT_DEFERRED); line_edit->connect("gui_input", callable_mp(this, &SpinBox::_line_edit_input)); range_click_timer = memnew(Timer); diff --git a/scene/gui/spin_box.h b/scene/gui/spin_box.h index d118b28334..c2f2ac3f5a 100644 --- a/scene/gui/spin_box.h +++ b/scene/gui/spin_box.h @@ -52,24 +52,31 @@ class SpinBox : public Range { String prefix; String suffix; + double custom_arrow_step = 0.0; void _line_edit_input(const Ref<InputEvent> &p_event); struct Drag { - float base_val = 0.0; + double base_val = 0.0; bool allowed = false; bool enabled = false; Vector2 capture_pos; - float diff_y = 0.0; + double diff_y = 0.0; } drag; + void _line_edit_focus_enter(); void _line_edit_focus_exit(); inline void _adjust_width_for_icon(const Ref<Texture2D> &icon); + struct ThemeCache { + Ref<Texture2D> updown_icon; + } theme_cache; + protected: virtual void gui_input(const Ref<InputEvent> &p_event) override; + virtual void _update_theme_item_cache() override; void _notification(int p_what); static void _bind_methods(); @@ -95,6 +102,8 @@ public: bool get_update_on_text_changed() const; void apply(); + void set_custom_arrow_step(const double p_custom_arrow_step); + double get_custom_arrow_step() const; SpinBox(); }; diff --git a/scene/gui/split_container.cpp b/scene/gui/split_container.cpp index d7aa516ee6..2ca1d6239e 100644 --- a/scene/gui/split_container.cpp +++ b/scene/gui/split_container.cpp @@ -33,11 +33,99 @@ #include "label.h" #include "margin_container.h" +void SplitContainerDragger::gui_input(const Ref<InputEvent> &p_event) { + ERR_FAIL_COND(p_event.is_null()); + + SplitContainer *sc = Object::cast_to<SplitContainer>(get_parent()); + + if (sc->collapsed || !sc->_getch(0) || !sc->_getch(1) || sc->dragger_visibility != SplitContainer::DRAGGER_VISIBLE) { + return; + } + + Ref<InputEventMouseButton> mb = p_event; + + if (mb.is_valid()) { + if (mb->get_button_index() == MouseButton::LEFT) { + if (mb->is_pressed()) { + sc->_compute_middle_sep(true); + dragging = true; + drag_ofs = sc->split_offset; + if (sc->vertical) { + drag_from = get_transform().xform(mb->get_position()).y; + } else { + drag_from = get_transform().xform(mb->get_position()).x; + } + } else { + dragging = false; + queue_redraw(); + } + } + } + + Ref<InputEventMouseMotion> mm = p_event; + + if (mm.is_valid()) { + if (!dragging) { + return; + } + + Vector2i in_parent_pos = get_transform().xform(mm->get_position()); + if (!sc->vertical && is_layout_rtl()) { + sc->split_offset = drag_ofs - ((sc->vertical ? in_parent_pos.y : in_parent_pos.x) - drag_from); + } else { + sc->split_offset = drag_ofs + ((sc->vertical ? in_parent_pos.y : in_parent_pos.x) - drag_from); + } + sc->_compute_middle_sep(true); + sc->queue_sort(); + sc->emit_signal(SNAME("dragged"), sc->get_split_offset()); + } +} + +Control::CursorShape SplitContainerDragger::get_cursor_shape(const Point2 &p_pos) const { + SplitContainer *sc = Object::cast_to<SplitContainer>(get_parent()); + + if (!sc->collapsed && sc->dragger_visibility == SplitContainer::DRAGGER_VISIBLE) { + return (sc->vertical ? CURSOR_VSPLIT : CURSOR_HSPLIT); + } + + return Control::get_cursor_shape(p_pos); +} + +void SplitContainerDragger::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_MOUSE_ENTER: { + mouse_inside = true; + SplitContainer *sc = Object::cast_to<SplitContainer>(get_parent()); + if (sc->get_theme_constant(SNAME("autohide"))) { + queue_redraw(); + } + } break; + + case NOTIFICATION_MOUSE_EXIT: { + mouse_inside = false; + SplitContainer *sc = Object::cast_to<SplitContainer>(get_parent()); + if (sc->get_theme_constant(SNAME("autohide"))) { + queue_redraw(); + } + } break; + + case NOTIFICATION_DRAW: { + SplitContainer *sc = Object::cast_to<SplitContainer>(get_parent()); + if (!dragging && !mouse_inside && sc->get_theme_constant(SNAME("autohide"))) { + return; + } + + Ref<Texture2D> tex = sc->get_theme_icon(SNAME("grabber")); + draw_texture(tex, (get_size() - tex->get_size()) / 2); + } break; + } +} + Control *SplitContainer::_getch(int p_idx) const { int idx = 0; - for (int i = 0; i < get_child_count(); i++) { - Control *c = Object::cast_to<Control>(get_child(i)); + for (int i = 0; i < get_child_count(false); i++) { + Control *c = Object::cast_to<Control>(get_child(i, false)); if (!c || !c->is_visible()) { continue; } @@ -55,58 +143,84 @@ Control *SplitContainer::_getch(int p_idx) const { return nullptr; } -void SplitContainer::_resort() { - int axis = vertical ? 1 : 0; +Ref<Texture2D> SplitContainer::_get_grabber_icon() const { + if (is_fixed) { + return theme_cache.grabber_icon; + } else { + if (vertical) { + return theme_cache.grabber_icon_v; + } else { + return theme_cache.grabber_icon_h; + } + } +} +void SplitContainer::_compute_middle_sep(bool p_clamp) { Control *first = _getch(0); Control *second = _getch(1); - // If we have only one element - if (!first || !second) { - if (first) { - fit_child_in_rect(first, Rect2(Point2(), get_size())); - } else if (second) { - fit_child_in_rect(second, Rect2(Point2(), get_size())); - } - return; - } - - // Determine expanded children + // Determine expanded children. bool first_expanded = (vertical ? first->get_v_size_flags() : first->get_h_size_flags()) & SIZE_EXPAND; bool second_expanded = (vertical ? second->get_v_size_flags() : second->get_h_size_flags()) & SIZE_EXPAND; - // Determine the separation between items - Ref<Texture2D> g = get_theme_icon(SNAME("grabber")); - int sep = get_theme_constant(SNAME("separation")); - sep = (dragger_visibility != DRAGGER_HIDDEN_COLLAPSED) ? MAX(sep, vertical ? g->get_height() : g->get_width()) : 0; + // Compute the minimum size. + int axis = vertical ? 1 : 0; + int size = get_size()[axis]; + int ms_first = first->get_combined_minimum_size()[axis]; + int ms_second = second->get_combined_minimum_size()[axis]; - // Compute the minimum size - Size2 ms_first = first->get_combined_minimum_size(); - Size2 ms_second = second->get_combined_minimum_size(); + // Determine the separation between items. + Ref<Texture2D> g = _get_grabber_icon(); + int sep = (dragger_visibility != DRAGGER_HIDDEN_COLLAPSED) ? MAX(theme_cache.separation, vertical ? g->get_height() : g->get_width()) : 0; - // Compute the separator position without the split offset - float ratio = first->get_stretch_ratio() / (first->get_stretch_ratio() + second->get_stretch_ratio()); - int no_offset_middle_sep = 0; + // Compute the wished separation_point. + int wished_middle_sep = 0; + int split_offset_with_collapse = 0; + if (!collapsed) { + split_offset_with_collapse = split_offset; + } if (first_expanded && second_expanded) { - no_offset_middle_sep = get_size()[axis] * ratio - sep / 2; + float ratio = first->get_stretch_ratio() / (first->get_stretch_ratio() + second->get_stretch_ratio()); + wished_middle_sep = size * ratio - sep / 2 + split_offset_with_collapse; } else if (first_expanded) { - no_offset_middle_sep = get_size()[axis] - ms_second[axis] - sep; + wished_middle_sep = size - sep + split_offset_with_collapse; } else { - no_offset_middle_sep = ms_first[axis]; + wished_middle_sep = split_offset_with_collapse; } - // Compute the final middle separation - middle_sep = no_offset_middle_sep; - if (!collapsed) { - int clamped_split_offset = CLAMP(split_offset, ms_first[axis] - no_offset_middle_sep, (get_size()[axis] - ms_second[axis] - sep) - no_offset_middle_sep); - middle_sep += clamped_split_offset; - if (should_clamp_split_offset) { - split_offset = clamped_split_offset; + // Clamp the middle sep to acceptatble values. + middle_sep = CLAMP(wished_middle_sep, ms_first, size - sep - ms_second); + + // Clamp the split_offset if requested. + if (p_clamp) { + split_offset -= wished_middle_sep - middle_sep; + p_clamp = false; + } +} + +void SplitContainer::_resort() { + Control *first = _getch(0); + Control *second = _getch(1); - should_clamp_split_offset = false; + // If we have only one element. + if (!first || !second) { + if (first) { + fit_child_in_rect(first, Rect2(Point2(), get_size())); + } else if (second) { + fit_child_in_rect(second, Rect2(Point2(), get_size())); } + dragging_area_control->hide(); + return; } + // If we have more that one. + _compute_middle_sep(false); + + // Determine the separation between items. + Ref<Texture2D> g = _get_grabber_icon(); + int sep = (dragger_visibility != DRAGGER_HIDDEN_COLLAPSED) ? MAX(theme_cache.separation, vertical ? g->get_height() : g->get_width()) : 0; + + // Move the children, including the dragger. if (vertical) { fit_child_in_rect(first, Rect2(Point2(0, 0), Size2(get_size().width, middle_sep))); int sofs = middle_sep + sep; @@ -124,16 +238,27 @@ void SplitContainer::_resort() { } } - update(); + // Handle the dragger visibility and position. + if (dragger_visibility == DRAGGER_VISIBLE && !collapsed) { + dragging_area_control->show(); + + int dragger_ctrl_size = MAX(sep, theme_cache.minimum_grab_thickness); + if (vertical) { + dragging_area_control->set_rect(Rect2(Point2(0, middle_sep - (dragger_ctrl_size - sep) / 2), Size2(get_size().width, dragger_ctrl_size))); + } else { + dragging_area_control->set_rect(Rect2(Point2(middle_sep - (dragger_ctrl_size - sep) / 2, 0), Size2(dragger_ctrl_size, get_size().height))); + } + + dragging_area_control->queue_redraw(); + } else { + dragging_area_control->hide(); + } } Size2 SplitContainer::get_minimum_size() const { - /* Calculate MINIMUM SIZE */ - Size2i minimum; - Ref<Texture2D> g = get_theme_icon(SNAME("grabber")); - int sep = get_theme_constant(SNAME("separation")); - sep = (dragger_visibility != DRAGGER_HIDDEN_COLLAPSED) ? MAX(sep, vertical ? g->get_height() : g->get_width()) : 0; + Ref<Texture2D> g = _get_grabber_icon(); + int sep = (dragger_visibility != DRAGGER_HIDDEN_COLLAPSED) ? MAX(theme_cache.separation, vertical ? g->get_height() : g->get_width()) : 0; for (int i = 0; i < 2; i++) { if (!_getch(i)) { @@ -162,6 +287,23 @@ Size2 SplitContainer::get_minimum_size() const { return minimum; } +void SplitContainer::_validate_property(PropertyInfo &p_property) const { + if (is_fixed && p_property.name == "vertical") { + p_property.usage = PROPERTY_USAGE_NONE; + } +} + +void SplitContainer::_update_theme_item_cache() { + Container::_update_theme_item_cache(); + + theme_cache.separation = get_theme_constant(SNAME("separation")); + theme_cache.minimum_grab_thickness = get_theme_constant(SNAME("minimum_grab_thickness")); + theme_cache.autohide = get_theme_constant(SNAME("autohide")); + theme_cache.grabber_icon = get_theme_icon(SNAME("grabber")); + theme_cache.grabber_icon_h = get_theme_icon(SNAME("h_grabber")); + theme_cache.grabber_icon_v = get_theme_icon(SNAME("v_grabber")); +} + void SplitContainer::_notification(int p_what) { switch (p_what) { case NOTIFICATION_TRANSLATION_CHANGED: @@ -173,130 +315,12 @@ void SplitContainer::_notification(int p_what) { _resort(); } break; - case NOTIFICATION_MOUSE_EXIT: { - mouse_inside = false; - if (get_theme_constant(SNAME("autohide"))) { - update(); - } - } break; - - case NOTIFICATION_DRAW: { - if (!_getch(0) || !_getch(1)) { - return; - } - - if (collapsed || (!dragging && !mouse_inside && get_theme_constant(SNAME("autohide")))) { - return; - } - - if (dragger_visibility != DRAGGER_VISIBLE) { - return; - } - - int sep = dragger_visibility != DRAGGER_HIDDEN_COLLAPSED ? get_theme_constant(SNAME("separation")) : 0; - Ref<Texture2D> tex = get_theme_icon(SNAME("grabber")); - Size2 size = get_size(); - - if (vertical) { - draw_texture(tex, Point2i((size.x - tex->get_width()) / 2, middle_sep + (sep - tex->get_height()) / 2)); - } else { - draw_texture(tex, Point2i(middle_sep + (sep - tex->get_width()) / 2, (size.y - tex->get_height()) / 2)); - } - } break; - case NOTIFICATION_THEME_CHANGED: { update_minimum_size(); } break; } } -void SplitContainer::gui_input(const Ref<InputEvent> &p_event) { - ERR_FAIL_COND(p_event.is_null()); - - if (collapsed || !_getch(0) || !_getch(1) || dragger_visibility != DRAGGER_VISIBLE) { - return; - } - - Ref<InputEventMouseButton> mb = p_event; - - if (mb.is_valid()) { - if (mb->get_button_index() == MouseButton::LEFT) { - if (mb->is_pressed()) { - int sep = get_theme_constant(SNAME("separation")); - - if (vertical) { - if (mb->get_position().y > middle_sep && mb->get_position().y < middle_sep + sep) { - dragging = true; - drag_from = mb->get_position().y; - drag_ofs = split_offset; - } - } else { - if (mb->get_position().x > middle_sep && mb->get_position().x < middle_sep + sep) { - dragging = true; - drag_from = mb->get_position().x; - drag_ofs = split_offset; - } - } - } else { - dragging = false; - } - } - } - - Ref<InputEventMouseMotion> mm = p_event; - - if (mm.is_valid()) { - bool mouse_inside_state = false; - if (vertical) { - mouse_inside_state = mm->get_position().y > middle_sep && mm->get_position().y < middle_sep + get_theme_constant(SNAME("separation")); - } else { - mouse_inside_state = mm->get_position().x > middle_sep && mm->get_position().x < middle_sep + get_theme_constant(SNAME("separation")); - } - - if (mouse_inside != mouse_inside_state) { - mouse_inside = mouse_inside_state; - if (get_theme_constant(SNAME("autohide"))) { - update(); - } - } - - if (!dragging) { - return; - } - - if (!vertical && is_layout_rtl()) { - split_offset = drag_ofs + (drag_from - (vertical ? mm->get_position().y : mm->get_position().x)); - } else { - split_offset = drag_ofs + ((vertical ? mm->get_position().y : mm->get_position().x) - drag_from); - } - should_clamp_split_offset = true; - queue_sort(); - emit_signal(SNAME("dragged"), get_split_offset()); - } -} - -Control::CursorShape SplitContainer::get_cursor_shape(const Point2 &p_pos) const { - if (dragging) { - return (vertical ? CURSOR_VSPLIT : CURSOR_HSPLIT); - } - - if (!collapsed && _getch(0) && _getch(1) && dragger_visibility == DRAGGER_VISIBLE) { - int sep = get_theme_constant(SNAME("separation")); - - if (vertical) { - if (p_pos.y > middle_sep && p_pos.y < middle_sep + sep) { - return CURSOR_VSPLIT; - } - } else { - if (p_pos.x > middle_sep && p_pos.x < middle_sep + sep) { - return CURSOR_HSPLIT; - } - } - } - - return Control::get_cursor_shape(p_pos); -} - void SplitContainer::set_split_offset(int p_offset) { if (split_offset == p_offset) { return; @@ -312,8 +336,11 @@ int SplitContainer::get_split_offset() const { } void SplitContainer::clamp_split_offset() { - should_clamp_split_offset = true; + if (!_getch(0) || !_getch(1)) { + return; + } + _compute_middle_sep(true); queue_sort(); } @@ -327,9 +354,12 @@ void SplitContainer::set_collapsed(bool p_collapsed) { } void SplitContainer::set_dragger_visibility(DraggerVisibility p_visibility) { + if (dragger_visibility == p_visibility) { + return; + } + dragger_visibility = p_visibility; queue_sort(); - update(); } SplitContainer::DraggerVisibility SplitContainer::get_dragger_visibility() const { @@ -340,6 +370,17 @@ bool SplitContainer::is_collapsed() const { return collapsed; } +void SplitContainer::set_vertical(bool p_vertical) { + ERR_FAIL_COND_MSG(is_fixed, "Can't change orientation of " + get_class() + "."); + vertical = p_vertical; + update_minimum_size(); + _resort(); +} + +bool SplitContainer::is_vertical() const { + return vertical; +} + Vector<int> SplitContainer::get_allowed_size_flags_horizontal() const { Vector<int> flags; flags.append(SIZE_FILL); @@ -375,11 +416,15 @@ void SplitContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_dragger_visibility", "mode"), &SplitContainer::set_dragger_visibility); ClassDB::bind_method(D_METHOD("get_dragger_visibility"), &SplitContainer::get_dragger_visibility); + ClassDB::bind_method(D_METHOD("set_vertical", "vertical"), &SplitContainer::set_vertical); + ClassDB::bind_method(D_METHOD("is_vertical"), &SplitContainer::is_vertical); + ADD_SIGNAL(MethodInfo("dragged", PropertyInfo(Variant::INT, "offset"))); ADD_PROPERTY(PropertyInfo(Variant::INT, "split_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_split_offset", "get_split_offset"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collapsed"), "set_collapsed", "is_collapsed"); ADD_PROPERTY(PropertyInfo(Variant::INT, "dragger_visibility", PROPERTY_HINT_ENUM, "Visible,Hidden,Hidden and Collapsed"), "set_dragger_visibility", "get_dragger_visibility"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vertical"), "set_vertical", "is_vertical"); BIND_ENUM_CONSTANT(DRAGGER_VISIBLE); BIND_ENUM_CONSTANT(DRAGGER_HIDDEN); @@ -388,4 +433,7 @@ void SplitContainer::_bind_methods() { SplitContainer::SplitContainer(bool p_vertical) { vertical = p_vertical; + + dragging_area_control = memnew(SplitContainerDragger); + add_child(dragging_area_control, false, Node::INTERNAL_MODE_BACK); } diff --git a/scene/gui/split_container.h b/scene/gui/split_container.h index a69ffe4de9..d297e3a3ea 100644 --- a/scene/gui/split_container.h +++ b/scene/gui/split_container.h @@ -33,8 +33,26 @@ #include "scene/gui/container.h" +class SplitContainerDragger : public Control { + GDCLASS(SplitContainerDragger, Control); + +protected: + void _notification(int p_what); + virtual void gui_input(const Ref<InputEvent> &p_event) override; + +private: + bool dragging = false; + int drag_from = 0; + int drag_ofs = 0; + bool mouse_inside = false; + +public: + virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const override; +}; + class SplitContainer : public Container { GDCLASS(SplitContainer, Container); + friend class SplitContainerDragger; public: enum DraggerVisibility { @@ -44,24 +62,38 @@ public: }; private: - bool should_clamp_split_offset = false; int split_offset = 0; int middle_sep = 0; bool vertical = false; - bool dragging = false; - int drag_from = 0; - int drag_ofs = 0; bool collapsed = false; DraggerVisibility dragger_visibility = DRAGGER_VISIBLE; - bool mouse_inside = false; + + SplitContainerDragger *dragging_area_control = nullptr; + + struct ThemeCache { + int separation = 0; + int minimum_grab_thickness = 0; + int autohide = 0; + Ref<Texture2D> grabber_icon; + Ref<Texture2D> grabber_icon_h; + Ref<Texture2D> grabber_icon_v; + } theme_cache; Control *_getch(int p_idx) const; + Ref<Texture2D> _get_grabber_icon() const; + void _compute_middle_sep(bool p_clamp); void _resort(); + void _dragging_area_gui_input(const Ref<InputEvent> &p_event); + protected: - virtual void gui_input(const Ref<InputEvent> &p_event) override; + bool is_fixed = false; + + virtual void _update_theme_item_cache() override; + void _notification(int p_what); + void _validate_property(PropertyInfo &p_property) const; static void _bind_methods(); public: @@ -75,7 +107,8 @@ public: void set_dragger_visibility(DraggerVisibility p_visibility); DraggerVisibility get_dragger_visibility() const; - virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const override; + void set_vertical(bool p_vertical); + bool is_vertical() const; virtual Size2 get_minimum_size() const override; @@ -92,7 +125,7 @@ class HSplitContainer : public SplitContainer { public: HSplitContainer() : - SplitContainer(false) {} + SplitContainer(false) { is_fixed = true; } }; class VSplitContainer : public SplitContainer { @@ -100,7 +133,7 @@ class VSplitContainer : public SplitContainer { public: VSplitContainer() : - SplitContainer(true) {} + SplitContainer(true) { is_fixed = true; } }; #endif // SPLIT_CONTAINER_H diff --git a/scene/gui/subviewport_container.cpp b/scene/gui/subviewport_container.cpp index 68281b6a72..3ad84cbc6d 100644 --- a/scene/gui/subviewport_container.cpp +++ b/scene/gui/subviewport_container.cpp @@ -53,10 +53,14 @@ Size2 SubViewportContainer::get_minimum_size() const { } void SubViewportContainer::set_stretch(bool p_enable) { + if (stretch == p_enable) { + return; + } + stretch = p_enable; update_minimum_size(); queue_sort(); - update(); + queue_redraw(); } bool SubViewportContainer::is_stretch_enabled() const { @@ -84,7 +88,7 @@ void SubViewportContainer::set_stretch_shrink(int p_shrink) { c->set_size(get_size() / shrink); } - update(); + queue_redraw(); } int SubViewportContainer::get_stretch_shrink() const { @@ -223,8 +227,8 @@ void SubViewportContainer::unhandled_input(const Ref<InputEvent> &p_event) { } } -TypedArray<String> SubViewportContainer::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray SubViewportContainer::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); bool has_viewport = false; for (int i = 0; i < get_child_count(); i++) { diff --git a/scene/gui/subviewport_container.h b/scene/gui/subviewport_container.h index 55b7802aa4..63a58b5f07 100644 --- a/scene/gui/subviewport_container.h +++ b/scene/gui/subviewport_container.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef VIEWPORTCONTAINER_H -#define VIEWPORTCONTAINER_H +#ifndef SUBVIEWPORT_CONTAINER_H +#define SUBVIEWPORT_CONTAINER_H #include "scene/gui/container.h" @@ -58,9 +58,9 @@ public: virtual Vector<int> get_allowed_size_flags_horizontal() const override; virtual Vector<int> get_allowed_size_flags_vertical() const override; - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; SubViewportContainer(); }; -#endif // VIEWPORTCONTAINER_H +#endif // SUBVIEWPORT_CONTAINER_H diff --git a/scene/gui/tab_bar.cpp b/scene/gui/tab_bar.cpp index d36a364677..cf6681f809 100644 --- a/scene/gui/tab_bar.cpp +++ b/scene/gui/tab_bar.cpp @@ -44,14 +44,7 @@ Size2 TabBar::get_minimum_size() const { return ms; } - Ref<StyleBox> tab_unselected = get_theme_stylebox(SNAME("tab_unselected")); - Ref<StyleBox> tab_selected = get_theme_stylebox(SNAME("tab_selected")); - Ref<StyleBox> tab_disabled = get_theme_stylebox(SNAME("tab_disabled")); - Ref<StyleBox> button_highlight = get_theme_stylebox(SNAME("button_highlight")); - Ref<Texture2D> close = get_theme_icon(SNAME("close")); - int hseparation = get_theme_constant(SNAME("h_separation")); - - int y_margin = MAX(MAX(tab_unselected->get_minimum_size().height, tab_selected->get_minimum_size().height), tab_disabled->get_minimum_size().height); + int y_margin = MAX(MAX(theme_cache.tab_unselected_style->get_minimum_size().height, theme_cache.tab_selected_style->get_minimum_size().height), theme_cache.tab_disabled_style->get_minimum_size().height); for (int i = 0; i < tabs.size(); i++) { if (tabs[i].hidden) { @@ -62,22 +55,22 @@ Size2 TabBar::get_minimum_size() const { Ref<StyleBox> style; if (tabs[i].disabled) { - style = tab_disabled; + style = theme_cache.tab_disabled_style; } else if (current == i) { - style = tab_selected; + style = theme_cache.tab_selected_style; } else { - style = tab_unselected; + style = theme_cache.tab_unselected_style; } ms.width += style->get_minimum_size().width; Ref<Texture2D> tex = tabs[i].icon; if (tex.is_valid()) { ms.height = MAX(ms.height, tex->get_size().height + y_margin); - ms.width += tex->get_size().width + hseparation; + ms.width += tex->get_size().width + theme_cache.h_separation; } if (!tabs[i].text.is_empty()) { - ms.width += tabs[i].size_text + hseparation; + ms.width += tabs[i].size_text + theme_cache.h_separation; } ms.height = MAX(ms.height, tabs[i].text_buf->get_size().y + y_margin); @@ -87,22 +80,22 @@ Size2 TabBar::get_minimum_size() const { Ref<Texture2D> rb = tabs[i].right_button; if (close_visible) { - ms.width += button_highlight->get_minimum_size().width + rb->get_width(); + ms.width += theme_cache.button_hl_style->get_minimum_size().width + rb->get_width(); } else { - ms.width += button_highlight->get_margin(SIDE_LEFT) + rb->get_width() + hseparation; + ms.width += theme_cache.button_hl_style->get_margin(SIDE_LEFT) + rb->get_width() + theme_cache.h_separation; } ms.height = MAX(ms.height, rb->get_height() + y_margin); } if (close_visible) { - ms.width += button_highlight->get_margin(SIDE_LEFT) + close->get_width() + hseparation; + ms.width += theme_cache.button_hl_style->get_margin(SIDE_LEFT) + theme_cache.close_icon->get_width() + theme_cache.h_separation; - ms.height = MAX(ms.height, close->get_height() + y_margin); + ms.height = MAX(ms.height, theme_cache.close_icon->get_height() + y_margin); } if (ms.width - ofs > style->get_minimum_size().width) { - ms.width -= hseparation; + ms.width -= theme_cache.h_separation; } } @@ -122,46 +115,43 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) { Point2 pos = mm->get_position(); if (buttons_visible) { - Ref<Texture2D> incr = get_theme_icon(SNAME("increment")); - Ref<Texture2D> decr = get_theme_icon(SNAME("decrement")); - if (is_layout_rtl()) { - if (pos.x < decr->get_width()) { + if (pos.x < theme_cache.decrement_icon->get_width()) { if (highlight_arrow != 1) { highlight_arrow = 1; - update(); + queue_redraw(); } - } else if (pos.x < incr->get_width() + decr->get_width()) { + } else if (pos.x < theme_cache.increment_icon->get_width() + theme_cache.decrement_icon->get_width()) { if (highlight_arrow != 0) { highlight_arrow = 0; - update(); + queue_redraw(); } } else if (highlight_arrow != -1) { highlight_arrow = -1; - update(); + queue_redraw(); } } else { - int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width(); - if (pos.x > limit_minus_buttons + decr->get_width()) { + int limit_minus_buttons = get_size().width - theme_cache.increment_icon->get_width() - theme_cache.decrement_icon->get_width(); + if (pos.x > limit_minus_buttons + theme_cache.decrement_icon->get_width()) { if (highlight_arrow != 1) { highlight_arrow = 1; - update(); + queue_redraw(); } } else if (pos.x > limit_minus_buttons) { if (highlight_arrow != 0) { highlight_arrow = 0; - update(); + queue_redraw(); } } else if (highlight_arrow != -1) { highlight_arrow = -1; - update(); + queue_redraw(); } } } if (get_viewport()->gui_is_dragging() && can_drop_data(pos, get_viewport()->gui_get_drag_data())) { dragging_valid_tab = true; - update(); + queue_redraw(); } _update_hover(); @@ -172,22 +162,22 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseButton> mb = p_event; if (mb.is_valid()) { - if (mb->is_pressed() && mb->get_button_index() == MouseButton::WHEEL_UP && !mb->is_command_pressed()) { + if (mb->is_pressed() && mb->get_button_index() == MouseButton::WHEEL_UP && !mb->is_command_or_control_pressed()) { if (scrolling_enabled && buttons_visible) { if (offset > 0) { offset--; _update_cache(); - update(); + queue_redraw(); } } } - if (mb->is_pressed() && mb->get_button_index() == MouseButton::WHEEL_DOWN && !mb->is_command_pressed()) { + if (mb->is_pressed() && mb->get_button_index() == MouseButton::WHEEL_DOWN && !mb->is_command_or_control_pressed()) { if (scrolling_enabled && buttons_visible) { if (missing_right && offset < tabs.size()) { offset++; _update_cache(); - update(); + queue_redraw(); } } } @@ -198,7 +188,7 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) { } rb_pressing = false; - update(); + queue_redraw(); } if (cb_pressing && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) { @@ -207,46 +197,43 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) { } cb_pressing = false; - update(); + queue_redraw(); } if (mb->is_pressed() && (mb->get_button_index() == MouseButton::LEFT || (select_with_rmb && mb->get_button_index() == MouseButton::RIGHT))) { Point2 pos = mb->get_position(); if (buttons_visible) { - Ref<Texture2D> incr = get_theme_icon(SNAME("increment")); - Ref<Texture2D> decr = get_theme_icon(SNAME("decrement")); - if (is_layout_rtl()) { - if (pos.x < decr->get_width()) { + if (pos.x < theme_cache.decrement_icon->get_width()) { if (missing_right) { offset++; _update_cache(); - update(); + queue_redraw(); } return; - } else if (pos.x < incr->get_width() + decr->get_width()) { + } else if (pos.x < theme_cache.increment_icon->get_width() + theme_cache.decrement_icon->get_width()) { if (offset > 0) { offset--; _update_cache(); - update(); + queue_redraw(); } return; } } else { - int limit = get_size().width - incr->get_width() - decr->get_width(); - if (pos.x > limit + decr->get_width()) { + int limit = get_size().width - theme_cache.increment_icon->get_width() - theme_cache.decrement_icon->get_width(); + if (pos.x > limit + theme_cache.decrement_icon->get_width()) { if (missing_right) { offset++; _update_cache(); - update(); + queue_redraw(); } return; } else if (pos.x > limit) { if (offset > 0) { offset--; _update_cache(); - update(); + queue_redraw(); } return; } @@ -266,13 +253,13 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) { if (tabs[i].rb_rect.has_point(pos)) { rb_pressing = true; - update(); + queue_redraw(); return; } if (tabs[i].cb_rect.has_point(pos) && (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current))) { cb_pressing = true; - update(); + queue_redraw(); return; } @@ -299,9 +286,6 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) { } void TabBar::_shape(int p_tab) { - Ref<Font> font = get_theme_font(SNAME("font")); - int font_size = get_theme_font_size(SNAME("font_size")); - tabs.write[p_tab].xl_text = atr(tabs[p_tab].text); tabs.write[p_tab].text_buf->clear(); tabs.write[p_tab].text_buf->set_width(-1); @@ -311,13 +295,43 @@ void TabBar::_shape(int p_tab) { tabs.write[p_tab].text_buf->set_direction((TextServer::Direction)tabs[p_tab].text_direction); } - tabs.write[p_tab].text_buf->add_string(tabs[p_tab].xl_text, font, font_size, tabs[p_tab].language); + tabs.write[p_tab].text_buf->add_string(tabs[p_tab].xl_text, theme_cache.font, theme_cache.font_size, tabs[p_tab].language); +} + +void TabBar::_update_theme_item_cache() { + Control::_update_theme_item_cache(); + + theme_cache.h_separation = get_theme_constant(SNAME("h_separation")); + + theme_cache.tab_unselected_style = get_theme_stylebox(SNAME("tab_unselected")); + theme_cache.tab_selected_style = get_theme_stylebox(SNAME("tab_selected")); + theme_cache.tab_disabled_style = get_theme_stylebox(SNAME("tab_disabled")); + + theme_cache.increment_icon = get_theme_icon(SNAME("increment")); + theme_cache.increment_hl_icon = get_theme_icon(SNAME("increment_highlight")); + theme_cache.decrement_icon = get_theme_icon(SNAME("decrement")); + theme_cache.decrement_hl_icon = get_theme_icon(SNAME("decrement_highlight")); + theme_cache.drop_mark_icon = get_theme_icon(SNAME("drop_mark")); + theme_cache.drop_mark_color = get_theme_color(SNAME("drop_mark_color")); + + theme_cache.font = get_theme_font(SNAME("font")); + theme_cache.font_size = get_theme_font_size(SNAME("font_size")); + theme_cache.outline_size = get_theme_constant(SNAME("outline_size")); + + theme_cache.font_selected_color = get_theme_color(SNAME("font_selected_color")); + theme_cache.font_unselected_color = get_theme_color(SNAME("font_unselected_color")); + theme_cache.font_disabled_color = get_theme_color(SNAME("font_disabled_color")); + theme_cache.font_outline_color = get_theme_color(SNAME("font_outline_color")); + + theme_cache.close_icon = get_theme_icon(SNAME("close")); + theme_cache.button_pressed_style = get_theme_stylebox(SNAME("button_pressed")); + theme_cache.button_hl_style = get_theme_stylebox(SNAME("button_highlight")); } void TabBar::_notification(int p_what) { switch (p_what) { case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { - update(); + queue_redraw(); } break; case NOTIFICATION_THEME_CHANGED: @@ -343,7 +357,7 @@ void TabBar::_notification(int p_what) { case NOTIFICATION_DRAG_END: { if (dragging_valid_tab) { dragging_valid_tab = false; - update(); + queue_redraw(); } } break; @@ -352,18 +366,9 @@ void TabBar::_notification(int p_what) { return; } - Ref<StyleBox> tab_unselected = get_theme_stylebox(SNAME("tab_unselected")); - Ref<StyleBox> tab_selected = get_theme_stylebox(SNAME("tab_selected")); - Ref<StyleBox> tab_disabled = get_theme_stylebox(SNAME("tab_disabled")); - Color font_selected_color = get_theme_color(SNAME("font_selected_color")); - Color font_unselected_color = get_theme_color(SNAME("font_unselected_color")); - Color font_disabled_color = get_theme_color(SNAME("font_disabled_color")); - Ref<Texture2D> incr = get_theme_icon(SNAME("increment")); - Ref<Texture2D> decr = get_theme_icon(SNAME("decrement")); - bool rtl = is_layout_rtl(); Vector2 size = get_size(); - int limit_minus_buttons = size.width - incr->get_width() - decr->get_width(); + int limit_minus_buttons = size.width - theme_cache.increment_icon->get_width() - theme_cache.decrement_icon->get_width(); int ofs = tabs[offset].ofs_cache; @@ -378,14 +383,14 @@ void TabBar::_notification(int p_what) { Color col; if (tabs[i].disabled) { - sb = tab_disabled; - col = font_disabled_color; + sb = theme_cache.tab_disabled_style; + col = theme_cache.font_disabled_color; } else if (i == current) { - sb = tab_selected; - col = font_selected_color; + sb = theme_cache.tab_selected_style; + col = theme_cache.font_selected_color; } else { - sb = tab_unselected; - col = font_unselected_color; + sb = theme_cache.tab_unselected_style; + col = theme_cache.font_unselected_color; } _draw_tab(sb, col, i, rtl ? size.width - ofs - tabs[i].size_cache : ofs); @@ -396,41 +401,38 @@ void TabBar::_notification(int p_what) { // Draw selected tab in the front, but only if it's visible. if (current >= offset && current <= max_drawn_tab && !tabs[current].hidden) { - Ref<StyleBox> sb = tabs[current].disabled ? tab_disabled : tab_selected; + Ref<StyleBox> sb = tabs[current].disabled ? theme_cache.tab_disabled_style : theme_cache.tab_selected_style; float x = rtl ? size.width - tabs[current].ofs_cache - tabs[current].size_cache : tabs[current].ofs_cache; - _draw_tab(sb, font_selected_color, current, x); + _draw_tab(sb, theme_cache.font_selected_color, current, x); } if (buttons_visible) { - Ref<Texture2D> incr_hl = get_theme_icon(SNAME("increment_highlight")); - Ref<Texture2D> decr_hl = get_theme_icon(SNAME("decrement_highlight")); - - int vofs = (size.height - incr->get_size().height) / 2; + int vofs = (size.height - theme_cache.increment_icon->get_size().height) / 2; if (rtl) { if (missing_right) { - draw_texture(highlight_arrow == 1 ? decr_hl : decr, Point2(0, vofs)); + draw_texture(highlight_arrow == 1 ? theme_cache.decrement_hl_icon : theme_cache.decrement_icon, Point2(0, vofs)); } else { - draw_texture(decr, Point2(0, vofs), Color(1, 1, 1, 0.5)); + draw_texture(theme_cache.decrement_icon, Point2(0, vofs), Color(1, 1, 1, 0.5)); } if (offset > 0) { - draw_texture(highlight_arrow == 0 ? incr_hl : incr, Point2(incr->get_size().width, vofs)); + draw_texture(highlight_arrow == 0 ? theme_cache.increment_hl_icon : theme_cache.increment_icon, Point2(theme_cache.increment_icon->get_size().width, vofs)); } else { - draw_texture(incr, Point2(incr->get_size().width, vofs), Color(1, 1, 1, 0.5)); + draw_texture(theme_cache.increment_icon, Point2(theme_cache.increment_icon->get_size().width, vofs), Color(1, 1, 1, 0.5)); } } else { if (offset > 0) { - draw_texture(highlight_arrow == 0 ? decr_hl : decr, Point2(limit_minus_buttons, vofs)); + draw_texture(highlight_arrow == 0 ? theme_cache.decrement_hl_icon : theme_cache.decrement_icon, Point2(limit_minus_buttons, vofs)); } else { - draw_texture(decr, Point2(limit_minus_buttons, vofs), Color(1, 1, 1, 0.5)); + draw_texture(theme_cache.decrement_icon, Point2(limit_minus_buttons, vofs), Color(1, 1, 1, 0.5)); } if (missing_right) { - draw_texture(highlight_arrow == 1 ? incr_hl : incr, Point2(limit_minus_buttons + decr->get_size().width, vofs)); + draw_texture(highlight_arrow == 1 ? theme_cache.increment_hl_icon : theme_cache.increment_icon, Point2(limit_minus_buttons + theme_cache.decrement_icon->get_size().width, vofs)); } else { - draw_texture(incr, Point2(limit_minus_buttons + decr->get_size().width, vofs), Color(1, 1, 1, 0.5)); + draw_texture(theme_cache.increment_icon, Point2(limit_minus_buttons + theme_cache.decrement_icon->get_size().width, vofs), Color(1, 1, 1, 0.5)); } } } @@ -462,10 +464,7 @@ void TabBar::_notification(int p_what) { } } - Ref<Texture2D> drop_mark = get_theme_icon(SNAME("drop_mark")); - Color drop_mark_color = get_theme_color(SNAME("drop_mark_color")); - - drop_mark->draw(get_canvas_item(), Point2(x - drop_mark->get_width() / 2, (size.height - drop_mark->get_height()) / 2), drop_mark_color); + theme_cache.drop_mark_icon->draw(get_canvas_item(), Point2(x - theme_cache.drop_mark_icon->get_width() / 2, (size.height - theme_cache.drop_mark_icon->get_height()) / 2), theme_cache.drop_mark_color); } } break; } @@ -475,10 +474,6 @@ void TabBar::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_in RID ci = get_canvas_item(); bool rtl = is_layout_rtl(); - Color font_outline_color = get_theme_color(SNAME("font_outline_color")); - int outline_size = get_theme_constant(SNAME("outline_size")); - int hseparation = get_theme_constant(SNAME("h_separation")); - Rect2 sb_rect = Rect2(p_x, 0, tabs[p_index].size_cache, get_size().height); p_tab_style->draw(ci, sb_rect); @@ -491,7 +486,7 @@ void TabBar::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_in if (icon.is_valid()) { icon->draw(ci, Point2i(rtl ? p_x - icon->get_width() : p_x, p_tab_style->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - icon->get_height()) / 2)); - p_x = rtl ? p_x - icon->get_width() - hseparation : p_x + icon->get_width() + hseparation; + p_x = rtl ? p_x - icon->get_width() - theme_cache.h_separation : p_x + icon->get_width() + theme_cache.h_separation; } // Draw the text. @@ -499,17 +494,17 @@ void TabBar::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_in Point2i text_pos = Point2i(rtl ? p_x - tabs[p_index].size_text : p_x, p_tab_style->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - tabs[p_index].text_buf->get_size().y) / 2); - if (outline_size > 0 && font_outline_color.a > 0) { - tabs[p_index].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color); + if (theme_cache.outline_size > 0 && theme_cache.font_outline_color.a > 0) { + tabs[p_index].text_buf->draw_outline(ci, text_pos, theme_cache.outline_size, theme_cache.font_outline_color); } tabs[p_index].text_buf->draw(ci, text_pos, p_font_color); - p_x = rtl ? p_x - tabs[p_index].size_text - hseparation : p_x + tabs[p_index].size_text + hseparation; + p_x = rtl ? p_x - tabs[p_index].size_text - theme_cache.h_separation : p_x + tabs[p_index].size_text + theme_cache.h_separation; } // Draw and calculate rect of the right button. if (tabs[p_index].right_button.is_valid()) { - Ref<StyleBox> style = get_theme_stylebox(SNAME("button_highlight")); + Ref<StyleBox> style = theme_cache.button_hl_style; Ref<Texture2D> rb = tabs[p_index].right_button; Rect2 rb_rect; @@ -521,7 +516,7 @@ void TabBar::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_in if (rb_hover == p_index) { if (rb_pressing) { - get_theme_stylebox(SNAME("button_pressed"))->draw(ci, rb_rect); + theme_cache.button_pressed_style->draw(ci, rb_rect); } else { style->draw(ci, rb_rect); } @@ -534,8 +529,8 @@ void TabBar::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_in // Draw and calculate rect of the close button. if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && p_index == current)) { - Ref<StyleBox> style = get_theme_stylebox(SNAME("button_highlight")); - Ref<Texture2D> cb = get_theme_icon(SNAME("close")); + Ref<StyleBox> style = theme_cache.button_hl_style; + Ref<Texture2D> cb = theme_cache.close_icon; Rect2 cb_rect; cb_rect.size = style->get_minimum_size() + cb->get_size(); @@ -546,7 +541,7 @@ void TabBar::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_in if (!tabs[p_index].disabled && cb_hover == p_index) { if (cb_pressing) { - get_theme_stylebox(SNAME("button_pressed"))->draw(ci, cb_rect); + theme_cache.button_pressed_style->draw(ci, cb_rect); } else { style->draw(ci, cb_rect); } @@ -581,7 +576,7 @@ void TabBar::set_tab_count(int p_count) { } } - update(); + queue_redraw(); update_minimum_size(); notify_property_list_changed(); } @@ -607,7 +602,7 @@ void TabBar::set_current_tab(int p_current) { if (scroll_to_selected) { ensure_tab_visible(current); } - update(); + queue_redraw(); emit_signal(SNAME("tab_changed"), p_current); } @@ -634,6 +629,11 @@ bool TabBar::get_offset_buttons_visible() const { void TabBar::set_tab_title(int p_tab, const String &p_title) { ERR_FAIL_INDEX(p_tab, tabs.size()); + + if (tabs[p_tab].text == p_title) { + return; + } + tabs.write[p_tab].text = p_title; _shape(p_tab); @@ -642,7 +642,7 @@ void TabBar::set_tab_title(int p_tab, const String &p_title) { if (scroll_to_selected) { ensure_tab_visible(current); } - update(); + queue_redraw(); update_minimum_size(); } @@ -658,7 +658,7 @@ void TabBar::set_tab_text_direction(int p_tab, Control::TextDirection p_text_dir if (tabs[p_tab].text_direction != p_text_direction) { tabs.write[p_tab].text_direction = p_text_direction; _shape(p_tab); - update(); + queue_redraw(); } } @@ -678,7 +678,7 @@ void TabBar::set_tab_language(int p_tab, const String &p_language) { if (scroll_to_selected) { ensure_tab_visible(current); } - update(); + queue_redraw(); update_minimum_size(); } } @@ -690,6 +690,11 @@ String TabBar::get_tab_language(int p_tab) const { void TabBar::set_tab_icon(int p_tab, const Ref<Texture2D> &p_icon) { ERR_FAIL_INDEX(p_tab, tabs.size()); + + if (tabs[p_tab].icon == p_icon) { + return; + } + tabs.write[p_tab].icon = p_icon; _update_cache(); @@ -697,7 +702,7 @@ void TabBar::set_tab_icon(int p_tab, const Ref<Texture2D> &p_icon) { if (scroll_to_selected) { ensure_tab_visible(current); } - update(); + queue_redraw(); update_minimum_size(); } @@ -708,6 +713,11 @@ Ref<Texture2D> TabBar::get_tab_icon(int p_tab) const { void TabBar::set_tab_disabled(int p_tab, bool p_disabled) { ERR_FAIL_INDEX(p_tab, tabs.size()); + + if (tabs[p_tab].disabled == p_disabled) { + return; + } + tabs.write[p_tab].disabled = p_disabled; _update_cache(); @@ -715,7 +725,7 @@ void TabBar::set_tab_disabled(int p_tab, bool p_disabled) { if (scroll_to_selected) { ensure_tab_visible(current); } - update(); + queue_redraw(); update_minimum_size(); } @@ -726,6 +736,11 @@ bool TabBar::is_tab_disabled(int p_tab) const { void TabBar::set_tab_hidden(int p_tab, bool p_hidden) { ERR_FAIL_INDEX(p_tab, tabs.size()); + + if (tabs[p_tab].hidden == p_hidden) { + return; + } + tabs.write[p_tab].hidden = p_hidden; _update_cache(); @@ -733,7 +748,7 @@ void TabBar::set_tab_hidden(int p_tab, bool p_hidden) { if (scroll_to_selected) { ensure_tab_visible(current); } - update(); + queue_redraw(); update_minimum_size(); } @@ -744,6 +759,11 @@ bool TabBar::is_tab_hidden(int p_tab) const { void TabBar::set_tab_button_icon(int p_tab, const Ref<Texture2D> &p_icon) { ERR_FAIL_INDEX(p_tab, tabs.size()); + + if (tabs[p_tab].right_button == p_icon) { + return; + } + tabs.write[p_tab].right_button = p_icon; _update_cache(); @@ -751,7 +771,7 @@ void TabBar::set_tab_button_icon(int p_tab, const Ref<Texture2D> &p_icon) { if (scroll_to_selected) { ensure_tab_visible(current); } - update(); + queue_redraw(); update_minimum_size(); } @@ -792,7 +812,7 @@ void TabBar::_update_hover() { } if (hover_buttons != -1) { - update(); + queue_redraw(); break; } } @@ -813,7 +833,7 @@ void TabBar::_update_hover() { cb_hover = hover_buttons; if (rb_hover != rb_hover_old || cb_hover != cb_hover_old) { - update(); + queue_redraw(); } } } @@ -824,14 +844,8 @@ void TabBar::_update_cache() { return; } - Ref<StyleBox> tab_disabled = get_theme_stylebox(SNAME("tab_disabled")); - Ref<StyleBox> tab_unselected = get_theme_stylebox(SNAME("tab_unselected")); - Ref<StyleBox> tab_selected = get_theme_stylebox(SNAME("tab_selected")); - Ref<Texture2D> incr = get_theme_icon(SNAME("increment")); - Ref<Texture2D> decr = get_theme_icon(SNAME("decrement")); - int limit = get_size().width; - int limit_minus_buttons = limit - incr->get_width() - decr->get_width(); + int limit_minus_buttons = limit - theme_cache.increment_icon->get_width() - theme_cache.decrement_icon->get_width(); int w = 0; @@ -915,7 +929,7 @@ void TabBar::_on_mouse_exited() { highlight_arrow = -1; dragging_valid_tab = false; - update(); + queue_redraw(); } void TabBar::add_tab(const String &p_str, const Ref<Texture2D> &p_icon) { @@ -930,7 +944,7 @@ void TabBar::add_tab(const String &p_str, const Ref<Texture2D> &p_icon) { if (scroll_to_selected) { ensure_tab_visible(current); } - update(); + queue_redraw(); update_minimum_size(); if (tabs.size() == 1 && is_inside_tree()) { @@ -949,7 +963,7 @@ void TabBar::clear_tabs() { current = 0; previous = 0; - update(); + queue_redraw(); update_minimum_size(); notify_property_list_changed(); } @@ -979,7 +993,7 @@ void TabBar::remove_tab(int p_idx) { } } - update(); + queue_redraw(); update_minimum_size(); notify_property_list_changed(); @@ -1127,7 +1141,7 @@ void TabBar::drop_data(const Point2 &p_point, const Variant &p_data) { set_current_tab(hover_now); } else { _update_cache(); - update(); + queue_redraw(); } update_minimum_size(); @@ -1155,10 +1169,15 @@ int TabBar::get_tab_idx_at_point(const Point2 &p_point) const { void TabBar::set_tab_alignment(AlignmentMode p_alignment) { ERR_FAIL_INDEX(p_alignment, ALIGNMENT_MAX); + + if (tab_alignment == p_alignment) { + return; + } + tab_alignment = p_alignment; _update_cache(); - update(); + queue_redraw(); } TabBar::AlignmentMode TabBar::get_tab_alignment() const { @@ -1180,7 +1199,7 @@ void TabBar::set_clip_tabs(bool p_clip_tabs) { if (scroll_to_selected) { ensure_tab_visible(current); } - update(); + queue_redraw(); update_minimum_size(); } @@ -1221,59 +1240,54 @@ void TabBar::move_tab(int p_from, int p_to) { if (scroll_to_selected) { ensure_tab_visible(current); } - update(); + queue_redraw(); notify_property_list_changed(); } int TabBar::get_tab_width(int p_idx) const { ERR_FAIL_INDEX_V(p_idx, tabs.size(), 0); - Ref<StyleBox> tab_unselected = get_theme_stylebox(SNAME("tab_unselected")); - Ref<StyleBox> tab_selected = get_theme_stylebox(SNAME("tab_selected")); - Ref<StyleBox> tab_disabled = get_theme_stylebox(SNAME("tab_disabled")); - int hseparation = get_theme_constant(SNAME("h_separation")); - Ref<StyleBox> style; if (tabs[p_idx].disabled) { - style = tab_disabled; + style = theme_cache.tab_disabled_style; } else if (current == p_idx) { - style = tab_selected; + style = theme_cache.tab_selected_style; } else { - style = tab_unselected; + style = theme_cache.tab_unselected_style; } int x = style->get_minimum_size().width; Ref<Texture2D> tex = tabs[p_idx].icon; if (tex.is_valid()) { - x += tex->get_width() + hseparation; + x += tex->get_width() + theme_cache.h_separation; } if (!tabs[p_idx].text.is_empty()) { - x += tabs[p_idx].size_text + hseparation; + x += tabs[p_idx].size_text + theme_cache.h_separation; } bool close_visible = cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && p_idx == current); if (tabs[p_idx].right_button.is_valid()) { - Ref<StyleBox> btn_style = get_theme_stylebox(SNAME("button_highlight")); + Ref<StyleBox> btn_style = theme_cache.button_hl_style; Ref<Texture2D> rb = tabs[p_idx].right_button; if (close_visible) { x += btn_style->get_minimum_size().width + rb->get_width(); } else { - x += btn_style->get_margin(SIDE_LEFT) + rb->get_width() + hseparation; + x += btn_style->get_margin(SIDE_LEFT) + rb->get_width() + theme_cache.h_separation; } } if (close_visible) { - Ref<StyleBox> btn_style = get_theme_stylebox(SNAME("button_highlight")); - Ref<Texture2D> cb = get_theme_icon(SNAME("close")); - x += btn_style->get_margin(SIDE_LEFT) + cb->get_width() + hseparation; + Ref<StyleBox> btn_style = theme_cache.button_hl_style; + Ref<Texture2D> cb = theme_cache.close_icon; + x += btn_style->get_margin(SIDE_LEFT) + cb->get_width() + theme_cache.h_separation; } if (x > style->get_minimum_size().width) { - x -= hseparation; + x -= theme_cache.h_separation; } return x; @@ -1284,9 +1298,7 @@ void TabBar::_ensure_no_over_offset() { return; } - Ref<Texture2D> incr = get_theme_icon(SNAME("increment")); - Ref<Texture2D> decr = get_theme_icon(SNAME("decrement")); - int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width(); + int limit_minus_buttons = get_size().width - theme_cache.increment_icon->get_width() - theme_cache.decrement_icon->get_width(); int prev_offset = offset; @@ -1307,7 +1319,7 @@ void TabBar::_ensure_no_over_offset() { if (prev_offset != offset) { _update_cache(); - update(); + queue_redraw(); } } @@ -1324,14 +1336,12 @@ void TabBar::ensure_tab_visible(int p_idx) { if (p_idx < offset) { offset = p_idx; _update_cache(); - update(); + queue_redraw(); return; } - Ref<Texture2D> incr = get_theme_icon(SNAME("increment")); - Ref<Texture2D> decr = get_theme_icon(SNAME("decrement")); - int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width(); + int limit_minus_buttons = get_size().width - theme_cache.increment_icon->get_width() - theme_cache.decrement_icon->get_width(); int total_w = tabs[max_drawn_tab].ofs_cache - tabs[offset].ofs_cache; for (int i = max_drawn_tab; i <= p_idx; i++) { @@ -1359,7 +1369,7 @@ void TabBar::ensure_tab_visible(int p_idx) { if (prev_offset != offset) { _update_cache(); - update(); + queue_redraw(); } } @@ -1374,6 +1384,11 @@ Rect2 TabBar::get_tab_rect(int p_tab) const { void TabBar::set_tab_close_display_policy(CloseButtonDisplayPolicy p_policy) { ERR_FAIL_INDEX(p_policy, CLOSE_BUTTON_MAX); + + if (cb_displaypolicy == p_policy) { + return; + } + cb_displaypolicy = p_policy; _update_cache(); @@ -1381,7 +1396,7 @@ void TabBar::set_tab_close_display_policy(CloseButtonDisplayPolicy p_policy) { if (scroll_to_selected) { ensure_tab_visible(current); } - update(); + queue_redraw(); update_minimum_size(); } @@ -1391,6 +1406,11 @@ TabBar::CloseButtonDisplayPolicy TabBar::get_tab_close_display_policy() const { void TabBar::set_max_tab_width(int p_width) { ERR_FAIL_COND(p_width < 0); + + if (max_width == p_width) { + return; + } + max_width = p_width; _update_cache(); @@ -1398,7 +1418,7 @@ void TabBar::set_max_tab_width(int p_width) { if (scroll_to_selected) { ensure_tab_visible(current); } - update(); + queue_redraw(); update_minimum_size(); } diff --git a/scene/gui/tab_bar.h b/scene/gui/tab_bar.h index d123385e47..ac4a6a195e 100644 --- a/scene/gui/tab_bar.h +++ b/scene/gui/tab_bar.h @@ -104,6 +104,34 @@ private: bool scroll_to_selected = true; int tabs_rearrange_group = -1; + struct ThemeCache { + int h_separation = 0; + + Ref<StyleBox> tab_unselected_style; + Ref<StyleBox> tab_selected_style; + Ref<StyleBox> tab_disabled_style; + + Ref<Texture2D> increment_icon; + Ref<Texture2D> increment_hl_icon; + Ref<Texture2D> decrement_icon; + Ref<Texture2D> decrement_hl_icon; + Ref<Texture2D> drop_mark_icon; + Color drop_mark_color; + + Ref<Font> font; + int font_size; + int outline_size = 0; + + Color font_selected_color; + Color font_unselected_color; + Color font_disabled_color; + Color font_outline_color; + + Ref<Texture2D> close_icon; + Ref<StyleBox> button_pressed_style; + Ref<StyleBox> button_hl_style; + } theme_cache; + int get_tab_width(int p_idx) const; void _ensure_no_over_offset(); @@ -117,6 +145,7 @@ private: protected: virtual void gui_input(const Ref<InputEvent> &p_event) override; + virtual void _update_theme_item_cache() override; bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index fa929344d4..ab4808d312 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -60,26 +60,24 @@ void TabContainer::gui_input(const Ref<InputEvent> &p_event) { } // Handle menu button. - Ref<Texture2D> menu = get_theme_icon(SNAME("menu")); - if (is_layout_rtl()) { - if (popup && pos.x < menu->get_width()) { + if (popup && pos.x < theme_cache.menu_icon->get_width()) { emit_signal(SNAME("pre_popup_pressed")); Vector2 popup_pos = get_screen_position(); - popup_pos.y += menu->get_height(); + popup_pos.y += theme_cache.menu_icon->get_height(); popup->set_position(popup_pos); popup->popup(); return; } } else { - if (popup && pos.x > size.width - menu->get_width()) { + if (popup && pos.x > size.width - theme_cache.menu_icon->get_width()) { emit_signal(SNAME("pre_popup_pressed")); Vector2 popup_pos = get_screen_position(); popup_pos.x += size.width - popup->get_size().width; - popup_pos.y += menu->get_height(); + popup_pos.y += theme_cache.menu_icon->get_height(); popup->set_position(popup_pos); popup->popup(); @@ -98,34 +96,33 @@ void TabContainer::gui_input(const Ref<InputEvent> &p_event) { if (pos.y > _get_top_margin()) { if (menu_hovered) { menu_hovered = false; - update(); + queue_redraw(); } return; } - Ref<Texture2D> menu = get_theme_icon(SNAME("menu")); if (popup) { if (is_layout_rtl()) { - if (pos.x <= menu->get_width()) { + if (pos.x <= theme_cache.menu_icon->get_width()) { if (!menu_hovered) { menu_hovered = true; - update(); + queue_redraw(); return; } } else if (menu_hovered) { menu_hovered = false; - update(); + queue_redraw(); } } else { - if (pos.x >= size.width - menu->get_width()) { + if (pos.x >= size.width - theme_cache.menu_icon->get_width()) { if (!menu_hovered) { menu_hovered = true; - update(); + queue_redraw(); return; } } else if (menu_hovered) { menu_hovered = false; - update(); + queue_redraw(); } } @@ -136,6 +133,41 @@ void TabContainer::gui_input(const Ref<InputEvent> &p_event) { } } +void TabContainer::_update_theme_item_cache() { + Container::_update_theme_item_cache(); + + theme_cache.side_margin = get_theme_constant(SNAME("side_margin")); + + theme_cache.panel_style = get_theme_stylebox(SNAME("panel")); + theme_cache.tabbar_style = get_theme_stylebox(SNAME("tabbar_background")); + + theme_cache.menu_icon = get_theme_icon(SNAME("menu")); + theme_cache.menu_hl_icon = get_theme_icon(SNAME("menu_highlight")); + + // TabBar overrides. + theme_cache.icon_separation = get_theme_constant(SNAME("icon_separation")); + theme_cache.outline_size = get_theme_constant(SNAME("outline_size")); + + theme_cache.tab_unselected_style = get_theme_stylebox(SNAME("tab_unselected")); + theme_cache.tab_selected_style = get_theme_stylebox(SNAME("tab_selected")); + theme_cache.tab_disabled_style = get_theme_stylebox(SNAME("tab_disabled")); + + theme_cache.increment_icon = get_theme_icon(SNAME("increment")); + theme_cache.increment_hl_icon = get_theme_icon(SNAME("increment_highlight")); + theme_cache.decrement_icon = get_theme_icon(SNAME("decrement")); + theme_cache.decrement_hl_icon = get_theme_icon(SNAME("decrement_highlight")); + theme_cache.drop_mark_icon = get_theme_icon(SNAME("drop_mark")); + theme_cache.drop_mark_color = get_theme_color(SNAME("drop_mark_color")); + + theme_cache.font_selected_color = get_theme_color(SNAME("font_selected_color")); + theme_cache.font_unselected_color = get_theme_color(SNAME("font_unselected_color")); + theme_cache.font_disabled_color = get_theme_color(SNAME("font_disabled_color")); + theme_cache.font_outline_color = get_theme_color(SNAME("font_outline_color")); + + theme_cache.tab_font = get_theme_font(SNAME("font")); + theme_cache.tab_font_size = get_theme_font_size(SNAME("font_size")); +} + void TabContainer::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { @@ -155,27 +187,26 @@ void TabContainer::_notification(int p_what) { Size2 size = get_size(); // Draw only the tab area if the header is hidden. - Ref<StyleBox> panel = get_theme_stylebox(SNAME("panel")); if (!tabs_visible) { - panel->draw(canvas, Rect2(0, 0, size.width, size.height)); + theme_cache.panel_style->draw(canvas, Rect2(0, 0, size.width, size.height)); return; } int header_height = _get_top_margin(); - panel->draw(canvas, Rect2(0, header_height, size.width, size.height - header_height)); + // Draw background for the tabbar. + theme_cache.tabbar_style->draw(canvas, Rect2(0, 0, size.width, header_height)); + // Draw the background for the tab's content. + theme_cache.panel_style->draw(canvas, Rect2(0, header_height, size.width, size.height - header_height)); // Draw the popup menu. if (get_popup()) { - Ref<Texture2D> menu = get_theme_icon(SNAME("menu")); - Ref<Texture2D> menu_hl = get_theme_icon(SNAME("menu_highlight")); - - int x = is_layout_rtl() ? 0 : get_size().width - menu->get_width(); + int x = is_layout_rtl() ? 0 : get_size().width - theme_cache.menu_icon->get_width(); if (menu_hovered) { - menu_hl->draw(get_canvas_item(), Point2(x, (header_height - menu_hl->get_height()) / 2)); + theme_cache.menu_hl_icon->draw(get_canvas_item(), Point2(x, (header_height - theme_cache.menu_hl_icon->get_height()) / 2)); } else { - menu->draw(get_canvas_item(), Point2(x, (header_height - menu->get_height()) / 2)); + theme_cache.menu_icon->draw(get_canvas_item(), Point2(x, (header_height - theme_cache.menu_icon->get_height()) / 2)); } } } break; @@ -194,23 +225,27 @@ void TabContainer::_on_theme_changed() { return; } - tab_bar->add_theme_style_override(SNAME("tab_unselected"), get_theme_stylebox(SNAME("tab_unselected"))); - tab_bar->add_theme_style_override(SNAME("tab_selected"), get_theme_stylebox(SNAME("tab_selected"))); - tab_bar->add_theme_style_override(SNAME("tab_disabled"), get_theme_stylebox(SNAME("tab_disabled"))); - tab_bar->add_theme_icon_override(SNAME("increment"), get_theme_icon(SNAME("increment"))); - tab_bar->add_theme_icon_override(SNAME("increment_highlight"), get_theme_icon(SNAME("increment_highlight"))); - tab_bar->add_theme_icon_override(SNAME("decrement"), get_theme_icon(SNAME("decrement"))); - tab_bar->add_theme_icon_override(SNAME("decrement_highlight"), get_theme_icon(SNAME("decrement_highlight"))); - tab_bar->add_theme_icon_override(SNAME("drop_mark"), get_theme_icon(SNAME("drop_mark"))); - tab_bar->add_theme_color_override(SNAME("drop_mark_color"), get_theme_color(SNAME("drop_mark_color"))); - tab_bar->add_theme_color_override(SNAME("font_selected_color"), get_theme_color(SNAME("font_selected_color"))); - tab_bar->add_theme_color_override(SNAME("font_unselected_color"), get_theme_color(SNAME("font_unselected_color"))); - tab_bar->add_theme_color_override(SNAME("font_disabled_color"), get_theme_color(SNAME("font_disabled_color"))); - tab_bar->add_theme_color_override(SNAME("font_outline_color"), get_theme_color(SNAME("font_outline_color"))); - tab_bar->add_theme_font_override(SNAME("font"), get_theme_font(SNAME("font"))); - tab_bar->add_theme_font_size_override(SNAME("font_size"), get_theme_font_size(SNAME("font_size"))); - tab_bar->add_theme_constant_override(SNAME("h_separation"), get_theme_constant(SNAME("icon_separation"))); - tab_bar->add_theme_constant_override(SNAME("outline_size"), get_theme_constant(SNAME("outline_size"))); + tab_bar->add_theme_style_override(SNAME("tab_unselected"), theme_cache.tab_unselected_style); + tab_bar->add_theme_style_override(SNAME("tab_selected"), theme_cache.tab_selected_style); + tab_bar->add_theme_style_override(SNAME("tab_disabled"), theme_cache.tab_disabled_style); + + tab_bar->add_theme_icon_override(SNAME("increment"), theme_cache.increment_icon); + tab_bar->add_theme_icon_override(SNAME("increment_highlight"), theme_cache.increment_hl_icon); + tab_bar->add_theme_icon_override(SNAME("decrement"), theme_cache.decrement_icon); + tab_bar->add_theme_icon_override(SNAME("decrement_highlight"), theme_cache.decrement_hl_icon); + tab_bar->add_theme_icon_override(SNAME("drop_mark"), theme_cache.drop_mark_icon); + tab_bar->add_theme_color_override(SNAME("drop_mark_color"), theme_cache.drop_mark_color); + + tab_bar->add_theme_color_override(SNAME("font_selected_color"), theme_cache.font_selected_color); + tab_bar->add_theme_color_override(SNAME("font_unselected_color"), theme_cache.font_unselected_color); + tab_bar->add_theme_color_override(SNAME("font_disabled_color"), theme_cache.font_disabled_color); + tab_bar->add_theme_color_override(SNAME("font_outline_color"), theme_cache.font_outline_color); + + tab_bar->add_theme_font_override(SNAME("font"), theme_cache.tab_font); + tab_bar->add_theme_font_size_override(SNAME("font_size"), theme_cache.tab_font_size); + + tab_bar->add_theme_constant_override(SNAME("h_separation"), theme_cache.icon_separation); + tab_bar->add_theme_constant_override(SNAME("outline_size"), theme_cache.outline_size); _update_margins(); if (get_tab_count() > 0) { @@ -218,13 +253,12 @@ void TabContainer::_on_theme_changed() { } else { update_minimum_size(); } - update(); + queue_redraw(); theme_changing = false; } void TabContainer::_repaint() { - Ref<StyleBox> sb = get_theme_stylebox(SNAME("panel")); Vector<Control *> controls = _get_tab_controls(); int current = get_current_tab(); @@ -233,16 +267,16 @@ void TabContainer::_repaint() { if (i == current) { c->show(); - c->set_anchors_and_offsets_preset(Control::PRESET_WIDE); + c->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); if (tabs_visible) { c->set_offset(SIDE_TOP, _get_top_margin()); } - c->set_offset(SIDE_TOP, c->get_offset(SIDE_TOP) + sb->get_margin(SIDE_TOP)); - c->set_offset(SIDE_LEFT, c->get_offset(SIDE_LEFT) + sb->get_margin(SIDE_LEFT)); - c->set_offset(SIDE_RIGHT, c->get_offset(SIDE_RIGHT) - sb->get_margin(SIDE_RIGHT)); - c->set_offset(SIDE_BOTTOM, c->get_offset(SIDE_BOTTOM) - sb->get_margin(SIDE_BOTTOM)); + c->set_offset(SIDE_TOP, c->get_offset(SIDE_TOP) + theme_cache.panel_style->get_margin(SIDE_TOP)); + c->set_offset(SIDE_LEFT, c->get_offset(SIDE_LEFT) + theme_cache.panel_style->get_margin(SIDE_LEFT)); + c->set_offset(SIDE_RIGHT, c->get_offset(SIDE_RIGHT) - theme_cache.panel_style->get_margin(SIDE_RIGHT)); + c->set_offset(SIDE_BOTTOM, c->get_offset(SIDE_BOTTOM) - theme_cache.panel_style->get_margin(SIDE_BOTTOM)); } else { c->hide(); } @@ -252,8 +286,7 @@ void TabContainer::_repaint() { } void TabContainer::_update_margins() { - int menu_width = get_theme_icon(SNAME("menu"))->get_width(); - int side_margin = get_theme_constant(SNAME("side_margin")); + int menu_width = theme_cache.menu_icon->get_width(); // Directly check for validity, to avoid errors when quitting. bool has_popup = popup_obj_id.is_valid(); @@ -267,7 +300,7 @@ void TabContainer::_update_margins() { switch (get_tab_alignment()) { case TabBar::ALIGNMENT_LEFT: { - tab_bar->set_offset(SIDE_LEFT, side_margin); + tab_bar->set_offset(SIDE_LEFT, theme_cache.side_margin); tab_bar->set_offset(SIDE_RIGHT, has_popup ? -menu_width : 0); } break; @@ -289,10 +322,10 @@ void TabContainer::_update_margins() { int total_tabs_width = last_tab_rect.position.x - first_tab_pos + last_tab_rect.size.width; // Calculate if all the tabs would still fit if the margin was present. - if (get_clip_tabs() && (tab_bar->get_offset_buttons_visible() || (get_tab_count() > 1 && (total_tabs_width + side_margin) > get_size().width))) { + if (get_clip_tabs() && (tab_bar->get_offset_buttons_visible() || (get_tab_count() > 1 && (total_tabs_width + theme_cache.side_margin) > get_size().width))) { tab_bar->set_offset(SIDE_RIGHT, has_popup ? -menu_width : 0); } else { - tab_bar->set_offset(SIDE_RIGHT, -side_margin); + tab_bar->set_offset(SIDE_RIGHT, -theme_cache.side_margin); } } break; @@ -304,7 +337,7 @@ void TabContainer::_update_margins() { void TabContainer::_on_mouse_exited() { if (menu_hovered) { menu_hovered = false; - update(); + queue_redraw(); } } @@ -312,7 +345,7 @@ Vector<Control *> TabContainer::_get_tab_controls() const { Vector<Control *> controls; for (int i = 0; i < get_child_count(); i++) { Control *control = Object::cast_to<Control>(get_child(i)); - if (!control || control->is_set_as_top_level() || control == tab_bar) { + if (!control || control->is_set_as_top_level() || control == tab_bar || control == child_removing) { continue; } @@ -486,12 +519,12 @@ void TabContainer::_refresh_tab_names() { } void TabContainer::add_child_notify(Node *p_child) { + Container::add_child_notify(p_child); + if (p_child == tab_bar) { return; } - Container::add_child_notify(p_child); - Control *c = Object::cast_to<Control>(p_child); if (!c || c->is_set_as_top_level()) { return; @@ -502,7 +535,7 @@ void TabContainer::add_child_notify(Node *p_child) { _update_margins(); if (get_tab_count() == 1) { - update(); + queue_redraw(); } p_child->connect("renamed", callable_mp(this, &TabContainer::_refresh_tab_names)); @@ -549,11 +582,16 @@ void TabContainer::remove_child_notify(Node *p_child) { return; } - tab_bar->remove_tab(get_tab_idx_from_control(c)); + int idx = get_tab_idx_from_control(c); + + // Before this, the tab control has not changed; after this, the tab control has changed. + child_removing = p_child; + tab_bar->remove_tab(idx); + child_removing = nullptr; _update_margins(); if (get_tab_count() == 0) { - update(); + queue_redraw(); } p_child->remove_meta("_tab_name"); @@ -613,6 +651,10 @@ int TabContainer::get_tab_idx_from_control(Control *p_child) const { } void TabContainer::set_tab_alignment(TabBar::AlignmentMode p_alignment) { + if (tab_bar->get_tab_alignment() == p_alignment) { + return; + } + tab_bar->set_tab_alignment(p_alignment); _update_margins(); } @@ -647,7 +689,7 @@ void TabContainer::set_tabs_visible(bool p_visible) { } } - update(); + queue_redraw(); update_minimum_size(); } @@ -674,6 +716,10 @@ void TabContainer::set_tab_title(int p_tab, const String &p_title) { Control *child = get_tab_control(p_tab); ERR_FAIL_COND(!child); + if (tab_bar->get_tab_title(p_tab) == p_title) { + return; + } + tab_bar->set_tab_title(p_tab, p_title); if (p_title == child->get_name()) { @@ -693,6 +739,10 @@ String TabContainer::get_tab_title(int p_tab) const { } void TabContainer::set_tab_icon(int p_tab, const Ref<Texture2D> &p_icon) { + if (tab_bar->get_tab_icon(p_tab) == p_icon) { + return; + } + tab_bar->set_tab_icon(p_tab, p_icon); _update_margins(); @@ -704,6 +754,10 @@ Ref<Texture2D> TabContainer::get_tab_icon(int p_tab) const { } void TabContainer::set_tab_disabled(int p_tab, bool p_disabled) { + if (tab_bar->is_tab_disabled(p_tab) == p_disabled) { + return; + } + tab_bar->set_tab_disabled(p_tab, p_disabled); _update_margins(); @@ -720,6 +774,10 @@ void TabContainer::set_tab_hidden(int p_tab, bool p_hidden) { Control *child = get_tab_control(p_tab); ERR_FAIL_COND(!child); + if (tab_bar->is_tab_hidden(p_tab) == p_hidden) { + return; + } + tab_bar->set_tab_hidden(p_tab, p_hidden); child->hide(); @@ -769,19 +827,18 @@ Size2 TabContainer::get_minimum_size() const { if (!get_clip_tabs()) { if (get_popup()) { - ms.x += get_theme_icon(SNAME("menu"))->get_width(); + ms.x += theme_cache.menu_icon->get_width(); } - int side_margin = get_theme_constant(SNAME("side_margin")); - if (side_margin > 0 && get_tab_alignment() != TabBar::ALIGNMENT_CENTER && + if (theme_cache.side_margin > 0 && get_tab_alignment() != TabBar::ALIGNMENT_CENTER && (get_tab_alignment() != TabBar::ALIGNMENT_RIGHT || !get_popup())) { - ms.x += side_margin; + ms.x += theme_cache.side_margin; } } } Vector<Control *> controls = _get_tab_controls(); - int max_control_height = 0; + Size2 largest_child_min_size; for (int i = 0; i < controls.size(); i++) { Control *c = controls[i]; @@ -790,13 +847,14 @@ Size2 TabContainer::get_minimum_size() const { } Size2 cms = c->get_combined_minimum_size(); - ms.x = MAX(ms.x, cms.x); - max_control_height = MAX(max_control_height, cms.y); + largest_child_min_size.x = MAX(largest_child_min_size.x, cms.x); + largest_child_min_size.y = MAX(largest_child_min_size.y, cms.y); } - ms.y += max_control_height; + ms.y += largest_child_min_size.y; + + Size2 panel_ms = theme_cache.panel_style->get_minimum_size(); - Size2 panel_ms = get_theme_stylebox(SNAME("panel"))->get_minimum_size(); - ms.x = MAX(ms.x, panel_ms.x); + ms.x = MAX(ms.x, largest_child_min_size.x + panel_ms.x); ms.y += panel_ms.y; return ms; @@ -806,10 +864,14 @@ void TabContainer::set_popup(Node *p_popup) { bool had_popup = get_popup(); Popup *popup = Object::cast_to<Popup>(p_popup); - popup_obj_id = popup ? popup->get_instance_id() : ObjectID(); + ObjectID popup_id = popup ? popup->get_instance_id() : ObjectID(); + if (popup_obj_id == popup_id) { + return; + } + popup_obj_id = popup_id; if (had_popup != bool(popup)) { - update(); + queue_redraw(); _update_margins(); if (!get_clip_tabs()) { update_minimum_size(); @@ -850,6 +912,10 @@ int TabContainer::get_tabs_rearrange_group() const { } void TabContainer::set_use_hidden_tabs_for_min_size(bool p_use_hidden_tabs) { + if (use_hidden_tabs_for_min_size == p_use_hidden_tabs) { + return; + } + use_hidden_tabs_for_min_size = p_use_hidden_tabs; update_minimum_size(); } diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h index 9adaa0d844..b552aa459b 100644 --- a/scene/gui/tab_container.h +++ b/scene/gui/tab_container.h @@ -46,6 +46,40 @@ class TabContainer : public Container { bool drag_to_rearrange_enabled = false; bool use_hidden_tabs_for_min_size = false; bool theme_changing = false; + Node *child_removing = nullptr; + + struct ThemeCache { + int side_margin = 0; + + Ref<StyleBox> panel_style; + Ref<StyleBox> tabbar_style; + + Ref<Texture2D> menu_icon; + Ref<Texture2D> menu_hl_icon; + + // TabBar overrides. + int icon_separation = 0; + int outline_size = 0; + + Ref<StyleBox> tab_unselected_style; + Ref<StyleBox> tab_selected_style; + Ref<StyleBox> tab_disabled_style; + + Ref<Texture2D> increment_icon; + Ref<Texture2D> increment_hl_icon; + Ref<Texture2D> decrement_icon; + Ref<Texture2D> decrement_hl_icon; + Ref<Texture2D> drop_mark_icon; + Color drop_mark_color; + + Color font_selected_color; + Color font_unselected_color; + Color font_disabled_color; + Color font_outline_color; + + Ref<Font> tab_font; + int tab_font_size; + } theme_cache; int _get_top_margin() const; Vector<Control *> _get_tab_controls() const; @@ -64,6 +98,8 @@ class TabContainer : public Container { protected: virtual void gui_input(const Ref<InputEvent> &p_event) override; + virtual void _update_theme_item_cache() override; + void _notification(int p_what); virtual void add_child_notify(Node *p_child) override; virtual void move_child_notify(Node *p_child) override; diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 9c6cd6bdb2..38302136d6 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -443,20 +443,22 @@ void TextEdit::_notification(int p_what) { case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: case NOTIFICATION_TRANSLATION_CHANGED: case NOTIFICATION_THEME_CHANGED: { - _update_caches(); - _update_wrap_at_column(true); + if (is_inside_tree()) { + _update_caches(); + _update_wrap_at_column(true); + } } break; case NOTIFICATION_WM_WINDOW_FOCUS_IN: { window_has_focus = true; draw_caret = true; - update(); + queue_redraw(); } break; case NOTIFICATION_WM_WINDOW_FOCUS_OUT: { window_has_focus = false; draw_caret = false; - update(); + queue_redraw(); } break; case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { @@ -1055,7 +1057,7 @@ void TextEdit::_notification(int p_what) { const Variant *argp[] = { &args[0], &args[1], &args[2] }; Callable::CallError ce; Variant ret; - gutter.custom_draw_callback.call(argp, 3, ret, ce); + gutter.custom_draw_callback.callp(argp, 3, ret, ce); } } break; } @@ -1199,13 +1201,14 @@ void TextEdit::_notification(int p_what) { current_color.a = font_readonly_color.a; } } + Color gl_color = current_color; if (selection.active && line >= selection.from_line && line <= selection.to_line) { // Selection int sel_from = (line > selection.from_line) ? TS->shaped_text_get_range(rid).x : selection.from_column; int sel_to = (line < selection.to_line) ? TS->shaped_text_get_range(rid).y : selection.to_column; if (glyphs[j].start >= sel_from && glyphs[j].end <= sel_to && override_selected_font_color) { - current_color = font_selected_color; + gl_color = font_selected_color; } } @@ -1215,29 +1218,29 @@ void TextEdit::_notification(int p_what) { if ((brace_open_match_line == line && brace_open_match_column == glyphs[j].start) || (caret.column == glyphs[j].start && caret.line == line && caret_wrap_index == line_wrap_index && (brace_open_matching || brace_open_mismatch))) { if (brace_open_mismatch) { - current_color = brace_mismatch_color; + gl_color = brace_mismatch_color; } - Rect2 rect = Rect2(char_pos, ofs_y + font->get_underline_position(font_size), glyphs[j].advance * glyphs[j].repeat, font->get_underline_thickness(font_size)); - draw_rect(rect, current_color); + Rect2 rect = Rect2(char_pos, ofs_y + font->get_underline_position(font_size), glyphs[j].advance * glyphs[j].repeat, MAX(font->get_underline_thickness(font_size) * get_theme_default_base_scale(), 1)); + draw_rect(rect, gl_color); } if ((brace_close_match_line == line && brace_close_match_column == glyphs[j].start) || (caret.column == glyphs[j].start + 1 && caret.line == line && caret_wrap_index == line_wrap_index && (brace_close_matching || brace_close_mismatch))) { if (brace_close_mismatch) { - current_color = brace_mismatch_color; + gl_color = brace_mismatch_color; } - Rect2 rect = Rect2(char_pos, ofs_y + font->get_underline_position(font_size), glyphs[j].advance * glyphs[j].repeat, font->get_underline_thickness(font_size)); - draw_rect(rect, current_color); + Rect2 rect = Rect2(char_pos, ofs_y + font->get_underline_position(font_size), glyphs[j].advance * glyphs[j].repeat, MAX(font->get_underline_thickness(font_size) * get_theme_default_base_scale(), 1)); + draw_rect(rect, gl_color); } } if (draw_tabs && ((glyphs[j].flags & TextServer::GRAPHEME_IS_TAB) == TextServer::GRAPHEME_IS_TAB)) { int yofs = (text_height - tab_icon->get_height()) / 2 - ldata->get_line_ascent(line_wrap_index); - tab_icon->draw(ci, Point2(char_pos, ofs_y + yofs), current_color); + tab_icon->draw(ci, Point2(char_pos, ofs_y + yofs), gl_color); } else if (draw_spaces && ((glyphs[j].flags & TextServer::GRAPHEME_IS_SPACE) == TextServer::GRAPHEME_IS_SPACE)) { int yofs = (text_height - space_icon->get_height()) / 2 - ldata->get_line_ascent(line_wrap_index); int xofs = (glyphs[j].advance * glyphs[j].repeat - space_icon->get_width()) / 2; - space_icon->draw(ci, Point2(char_pos + xofs, ofs_y + yofs), current_color); + space_icon->draw(ci, Point2(char_pos + xofs, ofs_y + yofs), gl_color); } } @@ -1245,10 +1248,10 @@ void TextEdit::_notification(int p_what) { for (int k = 0; k < glyphs[j].repeat; k++) { if (!clipped && (char_ofs + char_margin) >= xmargin_beg && (char_ofs + glyphs[j].advance + char_margin) <= xmargin_end) { if (glyphs[j].font_rid != RID()) { - TS->font_draw_glyph(glyphs[j].font_rid, ci, glyphs[j].font_size, Vector2(char_margin + char_ofs + ofs_x + glyphs[j].x_off, ofs_y + glyphs[j].y_off), glyphs[j].index, current_color); + TS->font_draw_glyph(glyphs[j].font_rid, ci, glyphs[j].font_size, Vector2(char_margin + char_ofs + ofs_x + glyphs[j].x_off, ofs_y + glyphs[j].y_off), glyphs[j].index, gl_color); had_glyphs_drawn = true; } else if ((glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) { - TS->draw_hex_code_box(ci, glyphs[j].font_size, Vector2(char_margin + char_ofs + ofs_x + glyphs[j].x_off, ofs_y + glyphs[j].y_off), glyphs[j].index, current_color); + TS->draw_hex_code_box(ci, glyphs[j].font_size, Vector2(char_margin + char_ofs + ofs_x + glyphs[j].x_off, ofs_y + glyphs[j].y_off), glyphs[j].index, gl_color); had_glyphs_drawn = true; } } @@ -1464,7 +1467,7 @@ void TextEdit::_notification(int p_what) { caret_end = caret_start + post_text.length(); } - DisplayServer::get_singleton()->virtual_keyboard_show(get_text(), get_global_rect(), true, -1, caret_start, caret_end); + DisplayServer::get_singleton()->virtual_keyboard_show(get_text(), get_global_rect(), DisplayServer::KEYBOARD_TYPE_MULTILINE, -1, caret_start, caret_end); } } break; @@ -1505,7 +1508,7 @@ void TextEdit::_notification(int p_what) { } text.invalidate_cache(caret.line, caret.column, true, t, structured_text_parser(st_parser, st_args, t)); - update(); + queue_redraw(); } } break; @@ -1612,7 +1615,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { } if (mb->is_pressed()) { - if (mb->get_button_index() == MouseButton::WHEEL_UP && !mb->is_command_pressed()) { + if (mb->get_button_index() == MouseButton::WHEEL_UP && !mb->is_command_or_control_pressed()) { if (mb->is_shift_pressed()) { h_scroll->set_value(h_scroll->get_value() - (100 * mb->get_factor())); } else if (mb->is_alt_pressed()) { @@ -1623,7 +1626,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { _scroll_up(3 * mb->get_factor()); } } - if (mb->get_button_index() == MouseButton::WHEEL_DOWN && !mb->is_command_pressed()) { + if (mb->get_button_index() == MouseButton::WHEEL_DOWN && !mb->is_command_or_control_pressed()) { if (mb->is_shift_pressed()) { h_scroll->set_value(h_scroll->get_value() + (100 * mb->get_factor())); } else if (mb->is_alt_pressed()) { @@ -1694,7 +1697,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { } selection.selecting_line = prev_line; selection.selecting_column = prev_col; - update(); + queue_redraw(); } else { if (caret.line < selection.selecting_line || (caret.line == selection.selecting_line && caret.column < selection.selecting_column)) { if (selection.shiftclick_left) { @@ -1716,7 +1719,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { selection.active = false; } - update(); + queue_redraw(); } } else if (drag_and_drop_selection_enabled && is_mouse_over_selection()) { selection.selecting_mode = SelectionMode::SELECTION_MODE_NONE; @@ -1744,7 +1747,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { last_dblclk = OS::get_singleton()->get_ticks_msec(); last_dblclk_pos = mb->get_position(); } - update(); + queue_redraw(); } if (is_middle_mouse_paste_enabled() && mb->get_button_index() == MouseButton::MIDDLE && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) { @@ -1878,7 +1881,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { if (current_hovered_gutter != hovered_gutter) { hovered_gutter = current_hovered_gutter; - update(); + queue_redraw(); } if (drag_action && can_drop_data(mpos, get_viewport()->gui_get_drag_data())) { @@ -1918,7 +1921,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { // Allow unicode handling if: // * No Modifiers are pressed (except shift) - bool allow_unicode_handling = !(k->is_command_pressed() || k->is_ctrl_pressed() || k->is_alt_pressed() || k->is_meta_pressed()); + bool allow_unicode_handling = !(k->is_command_or_control_pressed() || k->is_ctrl_pressed() || k->is_alt_pressed() || k->is_meta_pressed()); selection.selecting_text = false; @@ -2144,7 +2147,7 @@ void TextEdit::_swap_current_input_direction() { input_direction = TEXT_DIRECTION_LTR; } set_caret_column(caret.column); - update(); + queue_redraw(); } void TextEdit::_new_line(bool p_split_current_line, bool p_above) { @@ -2525,7 +2528,7 @@ void TextEdit::_delete(bool p_word, bool p_all_to_right) { } _remove_text(caret.line, caret.column, next_line, next_column); - update(); + queue_redraw(); } void TextEdit::_move_caret_document_start(bool p_select) { @@ -2789,7 +2792,7 @@ String TextEdit::get_tooltip(const Point2 &p_pos) const { const Variant *argp[] = { &args[0] }; Callable::CallError ce; Variant ret; - tooltip_callback.call(argp, 1, ret, ce); + tooltip_callback.callp(argp, 1, ret, ce); ERR_FAIL_COND_V_MSG(ce.error != Callable::CallError::CALL_OK, "", "Failed to call custom tooltip."); return ret; } @@ -2814,7 +2817,7 @@ void TextEdit::set_editable(const bool p_editable) { editable = p_editable; - update(); + queue_redraw(); } bool TextEdit::is_editable() const { @@ -2844,7 +2847,7 @@ void TextEdit::set_text_direction(Control::TextDirection p_text_direction) { menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_LTR), text_direction == TEXT_DIRECTION_LTR); menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_RTL), text_direction == TEXT_DIRECTION_RTL); } - update(); + queue_redraw(); } } @@ -2864,7 +2867,7 @@ void TextEdit::set_language(const String &p_language) { text.set_direction_and_language(dir, (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale()); text.invalidate_all(); _update_placeholder(); - update(); + queue_redraw(); } } @@ -2878,7 +2881,7 @@ void TextEdit::set_structured_text_bidi_override(TextServer::StructuredTextParse for (int i = 0; i < text.size(); i++) { text.set(i, text[i], structured_text_parser(st_parser, st_args, text[i])); } - update(); + queue_redraw(); } } @@ -2887,11 +2890,15 @@ TextServer::StructuredTextParser TextEdit::get_structured_text_bidi_override() c } void TextEdit::set_structured_text_bidi_override_options(Array p_args) { + if (st_args == p_args) { + return; + } + st_args = p_args; for (int i = 0; i < text.size(); i++) { text.set(i, text[i], structured_text_parser(st_parser, st_args, text[i])); } - update(); + queue_redraw(); } Array TextEdit::get_structured_text_bidi_override_options() const { @@ -2906,7 +2913,7 @@ void TextEdit::set_tab_size(const int p_size) { text.set_tab_size(p_size); text.invalidate_all_lines(); _update_placeholder(); - update(); + queue_redraw(); } int TextEdit::get_tab_size() const { @@ -2915,8 +2922,12 @@ int TextEdit::get_tab_size() const { // User controls void TextEdit::set_overtype_mode_enabled(const bool p_enabled) { + if (overtype_mode == p_enabled) { + return; + } + overtype_mode = p_enabled; - update(); + queue_redraw(); } bool TextEdit::is_overtype_mode_enabled() const { @@ -3012,7 +3023,7 @@ void TextEdit::set_text(const String &p_text) { set_caret_line(0); set_caret_column(0); - update(); + queue_redraw(); setting_text = false; emit_signal(SNAME("text_set")); } @@ -3034,9 +3045,13 @@ int TextEdit::get_line_count() const { } void TextEdit::set_placeholder(const String &p_text) { + if (placeholder_text == p_text) { + return; + } + placeholder_text = p_text; _update_placeholder(); - update(); + queue_redraw(); } String TextEdit::get_placeholder() const { @@ -3135,7 +3150,7 @@ void TextEdit::insert_line_at(int p_at, const String &p_text) { ++selection.to_line; } } - update(); + queue_redraw(); } void TextEdit::insert_text_at_caret(const String &p_text) { @@ -3152,7 +3167,7 @@ void TextEdit::insert_text_at_caret(const String &p_text) { set_caret_line(new_line, false); set_caret_column(new_column); - update(); + queue_redraw(); if (had_selection) { end_complex_operation(); @@ -3543,7 +3558,7 @@ void TextEdit::undo() { set_caret_line(undo_stack_pos->get().from_line, false); set_caret_column(undo_stack_pos->get().from_column); } - update(); + queue_redraw(); } void TextEdit::redo() { @@ -3578,7 +3593,7 @@ void TextEdit::redo() { set_caret_line(undo_stack_pos->get().to_line, false); set_caret_column(undo_stack_pos->get().to_column); undo_stack_pos = undo_stack_pos->next(); - update(); + queue_redraw(); } void TextEdit::clear_undo_history() { @@ -3943,8 +3958,12 @@ bool TextEdit::is_mouse_over_selection(bool p_edges) const { /* Caret */ void TextEdit::set_caret_type(CaretType p_type) { + if (caret_type == p_type) { + return; + } + caret_type = p_type; - update(); + queue_redraw(); } TextEdit::CaretType TextEdit::get_caret_type() const { @@ -3952,6 +3971,10 @@ TextEdit::CaretType TextEdit::get_caret_type() const { } void TextEdit::set_caret_blink_enabled(const bool p_enabled) { + if (caret_blink_enabled == p_enabled) { + return; + } + caret_blink_enabled = p_enabled; if (has_focus()) { @@ -3968,13 +3991,13 @@ bool TextEdit::is_caret_blink_enabled() const { return caret_blink_enabled; } -float TextEdit::get_caret_blink_speed() const { +float TextEdit::get_caret_blink_interval() const { return caret_blink_timer->get_wait_time(); } -void TextEdit::set_caret_blink_speed(const float p_speed) { - ERR_FAIL_COND(p_speed <= 0); - caret_blink_timer->set_wait_time(p_speed); +void TextEdit::set_caret_blink_interval(const float p_interval) { + ERR_FAIL_COND(p_interval <= 0); + caret_blink_timer->set_wait_time(p_interval); } void TextEdit::set_move_caret_on_right_click_enabled(const bool p_enabled) { @@ -4112,6 +4135,10 @@ String TextEdit::get_word_under_caret() const { /* Selection. */ void TextEdit::set_selecting_enabled(const bool p_enabled) { + if (selecting_enabled == p_enabled) { + return; + } + selecting_enabled = p_enabled; if (!selecting_enabled) { @@ -4124,6 +4151,10 @@ bool TextEdit::is_selecting_enabled() const { } void TextEdit::set_deselect_on_focus_loss_enabled(const bool p_enabled) { + if (deselect_on_focus_loss_enabled == p_enabled) { + return; + } + deselect_on_focus_loss_enabled = p_enabled; if (p_enabled && selection.active && !has_focus()) { deselect(); @@ -4187,7 +4218,7 @@ void TextEdit::select_all() { selection.shiftclick_left = true; set_caret_line(selection.to_line, false); set_caret_column(selection.to_column, false); - update(); + queue_redraw(); } void TextEdit::select_word_under_caret() { @@ -4282,7 +4313,7 @@ void TextEdit::select(int p_from_line, int p_from_column, int p_to_line, int p_t selection.shiftclick_left = true; } - update(); + queue_redraw(); } bool TextEdit::has_selection() const { @@ -4329,7 +4360,7 @@ int TextEdit::get_selection_to_column() const { void TextEdit::deselect() { selection.active = false; - update(); + queue_redraw(); } void TextEdit::delete_selection() { @@ -4342,7 +4373,7 @@ void TextEdit::delete_selection() { _remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column); set_caret_line(selection.from_line, false, false); set_caret_column(selection.from_column); - update(); + queue_redraw(); } /* Line wrapping. */ @@ -4429,8 +4460,12 @@ bool TextEdit::is_smooth_scroll_enabled() const { } void TextEdit::set_scroll_past_end_of_file_enabled(const bool p_enabled) { + if (scroll_past_end_of_file_enabled == p_enabled) { + return; + } + scroll_past_end_of_file_enabled = p_enabled; - update(); + queue_redraw(); } bool TextEdit::is_scroll_past_end_of_file_enabled() const { @@ -4654,7 +4689,7 @@ void TextEdit::adjust_viewport_to_caret() { } h_scroll->set_value(caret.x_ofs); - update(); + queue_redraw(); } void TextEdit::center_viewport_to_caret() { @@ -4707,16 +4742,18 @@ void TextEdit::center_viewport_to_caret() { } h_scroll->set_value(caret.x_ofs); - update(); + queue_redraw(); } /* Minimap */ void TextEdit::set_draw_minimap(bool p_enabled) { - if (draw_minimap != p_enabled) { - draw_minimap = p_enabled; - _update_wrap_at_column(); + if (draw_minimap == p_enabled) { + return; } - update(); + + draw_minimap = p_enabled; + _update_wrap_at_column(); + queue_redraw(); } bool TextEdit::is_drawing_minimap() const { @@ -4724,11 +4761,13 @@ bool TextEdit::is_drawing_minimap() const { } void TextEdit::set_minimap_width(int p_minimap_width) { - if (minimap_width != p_minimap_width) { - minimap_width = p_minimap_width; - _update_wrap_at_column(); + if (minimap_width == p_minimap_width) { + return; } - update(); + + minimap_width = p_minimap_width; + _update_wrap_at_column(); + queue_redraw(); } int TextEdit::get_minimap_width() const { @@ -4748,8 +4787,11 @@ void TextEdit::add_gutter(int p_at) { } text.add_gutter(p_at); + + _update_gutter_width(); + emit_signal(SNAME("gutter_added")); - update(); + queue_redraw(); } void TextEdit::remove_gutter(int p_gutter) { @@ -4758,8 +4800,11 @@ void TextEdit::remove_gutter(int p_gutter) { gutters.remove_at(p_gutter); text.remove_gutter(p_gutter); + + _update_gutter_width(); + emit_signal(SNAME("gutter_removed")); - update(); + queue_redraw(); } int TextEdit::get_gutter_count() const { @@ -4778,8 +4823,13 @@ String TextEdit::get_gutter_name(int p_gutter) const { void TextEdit::set_gutter_type(int p_gutter, GutterType p_type) { ERR_FAIL_INDEX(p_gutter, gutters.size()); + + if (gutters[p_gutter].type == p_type) { + return; + } + gutters.write[p_gutter].type = p_type; - update(); + queue_redraw(); } TextEdit::GutterType TextEdit::get_gutter_type(int p_gutter) const { @@ -4821,8 +4871,13 @@ bool TextEdit::is_gutter_drawn(int p_gutter) const { void TextEdit::set_gutter_clickable(int p_gutter, bool p_clickable) { ERR_FAIL_INDEX(p_gutter, gutters.size()); + + if (gutters[p_gutter].clickable == p_clickable) { + return; + } + gutters.write[p_gutter].clickable = p_clickable; - update(); + queue_redraw(); } bool TextEdit::is_gutter_clickable(int p_gutter) const { @@ -4870,14 +4925,18 @@ void TextEdit::merge_gutters(int p_from_line, int p_to_line) { text.set_line_gutter_clickable(p_to_line, i, true); } } - update(); + queue_redraw(); } void TextEdit::set_gutter_custom_draw(int p_gutter, const Callable &p_draw_callback) { ERR_FAIL_INDEX(p_gutter, gutters.size()); + if (gutters[p_gutter].custom_draw_callback == p_draw_callback) { + return; + } + gutters.write[p_gutter].custom_draw_callback = p_draw_callback; - update(); + queue_redraw(); } // Line gutters. @@ -4896,8 +4955,13 @@ Variant TextEdit::get_line_gutter_metadata(int p_line, int p_gutter) const { void TextEdit::set_line_gutter_text(int p_line, int p_gutter, const String &p_text) { ERR_FAIL_INDEX(p_line, text.size()); ERR_FAIL_INDEX(p_gutter, gutters.size()); + + if (text.get_line_gutter_text(p_line, p_gutter) == p_text) { + return; + } + text.set_line_gutter_text(p_line, p_gutter, p_text); - update(); + queue_redraw(); } String TextEdit::get_line_gutter_text(int p_line, int p_gutter) const { @@ -4909,8 +4973,13 @@ String TextEdit::get_line_gutter_text(int p_line, int p_gutter) const { void TextEdit::set_line_gutter_icon(int p_line, int p_gutter, const Ref<Texture2D> &p_icon) { ERR_FAIL_INDEX(p_line, text.size()); ERR_FAIL_INDEX(p_gutter, gutters.size()); + + if (text.get_line_gutter_icon(p_line, p_gutter) == p_icon) { + return; + } + text.set_line_gutter_icon(p_line, p_gutter, p_icon); - update(); + queue_redraw(); } Ref<Texture2D> TextEdit::get_line_gutter_icon(int p_line, int p_gutter) const { @@ -4922,8 +4991,13 @@ Ref<Texture2D> TextEdit::get_line_gutter_icon(int p_line, int p_gutter) const { void TextEdit::set_line_gutter_item_color(int p_line, int p_gutter, const Color &p_color) { ERR_FAIL_INDEX(p_line, text.size()); ERR_FAIL_INDEX(p_gutter, gutters.size()); + + if (text.get_line_gutter_item_color(p_line, p_gutter) == p_color) { + return; + } + text.set_line_gutter_item_color(p_line, p_gutter, p_color); - update(); + queue_redraw(); } Color TextEdit::get_line_gutter_item_color(int p_line, int p_gutter) const { @@ -4947,8 +5021,13 @@ bool TextEdit::is_line_gutter_clickable(int p_line, int p_gutter) const { // Line style void TextEdit::set_line_background_color(int p_line, const Color &p_color) { ERR_FAIL_INDEX(p_line, text.size()); + + if (text.get_line_background_color(p_line) == p_color) { + return; + } + text.set_line_background_color(p_line, p_color); - update(); + queue_redraw(); } Color TextEdit::get_line_background_color(int p_line) const { @@ -4958,11 +5037,15 @@ Color TextEdit::get_line_background_color(int p_line) const { /* Syntax Highlighting. */ void TextEdit::set_syntax_highlighter(Ref<SyntaxHighlighter> p_syntax_highlighter) { + if (syntax_highlighter == p_syntax_highlighter && syntax_highlighter.is_valid() == p_syntax_highlighter.is_valid()) { + return; + } + syntax_highlighter = p_syntax_highlighter; if (syntax_highlighter.is_valid()) { syntax_highlighter->set_text_edit(this); } - update(); + queue_redraw(); } Ref<SyntaxHighlighter> TextEdit::get_syntax_highlighter() const { @@ -4971,8 +5054,12 @@ Ref<SyntaxHighlighter> TextEdit::get_syntax_highlighter() const { /* Visual. */ void TextEdit::set_highlight_current_line(bool p_enabled) { + if (highlight_current_line == p_enabled) { + return; + } + highlight_current_line = p_enabled; - update(); + queue_redraw(); } bool TextEdit::is_highlight_current_line_enabled() const { @@ -4980,8 +5067,12 @@ bool TextEdit::is_highlight_current_line_enabled() const { } void TextEdit::set_highlight_all_occurrences(const bool p_enabled) { + if (highlight_all_occurrences == p_enabled) { + return; + } + highlight_all_occurrences = p_enabled; - update(); + queue_redraw(); } bool TextEdit::is_highlight_all_occurrences_enabled() const { @@ -4997,7 +5088,7 @@ void TextEdit::set_draw_control_chars(bool p_enabled) { text.set_draw_control_chars(draw_control_chars); text.invalidate_font(); _update_placeholder(); - update(); + queue_redraw(); } } @@ -5006,8 +5097,12 @@ bool TextEdit::get_draw_control_chars() const { } void TextEdit::set_draw_tabs(bool p_enabled) { + if (draw_tabs == p_enabled) { + return; + } + draw_tabs = p_enabled; - update(); + queue_redraw(); } bool TextEdit::is_drawing_tabs() const { @@ -5015,8 +5110,12 @@ bool TextEdit::is_drawing_tabs() const { } void TextEdit::set_draw_spaces(bool p_enabled) { + if (draw_spaces == p_enabled) { + return; + } + draw_spaces = p_enabled; - update(); + queue_redraw(); } bool TextEdit::is_drawing_spaces() const { @@ -5196,8 +5295,8 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("set_caret_blink_enabled", "enable"), &TextEdit::set_caret_blink_enabled); ClassDB::bind_method(D_METHOD("is_caret_blink_enabled"), &TextEdit::is_caret_blink_enabled); - ClassDB::bind_method(D_METHOD("set_caret_blink_speed", "blink_speed"), &TextEdit::set_caret_blink_speed); - ClassDB::bind_method(D_METHOD("get_caret_blink_speed"), &TextEdit::get_caret_blink_speed); + ClassDB::bind_method(D_METHOD("set_caret_blink_interval", "interval"), &TextEdit::set_caret_blink_interval); + ClassDB::bind_method(D_METHOD("get_caret_blink_interval"), &TextEdit::get_caret_blink_interval); ClassDB::bind_method(D_METHOD("set_move_caret_on_right_click_enabled", "enable"), &TextEdit::set_move_caret_on_right_click_enabled); ClassDB::bind_method(D_METHOD("is_move_caret_on_right_click_enabled"), &TextEdit::is_move_caret_on_right_click_enabled); @@ -5292,7 +5391,7 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("set_v_scroll_speed", "speed"), &TextEdit::set_v_scroll_speed); ClassDB::bind_method(D_METHOD("get_v_scroll_speed"), &TextEdit::get_v_scroll_speed); - ClassDB::bind_method(D_METHOD("set_fit_content_height_enabled"), &TextEdit::set_fit_content_height_enabled); + ClassDB::bind_method(D_METHOD("set_fit_content_height_enabled", "enabled"), &TextEdit::set_fit_content_height_enabled); ClassDB::bind_method(D_METHOD("is_fit_content_height_enabled"), &TextEdit::is_fit_content_height_enabled); ClassDB::bind_method(D_METHOD("get_scroll_pos_for_line", "line", "wrap_index"), &TextEdit::get_scroll_pos_for_line, DEFVAL(0)); @@ -5426,7 +5525,7 @@ void TextEdit::_bind_methods() { ADD_GROUP("Caret", "caret_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "caret_type", PROPERTY_HINT_ENUM, "Line,Block"), "set_caret_type", "get_caret_type"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_blink"), "set_caret_blink_enabled", "is_caret_blink_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.01,suffix:s"), "set_caret_blink_speed", "get_caret_blink_speed"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "caret_blink_interval", PROPERTY_HINT_RANGE, "0.1,10,0.01,suffix:s"), "set_caret_blink_interval", "get_caret_blink_interval"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_move_on_right_click"), "set_move_caret_on_right_click_enabled", "is_move_caret_on_right_click_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_mid_grapheme"), "set_caret_mid_grapheme_enabled", "is_caret_mid_grapheme_enabled"); @@ -5460,11 +5559,15 @@ void TextEdit::_bind_methods() { /* Internal API for CodeEdit. */ // Line hiding. void TextEdit::_set_hiding_enabled(bool p_enabled) { + if (hiding_enabled == p_enabled) { + return; + } + if (!p_enabled) { _unhide_all_lines(); } hiding_enabled = p_enabled; - update(); + queue_redraw(); } bool TextEdit::_is_hiding_enabled() const { @@ -5481,21 +5584,30 @@ void TextEdit::_unhide_all_lines() { text.set_hidden(i, false); } _update_scrollbars(); - update(); + queue_redraw(); } void TextEdit::_set_line_as_hidden(int p_line, bool p_hidden) { ERR_FAIL_INDEX(p_line, text.size()); + + if (text.is_hidden(p_line) == p_hidden) { + return; + } + if (_is_hiding_enabled() || !p_hidden) { text.set_hidden(p_line, p_hidden); } - update(); + queue_redraw(); } // Symbol lookup. void TextEdit::_set_symbol_lookup_word(const String &p_symbol) { + if (lookup_symbol_word == p_symbol) { + return; + } + lookup_symbol_word = p_symbol; - update(); + queue_redraw(); } /* Text manipulation */ @@ -5880,14 +5992,14 @@ void TextEdit::_reset_caret_blink_timer() { if (has_focus()) { caret_blink_timer->stop(); caret_blink_timer->start(); - update(); + queue_redraw(); } } void TextEdit::_toggle_draw_caret() { draw_caret = !draw_caret; if (is_visible_in_tree() && has_focus() && window_has_focus) { - update(); + queue_redraw(); } } @@ -5949,7 +6061,7 @@ void TextEdit::_update_selection_mode_pointer() { set_caret_line(line, false); set_caret_column(col); - update(); + queue_redraw(); click_select_held->start(); } @@ -6001,7 +6113,7 @@ void TextEdit::_update_selection_mode_word() { DisplayServer::get_singleton()->clipboard_set_primary(get_selected_text()); } - update(); + queue_redraw(); click_select_held->start(); } @@ -6032,7 +6144,7 @@ void TextEdit::_update_selection_mode_line() { DisplayServer::get_singleton()->clipboard_set_primary(get_selected_text()); } - update(); + queue_redraw(); click_select_held->start(); } @@ -6058,7 +6170,7 @@ void TextEdit::_post_shift_selection() { if (selection.active && selection.selecting_mode == SelectionMode::SELECTION_MODE_SHIFT) { select(selection.selecting_line, selection.selecting_column, caret.line, caret.column); - update(); + queue_redraw(); } selection.selecting_text = true; @@ -6220,7 +6332,7 @@ void TextEdit::_scroll_moved(double p_to_val) { caret.line_ofs = n_line; caret.wrap_ofs = wi; } - update(); + queue_redraw(); } double TextEdit::_get_visible_lines_offset() const { @@ -6342,7 +6454,7 @@ void TextEdit::_update_minimap_hover() { if (hovering_minimap) { // Only redraw if the hovering status changed. hovering_minimap = false; - update(); + queue_redraw(); } // Return early to avoid running the operations below when not needed. @@ -6355,7 +6467,7 @@ void TextEdit::_update_minimap_hover() { if (new_hovering_minimap != hovering_minimap) { // Only redraw if the hovering status changed. hovering_minimap = new_hovering_minimap; - update(); + queue_redraw(); } } @@ -6417,7 +6529,7 @@ void TextEdit::_update_gutter_width() { if (gutters_width > 0) { gutter_padding = 2; } - update(); + queue_redraw(); } /* Syntax highlighting. */ diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 6711cf8c7f..a8da878ede 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -195,6 +195,9 @@ private: void set(int p_line, const String &p_text, const Array &p_bidi_override); void set_hidden(int p_line, bool p_hidden) { + if (text[p_line].hidden == p_hidden) { + return; + } text.write[p_line].hidden = p_hidden; if (!p_hidden && text[p_line].width > max_width) { max_width = text[p_line].width; @@ -759,8 +762,8 @@ public: void set_caret_blink_enabled(const bool p_enabled); bool is_caret_blink_enabled() const; - void set_caret_blink_speed(const float p_speed); - float get_caret_blink_speed() const; + void set_caret_blink_interval(const float p_interval); + float get_caret_blink_interval() const; void set_move_caret_on_right_click_enabled(const bool p_enabled); bool is_move_caret_on_right_click_enabled() const; diff --git a/scene/gui/texture_button.cpp b/scene/gui/texture_button.cpp index 26acfaaa70..d9ab1c2c55 100644 --- a/scene/gui/texture_button.cpp +++ b/scene/gui/texture_button.cpp @@ -67,7 +67,7 @@ bool TextureButton::has_point(const Point2 &p_point) const { Rect2 rect = Rect2(); Size2 mask_size = click_mask->get_size(); - if (_position_rect.has_no_area()) { + if (!_position_rect.has_area()) { rect.size = mask_size; } else if (_tile) { // if the stretch mode is tile we offset the point to keep it inside the mask size @@ -112,7 +112,7 @@ bool TextureButton::has_point(const Point2 &p_point) const { } Point2i p = point; - return click_mask->get_bit(p); + return click_mask->get_bitv(p); } return Control::has_point(p_point); @@ -294,31 +294,50 @@ void TextureButton::_bind_methods() { } void TextureButton::set_normal_texture(const Ref<Texture2D> &p_normal) { + if (normal == p_normal) { + return; + } + normal = p_normal; - update(); + queue_redraw(); update_minimum_size(); } void TextureButton::set_pressed_texture(const Ref<Texture2D> &p_pressed) { + if (pressed == p_pressed) { + return; + } + pressed = p_pressed; - update(); + queue_redraw(); update_minimum_size(); } void TextureButton::set_hover_texture(const Ref<Texture2D> &p_hover) { + if (hover == p_hover) { + return; + } + hover = p_hover; - update(); + queue_redraw(); update_minimum_size(); } void TextureButton::set_disabled_texture(const Ref<Texture2D> &p_disabled) { + if (disabled == p_disabled) { + return; + } + disabled = p_disabled; - update(); + queue_redraw(); } void TextureButton::set_click_mask(const Ref<BitMap> &p_click_mask) { + if (click_mask == p_click_mask) { + return; + } click_mask = p_click_mask; - update(); + queue_redraw(); update_minimum_size(); } @@ -355,14 +374,22 @@ bool TextureButton::get_ignore_texture_size() const { } void TextureButton::set_ignore_texture_size(bool p_ignore) { + if (ignore_texture_size == p_ignore) { + return; + } + ignore_texture_size = p_ignore; update_minimum_size(); - update(); + queue_redraw(); } void TextureButton::set_stretch_mode(StretchMode p_stretch_mode) { + if (stretch_mode == p_stretch_mode) { + return; + } + stretch_mode = p_stretch_mode; - update(); + queue_redraw(); } TextureButton::StretchMode TextureButton::get_stretch_mode() const { @@ -370,8 +397,12 @@ TextureButton::StretchMode TextureButton::get_stretch_mode() const { } void TextureButton::set_flip_h(bool p_flip) { + if (hflip == p_flip) { + return; + } + hflip = p_flip; - update(); + queue_redraw(); } bool TextureButton::is_flipped_h() const { @@ -379,8 +410,12 @@ bool TextureButton::is_flipped_h() const { } void TextureButton::set_flip_v(bool p_flip) { + if (vflip == p_flip) { + return; + } + vflip = p_flip; - update(); + queue_redraw(); } bool TextureButton::is_flipped_v() const { diff --git a/scene/gui/texture_button.h b/scene/gui/texture_button.h index 5762949acd..9f6f7c1515 100644 --- a/scene/gui/texture_button.h +++ b/scene/gui/texture_button.h @@ -101,4 +101,5 @@ public: }; VARIANT_ENUM_CAST(TextureButton::StretchMode); + #endif // TEXTURE_BUTTON_H diff --git a/scene/gui/texture_progress_bar.cpp b/scene/gui/texture_progress_bar.cpp index 94e0a6f226..a9982b3ece 100644 --- a/scene/gui/texture_progress_bar.cpp +++ b/scene/gui/texture_progress_bar.cpp @@ -33,8 +33,12 @@ #include "core/config/engine.h" void TextureProgressBar::set_under_texture(const Ref<Texture2D> &p_texture) { + if (under == p_texture) { + return; + } + under = p_texture; - update(); + queue_redraw(); update_minimum_size(); } @@ -43,8 +47,12 @@ Ref<Texture2D> TextureProgressBar::get_under_texture() const { } void TextureProgressBar::set_over_texture(const Ref<Texture2D> &p_texture) { + if (over == p_texture) { + return; + } + over = p_texture; - update(); + queue_redraw(); if (under.is_null()) { update_minimum_size(); } @@ -56,8 +64,13 @@ Ref<Texture2D> TextureProgressBar::get_over_texture() const { void TextureProgressBar::set_stretch_margin(Side p_side, int p_size) { ERR_FAIL_INDEX((int)p_side, 4); + + if (stretch_margin[p_side] == p_size) { + return; + } + stretch_margin[p_side] = p_size; - update(); + queue_redraw(); update_minimum_size(); } @@ -67,8 +80,12 @@ int TextureProgressBar::get_stretch_margin(Side p_side) const { } void TextureProgressBar::set_nine_patch_stretch(bool p_stretch) { + if (nine_patch_stretch == p_stretch) { + return; + } + nine_patch_stretch = p_stretch; - update(); + queue_redraw(); update_minimum_size(); } @@ -91,8 +108,12 @@ Size2 TextureProgressBar::get_minimum_size() const { } void TextureProgressBar::set_progress_texture(const Ref<Texture2D> &p_texture) { + if (progress == p_texture) { + return; + } + progress = p_texture; - update(); + queue_redraw(); update_minimum_size(); } @@ -101,8 +122,12 @@ Ref<Texture2D> TextureProgressBar::get_progress_texture() const { } void TextureProgressBar::set_progress_offset(Point2 p_offset) { + if (progress_offset == p_offset) { + return; + } + progress_offset = p_offset; - update(); + queue_redraw(); } Point2 TextureProgressBar::get_progress_offset() const { @@ -110,8 +135,12 @@ Point2 TextureProgressBar::get_progress_offset() const { } void TextureProgressBar::set_tint_under(const Color &p_tint) { + if (tint_under == p_tint) { + return; + } + tint_under = p_tint; - update(); + queue_redraw(); } Color TextureProgressBar::get_tint_under() const { @@ -119,8 +148,12 @@ Color TextureProgressBar::get_tint_under() const { } void TextureProgressBar::set_tint_progress(const Color &p_tint) { + if (tint_progress == p_tint) { + return; + } + tint_progress = p_tint; - update(); + queue_redraw(); } Color TextureProgressBar::get_tint_progress() const { @@ -128,8 +161,12 @@ Color TextureProgressBar::get_tint_progress() const { } void TextureProgressBar::set_tint_over(const Color &p_tint) { + if (tint_over == p_tint) { + return; + } + tint_over = p_tint; - update(); + queue_redraw(); } Color TextureProgressBar::get_tint_over() const { @@ -548,8 +585,13 @@ void TextureProgressBar::_notification(int p_what) { void TextureProgressBar::set_fill_mode(int p_fill) { ERR_FAIL_INDEX(p_fill, FILL_MODE_MAX); + + if (mode == (FillMode)p_fill) { + return; + } + mode = (FillMode)p_fill; - update(); + queue_redraw(); } int TextureProgressBar::get_fill_mode() { @@ -563,8 +605,13 @@ void TextureProgressBar::set_radial_initial_angle(float p_angle) { while (p_angle < 0) { p_angle += 360; } + + if (rad_init_angle == p_angle) { + return; + } + rad_init_angle = p_angle; - update(); + queue_redraw(); } float TextureProgressBar::get_radial_initial_angle() { @@ -572,8 +619,14 @@ float TextureProgressBar::get_radial_initial_angle() { } void TextureProgressBar::set_fill_degrees(float p_angle) { - rad_max_degrees = CLAMP(p_angle, 0, 360); - update(); + float angle_clamped = CLAMP(p_angle, 0, 360); + + if (rad_max_degrees == angle_clamped) { + return; + } + + rad_max_degrees = angle_clamped; + queue_redraw(); } float TextureProgressBar::get_fill_degrees() { @@ -581,8 +634,12 @@ float TextureProgressBar::get_fill_degrees() { } void TextureProgressBar::set_radial_center_offset(const Point2 &p_off) { + if (rad_center_off == p_off) { + return; + } + rad_center_off = p_off; - update(); + queue_redraw(); } Point2 TextureProgressBar::get_radial_center_offset() { diff --git a/scene/gui/texture_rect.cpp b/scene/gui/texture_rect.cpp index ecdf55caf0..459e67091d 100644 --- a/scene/gui/texture_rect.cpp +++ b/scene/gui/texture_rect.cpp @@ -94,7 +94,7 @@ void TextureRect::_notification(int p_what) { Ref<AtlasTexture> p_atlas = texture; - if (p_atlas.is_valid() && region.has_no_area()) { + if (p_atlas.is_valid() && !region.has_area()) { Size2 scale_size(size.width / texture->get_width(), size.height / texture->get_height()); offset.width += hflip ? p_atlas->get_margin().get_position().width * scale_size.width * 2 : 0; @@ -104,10 +104,10 @@ void TextureRect::_notification(int p_what) { size.width *= hflip ? -1.0f : 1.0f; size.height *= vflip ? -1.0f : 1.0f; - if (region.has_no_area()) { - draw_texture_rect(texture, Rect2(offset, size), tile); - } else { + if (region.has_area()) { draw_texture_rect_region(texture, Rect2(offset, size), region); + } else { + draw_texture_rect(texture, Rect2(offset, size), tile); } } break; } @@ -150,7 +150,7 @@ void TextureRect::_bind_methods() { void TextureRect::_texture_changed() { if (texture.is_valid()) { - update(); + queue_redraw(); update_minimum_size(); } } @@ -170,7 +170,7 @@ void TextureRect::set_texture(const Ref<Texture2D> &p_tex) { texture->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TextureRect::_texture_changed)); } - update(); + queue_redraw(); update_minimum_size(); } @@ -179,8 +179,12 @@ Ref<Texture2D> TextureRect::get_texture() const { } void TextureRect::set_ignore_texture_size(bool p_ignore) { + if (ignore_texture_size == p_ignore) { + return; + } + ignore_texture_size = p_ignore; - update(); + queue_redraw(); update_minimum_size(); } @@ -189,8 +193,12 @@ bool TextureRect::get_ignore_texture_size() const { } void TextureRect::set_stretch_mode(StretchMode p_mode) { + if (stretch_mode == p_mode) { + return; + } + stretch_mode = p_mode; - update(); + queue_redraw(); } TextureRect::StretchMode TextureRect::get_stretch_mode() const { @@ -198,8 +206,12 @@ TextureRect::StretchMode TextureRect::get_stretch_mode() const { } void TextureRect::set_flip_h(bool p_flip) { + if (hflip == p_flip) { + return; + } + hflip = p_flip; - update(); + queue_redraw(); } bool TextureRect::is_flipped_h() const { @@ -207,8 +219,12 @@ bool TextureRect::is_flipped_h() const { } void TextureRect::set_flip_v(bool p_flip) { + if (vflip == p_flip) { + return; + } + vflip = p_flip; - update(); + queue_redraw(); } bool TextureRect::is_flipped_v() const { diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 4bb8208679..f82a853e56 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -126,13 +126,13 @@ void TreeItem::_change_tree(Tree *p_tree) { tree->pressing_for_editor = false; } - tree->update(); + tree->queue_redraw(); } tree = p_tree; if (tree) { - tree->update(); + tree->queue_redraw(); cells.resize(tree->columns.size()); } } @@ -141,6 +141,10 @@ void TreeItem::_change_tree(Tree *p_tree) { void TreeItem::set_cell_mode(int p_column, TreeCellMode p_mode) { ERR_FAIL_INDEX(p_column, cells.size()); + if (cells[p_column].mode == p_mode) { + return; + } + Cell &c = cells.write[p_column]; c.mode = p_mode; c.min = 0; @@ -166,6 +170,10 @@ TreeItem::TreeCellMode TreeItem::get_cell_mode(int p_column) const { void TreeItem::set_checked(int p_column, bool p_checked) { ERR_FAIL_INDEX(p_column, cells.size()); + if (cells[p_column].checked == p_checked) { + return; + } + cells.write[p_column].checked = p_checked; cells.write[p_column].indeterminate = false; cells.write[p_column].cached_minimum_size_dirty = true; @@ -259,6 +267,11 @@ void TreeItem::_propagate_check_through_parents(int p_column, bool p_emit_signal void TreeItem::set_text(int p_column, String p_text) { ERR_FAIL_INDEX(p_column, cells.size()); + + if (cells[p_column].text == p_text) { + return; + } + cells.write[p_column].text = p_text; cells.write[p_column].dirty = true; @@ -290,11 +303,14 @@ String TreeItem::get_text(int p_column) const { void TreeItem::set_text_direction(int p_column, Control::TextDirection p_text_direction) { ERR_FAIL_INDEX(p_column, cells.size()); ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3); - if (cells[p_column].text_direction != p_text_direction) { - cells.write[p_column].text_direction = p_text_direction; - cells.write[p_column].dirty = true; - _changed_notify(p_column); + + if (cells[p_column].text_direction == p_text_direction) { + return; } + + cells.write[p_column].text_direction = p_text_direction; + cells.write[p_column].dirty = true; + _changed_notify(p_column); cells.write[p_column].cached_minimum_size_dirty = true; } @@ -323,6 +339,10 @@ TextServer::StructuredTextParser TreeItem::get_structured_text_bidi_override(int void TreeItem::set_structured_text_bidi_override_options(int p_column, Array p_args) { ERR_FAIL_INDEX(p_column, cells.size()); + if (cells[p_column].st_args == p_args) { + return; + } + cells.write[p_column].st_args = p_args; cells.write[p_column].dirty = true; cells.write[p_column].cached_minimum_size_dirty = true; @@ -355,6 +375,10 @@ String TreeItem::get_language(int p_column) const { void TreeItem::set_suffix(int p_column, String p_suffix) { ERR_FAIL_INDEX(p_column, cells.size()); + if (cells[p_column].suffix == p_suffix) { + return; + } + cells.write[p_column].suffix = p_suffix; cells.write[p_column].cached_minimum_size_dirty = true; @@ -369,6 +393,10 @@ String TreeItem::get_suffix(int p_column) const { void TreeItem::set_icon(int p_column, const Ref<Texture2D> &p_icon) { ERR_FAIL_INDEX(p_column, cells.size()); + if (cells[p_column].icon == p_icon) { + return; + } + cells.write[p_column].icon = p_icon; cells.write[p_column].cached_minimum_size_dirty = true; @@ -383,6 +411,10 @@ Ref<Texture2D> TreeItem::get_icon(int p_column) const { void TreeItem::set_icon_region(int p_column, const Rect2 &p_icon_region) { ERR_FAIL_INDEX(p_column, cells.size()); + if (cells[p_column].icon_region == p_icon_region) { + return; + } + cells.write[p_column].icon_region = p_icon_region; cells.write[p_column].cached_minimum_size_dirty = true; @@ -396,6 +428,11 @@ Rect2 TreeItem::get_icon_region(int p_column) const { void TreeItem::set_icon_modulate(int p_column, const Color &p_modulate) { ERR_FAIL_INDEX(p_column, cells.size()); + + if (cells[p_column].icon_color == p_modulate) { + return; + } + cells.write[p_column].icon_color = p_modulate; _changed_notify(p_column); } @@ -408,6 +445,10 @@ Color TreeItem::get_icon_modulate(int p_column) const { void TreeItem::set_icon_max_width(int p_column, int p_max) { ERR_FAIL_INDEX(p_column, cells.size()); + if (cells[p_column].icon_max_w == p_max) { + return; + } + cells.write[p_column].icon_max_w = p_max; cells.write[p_column].cached_minimum_size_dirty = true; @@ -432,6 +473,10 @@ void TreeItem::set_range(int p_column, double p_value) { p_value = cells[p_column].max; } + if (cells[p_column].val == p_value) { + return; + } + cells.write[p_column].val = p_value; cells.write[p_column].dirty = true; _changed_notify(p_column); @@ -449,6 +494,11 @@ bool TreeItem::is_range_exponential(int p_column) const { void TreeItem::set_range_config(int p_column, double p_min, double p_max, double p_step, bool p_exp) { ERR_FAIL_INDEX(p_column, cells.size()); + + if (cells[p_column].min == p_min && cells[p_column].max == p_max && cells[p_column].step == p_step && cells[p_column].expr == p_exp) { + return; + } + cells.write[p_column].min = p_min; cells.write[p_column].max = p_max; cells.write[p_column].step = p_step; @@ -501,7 +551,7 @@ void TreeItem::set_collapsed(bool p_collapsed) { select(tree->selected_col); } - tree->update(); + tree->queue_redraw(); } } @@ -513,13 +563,64 @@ bool TreeItem::is_collapsed() { return collapsed; } +void TreeItem::set_collapsed_recursive(bool p_collapsed) { + if (!tree) { + return; + } + + set_collapsed(p_collapsed); + + TreeItem *child = get_first_child(); + while (child) { + child->set_collapsed_recursive(p_collapsed); + child = child->get_next(); + } +} + +bool TreeItem::_is_any_collapsed(bool p_only_visible) { + TreeItem *child = get_first_child(); + + // Check on children directly first (avoid recursing if possible). + while (child) { + if (child->get_first_child() && child->is_collapsed() && (!p_only_visible || (child->is_visible() && child->get_visible_child_count()))) { + return true; + } + child = child->get_next(); + } + + child = get_first_child(); + + // Otherwise recurse on children. + while (child) { + if (child->get_first_child() && (!p_only_visible || (child->is_visible() && child->get_visible_child_count())) && child->_is_any_collapsed(p_only_visible)) { + return true; + } + child = child->get_next(); + } + + return false; +} + +bool TreeItem::is_any_collapsed(bool p_only_visible) { + if (p_only_visible && !is_visible()) { + return false; + } + + // Collapsed if this is collapsed and it has children (only considers visible if only visible is set). + if (is_collapsed() && get_first_child() && (!p_only_visible || get_visible_child_count())) { + return true; + } + + return _is_any_collapsed(p_only_visible); +} + void TreeItem::set_visible(bool p_visible) { if (visible == p_visible) { return; } visible = p_visible; if (tree) { - tree->update(); + tree->queue_redraw(); _changed_notify(); } } @@ -537,6 +638,10 @@ void TreeItem::uncollapse_tree() { } void TreeItem::set_custom_minimum_height(int p_height) { + if (custom_min_height == p_height) { + return; + } + custom_min_height = p_height; for (Cell &c : cells) { @@ -556,7 +661,7 @@ TreeItem *TreeItem::create_child(int p_idx) { TreeItem *ti = memnew(TreeItem(tree)); if (tree) { ti->cells.resize(tree->columns.size()); - tree->update(); + tree->queue_redraw(); } TreeItem *l_prev = nullptr; @@ -723,7 +828,12 @@ TreeItem *TreeItem::get_next_visible(bool p_wrap) { TreeItem *TreeItem::get_child(int p_idx) { _create_children_cache(); + + if (p_idx < 0) { + p_idx += children_cache.size(); + } ERR_FAIL_INDEX_V(p_idx, children_cache.size(), nullptr); + return children_cache.get(p_idx); } @@ -743,9 +853,10 @@ int TreeItem::get_child_count() { return children_cache.size(); } -Array TreeItem::get_children() { +TypedArray<TreeItem> TreeItem::get_children() { + // Don't need to explicitly create children cache, because get_child_count creates it. int size = get_child_count(); - Array arr; + TypedArray<TreeItem> arr; arr.resize(size); for (int i = 0; i < size; i++) { arr[i] = children_cache[i]; @@ -765,6 +876,22 @@ int TreeItem::get_index() { return idx - 1; } +#ifdef DEV_ENABLED +void TreeItem::validate_cache() const { + if (!parent || parent->children_cache.is_empty()) { + return; + } + TreeItem *scan = parent->first_child; + int index = 0; + while (scan) { + DEV_ASSERT(parent->children_cache[index] == scan); + ++index; + scan = scan->get_next(); + } + DEV_ASSERT(index == parent->children_cache.size()); +} +#endif + void TreeItem::move_before(TreeItem *p_item) { ERR_FAIL_NULL(p_item); ERR_FAIL_COND(is_root); @@ -792,7 +919,11 @@ void TreeItem::move_before(TreeItem *p_item) { parent->children_cache.clear(); } else { parent->first_child = this; - parent->children_cache.insert(0, this); + // If the cache is empty, it has not been built but there + // are items in the tree (note p_item != nullptr,) so we cannot update it. + if (!parent->children_cache.is_empty()) { + parent->children_cache.insert(0, this); + } } prev = item_prev; @@ -800,8 +931,10 @@ void TreeItem::move_before(TreeItem *p_item) { p_item->prev = this; if (tree && old_tree == tree) { - tree->update(); + tree->queue_redraw(); } + + validate_cache(); } void TreeItem::move_after(TreeItem *p_item) { @@ -834,12 +967,17 @@ void TreeItem::move_after(TreeItem *p_item) { if (next) { parent->children_cache.clear(); } else { - parent->children_cache.append(this); + // If the cache is empty, it has not been built but there + // are items in the tree (note p_item != nullptr,) so we cannot update it. + if (!parent->children_cache.is_empty()) { + parent->children_cache.append(this); + } } if (tree && old_tree == tree) { - tree->update(); + tree->queue_redraw(); } + validate_cache(); } void TreeItem::remove_child(TreeItem *p_item) { @@ -852,8 +990,9 @@ void TreeItem::remove_child(TreeItem *p_item) { p_item->parent = nullptr; if (tree) { - tree->update(); + tree->queue_redraw(); } + validate_cache(); } void TreeItem::set_selectable(int p_column, bool p_selectable) { @@ -879,9 +1018,12 @@ void TreeItem::set_as_cursor(int p_column) { if (tree->select_mode != Tree::SELECT_MULTI) { return; } + if (tree->selected_col == p_column) { + return; + } tree->selected_item = this; tree->selected_col = p_column; - tree->update(); + tree->queue_redraw(); } void TreeItem::select(int p_column) { @@ -922,7 +1064,7 @@ Ref<Texture2D> TreeItem::get_button(int p_column, int p_idx) const { return cells[p_column].buttons[p_idx].texture; } -String TreeItem::get_button_tooltip(int p_column, int p_idx) const { +String TreeItem::get_button_tooltip_text(int p_column, int p_idx) const { ERR_FAIL_INDEX_V(p_column, cells.size(), String()); ERR_FAIL_INDEX_V(p_idx, cells[p_column].buttons.size(), String()); return cells[p_column].buttons[p_idx].tooltip; @@ -956,6 +1098,11 @@ void TreeItem::set_button(int p_column, int p_idx, const Ref<Texture2D> &p_butto ERR_FAIL_COND(p_button.is_null()); ERR_FAIL_INDEX(p_column, cells.size()); ERR_FAIL_INDEX(p_idx, cells[p_column].buttons.size()); + + if (cells[p_column].buttons[p_idx].texture == p_button) { + return; + } + cells.write[p_column].buttons.write[p_idx].texture = p_button; cells.write[p_column].cached_minimum_size_dirty = true; @@ -965,6 +1112,11 @@ void TreeItem::set_button(int p_column, int p_idx, const Ref<Texture2D> &p_butto void TreeItem::set_button_color(int p_column, int p_idx, const Color &p_color) { ERR_FAIL_INDEX(p_column, cells.size()); ERR_FAIL_INDEX(p_idx, cells[p_column].buttons.size()); + + if (cells[p_column].buttons[p_idx].color == p_color) { + return; + } + cells.write[p_column].buttons.write[p_idx].color = p_color; _changed_notify(p_column); } @@ -973,6 +1125,10 @@ void TreeItem::set_button_disabled(int p_column, int p_idx, bool p_disabled) { ERR_FAIL_INDEX(p_column, cells.size()); ERR_FAIL_INDEX(p_idx, cells[p_column].buttons.size()); + if (cells[p_column].buttons[p_idx].disabled == p_disabled) { + return; + } + cells.write[p_column].buttons.write[p_idx].disabled = p_disabled; cells.write[p_column].cached_minimum_size_dirty = true; @@ -989,6 +1145,10 @@ bool TreeItem::is_button_disabled(int p_column, int p_idx) const { void TreeItem::set_editable(int p_column, bool p_editable) { ERR_FAIL_INDEX(p_column, cells.size()); + if (cells[p_column].editable == p_editable) { + return; + } + cells.write[p_column].editable = p_editable; cells.write[p_column].cached_minimum_size_dirty = true; @@ -1002,6 +1162,11 @@ bool TreeItem::is_editable(int p_column) { void TreeItem::set_custom_color(int p_column, const Color &p_color) { ERR_FAIL_INDEX(p_column, cells.size()); + + if (cells[p_column].custom_color == true) { + return; + } + cells.write[p_column].custom_color = true; cells.write[p_column].color = p_color; _changed_notify(p_column); @@ -1046,18 +1211,23 @@ int TreeItem::get_custom_font_size(int p_column) const { return cells[p_column].custom_font_size; } -void TreeItem::set_tooltip(int p_column, const String &p_tooltip) { +void TreeItem::set_tooltip_text(int p_column, const String &p_tooltip) { ERR_FAIL_INDEX(p_column, cells.size()); cells.write[p_column].tooltip = p_tooltip; } -String TreeItem::get_tooltip(int p_column) const { +String TreeItem::get_tooltip_text(int p_column) const { ERR_FAIL_INDEX_V(p_column, cells.size(), ""); return cells[p_column].tooltip; } void TreeItem::set_custom_bg_color(int p_column, const Color &p_color, bool p_bg_outline) { ERR_FAIL_INDEX(p_column, cells.size()); + + if (cells[p_column].custom_bg_color && cells[p_column].custom_bg_outline == p_bg_outline && cells[p_column].bg_color == p_color) { + return; + } + cells.write[p_column].custom_bg_color = true; cells.write[p_column].custom_bg_outline = p_bg_outline; cells.write[p_column].bg_color = p_color; @@ -1094,6 +1264,10 @@ bool TreeItem::is_custom_set_as_button(int p_column) const { void TreeItem::set_text_alignment(int p_column, HorizontalAlignment p_alignment) { ERR_FAIL_INDEX(p_column, cells.size()); + if (cells[p_column].text_alignment == p_alignment) { + return; + } + cells.write[p_column].text_alignment = p_alignment; cells.write[p_column].cached_minimum_size_dirty = true; @@ -1108,6 +1282,10 @@ HorizontalAlignment TreeItem::get_text_alignment(int p_column) const { void TreeItem::set_expand_right(int p_column, bool p_enable) { ERR_FAIL_INDEX(p_column, cells.size()); + if (cells[p_column].expand_right == p_enable) { + return; + } + cells.write[p_column].expand_right = p_enable; cells.write[p_column].cached_minimum_size_dirty = true; @@ -1120,6 +1298,10 @@ bool TreeItem::get_expand_right(int p_column) const { } void TreeItem::set_disable_folding(bool p_disable) { + if (disable_folding == p_disable) { + return; + } + disable_folding = p_disable; for (Cell &c : cells) { @@ -1155,14 +1337,14 @@ Size2 TreeItem::get_minimum_size(int p_column) { // Icon. if (cell.mode == CELL_MODE_CHECK) { - size.width += tree->cache.checked->get_width() + tree->cache.hseparation; + size.width += tree->theme_cache.checked->get_width() + tree->theme_cache.hseparation; } if (cell.icon.is_valid()) { Size2i icon_size = cell.get_icon_size(); if (cell.icon_max_w > 0 && icon_size.width > cell.icon_max_w) { icon_size.width = cell.icon_max_w; } - size.width += icon_size.width + tree->cache.hseparation; + size.width += icon_size.width + tree->theme_cache.hseparation; size.height = MAX(size.height, icon_size.height); } @@ -1170,13 +1352,13 @@ Size2 TreeItem::get_minimum_size(int p_column) { for (int i = 0; i < cell.buttons.size(); i++) { Ref<Texture2D> texture = cell.buttons[i].texture; if (texture.is_valid()) { - Size2 button_size = texture->get_size() + tree->cache.button_pressed->get_minimum_size(); + Size2 button_size = texture->get_size() + tree->theme_cache.button_pressed->get_minimum_size(); size.width += button_size.width; size.height = MAX(size.height, button_size.height); } } if (cell.buttons.size() >= 2) { - size.width += (cell.buttons.size() - 1) * tree->cache.button_margin; + size.width += (cell.buttons.size() - 1) * tree->theme_cache.button_margin; } cells.write[p_column].cached_minimum_size = size; @@ -1275,6 +1457,9 @@ void TreeItem::_bind_methods() { ClassDB::bind_method(D_METHOD("set_collapsed", "enable"), &TreeItem::set_collapsed); ClassDB::bind_method(D_METHOD("is_collapsed"), &TreeItem::is_collapsed); + ClassDB::bind_method(D_METHOD("set_collapsed_recursive", "enable"), &TreeItem::set_collapsed_recursive); + ClassDB::bind_method(D_METHOD("is_any_collapsed", "only_visible"), &TreeItem::is_any_collapsed, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("set_visible", "enable"), &TreeItem::set_visible); ClassDB::bind_method(D_METHOD("is_visible"), &TreeItem::is_visible); @@ -1310,9 +1495,9 @@ void TreeItem::_bind_methods() { ClassDB::bind_method(D_METHOD("set_custom_as_button", "column", "enable"), &TreeItem::set_custom_as_button); ClassDB::bind_method(D_METHOD("is_custom_set_as_button", "column"), &TreeItem::is_custom_set_as_button); - ClassDB::bind_method(D_METHOD("add_button", "column", "button", "id", "disabled", "tooltip"), &TreeItem::add_button, DEFVAL(-1), DEFVAL(false), DEFVAL("")); + ClassDB::bind_method(D_METHOD("add_button", "column", "button", "id", "disabled", "tooltip_text"), &TreeItem::add_button, DEFVAL(-1), DEFVAL(false), DEFVAL("")); ClassDB::bind_method(D_METHOD("get_button_count", "column"), &TreeItem::get_button_count); - ClassDB::bind_method(D_METHOD("get_button_tooltip", "column", "button_idx"), &TreeItem::get_button_tooltip); + ClassDB::bind_method(D_METHOD("get_button_tooltip_text", "column", "button_idx"), &TreeItem::get_button_tooltip_text); ClassDB::bind_method(D_METHOD("get_button_id", "column", "button_idx"), &TreeItem::get_button_id); ClassDB::bind_method(D_METHOD("get_button_by_id", "column", "id"), &TreeItem::get_button_by_id); ClassDB::bind_method(D_METHOD("get_button", "column", "button_idx"), &TreeItem::get_button); @@ -1321,8 +1506,8 @@ void TreeItem::_bind_methods() { ClassDB::bind_method(D_METHOD("set_button_disabled", "column", "button_idx", "disabled"), &TreeItem::set_button_disabled); ClassDB::bind_method(D_METHOD("is_button_disabled", "column", "button_idx"), &TreeItem::is_button_disabled); - ClassDB::bind_method(D_METHOD("set_tooltip", "column", "tooltip"), &TreeItem::set_tooltip); - ClassDB::bind_method(D_METHOD("get_tooltip", "column"), &TreeItem::get_tooltip); + ClassDB::bind_method(D_METHOD("set_tooltip_text", "column", "tooltip"), &TreeItem::set_tooltip_text); + ClassDB::bind_method(D_METHOD("get_tooltip_text", "column"), &TreeItem::get_tooltip_text); ClassDB::bind_method(D_METHOD("set_text_alignment", "column", "text_alignment"), &TreeItem::set_text_alignment); ClassDB::bind_method(D_METHOD("get_text_alignment", "column"), &TreeItem::get_text_alignment); @@ -1391,6 +1576,7 @@ TreeItem::TreeItem(Tree *p_tree) { TreeItem::~TreeItem() { _unlink_from_tree(); + validate_cache(); prev = nullptr; clear_children(); _change_tree(nullptr); @@ -1403,68 +1589,68 @@ TreeItem::~TreeItem() { /**********************************************/ /**********************************************/ -void Tree::update_cache() { - cache.font = get_theme_font(SNAME("font")); - cache.font_size = get_theme_font_size(SNAME("font_size")); - cache.tb_font = get_theme_font(SNAME("title_button_font")); - cache.tb_font_size = get_theme_font_size(SNAME("title_button_font_size")); - cache.bg = get_theme_stylebox(SNAME("bg")); - cache.selected = get_theme_stylebox(SNAME("selected")); - cache.selected_focus = get_theme_stylebox(SNAME("selected_focus")); - cache.cursor = get_theme_stylebox(SNAME("cursor")); - cache.cursor_unfocus = get_theme_stylebox(SNAME("cursor_unfocused")); - cache.button_pressed = get_theme_stylebox(SNAME("button_pressed")); - - cache.checked = get_theme_icon(SNAME("checked")); - cache.unchecked = get_theme_icon(SNAME("unchecked")); - cache.indeterminate = get_theme_icon(SNAME("indeterminate")); - if (is_layout_rtl()) { - cache.arrow_collapsed = get_theme_icon(SNAME("arrow_collapsed_mirrored")); - } else { - cache.arrow_collapsed = get_theme_icon(SNAME("arrow_collapsed")); - } - cache.arrow = get_theme_icon(SNAME("arrow")); - cache.select_arrow = get_theme_icon(SNAME("select_arrow")); - cache.updown = get_theme_icon(SNAME("updown")); - - cache.custom_button = get_theme_stylebox(SNAME("custom_button")); - cache.custom_button_hover = get_theme_stylebox(SNAME("custom_button_hover")); - cache.custom_button_pressed = get_theme_stylebox(SNAME("custom_button_pressed")); - cache.custom_button_font_highlight = get_theme_color(SNAME("custom_button_font_highlight")); - - cache.font_color = get_theme_color(SNAME("font_color")); - cache.font_selected_color = get_theme_color(SNAME("font_selected_color")); - cache.drop_position_color = get_theme_color(SNAME("drop_position_color")); - cache.hseparation = get_theme_constant(SNAME("h_separation")); - cache.vseparation = get_theme_constant(SNAME("v_separation")); - cache.item_margin = get_theme_constant(SNAME("item_margin")); - cache.button_margin = get_theme_constant(SNAME("button_margin")); - - cache.font_outline_color = get_theme_color(SNAME("font_outline_color")); - cache.font_outline_size = get_theme_constant(SNAME("outline_size")); - - cache.draw_guides = get_theme_constant(SNAME("draw_guides")); - cache.guide_color = get_theme_color(SNAME("guide_color")); - cache.draw_relationship_lines = get_theme_constant(SNAME("draw_relationship_lines")); - cache.relationship_line_width = get_theme_constant(SNAME("relationship_line_width")); - cache.parent_hl_line_width = get_theme_constant(SNAME("parent_hl_line_width")); - cache.children_hl_line_width = get_theme_constant(SNAME("children_hl_line_width")); - cache.parent_hl_line_margin = get_theme_constant(SNAME("parent_hl_line_margin")); - cache.relationship_line_color = get_theme_color(SNAME("relationship_line_color")); - cache.parent_hl_line_color = get_theme_color(SNAME("parent_hl_line_color")); - cache.children_hl_line_color = get_theme_color(SNAME("children_hl_line_color")); - - cache.scroll_border = get_theme_constant(SNAME("scroll_border")); - cache.scroll_speed = get_theme_constant(SNAME("scroll_speed")); - - cache.title_button = get_theme_stylebox(SNAME("title_button_normal")); - cache.title_button_pressed = get_theme_stylebox(SNAME("title_button_pressed")); - cache.title_button_hover = get_theme_stylebox(SNAME("title_button_hover")); - cache.title_button_color = get_theme_color(SNAME("title_button_color")); - - cache.base_scale = get_theme_default_base_scale(); - - v_scroll->set_custom_step(cache.font->get_height(cache.font_size)); +void Tree::_update_theme_item_cache() { + Control::_update_theme_item_cache(); + + theme_cache.panel_style = get_theme_stylebox(SNAME("panel")); + theme_cache.focus_style = get_theme_stylebox(SNAME("focus")); + + theme_cache.font = get_theme_font(SNAME("font")); + theme_cache.font_size = get_theme_font_size(SNAME("font_size")); + theme_cache.tb_font = get_theme_font(SNAME("title_button_font")); + theme_cache.tb_font_size = get_theme_font_size(SNAME("title_button_font_size")); + + theme_cache.selected = get_theme_stylebox(SNAME("selected")); + theme_cache.selected_focus = get_theme_stylebox(SNAME("selected_focus")); + theme_cache.cursor = get_theme_stylebox(SNAME("cursor")); + theme_cache.cursor_unfocus = get_theme_stylebox(SNAME("cursor_unfocused")); + theme_cache.button_pressed = get_theme_stylebox(SNAME("button_pressed")); + + theme_cache.checked = get_theme_icon(SNAME("checked")); + theme_cache.unchecked = get_theme_icon(SNAME("unchecked")); + theme_cache.indeterminate = get_theme_icon(SNAME("indeterminate")); + theme_cache.arrow = get_theme_icon(SNAME("arrow")); + theme_cache.arrow_collapsed = get_theme_icon(SNAME("arrow_collapsed")); + theme_cache.arrow_collapsed_mirrored = get_theme_icon(SNAME("arrow_collapsed_mirrored")); + theme_cache.select_arrow = get_theme_icon(SNAME("select_arrow")); + theme_cache.updown = get_theme_icon(SNAME("updown")); + + theme_cache.custom_button = get_theme_stylebox(SNAME("custom_button")); + theme_cache.custom_button_hover = get_theme_stylebox(SNAME("custom_button_hover")); + theme_cache.custom_button_pressed = get_theme_stylebox(SNAME("custom_button_pressed")); + theme_cache.custom_button_font_highlight = get_theme_color(SNAME("custom_button_font_highlight")); + + theme_cache.font_color = get_theme_color(SNAME("font_color")); + theme_cache.font_selected_color = get_theme_color(SNAME("font_selected_color")); + theme_cache.drop_position_color = get_theme_color(SNAME("drop_position_color")); + theme_cache.hseparation = get_theme_constant(SNAME("h_separation")); + theme_cache.vseparation = get_theme_constant(SNAME("v_separation")); + theme_cache.item_margin = get_theme_constant(SNAME("item_margin")); + theme_cache.button_margin = get_theme_constant(SNAME("button_margin")); + + theme_cache.font_outline_color = get_theme_color(SNAME("font_outline_color")); + theme_cache.font_outline_size = get_theme_constant(SNAME("outline_size")); + + theme_cache.draw_guides = get_theme_constant(SNAME("draw_guides")); + theme_cache.guide_color = get_theme_color(SNAME("guide_color")); + theme_cache.draw_relationship_lines = get_theme_constant(SNAME("draw_relationship_lines")); + theme_cache.relationship_line_width = get_theme_constant(SNAME("relationship_line_width")); + theme_cache.parent_hl_line_width = get_theme_constant(SNAME("parent_hl_line_width")); + theme_cache.children_hl_line_width = get_theme_constant(SNAME("children_hl_line_width")); + theme_cache.parent_hl_line_margin = get_theme_constant(SNAME("parent_hl_line_margin")); + theme_cache.relationship_line_color = get_theme_color(SNAME("relationship_line_color")); + theme_cache.parent_hl_line_color = get_theme_color(SNAME("parent_hl_line_color")); + theme_cache.children_hl_line_color = get_theme_color(SNAME("children_hl_line_color")); + + theme_cache.scroll_border = get_theme_constant(SNAME("scroll_border")); + theme_cache.scroll_speed = get_theme_constant(SNAME("scroll_speed")); + + theme_cache.title_button = get_theme_stylebox(SNAME("title_button_normal")); + theme_cache.title_button_pressed = get_theme_stylebox(SNAME("title_button_pressed")); + theme_cache.title_button_hover = get_theme_stylebox(SNAME("title_button_hover")); + theme_cache.title_button_color = get_theme_color(SNAME("title_button_color")); + + theme_cache.base_scale = get_theme_default_base_scale(); } int Tree::compute_item_height(TreeItem *p_item) const { @@ -1472,7 +1658,7 @@ int Tree::compute_item_height(TreeItem *p_item) const { return 0; } - ERR_FAIL_COND_V(cache.font.is_null(), 0); + ERR_FAIL_COND_V(theme_cache.font.is_null(), 0); int height = 0; for (int i = 0; i < columns.size(); i++) { @@ -1490,7 +1676,7 @@ int Tree::compute_item_height(TreeItem *p_item) const { switch (p_item->cells[i].mode) { case TreeItem::CELL_MODE_CHECK: { - int check_icon_h = cache.checked->get_height(); + int check_icon_h = theme_cache.checked->get_height(); if (height < check_icon_h) { height = check_icon_h; } @@ -1510,7 +1696,7 @@ int Tree::compute_item_height(TreeItem *p_item) const { } } if (p_item->cells[i].mode == TreeItem::CELL_MODE_CUSTOM && p_item->cells[i].custom_button) { - height += cache.custom_button->get_minimum_size().height; + height += theme_cache.custom_button->get_minimum_size().height; } } break; @@ -1523,7 +1709,7 @@ int Tree::compute_item_height(TreeItem *p_item) const { height = item_min_height; } - height += cache.vseparation; + height += theme_cache.vseparation; return height; } @@ -1533,7 +1719,7 @@ int Tree::get_item_height(TreeItem *p_item) const { return 0; } int height = compute_item_height(p_item); - height += cache.vseparation; + height += theme_cache.vseparation; if (!p_item->collapsed) { /* if not collapsed, check the children */ @@ -1550,7 +1736,7 @@ int Tree::get_item_height(TreeItem *p_item) const { } void Tree::draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Color &p_color, const Color &p_icon_color, int p_ol_size, const Color &p_ol_color) { - ERR_FAIL_COND(cache.font.is_null()); + ERR_FAIL_COND(theme_cache.font.is_null()); Rect2i rect = p_rect; Size2 ts = p_cell.text_buf->get_size(); @@ -1562,7 +1748,7 @@ void Tree::draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Co if (p_cell.icon_max_w > 0 && bmsize.width > p_cell.icon_max_w) { bmsize.width = p_cell.icon_max_w; } - w += bmsize.width + cache.hseparation; + w += bmsize.width + theme_cache.hseparation; if (rect.size.width > 0 && (w + ts.width) > rect.size.width) { ts.width = rect.size.width - w; } @@ -1596,8 +1782,8 @@ void Tree::draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Co p_cell.text_buf->draw_outline(ci, draw_pos, p_ol_size, p_ol_color); } p_cell.text_buf->draw(ci, draw_pos, p_color); - rect.position.x += ts.width + cache.hseparation; - rect.size.x -= ts.width + cache.hseparation; + rect.position.x += ts.width + theme_cache.hseparation; + rect.size.x -= ts.width + theme_cache.hseparation; } if (!p_cell.icon.is_null()) { @@ -1609,8 +1795,8 @@ void Tree::draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Co } p_cell.draw_icon(ci, rect.position + Size2i(0, Math::floor((real_t)(rect.size.y - bmsize.y) / 2)), bmsize, p_icon_color); - rect.position.x += bmsize.x + cache.hseparation; - rect.size.x -= bmsize.x + cache.hseparation; + rect.position.x += bmsize.x + theme_cache.hseparation; + rect.size.x -= bmsize.x + theme_cache.hseparation; } if (!rtl) { @@ -1632,7 +1818,7 @@ void Tree::update_column(int p_col) { columns.write[p_col].text_buf->set_direction((TextServer::Direction)columns[p_col].text_direction); } - columns.write[p_col].text_buf->add_string(columns[p_col].title, cache.font, cache.font_size, columns[p_col].language); + columns.write[p_col].text_buf->add_string(columns[p_col].title, theme_cache.font, theme_cache.font_size, columns[p_col].language); } void Tree::update_item_cell(TreeItem *p_item, int p_col) { @@ -1681,14 +1867,14 @@ void Tree::update_item_cell(TreeItem *p_item, int p_col) { if (p_item->cells[p_col].custom_font.is_valid()) { font = p_item->cells[p_col].custom_font; } else { - font = cache.font; + font = theme_cache.font; } int font_size; if (p_item->cells[p_col].custom_font_size > 0) { font_size = p_item->cells[p_col].custom_font_size; } else { - font_size = cache.font_size; + font_size = theme_cache.font_size; } p_item->cells.write[p_col].text_buf->add_string(valtext, font, font_size, p_item->cells[p_col].language); TS->shaped_text_set_bidi_override(p_item->cells[p_col].text_buf->get_rid(), structured_text_parser(p_item->cells[p_col].st_parser, p_item->cells[p_col].st_args, valtext)); @@ -1708,7 +1894,7 @@ void Tree::update_item_cache(TreeItem *p_item) { } int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 &p_draw_size, TreeItem *p_item) { - if (p_pos.y - cache.offset.y > (p_draw_size.height)) { + if (p_pos.y - theme_cache.offset.y > (p_draw_size.height)) { return -1; //draw no more! } @@ -1724,18 +1910,18 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 bool rtl = cache.rtl; /* Calculate height of the label part */ - label_h += cache.vseparation; + label_h += theme_cache.vseparation; /* Draw label, if height fits */ bool skip = (p_item == root && hide_root); - if (!skip && (p_pos.y + label_h - cache.offset.y) > 0) { + if (!skip && (p_pos.y + label_h - theme_cache.offset.y) > 0) { // Draw separation. - ERR_FAIL_COND_V(cache.font.is_null(), -1); + ERR_FAIL_COND_V(theme_cache.font.is_null(), -1); - int ofs = p_pos.x + ((p_item->disable_folding || hide_folding) ? cache.hseparation : cache.item_margin); + int ofs = p_pos.x + ((p_item->disable_folding || hide_folding) ? theme_cache.hseparation : theme_cache.item_margin); int skip2 = 0; for (int i = 0; i < columns.size(); i++) { if (skip2) { @@ -1753,8 +1939,8 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 continue; } } else { - ofs += cache.hseparation; - w -= cache.hseparation; + ofs += theme_cache.hseparation; + w -= theme_cache.hseparation; } if (p_item->cells[i].expand_right) { @@ -1770,10 +1956,10 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 int button_w = 0; for (int j = p_item->cells[i].buttons.size() - 1; j >= 0; j--) { Ref<Texture2D> b = p_item->cells[i].buttons[j].texture; - button_w += b->get_size().width + cache.button_pressed->get_minimum_size().width + cache.button_margin; + button_w += b->get_size().width + theme_cache.button_pressed->get_minimum_size().width + theme_cache.button_margin; } - int total_ofs = ofs - cache.offset.x; + int total_ofs = ofs - theme_cache.offset.x; if (total_ofs + w > p_draw_size.width) { w = MAX(button_w, p_draw_size.width - total_ofs); @@ -1783,9 +1969,9 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 int bw = 0; for (int j = p_item->cells[i].buttons.size() - 1; j >= 0; j--) { Ref<Texture2D> b = p_item->cells[i].buttons[j].texture; - Size2 s = b->get_size() + cache.button_pressed->get_minimum_size(); + Size2 s = b->get_size() + theme_cache.button_pressed->get_minimum_size(); - Point2i o = Point2i(ofs + w - s.width, p_pos.y) - cache.offset + p_draw_ofs; + Point2i o = Point2i(ofs + w - s.width, p_pos.y) - theme_cache.offset + p_draw_ofs; if (cache.click_type == Cache::CLICK_BUTTON && cache.click_item == p_item && cache.click_column == i && cache.click_index == j && !p_item->cells[i].buttons[j].disabled) { // Being pressed. @@ -1793,48 +1979,48 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 if (rtl) { od.x = get_size().width - od.x - s.x; } - cache.button_pressed->draw(get_canvas_item(), Rect2(od.x, od.y, s.width, MAX(s.height, label_h))); + theme_cache.button_pressed->draw(get_canvas_item(), Rect2(od.x, od.y, s.width, MAX(s.height, label_h))); } o.y += (label_h - s.height) / 2; - o += cache.button_pressed->get_offset(); + o += theme_cache.button_pressed->get_offset(); if (rtl) { o.x = get_size().width - o.x - b->get_width(); } b->draw(ci, o, p_item->cells[i].buttons[j].disabled ? Color(1, 1, 1, 0.5) : p_item->cells[i].buttons[j].color); - w -= s.width + cache.button_margin; - bw += s.width + cache.button_margin; + w -= s.width + theme_cache.button_margin; + bw += s.width + theme_cache.button_margin; } - Rect2i item_rect = Rect2i(Point2i(ofs, p_pos.y) - cache.offset + p_draw_ofs, Size2i(w, label_h)); + Rect2i item_rect = Rect2i(Point2i(ofs, p_pos.y) - theme_cache.offset + p_draw_ofs, Size2i(w, label_h)); Rect2i cell_rect = item_rect; if (i != 0) { - cell_rect.position.x -= cache.hseparation; - cell_rect.size.x += cache.hseparation; + cell_rect.position.x -= theme_cache.hseparation; + cell_rect.size.x += theme_cache.hseparation; } - if (cache.draw_guides) { + if (theme_cache.draw_guides) { Rect2 r = cell_rect; if (rtl) { r.position.x = get_size().width - r.position.x - r.size.x; } - RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(r.position.x, r.position.y + r.size.height), r.position + r.size, cache.guide_color, 1); + RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(r.position.x, r.position.y + r.size.height), r.position + r.size, theme_cache.guide_color, 1); } if (i == 0) { if (p_item->cells[0].selected && select_mode == SELECT_ROW) { - Rect2i row_rect = Rect2i(Point2i(cache.bg->get_margin(SIDE_LEFT), item_rect.position.y), Size2i(get_size().width - cache.bg->get_minimum_size().width, item_rect.size.y)); + Rect2i row_rect = Rect2i(Point2i(theme_cache.panel_style->get_margin(SIDE_LEFT), item_rect.position.y), Size2i(get_size().width - theme_cache.panel_style->get_minimum_size().width, item_rect.size.y)); //Rect2 r = Rect2i(row_rect.pos,row_rect.size); //r.grow(cache.selected->get_margin(SIDE_LEFT)); if (rtl) { row_rect.position.x = get_size().width - row_rect.position.x - row_rect.size.x; } if (has_focus()) { - cache.selected_focus->draw(ci, row_rect); + theme_cache.selected_focus->draw(ci, row_rect); } else { - cache.selected->draw(ci, row_rect); + theme_cache.selected->draw(ci, row_rect); } } } @@ -1844,15 +2030,16 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 p_item->set_meta("__focus_rect", Rect2(r.position, r.size)); - if (rtl) { - r.position.x = get_size().width - r.position.x - r.size.x; - } - - if (p_item->cells[i].selected) { - if (has_focus()) { - cache.selected_focus->draw(ci, r); - } else { - cache.selected->draw(ci, r); + if (select_mode != SELECT_ROW) { + if (rtl) { + r.position.x = get_size().width - r.position.x - r.size.x; + } + if (p_item->cells[i].selected) { + if (has_focus()) { + theme_cache.selected_focus->draw(ci, r); + } else { + theme_cache.selected->draw(ci, r); + } } } } @@ -1863,8 +2050,8 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 r.position.x = p_draw_ofs.x; r.size.x = w + ofs; } else { - r.position.x -= cache.hseparation; - r.size.x += cache.hseparation; + r.position.x -= theme_cache.hseparation; + r.size.x += theme_cache.hseparation; } if (rtl) { r.position.x = get_size().width - r.position.x - r.size.x; @@ -1887,28 +2074,34 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 if (drop_mode_over == p_item) { if (drop_mode_section == 0 || drop_mode_section == -1) { // Line above. - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x, r.position.y, r.size.x, 1), cache.drop_position_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x, r.position.y, r.size.x, 1), theme_cache.drop_position_color); } if (drop_mode_section == 0) { // Side lines. - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x, r.position.y, 1, r.size.y), cache.drop_position_color); - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x + r.size.x - 1, r.position.y, 1, r.size.y), cache.drop_position_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x, r.position.y, 1, r.size.y), theme_cache.drop_position_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x + r.size.x - 1, r.position.y, 1, r.size.y), theme_cache.drop_position_color); } if (drop_mode_section == 0 || (drop_mode_section == 1 && (!p_item->get_first_child() || p_item->is_collapsed()))) { // Line below. - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x, r.position.y + r.size.y, r.size.x, 1), cache.drop_position_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x, r.position.y + r.size.y, r.size.x, 1), theme_cache.drop_position_color); } } else if (drop_mode_over == p_item->get_parent()) { if (drop_mode_section == 1 && !p_item->get_prev() /* && !drop_mode_over->is_collapsed() */) { // The drop_mode_over shouldn't ever be collapsed in here, otherwise we would be drawing a child of a collapsed item. // Line above. - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x, r.position.y, r.size.x, 1), cache.drop_position_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x, r.position.y, r.size.x, 1), theme_cache.drop_position_color); } } } - Color col = p_item->cells[i].custom_color ? p_item->cells[i].color : get_theme_color(p_item->cells[i].selected ? "font_selected_color" : "font_color"); - Color font_outline_color = cache.font_outline_color; - int outline_size = cache.font_outline_size; + Color col; + if (p_item->cells[i].custom_color) { + col = p_item->cells[i].color; + } else { + col = p_item->cells[i].selected ? theme_cache.font_selected_color : theme_cache.font_color; + } + + Color font_outline_color = theme_cache.font_outline_color; + int outline_size = theme_cache.font_outline_size; Color icon_col = p_item->cells[i].icon_color; if (p_item->cells[i].dirty) { @@ -1928,9 +2121,9 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 draw_item_rect(p_item->cells.write[i], item_rect, col, icon_col, outline_size, font_outline_color); } break; case TreeItem::CELL_MODE_CHECK: { - Ref<Texture2D> checked = cache.checked; - Ref<Texture2D> unchecked = cache.unchecked; - Ref<Texture2D> indeterminate = cache.indeterminate; + Ref<Texture2D> checked = theme_cache.checked; + Ref<Texture2D> unchecked = theme_cache.unchecked; + Ref<Texture2D> indeterminate = theme_cache.indeterminate; Point2i check_ofs = item_rect.position; check_ofs.y += Math::floor((real_t)(item_rect.size.y - checked->get_height()) / 2); @@ -1942,7 +2135,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 unchecked->draw(ci, check_ofs); } - int check_w = checked->get_width() + cache.hseparation; + int check_w = checked->get_width() + theme_cache.hseparation; text_pos.x += check_w; @@ -1958,7 +2151,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 break; } - Ref<Texture2D> downarrow = cache.select_arrow; + Ref<Texture2D> downarrow = theme_cache.select_arrow; int cell_width = item_rect.size.x - downarrow->get_width(); p_item->cells.write[i].text_buf->set_width(cell_width); @@ -1980,7 +2173,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 downarrow->draw(ci, arrow_pos); } else { - Ref<Texture2D> updown = cache.updown; + Ref<Texture2D> updown = theme_cache.updown; int cell_width = item_rect.size.x - updown->get_width(); @@ -2037,7 +2230,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 break; } - Ref<Texture2D> downarrow = cache.select_arrow; + Ref<Texture2D> downarrow = theme_cache.select_arrow; Rect2i ir = item_rect; @@ -2049,16 +2242,16 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 if (p_item->cells[i].custom_button) { if (cache.hover_item == p_item && cache.hover_cell == i) { if (Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT)) { - draw_style_box(cache.custom_button_pressed, ir); + draw_style_box(theme_cache.custom_button_pressed, ir); } else { - draw_style_box(cache.custom_button_hover, ir); - col = cache.custom_button_font_highlight; + draw_style_box(theme_cache.custom_button_hover, ir); + col = theme_cache.custom_button_font_highlight; } } else { - draw_style_box(cache.custom_button, ir); + draw_style_box(theme_cache.custom_button, ir); } - ir.size -= cache.custom_button->get_minimum_size(); - ir.position += cache.custom_button->get_offset(); + ir.size -= theme_cache.custom_button->get_minimum_size(); + ir.position += theme_cache.custom_button->get_offset(); } draw_item_rect(p_item->cells.write[i], ir, col, icon_col, outline_size, font_outline_color); @@ -2079,9 +2272,9 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 cell_rect.position.x = get_size().width - cell_rect.position.x - cell_rect.size.x; } if (has_focus()) { - cache.cursor->draw(ci, cell_rect); + theme_cache.cursor->draw(ci, cell_rect); } else { - cache.cursor_unfocus->draw(ci, cell_rect); + theme_cache.cursor_unfocus->draw(ci, cell_rect); } } } @@ -2091,13 +2284,17 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 Ref<Texture2D> arrow; if (p_item->collapsed) { - arrow = cache.arrow_collapsed; + if (is_layout_rtl()) { + arrow = theme_cache.arrow_collapsed_mirrored; + } else { + arrow = theme_cache.arrow_collapsed; + } } else { - arrow = cache.arrow; + arrow = theme_cache.arrow; } - Point2 apos = p_pos + Point2i(0, (label_h - arrow->get_height()) / 2) - cache.offset + p_draw_ofs; - apos.x += cache.item_margin - arrow->get_width(); + Point2 apos = p_pos + Point2i(0, (label_h - arrow->get_height()) / 2) - theme_cache.offset + p_draw_ofs; + apos.x += theme_cache.item_margin - arrow->get_width(); if (rtl) { apos.x = get_size().width - apos.x - arrow->get_width(); @@ -2110,7 +2307,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 Point2 children_pos = p_pos; if (!skip) { - children_pos.x += cache.item_margin; + children_pos.x += theme_cache.item_margin; htotal += label_h; children_pos.y += htotal; } @@ -2118,7 +2315,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 if (!p_item->collapsed) { /* if not collapsed, check the children */ TreeItem *c = p_item->first_child; - int base_ofs = children_pos.y - cache.offset.y + p_draw_ofs.y; + int base_ofs = children_pos.y - theme_cache.offset.y + p_draw_ofs.y; int prev_ofs = base_ofs; int prev_hl_ofs = base_ofs; @@ -2129,20 +2326,20 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 } // Draw relationship lines. - if (cache.draw_relationship_lines > 0 && (!hide_root || c->parent != root) && c->is_visible()) { - int root_ofs = children_pos.x + ((p_item->disable_folding || hide_folding) ? cache.hseparation : cache.item_margin); - int parent_ofs = p_pos.x + cache.item_margin; - Point2i root_pos = Point2i(root_ofs, children_pos.y + label_h / 2) - cache.offset + p_draw_ofs; + if (theme_cache.draw_relationship_lines > 0 && (!hide_root || c->parent != root) && c->is_visible()) { + int root_ofs = children_pos.x + ((p_item->disable_folding || hide_folding) ? theme_cache.hseparation : theme_cache.item_margin); + int parent_ofs = p_pos.x + theme_cache.item_margin; + Point2i root_pos = Point2i(root_ofs, children_pos.y + label_h / 2) - theme_cache.offset + p_draw_ofs; if (c->get_visible_child_count() > 0) { - root_pos -= Point2i(cache.arrow->get_width(), 0); + root_pos -= Point2i(theme_cache.arrow->get_width(), 0); } - float line_width = cache.relationship_line_width * Math::round(cache.base_scale); - float parent_line_width = cache.parent_hl_line_width * Math::round(cache.base_scale); - float children_line_width = cache.children_hl_line_width * Math::round(cache.base_scale); + float line_width = theme_cache.relationship_line_width * Math::round(theme_cache.base_scale); + float parent_line_width = theme_cache.parent_hl_line_width * Math::round(theme_cache.base_scale); + float children_line_width = theme_cache.children_hl_line_width * Math::round(theme_cache.base_scale); - Point2i parent_pos = Point2i(parent_ofs - cache.arrow->get_width() / 2, p_pos.y + label_h / 2 + cache.arrow->get_height() / 2) - cache.offset + p_draw_ofs; + Point2i parent_pos = Point2i(parent_ofs - theme_cache.arrow->get_width() / 2, p_pos.y + label_h / 2 + theme_cache.arrow->get_height() / 2) - theme_cache.offset + p_draw_ofs; int more_prev_ofs = 0; @@ -2156,43 +2353,43 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 if (_is_branch_selected(c)) { // If this item or one of its children is selected, we draw the line using parent highlight style. if (htotal >= 0) { - RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(parent_line_width / 2), root_pos.y), cache.parent_hl_line_color, parent_line_width); + RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(parent_line_width / 2), root_pos.y), theme_cache.parent_hl_line_color, parent_line_width); } - RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(parent_line_width / 2)), Point2i(parent_pos.x, prev_hl_ofs), cache.parent_hl_line_color, parent_line_width); + RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(parent_line_width / 2)), Point2i(parent_pos.x, prev_hl_ofs), theme_cache.parent_hl_line_color, parent_line_width); - more_prev_ofs = cache.parent_hl_line_margin; + more_prev_ofs = theme_cache.parent_hl_line_margin; prev_hl_ofs = root_pos.y + Math::floor(parent_line_width / 2); } else if (p_item->is_selected(0)) { // If parent item is selected (but this item is not), we draw the line using children highlight style. // Siblings of the selected branch can be drawn with a slight offset and their vertical line must appear as highlighted. if (_is_sibling_branch_selected(c)) { if (htotal >= 0) { - RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(parent_line_width / 2), root_pos.y), cache.children_hl_line_color, children_line_width); + RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(parent_line_width / 2), root_pos.y), theme_cache.children_hl_line_color, children_line_width); } - RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(parent_line_width / 2)), Point2i(parent_pos.x, prev_hl_ofs), cache.parent_hl_line_color, parent_line_width); + RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(parent_line_width / 2)), Point2i(parent_pos.x, prev_hl_ofs), theme_cache.parent_hl_line_color, parent_line_width); prev_hl_ofs = root_pos.y + Math::floor(parent_line_width / 2); } else { if (htotal >= 0) { - RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(children_line_width / 2), root_pos.y), cache.children_hl_line_color, children_line_width); + RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(children_line_width / 2), root_pos.y), theme_cache.children_hl_line_color, children_line_width); } - RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(children_line_width / 2)), Point2i(parent_pos.x, prev_ofs + Math::floor(children_line_width / 2)), cache.children_hl_line_color, children_line_width); + RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(children_line_width / 2)), Point2i(parent_pos.x, prev_ofs + Math::floor(children_line_width / 2)), theme_cache.children_hl_line_color, children_line_width); } } else { // If nothing of the above is true, we draw the line using normal style. // Siblings of the selected branch can be drawn with a slight offset and their vertical line must appear as highlighted. if (_is_sibling_branch_selected(c)) { if (htotal >= 0) { - RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + cache.parent_hl_line_margin, root_pos.y), cache.relationship_line_color, line_width); + RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + theme_cache.parent_hl_line_margin, root_pos.y), theme_cache.relationship_line_color, line_width); } - RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(parent_line_width / 2)), Point2i(parent_pos.x, prev_hl_ofs), cache.parent_hl_line_color, parent_line_width); + RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(parent_line_width / 2)), Point2i(parent_pos.x, prev_hl_ofs), theme_cache.parent_hl_line_color, parent_line_width); prev_hl_ofs = root_pos.y + Math::floor(parent_line_width / 2); } else { if (htotal >= 0) { - RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(line_width / 2), root_pos.y), cache.relationship_line_color, line_width); + RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(line_width / 2), root_pos.y), theme_cache.relationship_line_color, line_width); } - RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(line_width / 2)), Point2i(parent_pos.x, prev_ofs + Math::floor(line_width / 2)), cache.relationship_line_color, line_width); + RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(line_width / 2)), Point2i(parent_pos.x, prev_ofs + Math::floor(line_width / 2)), theme_cache.relationship_line_color, line_width); } } } @@ -2205,12 +2402,12 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 break; // Last loop done, stop. } - if (cache.draw_relationship_lines == 0) { + if (theme_cache.draw_relationship_lines == 0) { return -1; // No need to draw anymore, full stop. } htotal = -1; - children_pos.y = cache.offset.y + p_draw_size.height; + children_pos.y = theme_cache.offset.y + p_draw_size.height; } else { htotal += child_h; children_pos.y += child_h; @@ -2361,7 +2558,7 @@ Rect2 Tree::search_item_rect(TreeItem *p_from, TreeItem *p_item) { void Tree::_range_click_timeout() { if (range_item_last && !range_drag_enabled && Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT)) { - Point2 pos = get_local_mouse_position() - cache.bg->get_offset(); + Point2 pos = get_local_mouse_position() - theme_cache.panel_style->get_offset(); if (show_column_titles) { pos.y -= _get_title_button_height(); @@ -2379,7 +2576,7 @@ void Tree::_range_click_timeout() { Ref<InputEventMouseButton> mb; mb.instantiate(); - int x_limit = get_size().width - cache.bg->get_minimum_size().width; + int x_limit = get_size().width - theme_cache.panel_style->get_minimum_size().width; if (h_scroll->is_visible()) { x_limit -= h_scroll->get_minimum_size().width; } @@ -2388,7 +2585,7 @@ void Tree::_range_click_timeout() { propagate_mouse_activated = false; // done from outside, so signal handler can't clear the tree in the middle of emit (which is a common case) blocked++; - propagate_mouse_event(pos + cache.offset, 0, 0, x_limit + cache.offset.width, false, root, MouseButton::LEFT, mb); + propagate_mouse_event(pos + theme_cache.offset, 0, 0, x_limit + theme_cache.offset.width, false, root, MouseButton::LEFT, mb); blocked--; if (range_click_timer->is_one_shot()) { @@ -2417,7 +2614,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int return 0; } - int item_h = compute_item_height(p_item) + cache.vseparation; + int item_h = compute_item_height(p_item) + theme_cache.vseparation; bool skip = (p_item == root && hide_root); @@ -2428,8 +2625,12 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int return -1; } - if (!p_item->disable_folding && !hide_folding && p_item->first_child && (p_pos.x >= x_ofs && p_pos.x < (x_ofs + cache.item_margin))) { - p_item->set_collapsed(!p_item->is_collapsed()); + if (!p_item->disable_folding && !hide_folding && p_item->first_child && (p_pos.x >= x_ofs && p_pos.x < (x_ofs + theme_cache.item_margin))) { + if (enable_recursive_folding && p_mod->is_shift_pressed()) { + p_item->set_collapsed_recursive(!p_item->is_collapsed()); + } else { + p_item->set_collapsed(!p_item->is_collapsed()); + } return -1; } @@ -2447,7 +2648,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int if (p_item->cells[i].expand_right) { int plus = 1; while (i + plus < columns.size() && !p_item->cells[i + plus].editable && p_item->cells[i + plus].mode == TreeItem::CELL_MODE_STRING && p_item->cells[i + plus].text.is_empty() && p_item->cells[i + plus].icon.is_null()) { - col_width += cache.hseparation; + col_width += theme_cache.hseparation; col_width += get_column_width(i + plus); plus++; } @@ -2467,20 +2668,24 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int if (col == -1) { return -1; } else if (col == 0) { - int margin = x_ofs + cache.item_margin; //-cache.hseparation; - //int lm = cache.bg->get_margin(SIDE_LEFT); + int margin = x_ofs + theme_cache.item_margin; //-theme_cache.hseparation; + //int lm = theme_cache.panel_style->get_margin(SIDE_LEFT); col_width -= margin; limit_w -= margin; col_ofs += margin; x -= margin; } else { - col_width -= cache.hseparation; - limit_w -= cache.hseparation; - x -= cache.hseparation; + col_width -= theme_cache.hseparation; + limit_w -= theme_cache.hseparation; + x -= theme_cache.hseparation; } if (!p_item->disable_folding && !hide_folding && !p_item->cells[col].editable && !p_item->cells[col].selectable && p_item->get_first_child()) { - p_item->set_collapsed(!p_item->is_collapsed()); + if (enable_recursive_folding && p_mod->is_shift_pressed()) { + p_item->set_collapsed_recursive(!p_item->is_collapsed()); + } else { + p_item->set_collapsed(!p_item->is_collapsed()); + } return -1; //collapse/uncollapse because nothing can be done with item } @@ -2493,7 +2698,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int int button_w = 0; for (int j = p_item->cells[col].buttons.size() - 1; j >= 0; j--) { Ref<Texture2D> b = p_item->cells[col].buttons[j].texture; - button_w += b->get_size().width + cache.button_pressed->get_minimum_size().width + cache.button_margin; + button_w += b->get_size().width + theme_cache.button_pressed->get_minimum_size().width + theme_cache.button_margin; } col_width = MAX(button_w, MIN(limit_w, col_width)); @@ -2501,7 +2706,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int for (int j = c.buttons.size() - 1; j >= 0; j--) { Ref<Texture2D> b = c.buttons[j].texture; - int w = b->get_size().width + cache.button_pressed->get_minimum_size().width; + int w = b->get_size().width + theme_cache.button_pressed->get_minimum_size().width; if (x > col_width - w) { if (c.buttons[j].disabled) { @@ -2525,11 +2730,11 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int cache.click_item = p_item; cache.click_column = col; cache.click_pos = click_pos; - update(); + queue_redraw(); return -1; } - col_width -= w + cache.button_margin; + col_width -= w + theme_cache.button_margin; } if (p_button == MouseButton::LEFT || (p_button == MouseButton::RIGHT && allow_rmb_select)) { @@ -2543,7 +2748,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int return -1; } - if (select_mode == SELECT_MULTI && p_mod->is_command_pressed() && c.selectable) { + if (select_mode == SELECT_MULTI && p_mod->is_command_or_control_pressed() && c.selectable) { if (!c.selected || p_button == MouseButton::RIGHT) { p_item->select(col); emit_signal(SNAME("multi_selected"), p_item, col, true); @@ -2583,7 +2788,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int emit_signal(SNAME("multi_selected"),p_item,col,true); } */ - update(); + queue_redraw(); } } } @@ -2609,7 +2814,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int case TreeItem::CELL_MODE_CHECK: { bring_up_editor = false; //checkboxes are not edited with editor if (force_edit_checkbox_only_on_checkbox) { - if (x < cache.checked->get_width()) { + if (x < theme_cache.checked->get_width()) { p_item->set_checked(col, !c.checked); item_edited(col, p_item, p_button); } @@ -2631,7 +2836,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int } popup_menu->set_size(Size2(col_width, 0)); - popup_menu->set_position(get_screen_position() + Point2i(col_ofs, _get_title_button_height() + y_ofs + item_h) - cache.offset); + popup_menu->set_position(get_screen_position() + Point2i(col_ofs, _get_title_button_height() + y_ofs + item_h) - theme_cache.offset); popup_menu->popup(); popup_edited_item = p_item; popup_edited_item_col = col; @@ -2689,9 +2894,9 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int case TreeItem::CELL_MODE_CUSTOM: { edited_item = p_item; edited_col = col; - bool on_arrow = x > col_width - cache.select_arrow->get_width(); + bool on_arrow = x > col_width - theme_cache.select_arrow->get_width(); - custom_popup_rect = Rect2i(get_global_position() + Point2i(col_ofs, _get_title_button_height() + y_ofs + item_h - cache.offset.y), Size2(get_column_width(col), item_h)); + custom_popup_rect = Rect2i(get_global_position() + Point2i(col_ofs, _get_title_button_height() + y_ofs + item_h - theme_cache.offset.y), Size2(get_column_width(col), item_h)); if (on_arrow || !p_item->cells[col].custom_button) { emit_signal(SNAME("custom_popup_edited"), ((bool)(x >= (col_width - item_h / 2)))); @@ -2713,7 +2918,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int popup_pressing_edited_item = p_item; popup_pressing_edited_item_column = col; - pressing_item_rect = Rect2(get_global_position() + Point2i(col_ofs, _get_title_button_height() + y_ofs) - cache.offset, Size2(col_width, item_h)); + pressing_item_rect = Rect2(get_global_position() + Point2i(col_ofs, _get_title_button_height() + y_ofs) - theme_cache.offset, Size2(col_width, item_h)); pressing_for_editor_text = editor_text; pressing_for_editor = true; @@ -2722,8 +2927,8 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int Point2i new_pos = p_pos; if (!skip) { - x_ofs += cache.item_margin; - //new_pos.x-=cache.item_margin; + x_ofs += theme_cache.item_margin; + //new_pos.x-=theme_cache.item_margin; y_ofs += item_h; new_pos.y -= item_h; } @@ -2802,7 +3007,7 @@ void Tree::_text_editor_submit(String p_text) { } item_edited(popup_edited_item_col, popup_edited_item); - update(); + queue_redraw(); } void Tree::value_editor_changed(double p_value) { @@ -2815,8 +3020,11 @@ void Tree::value_editor_changed(double p_value) { TreeItem::Cell &c = popup_edited_item->cells.write[popup_edited_item_col]; c.val = p_value; + + text_editor->set_text(String::num(c.val, Math::range_step_decimals(c.step))); + item_edited(popup_edited_item_col, popup_edited_item); - update(); + queue_redraw(); } void Tree::popup_select(int p_option) { @@ -2830,7 +3038,7 @@ void Tree::popup_select(int p_option) { popup_edited_item->cells.write[popup_edited_item_col].val = p_option; //popup_edited_item->edited_signal.call( popup_edited_item_col ); - update(); + queue_redraw(); item_edited(popup_edited_item_col, popup_edited_item); } @@ -2857,7 +3065,7 @@ void Tree::_go_left() { selected_item->select(selected_col - 1); } } - update(); + queue_redraw(); accept_event(); ensure_cursor_is_visible(); } @@ -2878,7 +3086,7 @@ void Tree::_go_right() { selected_item->select(selected_col + 1); } } - update(); + queue_redraw(); ensure_cursor_is_visible(); accept_event(); } @@ -2907,7 +3115,7 @@ void Tree::_go_up() { } selected_item = prev; emit_signal(SNAME("cell_selected")); - update(); + queue_redraw(); } else { int col = selected_col < 0 ? 0 : selected_col; while (prev && !prev->cells[col].selectable) { @@ -2950,7 +3158,7 @@ void Tree::_go_down() { selected_item = next; emit_signal(SNAME("cell_selected")); - update(); + queue_redraw(); } else { int col = selected_col < 0 ? 0 : selected_col; @@ -2981,7 +3189,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventKey> k = p_event; - bool is_command = k.is_valid() && k->is_command_pressed(); + bool is_command = k.is_valid() && k->is_command_or_control_pressed(); if (p_event->is_action("ui_right") && p_event->is_pressed()) { if (!cursor_can_exit_tree) { accept_event(); @@ -3060,7 +3268,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { if (select_mode == SELECT_MULTI) { selected_item = next; emit_signal(SNAME("cell_selected")); - update(); + queue_redraw(); } else { while (next && !next->cells[selected_col].selectable) { next = next->get_next_visible(); @@ -3098,7 +3306,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { if (select_mode == SELECT_MULTI) { selected_item = prev; emit_signal(SNAME("cell_selected")); - update(); + queue_redraw(); } else { while (prev && !prev->cells[selected_col].selectable) { prev = prev->get_prev_visible(); @@ -3139,7 +3347,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { if (!k->is_pressed()) { return; } - if (k->is_command_pressed() || (k->is_shift_pressed() && k->get_unicode() == 0) || k->is_meta_pressed()) { + if (k->is_command_or_control_pressed() || (k->is_shift_pressed() && k->get_unicode() == 0) || k->is_meta_pressed()) { return; } if (!root) { @@ -3164,18 +3372,14 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseMotion> mm = p_event; if (mm.is_valid()) { - if (cache.font.is_null()) { // avoid a strange case that may corrupt stuff - update_cache(); - } - - Ref<StyleBox> bg = cache.bg; + Ref<StyleBox> bg = theme_cache.panel_style; bool rtl = is_layout_rtl(); Point2 pos = mm->get_position(); if (rtl) { pos.x = get_size().width - pos.x; } - pos -= cache.bg->get_offset(); + pos -= theme_cache.panel_style->get_offset(); Cache::ClickType old_hover = cache.hover_type; int old_index = cache.hover_index; @@ -3185,7 +3389,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { if (show_column_titles) { pos.y -= _get_title_button_height(); if (pos.y < 0) { - pos.x += cache.offset.x; + pos.x += theme_cache.offset.x; int len = 0; for (int i = 0; i < columns.size(); i++) { len += get_column_width(i); @@ -3203,7 +3407,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { if (rtl) { mpos.x = get_size().width - mpos.x; } - mpos -= cache.bg->get_offset(); + mpos -= theme_cache.panel_style->get_offset(); mpos.y -= _get_title_button_height(); if (mpos.y >= 0) { if (h_scroll->is_visible_in_tree()) { @@ -3222,11 +3426,11 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { if (drop_mode_flags) { if (it != drop_mode_over) { drop_mode_over = it; - update(); + queue_redraw(); } if (it && section != drop_mode_section) { drop_mode_section = section; - update(); + queue_redraw(); } } @@ -3235,14 +3439,14 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { if (it != old_it || col != old_col) { if (old_it && old_col >= old_it->cells.size()) { - // Columns may have changed since last update(). - update(); + // Columns may have changed since last redraw(). + queue_redraw(); } else { // Only need to update if mouse enters/exits a button bool was_over_button = old_it && old_it->cells[old_col].custom_button; bool is_over_button = it && it->cells[col].custom_button; if (was_over_button || is_over_button) { - update(); + queue_redraw(); } } } @@ -3251,7 +3455,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { // Update if mouse enters/exits columns if (cache.hover_type != old_hover || cache.hover_index != old_index) { - update(); + queue_redraw(); } if (pressing_for_editor && popup_pressing_edited_item && (popup_pressing_edited_item->get_cell_mode(popup_pressing_edited_item_column) == TreeItem::CELL_MODE_RANGE)) { @@ -3294,35 +3498,34 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseButton> mb = p_event; if (mb.is_valid()) { - if (cache.font.is_null()) { // avoid a strange case that may corrupt stuff - update_cache(); - } - bool rtl = is_layout_rtl(); if (!mb->is_pressed()) { - if (mb->get_button_index() == MouseButton::LEFT) { + if (mb->get_button_index() == MouseButton::LEFT || + mb->get_button_index() == MouseButton::RIGHT) { Point2 pos = mb->get_position(); if (rtl) { pos.x = get_size().width - pos.x; } - pos -= cache.bg->get_offset(); + pos -= theme_cache.panel_style->get_offset(); if (show_column_titles) { pos.y -= _get_title_button_height(); if (pos.y < 0) { - pos.x += cache.offset.x; + pos.x += theme_cache.offset.x; int len = 0; for (int i = 0; i < columns.size(); i++) { len += get_column_width(i); - if (pos.x < len) { - emit_signal(SNAME("column_title_pressed"), i); + if (pos.x < static_cast<real_t>(len)) { + emit_signal(SNAME("column_title_clicked"), i, mb->get_button_index()); break; } } } } + } + if (mb->get_button_index() == MouseButton::LEFT) { if (single_select_defer) { select_single_item(single_select_defer, root, single_select_defer_column); single_select_defer = nullptr; @@ -3387,7 +3590,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { cache.click_id = -1; cache.click_item = nullptr; cache.click_column = 0; - update(); + queue_redraw(); return; } @@ -3398,7 +3601,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { switch (mb->get_button_index()) { case MouseButton::RIGHT: case MouseButton::LEFT: { - Ref<StyleBox> bg = cache.bg; + Ref<StyleBox> bg = theme_cache.panel_style; Point2 pos = mb->get_position(); if (rtl) { @@ -3410,18 +3613,15 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { pos.y -= _get_title_button_height(); if (pos.y < 0) { - if (mb->get_button_index() == MouseButton::LEFT) { - pos.x += cache.offset.x; - int len = 0; - for (int i = 0; i < columns.size(); i++) { - len += get_column_width(i); - if (pos.x < len) { - cache.click_type = Cache::CLICK_TITLE; - cache.click_index = i; - //cache.click_id=; - update(); - break; - } + pos.x += theme_cache.offset.x; + int len = 0; + for (int i = 0; i < columns.size(); i++) { + len += get_column_width(i); + if (pos.x < static_cast<real_t>(len)) { + cache.click_type = Cache::CLICK_TITLE; + cache.click_index = i; + queue_redraw(); + break; } } break; @@ -3436,14 +3636,14 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { pressing_for_editor = false; propagate_mouse_activated = false; - int x_limit = get_size().width - cache.bg->get_minimum_size().width; + int x_limit = get_size().width - theme_cache.panel_style->get_minimum_size().width; if (h_scroll->is_visible()) { x_limit -= h_scroll->get_minimum_size().width; } cache.rtl = is_layout_rtl(); blocked++; - propagate_mouse_event(pos + cache.offset, 0, 0, x_limit + cache.offset.width, mb->is_double_click(), root, mb->get_button_index(), mb); + propagate_mouse_event(pos + theme_cache.offset, 0, 0, x_limit + theme_cache.offset.width, mb->is_double_click(), root, mb->get_button_index(), mb); blocked--; if (pressing_for_editor) { @@ -3477,7 +3677,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { } if (mb->get_button_index() == MouseButton::LEFT) { - if (get_item_at_position(mb->get_position()) == nullptr && !mb->is_shift_pressed() && !mb->is_ctrl_pressed() && !mb->is_command_pressed()) { + if (get_item_at_position(mb->get_position()) == nullptr && !mb->is_shift_pressed() && !mb->is_ctrl_pressed() && !mb->is_command_or_control_pressed()) { emit_signal(SNAME("nothing_selected")); } } @@ -3582,12 +3782,17 @@ bool Tree::edit_selected() { } else if (c.mode == TreeItem::CELL_MODE_STRING || c.mode == TreeItem::CELL_MODE_RANGE) { Rect2 popup_rect; - Vector2 ofs(0, (text_editor->get_size().height - rect.size.height) / 2); + Vector2 ofs(0, Math::floor((text_editor->get_size().height - rect.size.height) / 2)); // "floor()" centers vertically. Point2i textedpos = get_screen_position() + rect.position - ofs; cache.text_editor_position = textedpos; popup_rect.position = textedpos; popup_rect.size = rect.size; + + // Account for icon. + popup_rect.position.x += c.get_icon_size().x; + popup_rect.size.x -= c.get_icon_size().x; + text_editor->clear(); text_editor->set_text(c.mode == TreeItem::CELL_MODE_STRING ? c.text : String::num(c.val, Math::range_step_decimals(c.step))); text_editor->select_all(); @@ -3625,7 +3830,7 @@ bool Tree::is_editing() { } Size2 Tree::get_internal_min_size() const { - Size2i size = cache.bg->get_offset(); + Size2i size = theme_cache.panel_style->get_offset(); if (root) { size.height += get_item_height(root); } @@ -3648,23 +3853,23 @@ void Tree::update_scrollbars() { Size2 hmin = h_scroll->get_combined_minimum_size(); Size2 vmin = v_scroll->get_combined_minimum_size(); - v_scroll->set_begin(Point2(size.width - vmin.width, cache.bg->get_margin(SIDE_TOP))); - v_scroll->set_end(Point2(size.width, size.height - cache.bg->get_margin(SIDE_TOP) - cache.bg->get_margin(SIDE_BOTTOM))); + v_scroll->set_begin(Point2(size.width - vmin.width, theme_cache.panel_style->get_margin(SIDE_TOP))); + v_scroll->set_end(Point2(size.width, size.height - theme_cache.panel_style->get_margin(SIDE_TOP) - theme_cache.panel_style->get_margin(SIDE_BOTTOM))); h_scroll->set_begin(Point2(0, size.height - hmin.height)); h_scroll->set_end(Point2(size.width - vmin.width, size.height)); Size2 internal_min_size = get_internal_min_size(); - bool display_vscroll = internal_min_size.height + cache.bg->get_margin(SIDE_TOP) > size.height; - bool display_hscroll = internal_min_size.width + cache.bg->get_margin(SIDE_LEFT) > size.width; + bool display_vscroll = internal_min_size.height + theme_cache.panel_style->get_margin(SIDE_TOP) > size.height; + bool display_hscroll = internal_min_size.width + theme_cache.panel_style->get_margin(SIDE_LEFT) > size.width; for (int i = 0; i < 2; i++) { // Check twice, as both values are dependent on each other. if (display_hscroll) { - display_vscroll = internal_min_size.height + cache.bg->get_margin(SIDE_TOP) + hmin.height > size.height; + display_vscroll = internal_min_size.height + theme_cache.panel_style->get_margin(SIDE_TOP) + hmin.height > size.height; } if (display_vscroll) { - display_hscroll = internal_min_size.width + cache.bg->get_margin(SIDE_LEFT) + vmin.width > size.width; + display_hscroll = internal_min_size.width + theme_cache.panel_style->get_margin(SIDE_LEFT) + vmin.width > size.width; } } @@ -3672,29 +3877,29 @@ void Tree::update_scrollbars() { v_scroll->show(); v_scroll->set_max(internal_min_size.height); v_scroll->set_page(size.height - hmin.height - tbh); - cache.offset.y = v_scroll->get_value(); + theme_cache.offset.y = v_scroll->get_value(); } else { v_scroll->hide(); - cache.offset.y = 0; + theme_cache.offset.y = 0; } if (display_hscroll) { h_scroll->show(); h_scroll->set_max(internal_min_size.width); h_scroll->set_page(size.width - vmin.width); - cache.offset.x = h_scroll->get_value(); + theme_cache.offset.x = h_scroll->get_value(); } else { h_scroll->hide(); - cache.offset.x = 0; + theme_cache.offset.x = 0; } } int Tree::_get_title_button_height() const { - ERR_FAIL_COND_V(cache.font.is_null() || cache.title_button.is_null(), 0); + ERR_FAIL_COND_V(theme_cache.font.is_null() || theme_cache.title_button.is_null(), 0); int h = 0; if (show_column_titles) { for (int i = 0; i < columns.size(); i++) { - h = MAX(h, columns[i].text_buf->get_size().y + cache.title_button->get_minimum_size().height); + h = MAX(h, columns[i].text_buf->get_size().y + theme_cache.title_button->get_minimum_size().height); } } return h; @@ -3711,7 +3916,7 @@ void Tree::_notification(int p_what) { case NOTIFICATION_MOUSE_EXIT: { if (cache.hover_type != Cache::CLICK_NONE) { cache.hover_type = Cache::CLICK_NONE; - update(); + queue_redraw(); } } break; @@ -3719,20 +3924,16 @@ void Tree::_notification(int p_what) { drag_touching = false; } break; - case NOTIFICATION_ENTER_TREE: { - update_cache(); - } break; - case NOTIFICATION_DRAG_END: { drop_mode_flags = 0; scrolling = false; set_physics_process_internal(false); - update(); + queue_redraw(); } break; case NOTIFICATION_DRAG_BEGIN: { single_select_defer = nullptr; - if (cache.scroll_speed > 0) { + if (theme_cache.scroll_speed > 0) { scrolling = true; set_physics_process_internal(true); } @@ -3776,22 +3977,22 @@ void Tree::_notification(int p_what) { } Point2 mouse_position = get_viewport()->get_mouse_position() - get_global_position(); - if (scrolling && get_rect().grow(cache.scroll_border).has_point(mouse_position)) { + if (scrolling && get_rect().grow(theme_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 ((ABS(mouse_position.x) < ABS(mouse_position.x - get_size().width)) && (ABS(mouse_position.x) < theme_cache.scroll_border)) { + point.x = mouse_position.x - theme_cache.scroll_border; + } else if (ABS(mouse_position.x - get_size().width) < theme_cache.scroll_border) { + point.x = mouse_position.x - (get_size().width - theme_cache.scroll_border); } - 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); + if ((ABS(mouse_position.y) < ABS(mouse_position.y - get_size().height)) && (ABS(mouse_position.y) < theme_cache.scroll_border)) { + point.y = mouse_position.y - theme_cache.scroll_border; + } else if (ABS(mouse_position.y - get_size().height) < theme_cache.scroll_border) { + point.y = mouse_position.y - (get_size().height - theme_cache.scroll_border); } - point *= cache.scroll_speed * get_physics_process_delta_time(); + point *= theme_cache.scroll_speed * get_physics_process_delta_time(); point += get_scroll(); h_scroll->set_value(point.x); v_scroll->set_value(point.y); @@ -3799,13 +4000,12 @@ void Tree::_notification(int p_what) { } break; case NOTIFICATION_DRAW: { - update_cache(); + v_scroll->set_custom_step(theme_cache.font->get_height(theme_cache.font_size)); + update_scrollbars(); RID ci = get_canvas_item(); - Ref<StyleBox> bg = cache.bg; - Color font_outline_color = get_theme_color(SNAME("font_outline_color")); - int outline_size = get_theme_constant(SNAME("outline_size")); + Ref<StyleBox> bg = theme_cache.panel_style; Point2 draw_ofs; draw_ofs += bg->get_offset(); @@ -3829,11 +4029,11 @@ void Tree::_notification(int p_what) { if (show_column_titles) { //title buttons - int ofs2 = cache.bg->get_margin(SIDE_LEFT); + int ofs2 = theme_cache.panel_style->get_margin(SIDE_LEFT); for (int i = 0; i < columns.size(); i++) { - Ref<StyleBox> sb = (cache.click_type == Cache::CLICK_TITLE && cache.click_index == i) ? cache.title_button_pressed : ((cache.hover_type == Cache::CLICK_TITLE && cache.hover_index == i) ? cache.title_button_hover : cache.title_button); - Ref<Font> f = cache.tb_font; - Rect2 tbrect = Rect2(ofs2 - cache.offset.x, bg->get_margin(SIDE_TOP), get_column_width(i), tbh); + Ref<StyleBox> sb = (cache.click_type == Cache::CLICK_TITLE && cache.click_index == i) ? theme_cache.title_button_pressed : ((cache.hover_type == Cache::CLICK_TITLE && cache.hover_index == i) ? theme_cache.title_button_hover : theme_cache.title_button); + Ref<Font> f = theme_cache.tb_font; + Rect2 tbrect = Rect2(ofs2 - theme_cache.offset.x, bg->get_margin(SIDE_TOP), get_column_width(i), tbh); if (cache.rtl) { tbrect.position.x = get_size().width - tbrect.size.x - tbrect.position.x; } @@ -3844,19 +4044,18 @@ void Tree::_notification(int p_what) { columns.write[i].text_buf->set_width(clip_w); Vector2 text_pos = tbrect.position + Point2i(sb->get_offset().x + (tbrect.size.width - columns[i].text_buf->get_size().x) / 2, (tbrect.size.height - columns[i].text_buf->get_size().y) / 2); - if (outline_size > 0 && font_outline_color.a > 0) { - columns[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color); + if (theme_cache.font_outline_size > 0 && theme_cache.font_outline_color.a > 0) { + columns[i].text_buf->draw_outline(ci, text_pos, theme_cache.font_outline_size, theme_cache.font_outline_color); } - columns[i].text_buf->draw(ci, text_pos, cache.title_button_color); + columns[i].text_buf->draw(ci, text_pos, theme_cache.title_button_color); } } - // Draw the background focus outline last, so that it is drawn in front of the section headings. + // Draw the focus outline last, so that it is drawn in front of the section headings. // Otherwise, section heading backgrounds can appear to be in front of the focus outline when scrolling. if (has_focus()) { RenderingServer::get_singleton()->canvas_item_add_clip_ignore(ci, true); - const Ref<StyleBox> bg_focus = get_theme_stylebox(SNAME("bg_focus")); - bg_focus->draw(ci, Rect2(Point2(), get_size())); + theme_cache.focus_style->draw(ci, Rect2(Point2(), get_size())); RenderingServer::get_singleton()->canvas_item_add_clip_ignore(ci, false); } } break; @@ -3864,7 +4063,6 @@ void Tree::_notification(int p_what) { case NOTIFICATION_THEME_CHANGED: case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: case NOTIFICATION_TRANSLATION_CHANGED: { - update_cache(); _update_all(); } break; @@ -3899,7 +4097,7 @@ Size2 Tree::get_minimum_size() const { return Size2(); } else { Vector2 min_size = get_internal_min_size(); - Ref<StyleBox> bg = cache.bg; + Ref<StyleBox> bg = theme_cache.panel_style; if (bg.is_valid()) { min_size.x += bg->get_margin(SIDE_LEFT) + bg->get_margin(SIDE_RIGHT); min_size.y += bg->get_margin(SIDE_TOP) + bg->get_margin(SIDE_BOTTOM); @@ -3969,7 +4167,7 @@ void Tree::item_changed(int p_column, TreeItem *p_item) { if (p_item != nullptr && p_column >= 0 && p_column < p_item->cells.size()) { p_item->cells.write[p_column].dirty = true; } - update(); + queue_redraw(); } void Tree::item_selected(int p_column, TreeItem *p_item) { @@ -3988,7 +4186,7 @@ void Tree::item_selected(int p_column, TreeItem *p_item) { } else { select_single_item(p_item, root, p_column); } - update(); + queue_redraw(); } void Tree::item_deselected(int p_column, TreeItem *p_item) { @@ -4003,7 +4201,7 @@ void Tree::item_deselected(int p_column, TreeItem *p_item) { if (select_mode == SELECT_MULTI || select_mode == SELECT_SINGLE) { p_item->cells.write[p_column].selected = false; } - update(); + queue_redraw(); } void Tree::set_select_mode(SelectMode p_mode) { @@ -4026,7 +4224,7 @@ void Tree::deselect_all() { selected_item = nullptr; selected_col = -1; - update(); + queue_redraw(); } bool Tree::is_anything_selected() { @@ -4055,12 +4253,16 @@ void Tree::clear() { popup_edited_item = nullptr; popup_pressing_edited_item = nullptr; - update(); + queue_redraw(); }; void Tree::set_hide_root(bool p_enabled) { + if (hide_root == p_enabled) { + return; + } + hide_root = p_enabled; - update(); + queue_redraw(); } bool Tree::is_root_hidden() const { @@ -4070,31 +4272,48 @@ bool Tree::is_root_hidden() const { void Tree::set_column_custom_minimum_width(int p_column, int p_min_width) { ERR_FAIL_INDEX(p_column, columns.size()); + if (columns[p_column].custom_min_width == p_min_width) { + return; + } + if (p_min_width < 0) { return; } columns.write[p_column].custom_min_width = p_min_width; - update(); + queue_redraw(); } void Tree::set_column_expand(int p_column, bool p_expand) { ERR_FAIL_INDEX(p_column, columns.size()); + if (columns[p_column].expand == p_expand) { + return; + } + columns.write[p_column].expand = p_expand; - update(); + queue_redraw(); } void Tree::set_column_expand_ratio(int p_column, int p_ratio) { ERR_FAIL_INDEX(p_column, columns.size()); + + if (columns[p_column].expand_ratio == p_ratio) { + return; + } + columns.write[p_column].expand_ratio = p_ratio; - update(); + queue_redraw(); } void Tree::set_column_clip_content(int p_column, bool p_fit) { ERR_FAIL_INDEX(p_column, columns.size()); + if (columns[p_column].clip_content == p_fit) { + return; + } + columns.write[p_column].clip_content = p_fit; - update(); + queue_redraw(); } bool Tree::is_column_expanding(int p_column) const { @@ -4174,7 +4393,7 @@ int Tree::get_column_minimum_width(int p_column) const { // Check if the visible title of the column is wider. if (show_column_titles) { - min_width = MAX(cache.font->get_string_size(columns[p_column].title, HORIZONTAL_ALIGNMENT_LEFT, -1, cache.font_size).width + cache.bg->get_margin(SIDE_LEFT) + cache.bg->get_margin(SIDE_RIGHT), min_width); + min_width = MAX(theme_cache.font->get_string_size(columns[p_column].title, HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).width + theme_cache.panel_style->get_margin(SIDE_LEFT) + theme_cache.panel_style->get_margin(SIDE_RIGHT), min_width); } if (!columns[p_column].clip_content) { @@ -4198,9 +4417,9 @@ int Tree::get_column_minimum_width(int p_column) const { // Get the item minimum size. Size2 item_size = item->get_minimum_size(p_column); if (p_column == 0) { - item_size.width += cache.item_margin * depth; + item_size.width += theme_cache.item_margin * depth; } else { - item_size.width += cache.hseparation; + item_size.width += theme_cache.hseparation; } // Check if the item is wider. @@ -4219,7 +4438,7 @@ int Tree::get_column_width(int p_column) const { if (columns[p_column].expand) { int expand_area = get_size().width; - Ref<StyleBox> bg = cache.bg; + Ref<StyleBox> bg = theme_cache.panel_style; if (bg.is_valid()) { expand_area -= bg->get_margin(SIDE_LEFT) + bg->get_margin(SIDE_RIGHT); @@ -4267,7 +4486,7 @@ void Tree::set_columns(int p_columns) { if (selected_col >= p_columns) { selected_col = p_columns - 1; } - update(); + queue_redraw(); } int Tree::get_columns() const { @@ -4275,7 +4494,7 @@ int Tree::get_columns() const { } void Tree::_scroll_moved(float) { - update(); + queue_redraw(); } Rect2 Tree::get_custom_popup_rect() const { @@ -4296,7 +4515,7 @@ int Tree::get_item_offset(TreeItem *p_item) const { ofs += compute_item_height(it); if (it != root || !hide_root) { - ofs += cache.vseparation; + ofs += theme_cache.vseparation; } if (it->first_child && !it->collapsed) { @@ -4327,14 +4546,14 @@ void Tree::ensure_cursor_is_visible() { return; // Nothing under cursor. } - const Size2 area_size = get_size() - cache.bg->get_minimum_size(); + const Size2 area_size = get_size() - theme_cache.panel_style->get_minimum_size(); int y_offset = get_item_offset(selected_item); if (y_offset != -1) { const int tbh = _get_title_button_height(); y_offset -= tbh; - const int cell_h = compute_item_height(selected_item) + cache.vseparation; + const int cell_h = compute_item_height(selected_item) + theme_cache.vseparation; const int screen_h = area_size.height - h_scroll->get_combined_minimum_size().height - tbh; if (cell_h > screen_h) { // Screen size is too small, maybe it was not resized yet. @@ -4401,7 +4620,7 @@ Rect2 Tree::get_item_rect(TreeItem *p_item, int p_column, int p_button) const { Vector2 ofst = Vector2(r.position.x + r.size.x, r.position.y); for (int j = c.buttons.size() - 1; j >= 0; j--) { Ref<Texture2D> b = c.buttons[j].texture; - Size2 size = b->get_size() + cache.button_pressed->get_minimum_size(); + Size2 size = b->get_size() + theme_cache.button_pressed->get_minimum_size(); ofst.x -= size.x; if (j == p_button) { @@ -4415,8 +4634,12 @@ Rect2 Tree::get_item_rect(TreeItem *p_item, int p_column, int p_button) const { } void Tree::set_column_titles_visible(bool p_show) { + if (show_column_titles == p_show) { + return; + } + show_column_titles = p_show; - update(); + queue_redraw(); } bool Tree::are_column_titles_visible() const { @@ -4425,12 +4648,14 @@ bool Tree::are_column_titles_visible() const { void Tree::set_column_title(int p_column, const String &p_title) { ERR_FAIL_INDEX(p_column, columns.size()); - if (cache.font.is_null()) { // avoid a strange case that may corrupt stuff - update_cache(); + + if (columns[p_column].title == p_title) { + return; } + columns.write[p_column].title = p_title; update_column(p_column); - update(); + queue_redraw(); } String Tree::get_column_title(int p_column) const { @@ -4444,7 +4669,7 @@ void Tree::set_column_title_direction(int p_column, Control::TextDirection p_tex if (columns[p_column].text_direction != p_text_direction) { columns.write[p_column].text_direction = p_text_direction; update_column(p_column); - update(); + queue_redraw(); } } @@ -4458,7 +4683,7 @@ void Tree::set_column_title_language(int p_column, const String &p_language) { if (columns[p_column].language != p_language) { columns.write[p_column].language = p_language; update_column(p_column); - update(); + queue_redraw(); } } @@ -4489,7 +4714,7 @@ void Tree::scroll_to_item(TreeItem *p_item, bool p_center_on_item) { const real_t tree_height = get_size().y; const Rect2 item_rect = get_item_rect(p_item); const real_t item_y = item_rect.position.y; - const real_t item_height = item_rect.size.y + cache.vseparation; + const real_t item_height = item_rect.size.y + theme_cache.vseparation; if (p_center_on_item) { v_scroll->set_value(item_y - (tree_height - item_height) / 2.0f); @@ -4506,6 +4731,10 @@ void Tree::scroll_to_item(TreeItem *p_item, bool p_center_on_item) { } void Tree::set_h_scroll_enabled(bool p_enable) { + if (h_scroll_enabled == p_enable) { + return; + } + h_scroll_enabled = p_enable; update_minimum_size(); } @@ -4515,6 +4744,10 @@ bool Tree::is_h_scroll_enabled() const { } void Tree::set_v_scroll_enabled(bool p_enable) { + if (v_scroll_enabled == p_enable) { + return; + } + v_scroll_enabled = p_enable; update_minimum_size(); } @@ -4605,7 +4838,7 @@ TreeItem *Tree::_find_item_at_pos(TreeItem *p_item, const Point2 &p_pos, int &r_ Point2 pos = p_pos; if ((root != p_item || !hide_root) && p_item->is_visible()) { - h = compute_item_height(p_item) + cache.vseparation; + h = compute_item_height(p_item) + theme_cache.vseparation; if (pos.y < h) { if (drop_mode_flags == DROP_MODE_ON_ITEM) { section = 0; @@ -4662,7 +4895,7 @@ int Tree::get_column_at_position(const Point2 &p_pos) const { if (is_layout_rtl()) { pos.x = get_size().width - pos.x; } - pos -= cache.bg->get_offset(); + pos -= theme_cache.panel_style->get_offset(); pos.y -= _get_title_button_height(); if (pos.y < 0) { return -1; @@ -4692,7 +4925,7 @@ int Tree::get_drop_section_at_position(const Point2 &p_pos) const { if (is_layout_rtl()) { pos.x = get_size().width - pos.x; } - pos -= cache.bg->get_offset(); + pos -= theme_cache.panel_style->get_offset(); pos.y -= _get_title_button_height(); if (pos.y < 0) { return -100; @@ -4722,7 +4955,7 @@ TreeItem *Tree::get_item_at_position(const Point2 &p_pos) const { if (is_layout_rtl()) { pos.x = get_size().width - pos.x; } - pos -= cache.bg->get_offset(); + pos -= theme_cache.panel_style->get_offset(); pos.y -= _get_title_button_height(); if (pos.y < 0) { return nullptr; @@ -4749,7 +4982,7 @@ TreeItem *Tree::get_item_at_position(const Point2 &p_pos) const { int Tree::get_button_id_at_position(const Point2 &p_pos) const { if (root) { Point2 pos = p_pos; - pos -= cache.bg->get_offset(); + pos -= theme_cache.panel_style->get_offset(); pos.y -= _get_title_button_height(); if (pos.y < 0) { return -1; @@ -4775,7 +5008,7 @@ int Tree::get_button_id_at_position(const Point2 &p_pos) const { for (int j = c.buttons.size() - 1; j >= 0; j--) { Ref<Texture2D> b = c.buttons[j].texture; - Size2 size = b->get_size() + cache.button_pressed->get_minimum_size(); + Size2 size = b->get_size() + theme_cache.button_pressed->get_minimum_size(); if (pos.x > col_width - size.width) { return c.buttons[j].id; } @@ -4790,7 +5023,7 @@ int Tree::get_button_id_at_position(const Point2 &p_pos) const { String Tree::get_tooltip(const Point2 &p_pos) const { if (root) { Point2 pos = p_pos; - pos -= cache.bg->get_offset(); + pos -= theme_cache.panel_style->get_offset(); pos.y -= _get_title_button_height(); if (pos.y < 0) { return Control::get_tooltip(p_pos); @@ -4816,7 +5049,7 @@ String Tree::get_tooltip(const Point2 &p_pos) const { for (int j = c.buttons.size() - 1; j >= 0; j--) { Ref<Texture2D> b = c.buttons[j].texture; - Size2 size = b->get_size() + cache.button_pressed->get_minimum_size(); + Size2 size = b->get_size() + theme_cache.button_pressed->get_minimum_size(); if (pos.x > col_width - size.width) { String tooltip = c.buttons[j].tooltip; if (!tooltip.is_empty()) { @@ -4826,10 +5059,10 @@ String Tree::get_tooltip(const Point2 &p_pos) const { col_width -= size.width; } String ret; - if (it->get_tooltip(col) == "") { + if (it->get_tooltip_text(col) == "") { ret = it->get_text(col); } else { - ret = it->get_tooltip(col); + ret = it->get_tooltip_text(col); } return ret; } @@ -4843,14 +5076,26 @@ void Tree::set_cursor_can_exit_tree(bool p_enable) { } void Tree::set_hide_folding(bool p_hide) { + if (hide_folding == p_hide) { + return; + } + hide_folding = p_hide; - update(); + queue_redraw(); } bool Tree::is_folding_hidden() const { return hide_folding; } +void Tree::set_enable_recursive_folding(bool p_enable) { + enable_recursive_folding = p_enable; +} + +bool Tree::is_recursive_folding_enabled() const { + return enable_recursive_folding; +} + void Tree::set_drop_mode_flags(int p_flags) { if (drop_mode_flags == p_flags) { return; @@ -4860,7 +5105,7 @@ void Tree::set_drop_mode_flags(int p_flags) { drop_mode_over = nullptr; } - update(); + queue_redraw(); } int Tree::get_drop_mode_flags() const { @@ -4954,6 +5199,9 @@ void Tree::_bind_methods() { ClassDB::bind_method(D_METHOD("set_hide_folding", "hide"), &Tree::set_hide_folding); ClassDB::bind_method(D_METHOD("is_folding_hidden"), &Tree::is_folding_hidden); + ClassDB::bind_method(D_METHOD("set_enable_recursive_folding", "enable"), &Tree::set_enable_recursive_folding); + ClassDB::bind_method(D_METHOD("is_recursive_folding_enabled"), &Tree::is_recursive_folding_enabled); + ClassDB::bind_method(D_METHOD("set_drop_mode_flags", "flags"), &Tree::set_drop_mode_flags); ClassDB::bind_method(D_METHOD("get_drop_mode_flags"), &Tree::get_drop_mode_flags); @@ -4968,6 +5216,7 @@ void Tree::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_reselect"), "set_allow_reselect", "get_allow_reselect"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_rmb_select"), "set_allow_rmb_select", "get_allow_rmb_select"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_folding"), "set_hide_folding", "is_folding_hidden"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enable_recursive_folding"), "set_enable_recursive_folding", "is_recursive_folding_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_root"), "set_hide_root", "is_root_hidden"); ADD_PROPERTY(PropertyInfo(Variant::INT, "drop_mode_flags", PROPERTY_HINT_FLAGS, "On Item,In Between"), "set_drop_mode_flags", "get_drop_mode_flags"); ADD_PROPERTY(PropertyInfo(Variant::INT, "select_mode", PROPERTY_HINT_ENUM, "Single,Row,Multi"), "set_select_mode", "get_select_mode"); @@ -4988,7 +5237,7 @@ void Tree::_bind_methods() { ADD_SIGNAL(MethodInfo("button_clicked", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "TreeItem"), PropertyInfo(Variant::INT, "column"), PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::INT, "mouse_button_index"))); ADD_SIGNAL(MethodInfo("custom_popup_edited", PropertyInfo(Variant::BOOL, "arrow_clicked"))); ADD_SIGNAL(MethodInfo("item_activated")); - ADD_SIGNAL(MethodInfo("column_title_pressed", PropertyInfo(Variant::INT, "column"))); + ADD_SIGNAL(MethodInfo("column_title_clicked", PropertyInfo(Variant::INT, "column"), PropertyInfo(Variant::INT, "mouse_button_index"))); ADD_SIGNAL(MethodInfo("nothing_selected")); BIND_ENUM_CONSTANT(SELECT_SINGLE); @@ -5015,7 +5264,7 @@ Tree::Tree() { popup_editor_vb = memnew(VBoxContainer); popup_editor->add_child(popup_editor_vb); popup_editor_vb->add_theme_constant_override("separation", 0); - popup_editor_vb->set_anchors_and_offsets_preset(PRESET_WIDE); + popup_editor_vb->set_anchors_and_offsets_preset(PRESET_FULL_RECT); text_editor = memnew(LineEdit); popup_editor_vb->add_child(text_editor); text_editor->set_v_size_flags(SIZE_EXPAND_FILL); @@ -5048,8 +5297,6 @@ Tree::Tree() { set_mouse_filter(MOUSE_FILTER_STOP); set_clip_contents(true); - - update_cache(); } Tree::~Tree() { diff --git a/scene/gui/tree.h b/scene/gui/tree.h index 1690e7ac57..f994a5cec1 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -173,6 +173,8 @@ private: } } + bool _is_any_collapsed(bool p_only_visible); + protected: static void _bind_methods(); @@ -245,7 +247,7 @@ public: void add_button(int p_column, const Ref<Texture2D> &p_button, int p_id = -1, bool p_disabled = false, const String &p_tooltip = ""); int get_button_count(int p_column) const; - String get_button_tooltip(int p_column, int p_idx) const; + String get_button_tooltip_text(int p_column, int p_idx) const; Ref<Texture2D> get_button(int p_column, int p_idx) const; int get_button_id(int p_column, int p_idx) const; void erase_button(int p_column, int p_idx); @@ -272,6 +274,9 @@ public: void set_collapsed(bool p_collapsed); bool is_collapsed(); + void set_collapsed_recursive(bool p_collapsed); + bool is_any_collapsed(bool p_only_visible = false); + void set_visible(bool p_visible); bool is_visible(); @@ -308,8 +313,8 @@ public: void set_custom_as_button(int p_column, bool p_button); bool is_custom_set_as_button(int p_column) const; - void set_tooltip(int p_column, const String &p_tooltip); - String get_tooltip(int p_column) const; + void set_tooltip_text(int p_column, const String &p_tooltip); + String get_tooltip_text(int p_column) const; void set_text_alignment(int p_column, HorizontalAlignment p_alignment); HorizontalAlignment get_text_alignment(int p_column) const; @@ -339,9 +344,16 @@ public: TreeItem *get_child(int p_idx); int get_visible_child_count(); int get_child_count(); - Array get_children(); + TypedArray<TreeItem> get_children(); int get_index(); +#ifdef DEV_ENABLED + // This debugging code can be removed once the current refactoring of this class is complete. + void validate_cache() const; +#else + void validate_cache() const {} +#endif + void move_before(TreeItem *p_item); void move_after(TreeItem *p_item); @@ -475,12 +487,15 @@ private: void propagate_set_columns(TreeItem *p_item); - struct Cache { + struct ThemeCache { + Ref<StyleBox> panel_style; + Ref<StyleBox> focus_style; + Ref<Font> font; Ref<Font> tb_font; int font_size = 0; int tb_font_size = 0; - Ref<StyleBox> bg; + Ref<StyleBox> selected; Ref<StyleBox> selected_focus; Ref<StyleBox> cursor; @@ -498,8 +513,9 @@ private: Ref<Texture2D> checked; Ref<Texture2D> unchecked; Ref<Texture2D> indeterminate; - Ref<Texture2D> arrow_collapsed; Ref<Texture2D> arrow; + Ref<Texture2D> arrow_collapsed; + Ref<Texture2D> arrow_collapsed_mirrored; Ref<Texture2D> select_arrow; Ref<Texture2D> updown; @@ -529,7 +545,9 @@ private: int scroll_border = 0; int scroll_speed = 0; int font_outline_size = 0; + } theme_cache; + struct Cache { enum ClickType { CLICK_NONE, CLICK_TITLE, @@ -552,7 +570,6 @@ private: Point2i text_editor_position; bool rtl = false; - } cache; int _get_title_button_height() const; @@ -565,7 +582,6 @@ private: bool v_scroll_enabled = true; Size2 get_internal_min_size() const; - void update_cache(); void update_scrollbars(); Rect2 search_item_rect(TreeItem *p_from, TreeItem *p_item); @@ -602,6 +618,8 @@ private: bool hide_folding = false; + bool enable_recursive_folding = true; + int _count_selected_items(TreeItem *p_from) const; bool _is_branch_selected(TreeItem *p_from) const; bool _is_sibling_branch_selected(TreeItem *p_from) const; @@ -613,6 +631,8 @@ private: bool _scroll(bool p_horizontal, float p_pages); protected: + virtual void _update_theme_item_cache() override; + static void _bind_methods(); public: @@ -699,6 +719,9 @@ public: void set_hide_folding(bool p_hide); bool is_folding_hidden() const; + void set_enable_recursive_folding(bool p_enable); + bool is_recursive_folding_enabled() const; + void set_drop_mode_flags(int p_flags); int get_drop_mode_flags() const; @@ -719,4 +742,5 @@ public: VARIANT_ENUM_CAST(Tree::SelectMode); VARIANT_ENUM_CAST(Tree::DropModeFlags); -#endif + +#endif // TREE_H diff --git a/scene/gui/video_stream_player.cpp b/scene/gui/video_stream_player.cpp index 86334882fa..0ea49b1645 100644 --- a/scene/gui/video_stream_player.cpp +++ b/scene/gui/video_stream_player.cpp @@ -208,8 +208,12 @@ Size2 VideoStreamPlayer::get_minimum_size() const { } void VideoStreamPlayer::set_expand(bool p_expand) { + if (expand == p_expand) { + return; + } + expand = p_expand; - update(); + queue_redraw(); update_minimum_size(); } @@ -225,7 +229,7 @@ void VideoStreamPlayer::set_stream(const Ref<VideoStream> &p_stream) { stream = p_stream; if (stream.is_valid()) { stream->set_audio_track(audio_track); - playback = stream->instance_playback(); + playback = stream->instantiate_playback(); } else { playback = Ref<VideoStreamPlayback>(); } @@ -257,7 +261,7 @@ void VideoStreamPlayer::set_stream(const Ref<VideoStream> &p_stream) { AudioServer::get_singleton()->unlock(); } - update(); + queue_redraw(); if (!expand) { update_minimum_size(); @@ -306,6 +310,10 @@ bool VideoStreamPlayer::is_playing() const { } void VideoStreamPlayer::set_paused(bool p_paused) { + if (paused == p_paused) { + return; + } + paused = p_paused; if (!p_paused && !can_process()) { paused_from_tree = true; @@ -354,7 +362,7 @@ void VideoStreamPlayer::set_volume_db(float p_db) { if (p_db < -79) { set_volume(0); } else { - set_volume(Math::db2linear(p_db)); + set_volume(Math::db_to_linear(p_db)); } } @@ -362,7 +370,7 @@ float VideoStreamPlayer::get_volume_db() const { if (volume == 0) { return -80; } else { - return Math::linear2db(volume); + return Math::linear_to_db(volume); } } @@ -373,14 +381,14 @@ String VideoStreamPlayer::get_stream_name() const { return stream->get_name(); } -float VideoStreamPlayer::get_stream_position() const { +double VideoStreamPlayer::get_stream_position() const { if (playback.is_null()) { return 0; } return playback->get_playback_position(); } -void VideoStreamPlayer::set_stream_position(float p_position) { +void VideoStreamPlayer::set_stream_position(double p_position) { if (playback.is_valid()) { playback->seek(p_position); } diff --git a/scene/gui/video_stream_player.h b/scene/gui/video_stream_player.h index 913e7905b6..b1ba8a65d7 100644 --- a/scene/gui/video_stream_player.h +++ b/scene/gui/video_stream_player.h @@ -79,7 +79,7 @@ class VideoStreamPlayer : public Control { protected: static void _bind_methods(); void _notification(int p_notification); - void _validate_property(PropertyInfo &p_property) const override; + void _validate_property(PropertyInfo &p_property) const; public: Size2 get_minimum_size() const override; @@ -105,8 +105,8 @@ public: float get_volume_db() const; String get_stream_name() const; - float get_stream_position() const; - void set_stream_position(float p_position); + double get_stream_position() const; + void set_stream_position(double p_position); void set_autoplay(bool p_enable); bool has_autoplay() const; diff --git a/scene/gui/view_panner.cpp b/scene/gui/view_panner.cpp index 892d0aba29..3b7f499a07 100644 --- a/scene/gui/view_panner.cpp +++ b/scene/gui/view_panner.cpp @@ -135,7 +135,7 @@ void ViewPanner::callback_helper(Callable p_callback, Vector<Variant> p_args) { Variant result; Callable::CallError ce; - p_callback.call(argptr, p_args.size(), result, ce); + p_callback.callp(argptr, p_args.size(), result, ce); } void ViewPanner::set_callbacks(Callable p_scroll_callback, Callable p_pan_callback, Callable p_zoom_callback) { diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp index 5e90615ac1..05d86f77f2 100644 --- a/scene/main/canvas_item.cpp +++ b/scene/main/canvas_item.cpp @@ -64,9 +64,6 @@ void CanvasItem::_propagate_visibility_changed(bool p_parent_visible_in_tree) { if (!visible) { return; } - if (p_parent_visible_in_tree && first_draw) { // Avoid propagating it twice. - first_draw = false; - } _handle_visibility_change(p_parent_visible_in_tree); } @@ -91,7 +88,7 @@ void CanvasItem::_handle_visibility_change(bool p_visible) { notification(NOTIFICATION_VISIBILITY_CHANGED); if (p_visible) { - update(); + queue_redraw(); } else { emit_signal(SceneStringNames::get_singleton()->hidden); } @@ -124,7 +121,7 @@ CanvasItem *CanvasItem::get_current_item_drawn() { return current_item_drawn; } -void CanvasItem::_update_callback() { +void CanvasItem::_redraw_callback() { if (!is_inside_tree()) { pending_update = false; return; @@ -133,10 +130,6 @@ void CanvasItem::_update_callback() { RenderingServer::get_singleton()->canvas_item_clear(get_canvas_item()); //todo updating = true - only allow drawing here if (is_visible_in_tree()) { - if (first_draw) { - notification(NOTIFICATION_VISIBILITY_CHANGED); - first_draw = false; - } drawing = true; current_item_drawn = this; notification(NOTIFICATION_DRAW); @@ -230,16 +223,16 @@ void CanvasItem::_enter_canvas() { RenderingServer::get_singleton()->canvas_item_set_parent(canvas_item, canvas); - group = "root_canvas" + itos(canvas.get_id()); + canvas_group = "root_canvas" + itos(canvas.get_id()); - add_to_group(group); + add_to_group(canvas_group); if (canvas_layer) { canvas_layer->reset_sort_index(); } else { get_viewport()->gui_reset_canvas_sort_index(); } - get_tree()->call_group_flags(SceneTree::GROUP_CALL_UNIQUE | SceneTree::GROUP_CALL_DEFERRED, group, SNAME("_top_level_raise_self")); + get_tree()->call_group_flags(SceneTree::GROUP_CALL_UNIQUE | SceneTree::GROUP_CALL_DEFERRED, canvas_group, SNAME("_top_level_raise_self")); } else { CanvasItem *parent = get_parent_item(); @@ -249,7 +242,7 @@ void CanvasItem::_enter_canvas() { } pending_update = false; - update(); + queue_redraw(); notification(NOTIFICATION_ENTER_CANVAS); } @@ -258,14 +251,16 @@ void CanvasItem::_exit_canvas() { notification(NOTIFICATION_EXIT_CANVAS, true); //reverse the notification RenderingServer::get_singleton()->canvas_item_set_parent(canvas_item, RID()); canvas_layer = nullptr; - group = StringName(); + if (canvas_group != StringName()) { + remove_from_group(canvas_group); + canvas_group = StringName(); + } } void CanvasItem::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { ERR_FAIL_COND(!is_inside_tree()); - first_draw = true; Node *parent = get_parent(); if (parent) { @@ -304,6 +299,10 @@ void CanvasItem::_notification(int p_what) { } } + RenderingServer::get_singleton()->canvas_item_set_visible(canvas_item, is_visible_in_tree()); // The visibility of the parent may change. + if (is_visible_in_tree()) { + notification(NOTIFICATION_VISIBILITY_CHANGED); // Considered invisible until entered. + } _enter_canvas(); _update_texture_filter_changed(false); @@ -319,8 +318,8 @@ void CanvasItem::_notification(int p_what) { break; } - if (group != StringName()) { - get_tree()->call_group_flags(SceneTree::GROUP_CALL_UNIQUE | SceneTree::GROUP_CALL_DEFERRED, group, "_top_level_raise_self"); + if (canvas_group != StringName()) { + get_tree()->call_group_flags(SceneTree::GROUP_CALL_UNIQUE | SceneTree::GROUP_CALL_DEFERRED, canvas_group, "_top_level_raise_self"); } else { CanvasItem *p = get_parent_item(); ERR_FAIL_COND(!p); @@ -339,6 +338,7 @@ void CanvasItem::_notification(int p_what) { } if (window) { window->disconnect(SceneStringNames::get_singleton()->visibility_changed, callable_mp(this, &CanvasItem::_window_visibility_changed)); + window = nullptr; } global_invalid = true; parent_visible_in_tree = false; @@ -356,7 +356,7 @@ void CanvasItem::_window_visibility_changed() { } } -void CanvasItem::update() { +void CanvasItem::queue_redraw() { if (!is_inside_tree()) { return; } @@ -366,7 +366,14 @@ void CanvasItem::update() { pending_update = true; - MessageQueue::get_singleton()->push_call(this, SNAME("_update_callback")); + MessageQueue::get_singleton()->push_callable(callable_mp(this, &CanvasItem::_redraw_callback)); +} + +void CanvasItem::move_to_front() { + if (!get_parent()) { + return; + } + get_parent()->move_child(this, -1); } void CanvasItem::set_modulate(const Color &p_modulate) { @@ -439,7 +446,7 @@ int CanvasItem::get_light_mask() const { void CanvasItem::item_rect_changed(bool p_size_changed) { if (p_size_changed) { - update(); + queue_redraw(); } emit_signal(SceneStringNames::get_singleton()->item_rect_changed); } @@ -588,6 +595,12 @@ void CanvasItem::draw_msdf_texture_rect_region(const Ref<Texture2D> &p_texture, RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(canvas_item, p_rect, p_texture->get_rid(), p_src_rect, p_modulate, p_outline, p_pixel_range); } +void CanvasItem::draw_lcd_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate) { + ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + ERR_FAIL_COND(p_texture.is_null()); + RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(canvas_item, p_rect, p_texture->get_rid(), p_src_rect, p_modulate); +} + void CanvasItem::draw_style_box(const Ref<StyleBox> &p_style_box, const Rect2 &p_rect) { ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); @@ -658,32 +671,32 @@ void CanvasItem::draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Tex RenderingServer::get_singleton()->canvas_item_add_multimesh(canvas_item, p_multimesh->get_rid(), texture_rid); } -void CanvasItem::draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, const Color &p_modulate, uint16_t p_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { +void CanvasItem::draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, const Color &p_modulate, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); ERR_FAIL_COND(p_font.is_null()); - p_font->draw_string(canvas_item, p_pos, p_text, p_alignment, p_width, p_font_size, p_modulate, p_flags, p_direction, p_orientation); + p_font->draw_string(canvas_item, p_pos, p_text, p_alignment, p_width, p_font_size, p_modulate, p_jst_flags, p_direction, p_orientation); } -void CanvasItem::draw_multiline_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_max_lines, const Color &p_modulate, uint16_t p_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { +void CanvasItem::draw_multiline_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_max_lines, const Color &p_modulate, BitField<TextServer::LineBreakFlag> p_brk_flags, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); ERR_FAIL_COND(p_font.is_null()); - p_font->draw_multiline_string(canvas_item, p_pos, p_text, p_alignment, p_width, p_font_size, p_max_lines, p_modulate, p_flags, p_direction, p_orientation); + p_font->draw_multiline_string(canvas_item, p_pos, p_text, p_alignment, p_width, p_font_size, p_max_lines, p_modulate, p_brk_flags, p_jst_flags, p_direction, p_orientation); } -void CanvasItem::draw_string_outline(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_size, const Color &p_modulate, uint16_t p_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { +void CanvasItem::draw_string_outline(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_size, const Color &p_modulate, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); ERR_FAIL_COND(p_font.is_null()); - p_font->draw_string_outline(canvas_item, p_pos, p_text, p_alignment, p_width, p_font_size, p_size, p_modulate, p_flags, p_direction, p_orientation); + p_font->draw_string_outline(canvas_item, p_pos, p_text, p_alignment, p_width, p_font_size, p_size, p_modulate, p_jst_flags, p_direction, p_orientation); } -void CanvasItem::draw_multiline_string_outline(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_max_lines, int p_size, const Color &p_modulate, uint16_t p_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { +void CanvasItem::draw_multiline_string_outline(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_max_lines, int p_size, const Color &p_modulate, BitField<TextServer::LineBreakFlag> p_brk_flags, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); ERR_FAIL_COND(p_font.is_null()); - p_font->draw_multiline_string_outline(canvas_item, p_pos, p_text, p_alignment, p_width, p_font_size, p_max_lines, p_size, p_modulate, p_flags, p_direction, p_orientation); + p_font->draw_multiline_string_outline(canvas_item, p_pos, p_text, p_alignment, p_width, p_font_size, p_max_lines, p_size, p_modulate, p_brk_flags, p_jst_flags, p_direction, p_orientation); } void CanvasItem::draw_char(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, int p_font_size, const Color &p_modulate) const { @@ -862,7 +875,6 @@ void CanvasItem::force_update_transform() { void CanvasItem::_bind_methods() { ClassDB::bind_method(D_METHOD("_top_level_raise_self"), &CanvasItem::_top_level_raise_self); - ClassDB::bind_method(D_METHOD("_update_callback"), &CanvasItem::_update_callback); #ifdef TOOLS_ENABLED ClassDB::bind_method(D_METHOD("_edit_set_state", "state"), &CanvasItem::_edit_set_state); @@ -891,7 +903,8 @@ void CanvasItem::_bind_methods() { ClassDB::bind_method(D_METHOD("show"), &CanvasItem::show); ClassDB::bind_method(D_METHOD("hide"), &CanvasItem::hide); - ClassDB::bind_method(D_METHOD("update"), &CanvasItem::update); + ClassDB::bind_method(D_METHOD("queue_redraw"), &CanvasItem::queue_redraw); + ClassDB::bind_method(D_METHOD("move_to_front"), &CanvasItem::move_to_front); ClassDB::bind_method(D_METHOD("set_as_top_level", "enable"), &CanvasItem::set_as_top_level); ClassDB::bind_method(D_METHOD("is_set_as_top_level"), &CanvasItem::is_set_as_top_level); @@ -920,14 +933,15 @@ void CanvasItem::_bind_methods() { ClassDB::bind_method(D_METHOD("draw_texture_rect", "texture", "rect", "tile", "modulate", "transpose"), &CanvasItem::draw_texture_rect, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(false)); ClassDB::bind_method(D_METHOD("draw_texture_rect_region", "texture", "rect", "src_rect", "modulate", "transpose", "clip_uv"), &CanvasItem::draw_texture_rect_region, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(false), DEFVAL(true)); ClassDB::bind_method(D_METHOD("draw_msdf_texture_rect_region", "texture", "rect", "src_rect", "modulate", "outline", "pixel_range"), &CanvasItem::draw_msdf_texture_rect_region, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(0.0), DEFVAL(4.0)); + ClassDB::bind_method(D_METHOD("draw_lcd_texture_rect_region", "texture", "rect", "src_rect", "modulate"), &CanvasItem::draw_lcd_texture_rect_region, DEFVAL(Color(1, 1, 1, 1))); ClassDB::bind_method(D_METHOD("draw_style_box", "style_box", "rect"), &CanvasItem::draw_style_box); ClassDB::bind_method(D_METHOD("draw_primitive", "points", "colors", "uvs", "texture", "width"), &CanvasItem::draw_primitive, DEFVAL(Ref<Texture2D>()), DEFVAL(1.0)); ClassDB::bind_method(D_METHOD("draw_polygon", "points", "colors", "uvs", "texture"), &CanvasItem::draw_polygon, DEFVAL(PackedVector2Array()), DEFVAL(Ref<Texture2D>())); ClassDB::bind_method(D_METHOD("draw_colored_polygon", "points", "color", "uvs", "texture"), &CanvasItem::draw_colored_polygon, DEFVAL(PackedVector2Array()), DEFVAL(Ref<Texture2D>())); - ClassDB::bind_method(D_METHOD("draw_string", "font", "pos", "text", "alignment", "width", "font_size", "modulate", "flags", "direction", "orientation"), &CanvasItem::draw_string, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); - ClassDB::bind_method(D_METHOD("draw_multiline_string", "font", "pos", "text", "alignment", "width", "font_size", "max_lines", "modulate", "flags", "direction", "orientation"), &CanvasItem::draw_multiline_string, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); - ClassDB::bind_method(D_METHOD("draw_string_outline", "font", "pos", "text", "alignment", "width", "font_size", "size", "modulate", "flags", "direction", "orientation"), &CanvasItem::draw_string_outline, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); - ClassDB::bind_method(D_METHOD("draw_multiline_string_outline", "font", "pos", "text", "alignment", "width", "font_size", "max_lines", "size", "modulate", "flags", "direction", "orientation"), &CanvasItem::draw_multiline_string_outline, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + ClassDB::bind_method(D_METHOD("draw_string", "font", "pos", "text", "alignment", "width", "font_size", "modulate", "jst_flags", "direction", "orientation"), &CanvasItem::draw_string, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + ClassDB::bind_method(D_METHOD("draw_multiline_string", "font", "pos", "text", "alignment", "width", "font_size", "max_lines", "modulate", "brk_flags", "jst_flags", "direction", "orientation"), &CanvasItem::draw_multiline_string, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + ClassDB::bind_method(D_METHOD("draw_string_outline", "font", "pos", "text", "alignment", "width", "font_size", "size", "modulate", "jst_flags", "direction", "orientation"), &CanvasItem::draw_string_outline, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + ClassDB::bind_method(D_METHOD("draw_multiline_string_outline", "font", "pos", "text", "alignment", "width", "font_size", "max_lines", "size", "modulate", "brk_flags", "jst_flags", "direction", "orientation"), &CanvasItem::draw_multiline_string_outline, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); ClassDB::bind_method(D_METHOD("draw_char", "font", "pos", "char", "font_size", "modulate"), &CanvasItem::draw_char, DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(Color(1.0, 1.0, 1.0))); ClassDB::bind_method(D_METHOD("draw_char_outline", "font", "pos", "char", "font_size", "size", "modulate"), &CanvasItem::draw_char_outline, DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(Color(1.0, 1.0, 1.0))); ClassDB::bind_method(D_METHOD("draw_mesh", "mesh", "texture", "transform", "modulate"), &CanvasItem::draw_mesh, DEFVAL(Transform2D()), DEFVAL(Color(1, 1, 1, 1))); @@ -987,7 +1001,7 @@ void CanvasItem::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "light_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_light_mask", "get_light_mask"); ADD_GROUP("Texture", "texture_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Inherit,Nearest,Linear,Nearest Mipmap,Linear Mipmap,Nearest Mipmap Aniso.,Linear Mipmap Aniso."), "set_texture_filter", "get_texture_filter"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Inherit,Nearest,Linear,Nearest Mipmap,Linear Mipmap,Nearest Mipmap Anisotropic,Linear Mipmap Anisotropic"), "set_texture_filter", "get_texture_filter"); ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_repeat", PROPERTY_HINT_ENUM, "Inherit,Disabled,Enabled,Mirror"), "set_texture_repeat", "get_texture_repeat"); ADD_GROUP("Material", ""); @@ -1094,7 +1108,7 @@ void CanvasItem::_update_texture_filter_changed(bool p_propagate) { texture_filter_cache = RS::CanvasItemTextureFilter(texture_filter); } RS::get_singleton()->canvas_item_set_default_texture_filter(get_canvas_item(), texture_filter_cache); - update(); + queue_redraw(); if (p_propagate) { for (CanvasItem *E : children_items) { @@ -1135,7 +1149,7 @@ void CanvasItem::_update_texture_repeat_changed(bool p_propagate) { texture_repeat_cache = RS::CanvasItemTextureRepeat(texture_repeat); } RS::get_singleton()->canvas_item_set_default_texture_repeat(get_canvas_item(), texture_repeat_cache); - update(); + queue_redraw(); if (p_propagate) { for (CanvasItem *E : children_items) { if (!E->top_level && E->texture_repeat == TEXTURE_REPEAT_PARENT_NODE) { @@ -1327,7 +1341,7 @@ void CanvasTexture::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::COLOR, "specular_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_specular_color", "get_specular_color"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "specular_shininess", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_specular_shininess", "get_specular_shininess"); ADD_GROUP("Texture", "texture_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Inherit,Nearest,Linear,Nearest Mipmap,Linear Mipmap,Nearest Mipmap Aniso.,Linear Mipmap Aniso."), "set_texture_filter", "get_texture_filter"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Inherit,Nearest,Linear,Nearest Mipmap,Linear Mipmap,Nearest Mipmap Anisotropic,Linear Mipmap Anisotropic"), "set_texture_filter", "get_texture_filter"); ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_repeat", PROPERTY_HINT_ENUM, "Inherit,Disabled,Enabled,Mirror"), "set_texture_repeat", "get_texture_repeat"); } diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h index c88878725f..b80289fdb4 100644 --- a/scene/main/canvas_item.h +++ b/scene/main/canvas_item.h @@ -70,7 +70,7 @@ private: mutable SelfList<Node> xform_change; RID canvas_item; - StringName group; + StringName canvas_group; CanvasLayer *canvas_layer = nullptr; @@ -83,7 +83,6 @@ private: int light_mask = 1; Window *window = nullptr; - bool first_draw = false; bool visible = true; bool parent_visible_in_tree = false; bool clip_children = false; @@ -111,7 +110,7 @@ private: void _propagate_visibility_changed(bool p_parent_visible_in_tree); void _handle_visibility_change(bool p_visible); - void _update_callback(); + void _redraw_callback(); void _enter_canvas(); void _exit_canvas(); @@ -198,7 +197,8 @@ public: void show(); void hide(); - void update(); + void queue_redraw(); + void move_to_front(); void set_clip_children(bool p_enabled); bool is_clipping_children() const; @@ -227,6 +227,7 @@ public: void draw_texture_rect(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false); void draw_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = false); void draw_msdf_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), double p_outline = 0.0, double p_pixel_range = 4.0); + void draw_lcd_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1)); void draw_style_box(const Ref<StyleBox> &p_style_box, const Rect2 &p_rect); void draw_primitive(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, Ref<Texture2D> p_texture = Ref<Texture2D>(), real_t p_width = 1); void draw_polygon(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), Ref<Texture2D> p_texture = Ref<Texture2D>()); @@ -235,11 +236,11 @@ public: void draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture2D> &p_texture, const Transform2D &p_transform = Transform2D(), const Color &p_modulate = Color(1, 1, 1)); void draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Texture2D> &p_texture); - void draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = Font::DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1.0, 1.0, 1.0), uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const; - void draw_multiline_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = Font::DEFAULT_FONT_SIZE, int p_max_lines = -1, const Color &p_modulate = Color(1.0, 1.0, 1.0), uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const; + void draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = Font::DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1.0, 1.0, 1.0), BitField<TextServer::JustificationFlag> p_jst_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const; + void draw_multiline_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = Font::DEFAULT_FONT_SIZE, int p_max_lines = -1, const Color &p_modulate = Color(1.0, 1.0, 1.0), BitField<TextServer::LineBreakFlag> p_brk_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND, BitField<TextServer::JustificationFlag> p_jst_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const; - void draw_string_outline(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_size = 1, int p_font_size = Font::DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1.0, 1.0, 1.0), uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const; - void draw_multiline_string_outline(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = Font::DEFAULT_FONT_SIZE, int p_max_lines = -1, int p_size = 1, const Color &p_modulate = Color(1.0, 1.0, 1.0), uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const; + void draw_string_outline(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_size = 1, int p_font_size = Font::DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1.0, 1.0, 1.0), BitField<TextServer::JustificationFlag> p_jst_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const; + void draw_multiline_string_outline(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = Font::DEFAULT_FONT_SIZE, int p_max_lines = -1, int p_size = 1, const Color &p_modulate = Color(1.0, 1.0, 1.0), BitField<TextServer::LineBreakFlag> p_brk_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND, BitField<TextServer::JustificationFlag> p_jst_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const; void draw_char(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, int p_font_size = Font::DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1.0, 1.0, 1.0)) const; void draw_char_outline(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, int p_font_size = Font::DEFAULT_FONT_SIZE, int p_size = 1, const Color &p_modulate = Color(1.0, 1.0, 1.0)) const; diff --git a/scene/main/canvas_layer.cpp b/scene/main/canvas_layer.cpp index 3a071ef542..214efe432b 100644 --- a/scene/main/canvas_layer.cpp +++ b/scene/main/canvas_layer.cpp @@ -286,9 +286,9 @@ void CanvasLayer::_update_follow_viewport(bool p_force_exit) { } } -void CanvasLayer::_validate_property(PropertyInfo &property) const { - if (!follow_viewport && property.name == "follow_viewport_scale") { - property.usage = PROPERTY_USAGE_NO_EDITOR; +void CanvasLayer::_validate_property(PropertyInfo &p_property) const { + if (!follow_viewport && p_property.name == "follow_viewport_scale") { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } } @@ -329,14 +329,14 @@ void CanvasLayer::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible"); ADD_GROUP("Transform", ""); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset", PROPERTY_HINT_NONE, "suffix:px"), "set_offset", "get_offset"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation", PROPERTY_HINT_RANGE, "-1080,1080,0.1,or_lesser,or_greater,radians"), "set_rotation", "get_rotation"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation", PROPERTY_HINT_RANGE, "-1080,1080,0.1,or_less,or_greater,radians"), "set_rotation", "get_rotation"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scale", PROPERTY_HINT_LINK), "set_scale", "get_scale"); ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "transform", PROPERTY_HINT_NONE, "suffix:px"), "set_transform", "get_transform"); ADD_GROUP("", ""); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "custom_viewport", PROPERTY_HINT_RESOURCE_TYPE, "Viewport", PROPERTY_USAGE_NONE), "set_custom_viewport", "get_custom_viewport"); ADD_GROUP("Follow Viewport", "follow_viewport"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "follow_viewport_enable"), "set_follow_viewport", "is_following_viewport"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "follow_viewport_scale", PROPERTY_HINT_RANGE, "0.001,1000,0.001,or_greater,or_lesser"), "set_follow_viewport_scale", "get_follow_viewport_scale"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "follow_viewport_enabled"), "set_follow_viewport", "is_following_viewport"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "follow_viewport_scale", PROPERTY_HINT_RANGE, "0.001,1000,0.001,or_greater,or_less"), "set_follow_viewport_scale", "get_follow_viewport_scale"); ADD_SIGNAL(MethodInfo("visibility_changed")); } diff --git a/scene/main/canvas_layer.h b/scene/main/canvas_layer.h index 2493675b31..74b5ebd453 100644 --- a/scene/main/canvas_layer.h +++ b/scene/main/canvas_layer.h @@ -64,7 +64,7 @@ class CanvasLayer : public Node { protected: void _notification(int p_what); static void _bind_methods(); - void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; public: void set_layer(int p_xform); diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp index 9a23bc65bf..2c395ec07d 100644 --- a/scene/main/http_request.cpp +++ b/scene/main/http_request.cpp @@ -32,15 +32,12 @@ #include "core/io/compression.h" #include "scene/main/timer.h" -void HTTPRequest::_redirect_request(const String &p_new_url) { -} - Error HTTPRequest::_request() { - return client->connect_to_host(url, port, use_ssl, validate_ssl); + return client->connect_to_host(url, port, use_tls, validate_tls); } Error HTTPRequest::_parse_url(const String &p_url) { - use_ssl = false; + use_tls = false; request_string = ""; port = 80; request_sent = false; @@ -48,18 +45,19 @@ Error HTTPRequest::_parse_url(const String &p_url) { body_len = -1; body.clear(); downloaded.set(0); + final_body_size.set(0); redirections = 0; String scheme; Error err = p_url.parse_url(scheme, url, port, request_string); ERR_FAIL_COND_V_MSG(err != OK, err, "Error parsing URL: " + p_url + "."); if (scheme == "https://") { - use_ssl = true; + use_tls = true; } else if (scheme != "http://") { ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Invalid URL scheme: " + scheme + "."); } if (port == 0) { - port = use_ssl ? 443 : 80; + port = use_tls ? 443 : 80; } if (request_string.is_empty()) { request_string = "/"; @@ -98,7 +96,7 @@ String HTTPRequest::get_header_value(const PackedStringArray &p_headers, const S return value; } -Error HTTPRequest::request(const String &p_url, const Vector<String> &p_custom_headers, bool p_ssl_validate_domain, HTTPClient::Method p_method, const String &p_request_data) { +Error HTTPRequest::request(const String &p_url, const Vector<String> &p_custom_headers, bool p_tls_validate_domain, HTTPClient::Method p_method, const String &p_request_data) { // Copy the string into a raw buffer. Vector<uint8_t> raw_data; @@ -110,10 +108,10 @@ Error HTTPRequest::request(const String &p_url, const Vector<String> &p_custom_h memcpy(w, charstr.ptr(), len); } - return request_raw(p_url, p_custom_headers, p_ssl_validate_domain, p_method, raw_data); + return request_raw(p_url, p_custom_headers, p_tls_validate_domain, p_method, raw_data); } -Error HTTPRequest::request_raw(const String &p_url, const Vector<String> &p_custom_headers, bool p_ssl_validate_domain, HTTPClient::Method p_method, const Vector<uint8_t> &p_request_data_raw) { +Error HTTPRequest::request_raw(const String &p_url, const Vector<String> &p_custom_headers, bool p_tls_validate_domain, HTTPClient::Method p_method, const Vector<uint8_t> &p_request_data_raw) { ERR_FAIL_COND_V(!is_inside_tree(), ERR_UNCONFIGURED); ERR_FAIL_COND_V_MSG(requesting, ERR_BUSY, "HTTPRequest is processing a request. Wait for completion or cancel it before attempting a new one."); @@ -129,7 +127,7 @@ Error HTTPRequest::request_raw(const String &p_url, const Vector<String> &p_cust return err; } - validate_ssl = p_ssl_validate_domain; + validate_tls = p_tls_validate_domain; headers = p_custom_headers; @@ -153,7 +151,7 @@ Error HTTPRequest::request_raw(const String &p_url, const Vector<String> &p_cust client->set_blocking_mode(false); err = _request(); if (err != OK) { - call_deferred(SNAME("_request_done"), RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray()); + _defer_done(RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray()); return ERR_CANT_CONNECT; } @@ -169,7 +167,7 @@ void HTTPRequest::_thread_func(void *p_userdata) { Error err = hr->_request(); if (err != OK) { - hr->call_deferred(SNAME("_request_done"), RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray()); + hr->_defer_done(RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray()); } else { while (!hr->thread_request_quit.is_set()) { bool exit = hr->_update_connection(); @@ -198,6 +196,7 @@ void HTTPRequest::cancel_request() { } file.unref(); + decompressor.unref(); client->close(); body.clear(); got_response = false; @@ -208,7 +207,7 @@ void HTTPRequest::cancel_request() { bool HTTPRequest::_handle_response(bool *ret_value) { if (!client->has_response()) { - call_deferred(SNAME("_request_done"), RESULT_NO_RESPONSE, 0, PackedStringArray(), PackedByteArray()); + _defer_done(RESULT_NO_RESPONSE, 0, PackedStringArray(), PackedByteArray()); *ret_value = true; return true; } @@ -219,6 +218,9 @@ bool HTTPRequest::_handle_response(bool *ret_value) { client->get_response_headers(&rheaders); response_headers.clear(); downloaded.set(0); + final_body_size.set(0); + decompressor.unref(); + for (const String &E : rheaders) { response_headers.push_back(E); } @@ -227,7 +229,7 @@ bool HTTPRequest::_handle_response(bool *ret_value) { // Handle redirect. if (max_redirects >= 0 && redirections >= max_redirects) { - call_deferred(SNAME("_request_done"), RESULT_REDIRECT_LIMIT_REACHED, response_code, response_headers, PackedByteArray()); + _defer_done(RESULT_REDIRECT_LIMIT_REACHED, response_code, response_headers, PackedByteArray()); *ret_value = true; return true; } @@ -259,6 +261,7 @@ bool HTTPRequest::_handle_response(bool *ret_value) { body_len = -1; body.clear(); downloaded.set(0); + final_body_size.set(0); redirections = new_redirs; *ret_value = false; return true; @@ -266,13 +269,26 @@ bool HTTPRequest::_handle_response(bool *ret_value) { } } + // Check if we need to start streaming decompression. + String content_encoding; + if (accept_gzip) { + content_encoding = get_header_value(response_headers, "Content-Encoding").to_lower(); + } + if (content_encoding == "gzip") { + decompressor.instantiate(); + decompressor->start_decompression(false, get_download_chunk_size() * 2); + } else if (content_encoding == "deflate") { + decompressor.instantiate(); + decompressor->start_decompression(true, get_download_chunk_size() * 2); + } + return false; } bool HTTPRequest::_update_connection() { switch (client->get_status()) { case HTTPClient::STATUS_DISCONNECTED: { - call_deferred(SNAME("_request_done"), RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray()); + _defer_done(RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray()); return true; // End it, since it's disconnected. } break; case HTTPClient::STATUS_RESOLVING: { @@ -281,7 +297,7 @@ bool HTTPRequest::_update_connection() { return false; } break; case HTTPClient::STATUS_CANT_RESOLVE: { - call_deferred(SNAME("_request_done"), RESULT_CANT_RESOLVE, 0, PackedStringArray(), PackedByteArray()); + _defer_done(RESULT_CANT_RESOLVE, 0, PackedStringArray(), PackedByteArray()); return true; } break; @@ -291,7 +307,7 @@ bool HTTPRequest::_update_connection() { return false; } break; // Connecting to IP. case HTTPClient::STATUS_CANT_CONNECT: { - call_deferred(SNAME("_request_done"), RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray()); + _defer_done(RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray()); return true; } break; @@ -306,16 +322,16 @@ bool HTTPRequest::_update_connection() { return ret_value; } - call_deferred(SNAME("_request_done"), RESULT_SUCCESS, response_code, response_headers, PackedByteArray()); + _defer_done(RESULT_SUCCESS, response_code, response_headers, PackedByteArray()); return true; } if (body_len < 0) { // Chunked transfer is done. - call_deferred(SNAME("_request_done"), RESULT_SUCCESS, response_code, response_headers, body); + _defer_done(RESULT_SUCCESS, response_code, response_headers, body); return true; } - call_deferred(SNAME("_request_done"), RESULT_CHUNKED_BODY_SIZE_MISMATCH, response_code, response_headers, PackedByteArray()); + _defer_done(RESULT_CHUNKED_BODY_SIZE_MISMATCH, response_code, response_headers, PackedByteArray()); return true; // Request might have been done. } else { @@ -324,7 +340,7 @@ bool HTTPRequest::_update_connection() { int size = request_data.size(); Error err = client->request(method, request_string, headers, size > 0 ? request_data.ptr() : nullptr, size); if (err != OK) { - call_deferred(SNAME("_request_done"), RESULT_CONNECTION_ERROR, 0, PackedStringArray(), PackedByteArray()); + _defer_done(RESULT_CONNECTION_ERROR, 0, PackedStringArray(), PackedByteArray()); return true; } @@ -347,7 +363,7 @@ bool HTTPRequest::_update_connection() { } if (!client->is_response_chunked() && client->get_response_body_length() == 0) { - call_deferred(SNAME("_request_done"), RESULT_SUCCESS, response_code, response_headers, PackedByteArray()); + _defer_done(RESULT_SUCCESS, response_code, response_headers, PackedByteArray()); return true; } @@ -356,14 +372,14 @@ bool HTTPRequest::_update_connection() { body_len = client->get_response_body_length(); if (body_size_limit >= 0 && body_len > body_size_limit) { - call_deferred(SNAME("_request_done"), RESULT_BODY_SIZE_LIMIT_EXCEEDED, response_code, response_headers, PackedByteArray()); + _defer_done(RESULT_BODY_SIZE_LIMIT_EXCEEDED, response_code, response_headers, PackedByteArray()); return true; } if (!download_to_file.is_empty()) { file = FileAccess::open(download_to_file, FileAccess::WRITE); if (file.is_null()) { - call_deferred(SNAME("_request_done"), RESULT_DOWNLOAD_FILE_CANT_OPEN, response_code, response_headers, PackedByteArray()); + _defer_done(RESULT_DOWNLOAD_FILE_CANT_OPEN, response_code, response_headers, PackedByteArray()); return true; } } @@ -375,14 +391,33 @@ bool HTTPRequest::_update_connection() { } PackedByteArray chunk = client->read_response_body_chunk(); + downloaded.add(chunk.size()); + + // Decompress chunk if needed. + if (decompressor.is_valid()) { + Error err = decompressor->put_data(chunk.ptr(), chunk.size()); + if (err == OK) { + chunk.resize(decompressor->get_available_bytes()); + err = decompressor->get_data(chunk.ptrw(), chunk.size()); + } + if (err != OK) { + _defer_done(RESULT_BODY_DECOMPRESS_FAILED, response_code, response_headers, PackedByteArray()); + return true; + } + } + final_body_size.add(chunk.size()); + + if (body_size_limit >= 0 && final_body_size.get() > body_size_limit) { + _defer_done(RESULT_BODY_SIZE_LIMIT_EXCEEDED, response_code, response_headers, PackedByteArray()); + return true; + } if (chunk.size()) { - downloaded.add(chunk.size()); if (file.is_valid()) { const uint8_t *r = chunk.ptr(); file->store_buffer(r, chunk.size()); if (file->get_error() != OK) { - call_deferred(SNAME("_request_done"), RESULT_DOWNLOAD_FILE_WRITE_ERROR, response_code, response_headers, PackedByteArray()); + _defer_done(RESULT_DOWNLOAD_FILE_WRITE_ERROR, response_code, response_headers, PackedByteArray()); return true; } } else { @@ -390,19 +425,14 @@ bool HTTPRequest::_update_connection() { } } - if (body_size_limit >= 0 && downloaded.get() > body_size_limit) { - call_deferred(SNAME("_request_done"), RESULT_BODY_SIZE_LIMIT_EXCEEDED, response_code, response_headers, PackedByteArray()); - return true; - } - if (body_len >= 0) { if (downloaded.get() == body_len) { - call_deferred(SNAME("_request_done"), RESULT_SUCCESS, response_code, response_headers, body); + _defer_done(RESULT_SUCCESS, response_code, response_headers, body); return true; } } else if (client->get_status() == HTTPClient::STATUS_DISCONNECTED) { // We read till EOF, with no errors. Request is done. - call_deferred(SNAME("_request_done"), RESULT_SUCCESS, response_code, response_headers, body); + _defer_done(RESULT_SUCCESS, response_code, response_headers, body); return true; } @@ -410,11 +440,11 @@ bool HTTPRequest::_update_connection() { } break; // Request resulted in body: break which must be read. case HTTPClient::STATUS_CONNECTION_ERROR: { - call_deferred(SNAME("_request_done"), RESULT_CONNECTION_ERROR, 0, PackedStringArray(), PackedByteArray()); + _defer_done(RESULT_CONNECTION_ERROR, 0, PackedStringArray(), PackedByteArray()); return true; } break; - case HTTPClient::STATUS_SSL_HANDSHAKE_ERROR: { - call_deferred(SNAME("_request_done"), RESULT_SSL_HANDSHAKE_ERROR, 0, PackedStringArray(), PackedByteArray()); + case HTTPClient::STATUS_TLS_HANDSHAKE_ERROR: { + _defer_done(RESULT_TLS_HANDSHAKE_ERROR, 0, PackedStringArray(), PackedByteArray()); return true; } break; } @@ -422,41 +452,13 @@ bool HTTPRequest::_update_connection() { ERR_FAIL_V(false); } +void HTTPRequest::_defer_done(int p_status, int p_code, const PackedStringArray &p_headers, const PackedByteArray &p_data) { + call_deferred(SNAME("_request_done"), p_status, p_code, p_headers, p_data); +} + void HTTPRequest::_request_done(int p_status, int p_code, const PackedStringArray &p_headers, const PackedByteArray &p_data) { cancel_request(); - // Determine if the request body is compressed. - bool is_compressed; - String content_encoding = get_header_value(p_headers, "Content-Encoding").to_lower(); - Compression::Mode mode; - if (content_encoding == "gzip") { - mode = Compression::Mode::MODE_GZIP; - is_compressed = true; - } else if (content_encoding == "deflate") { - mode = Compression::Mode::MODE_DEFLATE; - is_compressed = true; - } else { - is_compressed = false; - } - - if (accept_gzip && is_compressed && p_data.size() > 0) { - // Decompress request body - PackedByteArray decompressed; - int result = Compression::decompress_dynamic(&decompressed, body_size_limit, p_data.ptr(), p_data.size(), mode); - if (result == OK) { - emit_signal(SNAME("request_completed"), p_status, p_code, p_headers, decompressed); - return; - } else if (result == -5) { - WARN_PRINT("Decompressed size of HTTP response body exceeded body_size_limit"); - p_status = RESULT_BODY_SIZE_LIMIT_EXCEEDED; - // Just return the raw data if we failed to decompress it. - } else { - WARN_PRINT("Failed to decompress HTTP response body"); - p_status = RESULT_BODY_DECOMPRESS_FAILED; - // Just return the raw data if we failed to decompress it. - } - } - emit_signal(SNAME("request_completed"), p_status, p_code, p_headers, p_data); } @@ -566,12 +568,12 @@ double HTTPRequest::get_timeout() { void HTTPRequest::_timeout() { cancel_request(); - call_deferred(SNAME("_request_done"), RESULT_TIMEOUT, 0, PackedStringArray(), PackedByteArray()); + _defer_done(RESULT_TIMEOUT, 0, PackedStringArray(), PackedByteArray()); } void HTTPRequest::_bind_methods() { - ClassDB::bind_method(D_METHOD("request", "url", "custom_headers", "ssl_validate_domain", "method", "request_data"), &HTTPRequest::request, DEFVAL(PackedStringArray()), DEFVAL(true), DEFVAL(HTTPClient::METHOD_GET), DEFVAL(String())); - ClassDB::bind_method(D_METHOD("request_raw", "url", "custom_headers", "ssl_validate_domain", "method", "request_data_raw"), &HTTPRequest::request_raw, DEFVAL(PackedStringArray()), DEFVAL(true), DEFVAL(HTTPClient::METHOD_GET), DEFVAL(PackedByteArray())); + ClassDB::bind_method(D_METHOD("request", "url", "custom_headers", "tls_validate_domain", "method", "request_data"), &HTTPRequest::request, DEFVAL(PackedStringArray()), DEFVAL(true), DEFVAL(HTTPClient::METHOD_GET), DEFVAL(String())); + ClassDB::bind_method(D_METHOD("request_raw", "url", "custom_headers", "tls_validate_domain", "method", "request_data_raw"), &HTTPRequest::request_raw, DEFVAL(PackedStringArray()), DEFVAL(true), DEFVAL(HTTPClient::METHOD_GET), DEFVAL(PackedByteArray())); ClassDB::bind_method(D_METHOD("cancel_request"), &HTTPRequest::cancel_request); ClassDB::bind_method(D_METHOD("get_http_client_status"), &HTTPRequest::get_http_client_status); @@ -594,7 +596,6 @@ void HTTPRequest::_bind_methods() { ClassDB::bind_method(D_METHOD("get_downloaded_bytes"), &HTTPRequest::get_downloaded_bytes); ClassDB::bind_method(D_METHOD("get_body_size"), &HTTPRequest::get_body_size); - ClassDB::bind_method(D_METHOD("_redirect_request"), &HTTPRequest::_redirect_request); ClassDB::bind_method(D_METHOD("_request_done"), &HTTPRequest::_request_done); ClassDB::bind_method(D_METHOD("set_timeout", "timeout"), &HTTPRequest::set_timeout); @@ -621,7 +622,7 @@ void HTTPRequest::_bind_methods() { BIND_ENUM_CONSTANT(RESULT_CANT_CONNECT); BIND_ENUM_CONSTANT(RESULT_CANT_RESOLVE); BIND_ENUM_CONSTANT(RESULT_CONNECTION_ERROR); - BIND_ENUM_CONSTANT(RESULT_SSL_HANDSHAKE_ERROR); + BIND_ENUM_CONSTANT(RESULT_TLS_HANDSHAKE_ERROR); BIND_ENUM_CONSTANT(RESULT_NO_RESPONSE); BIND_ENUM_CONSTANT(RESULT_BODY_SIZE_LIMIT_EXCEEDED); BIND_ENUM_CONSTANT(RESULT_BODY_DECOMPRESS_FAILED); diff --git a/scene/main/http_request.h b/scene/main/http_request.h index 49b4b1b30c..80445684b0 100644 --- a/scene/main/http_request.h +++ b/scene/main/http_request.h @@ -28,10 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef HTTPREQUEST_H -#define HTTPREQUEST_H +#ifndef HTTP_REQUEST_H +#define HTTP_REQUEST_H #include "core/io/http_client.h" +#include "core/io/stream_peer_gzip.h" #include "core/os/thread.h" #include "core/templates/safe_refcount.h" #include "scene/main/node.h" @@ -48,7 +49,7 @@ public: RESULT_CANT_CONNECT, RESULT_CANT_RESOLVE, RESULT_CONNECTION_ERROR, - RESULT_SSL_HANDSHAKE_ERROR, + RESULT_TLS_HANDSHAKE_ERROR, RESULT_NO_RESPONSE, RESULT_BODY_SIZE_LIMIT_EXCEEDED, RESULT_BODY_DECOMPRESS_FAILED, @@ -67,8 +68,8 @@ private: String url; int port = 80; Vector<String> headers; - bool validate_ssl = false; - bool use_ssl = false; + bool validate_tls = false; + bool use_tls = false; HTTPClient::Method method; Vector<uint8_t> request_data; @@ -84,10 +85,12 @@ private: String download_to_file; + Ref<StreamPeerGZIP> decompressor; Ref<FileAccess> file; int body_len = -1; SafeNumeric<int> downloaded; + SafeNumeric<int> final_body_size; int body_size_limit = -1; int redirections = 0; @@ -113,6 +116,7 @@ private: Thread thread; + void _defer_done(int p_status, int p_code, const PackedStringArray &p_headers, const PackedByteArray &p_data); void _request_done(int p_status, int p_code, const PackedStringArray &p_headers, const PackedByteArray &p_data); static void _thread_func(void *p_userdata); @@ -121,8 +125,8 @@ protected: static void _bind_methods(); public: - Error request(const String &p_url, const Vector<String> &p_custom_headers = Vector<String>(), bool p_ssl_validate_domain = true, HTTPClient::Method p_method = HTTPClient::METHOD_GET, const String &p_request_data = ""); //connects to a full url and perform request - Error request_raw(const String &p_url, const Vector<String> &p_custom_headers = Vector<String>(), bool p_ssl_validate_domain = true, HTTPClient::Method p_method = HTTPClient::METHOD_GET, const Vector<uint8_t> &p_request_data_raw = Vector<uint8_t>()); //connects to a full url and perform request + Error request(const String &p_url, const Vector<String> &p_custom_headers = Vector<String>(), bool p_tls_validate_domain = true, HTTPClient::Method p_method = HTTPClient::METHOD_GET, const String &p_request_data = ""); //connects to a full url and perform request + Error request_raw(const String &p_url, const Vector<String> &p_custom_headers = Vector<String>(), bool p_tls_validate_domain = true, HTTPClient::Method p_method = HTTPClient::METHOD_GET, const Vector<uint8_t> &p_request_data_raw = Vector<uint8_t>()); //connects to a full url and perform request void cancel_request(); HTTPClient::Status get_http_client_status() const; @@ -162,4 +166,4 @@ public: VARIANT_ENUM_CAST(HTTPRequest::Result); -#endif // HTTPREQUEST_H +#endif // HTTP_REQUEST_H diff --git a/scene/main/missing_node.cpp b/scene/main/missing_node.cpp index 395fdad9e4..7ce527fd9c 100644 --- a/scene/main/missing_node.cpp +++ b/scene/main/missing_node.cpp @@ -74,9 +74,9 @@ bool MissingNode::is_recording_properties() const { return recording_properties; } -TypedArray<String> MissingNode::get_configuration_warnings() const { +PackedStringArray MissingNode::get_configuration_warnings() const { // The mere existence of this node is warning. - TypedArray<String> ret; + PackedStringArray ret; ret.push_back(vformat(RTR("This node was saved as class type '%s', which was no longer available when this scene was loaded."), original_class)); ret.push_back(RTR("Data from the original node is kept as a placeholder until this type of node is available again. It can hence be safely re-saved without risk of data loss.")); return ret; diff --git a/scene/main/missing_node.h b/scene/main/missing_node.h index d200fbb47f..0003f71f29 100644 --- a/scene/main/missing_node.h +++ b/scene/main/missing_node.h @@ -55,7 +55,7 @@ public: void set_recording_properties(bool p_enable); bool is_recording_properties() const; - virtual TypedArray<String> get_configuration_warnings() const override; + virtual PackedStringArray get_configuration_warnings() const override; MissingNode(); }; diff --git a/scene/main/multiplayer_api.cpp b/scene/main/multiplayer_api.cpp new file mode 100644 index 0000000000..2d2103f031 --- /dev/null +++ b/scene/main/multiplayer_api.cpp @@ -0,0 +1,416 @@ +/*************************************************************************/ +/* multiplayer_api.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "multiplayer_api.h" + +#include "core/debugger/engine_debugger.h" +#include "core/io/marshalls.h" + +#include <stdint.h> + +#ifdef DEBUG_ENABLED +#include "core/os/os.h" +#endif + +StringName MultiplayerAPI::default_interface = StringName(); + +void MultiplayerAPI::set_default_interface(const StringName &p_interface) { + ERR_FAIL_COND_MSG(!ClassDB::is_parent_class(p_interface, MultiplayerAPI::get_class_static()), vformat("Can't make %s the default multiplayer interface since it does not extend MultiplayerAPI.", p_interface)); + default_interface = p_interface; +} + +StringName MultiplayerAPI::get_default_interface() { + return default_interface; +} + +Ref<MultiplayerAPI> MultiplayerAPI::create_default_interface() { + if (default_interface != StringName()) { + return Ref<MultiplayerAPI>(Object::cast_to<MultiplayerAPI>(ClassDB::instantiate(default_interface))); + } + return Ref<MultiplayerAPI>(memnew(MultiplayerAPIExtension)); +} + +// The variant is compressed and encoded; The first byte contains all the meta +// information and the format is: +// - The first LSB 6 bits are used for the variant type. +// - The next two bits are used to store the encoding mode. +// - Boolean values uses the encoding mode to store the value. +#define VARIANT_META_TYPE_MASK 0x3F +#define VARIANT_META_EMODE_MASK 0xC0 +#define VARIANT_META_BOOL_MASK 0x80 +#define ENCODE_8 0 << 6 +#define ENCODE_16 1 << 6 +#define ENCODE_32 2 << 6 +#define ENCODE_64 3 << 6 +Error MultiplayerAPI::encode_and_compress_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bool p_allow_object_decoding) { + // Unreachable because `VARIANT_MAX` == 38 and `ENCODE_VARIANT_MASK` == 77 + CRASH_COND(p_variant.get_type() > VARIANT_META_TYPE_MASK); + + uint8_t *buf = r_buffer; + r_len = 0; + uint8_t encode_mode = 0; + + switch (p_variant.get_type()) { + case Variant::BOOL: { + if (buf) { + // We don't use encode_mode for booleans, so we can use it to store the value. + buf[0] = (p_variant.operator bool()) ? (1 << 7) : 0; + buf[0] |= p_variant.get_type(); + } + r_len += 1; + } break; + case Variant::INT: { + if (buf) { + // Reserve the first byte for the meta. + buf += 1; + } + r_len += 1; + int64_t val = p_variant; + if (val <= (int64_t)INT8_MAX && val >= (int64_t)INT8_MIN) { + // Use 8 bit + encode_mode = ENCODE_8; + if (buf) { + buf[0] = val; + } + r_len += 1; + } else if (val <= (int64_t)INT16_MAX && val >= (int64_t)INT16_MIN) { + // Use 16 bit + encode_mode = ENCODE_16; + if (buf) { + encode_uint16(val, buf); + } + r_len += 2; + } else if (val <= (int64_t)INT32_MAX && val >= (int64_t)INT32_MIN) { + // Use 32 bit + encode_mode = ENCODE_32; + if (buf) { + encode_uint32(val, buf); + } + r_len += 4; + } else { + // Use 64 bit + encode_mode = ENCODE_64; + if (buf) { + encode_uint64(val, buf); + } + r_len += 8; + } + // Store the meta + if (buf) { + buf -= 1; + buf[0] = encode_mode | p_variant.get_type(); + } + } break; + default: + // Any other case is not yet compressed. + Error err = encode_variant(p_variant, r_buffer, r_len, p_allow_object_decoding); + if (err != OK) { + return err; + } + if (r_buffer) { + // The first byte is not used by the marshalling, so store the type + // so we know how to decompress and decode this variant. + r_buffer[0] = p_variant.get_type(); + } + } + + return OK; +} + +Error MultiplayerAPI::decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len, bool p_allow_object_decoding) { + const uint8_t *buf = p_buffer; + int len = p_len; + + ERR_FAIL_COND_V(len < 1, ERR_INVALID_DATA); + uint8_t type = buf[0] & VARIANT_META_TYPE_MASK; + uint8_t encode_mode = buf[0] & VARIANT_META_EMODE_MASK; + + ERR_FAIL_COND_V(type >= Variant::VARIANT_MAX, ERR_INVALID_DATA); + + switch (type) { + case Variant::BOOL: { + bool val = (buf[0] & VARIANT_META_BOOL_MASK) > 0; + r_variant = val; + if (r_len) { + *r_len = 1; + } + } break; + case Variant::INT: { + buf += 1; + len -= 1; + if (r_len) { + *r_len = 1; + } + if (encode_mode == ENCODE_8) { + // 8 bits. + ERR_FAIL_COND_V(len < 1, ERR_INVALID_DATA); + int8_t val = buf[0]; + r_variant = val; + if (r_len) { + (*r_len) += 1; + } + } else if (encode_mode == ENCODE_16) { + // 16 bits. + ERR_FAIL_COND_V(len < 2, ERR_INVALID_DATA); + int16_t val = decode_uint16(buf); + r_variant = val; + if (r_len) { + (*r_len) += 2; + } + } else if (encode_mode == ENCODE_32) { + // 32 bits. + ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); + int32_t val = decode_uint32(buf); + r_variant = val; + if (r_len) { + (*r_len) += 4; + } + } else { + // 64 bits. + ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA); + int64_t val = decode_uint64(buf); + r_variant = val; + if (r_len) { + (*r_len) += 8; + } + } + } break; + default: + Error err = decode_variant(r_variant, p_buffer, p_len, r_len, p_allow_object_decoding); + if (err != OK) { + return err; + } + } + + return OK; +} + +Error MultiplayerAPI::encode_and_compress_variants(const Variant **p_variants, int p_count, uint8_t *p_buffer, int &r_len, bool *r_raw, bool p_allow_object_decoding) { + r_len = 0; + int size = 0; + + if (p_count == 0) { + if (r_raw) { + *r_raw = true; + } + return OK; + } + + // Try raw encoding optimization. + if (r_raw && p_count == 1) { + *r_raw = false; + const Variant &v = *(p_variants[0]); + if (v.get_type() == Variant::PACKED_BYTE_ARRAY) { + *r_raw = true; + const PackedByteArray pba = v; + if (p_buffer) { + memcpy(p_buffer, pba.ptr(), pba.size()); + } + r_len += pba.size(); + } else { + encode_and_compress_variant(v, p_buffer, size, p_allow_object_decoding); + r_len += size; + } + return OK; + } + + // Regular encoding. + for (int i = 0; i < p_count; i++) { + const Variant &v = *(p_variants[i]); + encode_and_compress_variant(v, p_buffer ? p_buffer + r_len : nullptr, size, p_allow_object_decoding); + r_len += size; + } + return OK; +} + +Error MultiplayerAPI::decode_and_decompress_variants(Vector<Variant> &r_variants, const uint8_t *p_buffer, int p_len, int &r_len, bool p_raw, bool p_allow_object_decoding) { + r_len = 0; + int argc = r_variants.size(); + if (argc == 0 && p_raw) { + return OK; + } + ERR_FAIL_COND_V(p_raw && argc != 1, ERR_INVALID_DATA); + if (p_raw) { + r_len = p_len; + PackedByteArray pba; + pba.resize(p_len); + memcpy(pba.ptrw(), p_buffer, p_len); + r_variants.write[0] = pba; + return OK; + } + + Vector<Variant> args; + Vector<const Variant *> argp; + args.resize(argc); + + for (int i = 0; i < argc; i++) { + ERR_FAIL_COND_V_MSG(r_len >= p_len, ERR_INVALID_DATA, "Invalid packet received. Size too small."); + + int vlen; + Error err = MultiplayerAPI::decode_and_decompress_variant(r_variants.write[i], &p_buffer[r_len], p_len - r_len, &vlen, p_allow_object_decoding); + ERR_FAIL_COND_V_MSG(err != OK, err, "Invalid packet received. Unable to decode state variable."); + r_len += vlen; + } + return OK; +} + +Error MultiplayerAPI::_rpc_bind(int p_peer, Object *p_object, const StringName &p_method, Array p_args) { + Vector<Variant> args; + Vector<const Variant *> argsp; + args.resize(p_args.size()); + argsp.resize(p_args.size()); + Variant *ptr = args.ptrw(); + const Variant **pptr = argsp.ptrw(); + for (int i = 0; i < p_args.size(); i++) { + ptr[i] = p_args[i]; + pptr[i] = &ptr[i]; + } + return rpcp(p_object, p_peer, p_method, argsp.size() ? argsp.ptrw() : nullptr, argsp.size()); +} + +void MultiplayerAPI::_bind_methods() { + ClassDB::bind_method(D_METHOD("has_multiplayer_peer"), &MultiplayerAPI::has_multiplayer_peer); + ClassDB::bind_method(D_METHOD("get_multiplayer_peer"), &MultiplayerAPI::get_multiplayer_peer); + ClassDB::bind_method(D_METHOD("set_multiplayer_peer", "peer"), &MultiplayerAPI::set_multiplayer_peer); + ClassDB::bind_method(D_METHOD("get_unique_id"), &MultiplayerAPI::get_unique_id); + ClassDB::bind_method(D_METHOD("is_server"), &MultiplayerAPI::is_server); + ClassDB::bind_method(D_METHOD("get_remote_sender_id"), &MultiplayerAPI::get_remote_sender_id); + ClassDB::bind_method(D_METHOD("poll"), &MultiplayerAPI::poll); + ClassDB::bind_method(D_METHOD("rpc", "peer", "object", "method", "arguments"), &MultiplayerAPI::_rpc_bind, DEFVAL(Array())); + ClassDB::bind_method(D_METHOD("object_configuration_add", "object", "configuration"), &MultiplayerAPI::object_configuration_add); + ClassDB::bind_method(D_METHOD("object_configuration_remove", "object", "configuration"), &MultiplayerAPI::object_configuration_remove); + + ClassDB::bind_method(D_METHOD("get_peers"), &MultiplayerAPI::get_peer_ids); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multiplayer_peer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerPeer", PROPERTY_USAGE_NONE), "set_multiplayer_peer", "get_multiplayer_peer"); + + ClassDB::bind_static_method("MultiplayerAPI", D_METHOD("set_default_interface", "interface_name"), &MultiplayerAPI::set_default_interface); + ClassDB::bind_static_method("MultiplayerAPI", D_METHOD("get_default_interface"), &MultiplayerAPI::get_default_interface); + ClassDB::bind_static_method("MultiplayerAPI", D_METHOD("create_default_interface"), &MultiplayerAPI::create_default_interface); + + ADD_SIGNAL(MethodInfo("peer_connected", PropertyInfo(Variant::INT, "id"))); + ADD_SIGNAL(MethodInfo("peer_disconnected", PropertyInfo(Variant::INT, "id"))); + ADD_SIGNAL(MethodInfo("connected_to_server")); + ADD_SIGNAL(MethodInfo("connection_failed")); + ADD_SIGNAL(MethodInfo("server_disconnected")); + + BIND_ENUM_CONSTANT(RPC_MODE_DISABLED); + BIND_ENUM_CONSTANT(RPC_MODE_ANY_PEER); + BIND_ENUM_CONSTANT(RPC_MODE_AUTHORITY); +} + +/// MultiplayerAPIExtension + +Error MultiplayerAPIExtension::poll() { + int err; + if (GDVIRTUAL_CALL(_poll, err)) { + return (Error)err; + } + return OK; +} + +void MultiplayerAPIExtension::set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer) { + GDVIRTUAL_CALL(_set_multiplayer_peer, p_peer); +} + +Ref<MultiplayerPeer> MultiplayerAPIExtension::get_multiplayer_peer() { + Ref<MultiplayerPeer> peer; + if (GDVIRTUAL_CALL(_get_multiplayer_peer, peer)) { + return peer; + } + return nullptr; +} + +int MultiplayerAPIExtension::get_unique_id() { + int id; + if (GDVIRTUAL_CALL(_get_unique_id, id)) { + return id; + } + return 1; +} + +Vector<int> MultiplayerAPIExtension::get_peer_ids() { + Vector<int> ids; + if (GDVIRTUAL_CALL(_get_peer_ids, ids)) { + return ids; + } + return Vector<int>(); +} + +Error MultiplayerAPIExtension::rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) { + if (!GDVIRTUAL_IS_OVERRIDDEN(_rpc)) { + return ERR_UNAVAILABLE; + } + Array args; + for (int i = 0; i < p_argcount; i++) { + args.push_back(*p_arg[i]); + } + int ret; + if (GDVIRTUAL_CALL(_rpc, p_peer_id, p_obj, p_method, args, ret)) { + return (Error)ret; + } + return FAILED; +} + +int MultiplayerAPIExtension::get_remote_sender_id() { + int id; + if (GDVIRTUAL_CALL(_get_remote_sender_id, id)) { + return id; + } + return 0; +} + +Error MultiplayerAPIExtension::object_configuration_add(Object *p_object, Variant p_config) { + int err; + if (GDVIRTUAL_CALL(_object_configuration_add, p_object, p_config, err)) { + return (Error)err; + } + return ERR_UNAVAILABLE; +} + +Error MultiplayerAPIExtension::object_configuration_remove(Object *p_object, Variant p_config) { + int err; + if (GDVIRTUAL_CALL(_object_configuration_remove, p_object, p_config, err)) { + return (Error)err; + } + return ERR_UNAVAILABLE; +} + +void MultiplayerAPIExtension::_bind_methods() { + GDVIRTUAL_BIND(_poll); + GDVIRTUAL_BIND(_set_multiplayer_peer, "multiplayer_peer"); + GDVIRTUAL_BIND(_get_multiplayer_peer); + GDVIRTUAL_BIND(_get_unique_id); + GDVIRTUAL_BIND(_get_peer_ids); + GDVIRTUAL_BIND(_rpc, "peer", "object", "method", "args"); + GDVIRTUAL_BIND(_get_remote_sender_id); + GDVIRTUAL_BIND(_object_configuration_add, "object", "configuration"); + GDVIRTUAL_BIND(_object_configuration_remove, "object", "configuration"); +} diff --git a/scene/main/multiplayer_api.h b/scene/main/multiplayer_api.h new file mode 100644 index 0000000000..c1d90d651e --- /dev/null +++ b/scene/main/multiplayer_api.h @@ -0,0 +1,115 @@ +/*************************************************************************/ +/* multiplayer_api.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef MULTIPLAYER_API_H +#define MULTIPLAYER_API_H + +#include "core/object/ref_counted.h" +#include "scene/main/multiplayer_peer.h" + +class MultiplayerAPI : public RefCounted { + GDCLASS(MultiplayerAPI, RefCounted); + +private: + static StringName default_interface; + +protected: + static void _bind_methods(); + Error _rpc_bind(int p_peer, Object *p_obj, const StringName &p_method, Array args = Array()); + +public: + enum RPCMode { + RPC_MODE_DISABLED, // No rpc for this method, calls to this will be blocked (default) + RPC_MODE_ANY_PEER, // Any peer can call this RPC + RPC_MODE_AUTHORITY, // Only the node's multiplayer authority (server by default) can call this RPC + }; + + static Ref<MultiplayerAPI> create_default_interface(); + static void set_default_interface(const StringName &p_interface); + static StringName get_default_interface(); + + static Error encode_and_compress_variant(const Variant &p_variant, uint8_t *p_buffer, int &r_len, bool p_allow_object_decoding); + static Error decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len, bool p_allow_object_decoding); + static Error encode_and_compress_variants(const Variant **p_variants, int p_count, uint8_t *p_buffer, int &r_len, bool *r_raw = nullptr, bool p_allow_object_decoding = false); + static Error decode_and_decompress_variants(Vector<Variant> &r_variants, const uint8_t *p_buffer, int p_len, int &r_len, bool p_raw = false, bool p_allow_object_decoding = false); + + virtual Error poll() = 0; + virtual void set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer) = 0; + virtual Ref<MultiplayerPeer> get_multiplayer_peer() = 0; + virtual int get_unique_id() = 0; + virtual Vector<int> get_peer_ids() = 0; + + virtual Error rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) = 0; + virtual int get_remote_sender_id() = 0; + + virtual Error object_configuration_add(Object *p_object, Variant p_config) = 0; + virtual Error object_configuration_remove(Object *p_object, Variant p_config) = 0; + + bool has_multiplayer_peer() { return get_multiplayer_peer().is_valid(); } + bool is_server() { return get_unique_id() == MultiplayerPeer::TARGET_PEER_SERVER; } + + MultiplayerAPI() {} + virtual ~MultiplayerAPI() {} +}; + +VARIANT_ENUM_CAST(MultiplayerAPI::RPCMode); + +class MultiplayerAPIExtension : public MultiplayerAPI { + GDCLASS(MultiplayerAPIExtension, MultiplayerAPI); + +protected: + static void _bind_methods(); + +public: + virtual Error poll() override; + virtual void set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer) override; + virtual Ref<MultiplayerPeer> get_multiplayer_peer() override; + virtual int get_unique_id() override; + virtual Vector<int> get_peer_ids() override; + + virtual Error rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) override; + virtual int get_remote_sender_id() override; + + virtual Error object_configuration_add(Object *p_object, Variant p_config) override; + virtual Error object_configuration_remove(Object *p_object, Variant p_config) override; + + // Extensions + GDVIRTUAL0R(int, _poll); + GDVIRTUAL1(_set_multiplayer_peer, Ref<MultiplayerPeer>); + GDVIRTUAL0R(Ref<MultiplayerPeer>, _get_multiplayer_peer); + GDVIRTUAL0RC(int, _get_unique_id); + GDVIRTUAL0RC(PackedInt32Array, _get_peer_ids); + GDVIRTUAL4R(int, _rpc, int, Object *, StringName, Array); + GDVIRTUAL0RC(int, _get_remote_sender_id); + GDVIRTUAL2R(int, _object_configuration_add, Object *, Variant); + GDVIRTUAL2R(int, _object_configuration_remove, Object *, Variant); +}; + +#endif // MULTIPLAYER_API_H diff --git a/scene/main/multiplayer_peer.cpp b/scene/main/multiplayer_peer.cpp new file mode 100644 index 0000000000..9b63118f7c --- /dev/null +++ b/scene/main/multiplayer_peer.cpp @@ -0,0 +1,207 @@ +/*************************************************************************/ +/* multiplayer_peer.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "multiplayer_peer.h" + +#include "core/os/os.h" + +uint32_t MultiplayerPeer::generate_unique_id() const { + uint32_t hash = 0; + + while (hash == 0 || hash == 1) { + hash = hash_murmur3_one_32( + (uint32_t)OS::get_singleton()->get_ticks_usec()); + hash = hash_murmur3_one_32( + (uint32_t)OS::get_singleton()->get_unix_time(), hash); + hash = hash_murmur3_one_32( + (uint32_t)OS::get_singleton()->get_user_data_dir().hash64(), hash); + hash = hash_murmur3_one_32( + (uint32_t)((uint64_t)this), hash); // Rely on ASLR heap + hash = hash_murmur3_one_32( + (uint32_t)((uint64_t)&hash), hash); // Rely on ASLR stack + + hash = hash_fmix32(hash); + hash = hash & 0x7FFFFFFF; // Make it compatible with unsigned, since negative ID is used for exclusion + } + + return hash; +} + +void MultiplayerPeer::set_transfer_channel(int p_channel) { + transfer_channel = p_channel; +} + +int MultiplayerPeer::get_transfer_channel() const { + return transfer_channel; +} + +void MultiplayerPeer::set_transfer_mode(TransferMode p_mode) { + transfer_mode = p_mode; +} + +MultiplayerPeer::TransferMode MultiplayerPeer::get_transfer_mode() const { + return transfer_mode; +} + +void MultiplayerPeer::set_refuse_new_connections(bool p_enable) { + refuse_connections = p_enable; +} + +bool MultiplayerPeer::is_refusing_new_connections() const { + return refuse_connections; +} + +void MultiplayerPeer::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_transfer_channel", "channel"), &MultiplayerPeer::set_transfer_channel); + ClassDB::bind_method(D_METHOD("get_transfer_channel"), &MultiplayerPeer::get_transfer_channel); + ClassDB::bind_method(D_METHOD("set_transfer_mode", "mode"), &MultiplayerPeer::set_transfer_mode); + ClassDB::bind_method(D_METHOD("get_transfer_mode"), &MultiplayerPeer::get_transfer_mode); + ClassDB::bind_method(D_METHOD("set_target_peer", "id"), &MultiplayerPeer::set_target_peer); + + ClassDB::bind_method(D_METHOD("get_packet_peer"), &MultiplayerPeer::get_packet_peer); + + ClassDB::bind_method(D_METHOD("poll"), &MultiplayerPeer::poll); + + ClassDB::bind_method(D_METHOD("get_connection_status"), &MultiplayerPeer::get_connection_status); + ClassDB::bind_method(D_METHOD("get_unique_id"), &MultiplayerPeer::get_unique_id); + ClassDB::bind_method(D_METHOD("generate_unique_id"), &MultiplayerPeer::generate_unique_id); + + ClassDB::bind_method(D_METHOD("set_refuse_new_connections", "enable"), &MultiplayerPeer::set_refuse_new_connections); + ClassDB::bind_method(D_METHOD("is_refusing_new_connections"), &MultiplayerPeer::is_refusing_new_connections); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_connections"), "set_refuse_new_connections", "is_refusing_new_connections"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "transfer_mode", PROPERTY_HINT_ENUM, "Unreliable,Unreliable Ordered,Reliable"), "set_transfer_mode", "get_transfer_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "transfer_channel", PROPERTY_HINT_RANGE, "0,255,1"), "set_transfer_channel", "get_transfer_channel"); + + BIND_ENUM_CONSTANT(CONNECTION_DISCONNECTED); + BIND_ENUM_CONSTANT(CONNECTION_CONNECTING); + BIND_ENUM_CONSTANT(CONNECTION_CONNECTED); + + BIND_CONSTANT(TARGET_PEER_BROADCAST); + BIND_CONSTANT(TARGET_PEER_SERVER); + + BIND_ENUM_CONSTANT(TRANSFER_MODE_UNRELIABLE); + BIND_ENUM_CONSTANT(TRANSFER_MODE_UNRELIABLE_ORDERED); + BIND_ENUM_CONSTANT(TRANSFER_MODE_RELIABLE); + + ADD_SIGNAL(MethodInfo("peer_connected", PropertyInfo(Variant::INT, "id"))); + ADD_SIGNAL(MethodInfo("peer_disconnected", PropertyInfo(Variant::INT, "id"))); + ADD_SIGNAL(MethodInfo("server_disconnected")); + ADD_SIGNAL(MethodInfo("connection_succeeded")); + ADD_SIGNAL(MethodInfo("connection_failed")); +} + +/*************/ + +Error MultiplayerPeerExtension::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { + Error err; + if (GDVIRTUAL_CALL(_get_packet, r_buffer, &r_buffer_size, err)) { + return err; + } + if (GDVIRTUAL_IS_OVERRIDDEN(_get_packet_script)) { + if (!GDVIRTUAL_CALL(_get_packet_script, script_buffer)) { + return FAILED; + } + + if (script_buffer.size() == 0) { + return Error::ERR_UNAVAILABLE; + } + + *r_buffer = script_buffer.ptr(); + r_buffer_size = script_buffer.size(); + + return Error::OK; + } + WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_packet_native is unimplemented!"); + return FAILED; +} + +Error MultiplayerPeerExtension::put_packet(const uint8_t *p_buffer, int p_buffer_size) { + Error err; + if (GDVIRTUAL_CALL(_put_packet, p_buffer, p_buffer_size, err)) { + return err; + } + if (GDVIRTUAL_IS_OVERRIDDEN(_put_packet_script)) { + PackedByteArray a; + a.resize(p_buffer_size); + memcpy(a.ptrw(), p_buffer, p_buffer_size); + + if (!GDVIRTUAL_CALL(_put_packet_script, a, err)) { + return FAILED; + } + return (Error)err; + } + WARN_PRINT_ONCE("MultiplayerPeerExtension::_put_packet_native is unimplemented!"); + return FAILED; +} + +void MultiplayerPeerExtension::set_refuse_new_connections(bool p_enable) { + if (GDVIRTUAL_CALL(_set_refuse_new_connections, p_enable)) { + return; + } + MultiplayerPeer::set_refuse_new_connections(p_enable); +} + +bool MultiplayerPeerExtension::is_refusing_new_connections() const { + bool refusing; + if (GDVIRTUAL_CALL(_is_refusing_new_connections, refusing)) { + return refusing; + } + return MultiplayerPeer::is_refusing_new_connections(); +} + +void MultiplayerPeerExtension::_bind_methods() { + GDVIRTUAL_BIND(_get_packet, "r_buffer", "r_buffer_size"); + GDVIRTUAL_BIND(_put_packet, "p_buffer", "p_buffer_size"); + GDVIRTUAL_BIND(_get_available_packet_count); + GDVIRTUAL_BIND(_get_max_packet_size); + + GDVIRTUAL_BIND(_get_packet_script) + GDVIRTUAL_BIND(_put_packet_script, "p_buffer"); + + GDVIRTUAL_BIND(_set_transfer_channel, "p_channel"); + GDVIRTUAL_BIND(_get_transfer_channel); + + GDVIRTUAL_BIND(_set_transfer_mode, "p_mode"); + GDVIRTUAL_BIND(_get_transfer_mode); + + GDVIRTUAL_BIND(_set_target_peer, "p_peer"); + + GDVIRTUAL_BIND(_get_packet_peer); + GDVIRTUAL_BIND(_is_server); + GDVIRTUAL_BIND(_poll); + GDVIRTUAL_BIND(_get_unique_id); + GDVIRTUAL_BIND(_set_refuse_new_connections, "p_enable"); + GDVIRTUAL_BIND(_is_refusing_new_connections); + GDVIRTUAL_BIND(_get_connection_status); + + ADD_PROPERTY_DEFAULT("transfer_mode", TRANSFER_MODE_RELIABLE); + ADD_PROPERTY_DEFAULT("transfer_channel", 0); +} diff --git a/scene/main/multiplayer_peer.h b/scene/main/multiplayer_peer.h new file mode 100644 index 0000000000..ab7483ece5 --- /dev/null +++ b/scene/main/multiplayer_peer.h @@ -0,0 +1,138 @@ +/*************************************************************************/ +/* multiplayer_peer.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef MULTIPLAYER_PEER_H +#define MULTIPLAYER_PEER_H + +#include "core/io/packet_peer.h" + +#include "core/extension/ext_wrappers.gen.inc" +#include "core/object/gdvirtual.gen.inc" +#include "core/object/script_language.h" +#include "core/variant/native_ptr.h" + +class MultiplayerPeer : public PacketPeer { + GDCLASS(MultiplayerPeer, PacketPeer); + +public: + enum TransferMode { + TRANSFER_MODE_UNRELIABLE, + TRANSFER_MODE_UNRELIABLE_ORDERED, + TRANSFER_MODE_RELIABLE + }; + +protected: + static void _bind_methods(); + +private: + int transfer_channel = 0; + TransferMode transfer_mode = TRANSFER_MODE_RELIABLE; + bool refuse_connections = false; + +public: + enum { + TARGET_PEER_BROADCAST = 0, + TARGET_PEER_SERVER = 1 + }; + + enum ConnectionStatus { + CONNECTION_DISCONNECTED, + CONNECTION_CONNECTING, + CONNECTION_CONNECTED, + }; + + virtual void set_transfer_channel(int p_channel); + virtual int get_transfer_channel() const; + virtual void set_transfer_mode(TransferMode p_mode); + virtual TransferMode get_transfer_mode() const; + virtual void set_refuse_new_connections(bool p_enable); + virtual bool is_refusing_new_connections() const; + + virtual void set_target_peer(int p_peer_id) = 0; + + virtual int get_packet_peer() const = 0; + + virtual bool is_server() const = 0; + + virtual void poll() = 0; + + virtual int get_unique_id() const = 0; + + virtual ConnectionStatus get_connection_status() const = 0; + + uint32_t generate_unique_id() const; + + MultiplayerPeer() {} +}; + +VARIANT_ENUM_CAST(MultiplayerPeer::ConnectionStatus); +VARIANT_ENUM_CAST(MultiplayerPeer::TransferMode); + +class MultiplayerPeerExtension : public MultiplayerPeer { + GDCLASS(MultiplayerPeerExtension, MultiplayerPeer); + +protected: + static void _bind_methods(); + + PackedByteArray script_buffer; + +public: + /* PacketPeer extension */ + virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; ///< buffer is GONE after next get_packet + GDVIRTUAL2R(Error, _get_packet, GDNativeConstPtr<const uint8_t *>, GDNativePtr<int>); + GDVIRTUAL0R(PackedByteArray, _get_packet_script); // For GDScript. + + virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override; + GDVIRTUAL2R(Error, _put_packet, GDNativeConstPtr<const uint8_t>, int); + GDVIRTUAL1R(Error, _put_packet_script, PackedByteArray); // For GDScript. + + EXBIND0RC(int, get_available_packet_count); + EXBIND0RC(int, get_max_packet_size); + + /* MultiplayerPeer extension */ + virtual void set_refuse_new_connections(bool p_enable) override; + GDVIRTUAL1(_set_refuse_new_connections, bool); // Optional. + + virtual bool is_refusing_new_connections() const override; + GDVIRTUAL0RC(bool, _is_refusing_new_connections); // Optional. + + EXBIND1(set_transfer_channel, int); + EXBIND0RC(int, get_transfer_channel); + EXBIND1(set_transfer_mode, TransferMode); + EXBIND0RC(TransferMode, get_transfer_mode); + EXBIND1(set_target_peer, int); + EXBIND0RC(int, get_packet_peer); + EXBIND0RC(bool, is_server); + EXBIND0(poll); + EXBIND0RC(int, get_unique_id); + EXBIND0RC(ConnectionStatus, get_connection_status); +}; + +#endif // MULTIPLAYER_PEER_H diff --git a/scene/main/node.cpp b/scene/main/node.cpp index b4701637a4..bbdba16cb5 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -33,12 +33,12 @@ #include "core/config/project_settings.h" #include "core/core_string_names.h" #include "core/io/resource_loader.h" -#include "core/multiplayer/multiplayer_api.h" #include "core/object/message_queue.h" #include "core/string/print_string.h" #include "instance_placeholder.h" #include "scene/animation/tween.h" #include "scene/debugger/scene_debugger.h" +#include "scene/main/multiplayer_api.h" #include "scene/resources/packed_scene.h" #include "scene/scene_string_names.h" #include "viewport.h" @@ -328,12 +328,21 @@ void Node::move_child(Node *p_child, int p_pos) { // We need to check whether node is internal and move it only in the relevant node range. if (p_child->_is_internal_front()) { + if (p_pos < 0) { + p_pos += data.internal_children_front; + } ERR_FAIL_INDEX_MSG(p_pos, data.internal_children_front, vformat("Invalid new child position: %d. Child is internal.", p_pos)); _move_child(p_child, p_pos); } else if (p_child->_is_internal_back()) { + if (p_pos < 0) { + p_pos += data.internal_children_back; + } ERR_FAIL_INDEX_MSG(p_pos, data.internal_children_back, vformat("Invalid new child position: %d. Child is internal.", p_pos)); _move_child(p_child, data.children.size() - data.internal_children_back + p_pos); } else { + if (p_pos < 0) { + p_pos += get_child_count(false); + } ERR_FAIL_INDEX_MSG(p_pos, data.children.size() + 1 - data.internal_children_front - data.internal_children_back, vformat("Invalid new child position: %d.", p_pos)); _move_child(p_child, p_pos + data.internal_children_front); } @@ -389,21 +398,6 @@ void Node::_move_child(Node *p_child, int p_pos, bool p_ignore_end) { data.blocked--; } -void Node::raise() { - if (!data.parent) { - return; - } - - // Internal children move within a different index range. - if (_is_internal_front()) { - data.parent->move_child(this, data.parent->data.internal_children_front - 1); - } else if (_is_internal_back()) { - data.parent->move_child(this, data.parent->data.internal_children_back - 1); - } else { - data.parent->move_child(this, data.parent->get_child_count(false) - 1); - } -} - void Node::_propagate_groups_dirty() { for (const KeyValue<StringName, GroupData> &E : data.grouped) { if (E.value.group) { @@ -582,35 +576,30 @@ bool Node::is_multiplayer_authority() const { /***** RPC CONFIG ********/ -uint16_t Node::rpc_config(const StringName &p_method, Multiplayer::RPCMode p_rpc_mode, bool p_call_local, Multiplayer::TransferMode p_transfer_mode, int p_channel) { - for (int i = 0; i < data.rpc_methods.size(); i++) { - if (data.rpc_methods[i].name == p_method) { - Multiplayer::RPCConfig &nd = data.rpc_methods.write[i]; - nd.rpc_mode = p_rpc_mode; - nd.transfer_mode = p_transfer_mode; - nd.call_local = p_call_local; - nd.channel = p_channel; - return i | (1 << 15); - } +void Node::rpc_config(const StringName &p_method, const Variant &p_config) { + if (data.rpc_config.get_type() != Variant::DICTIONARY) { + data.rpc_config = Dictionary(); + } + Dictionary node_config = data.rpc_config; + if (p_config.get_type() == Variant::NIL) { + node_config.erase(p_method); + } else { + ERR_FAIL_COND(p_config.get_type() != Variant::DICTIONARY); + node_config[p_method] = p_config; } - // New method - Multiplayer::RPCConfig nd; - nd.name = p_method; - nd.rpc_mode = p_rpc_mode; - nd.transfer_mode = p_transfer_mode; - nd.channel = p_channel; - nd.call_local = p_call_local; - data.rpc_methods.push_back(nd); - return ((uint16_t)data.rpc_methods.size() - 1) | (1 << 15); +} + +const Variant Node::get_node_rpc_config() const { + return data.rpc_config; } /***** RPC FUNCTIONS ********/ -void Node::_rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { +Error Node::_rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { if (p_argcount < 1) { r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; r_error.argument = 1; - return; + return ERR_INVALID_PARAMETER; } Variant::Type type = p_args[0]->get_type(); @@ -618,28 +607,28 @@ void Node::_rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; r_error.argument = 0; r_error.expected = Variant::STRING_NAME; - return; + return ERR_INVALID_PARAMETER; } StringName method = (*p_args[0]).operator StringName(); - rpcp(0, method, &p_args[1], p_argcount - 1); - + Error err = rpcp(0, method, &p_args[1], p_argcount - 1); r_error.error = Callable::CallError::CALL_OK; + return err; } -void Node::_rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { +Error Node::_rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { if (p_argcount < 2) { r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; r_error.argument = 2; - return; + return ERR_INVALID_PARAMETER; } if (p_args[0]->get_type() != Variant::INT) { r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; r_error.argument = 0; r_error.expected = Variant::INT; - return; + return ERR_INVALID_PARAMETER; } Variant::Type type = p_args[1]->get_type(); @@ -647,20 +636,20 @@ void Node::_rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallEr r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; r_error.argument = 1; r_error.expected = Variant::STRING_NAME; - return; + return ERR_INVALID_PARAMETER; } int peer_id = *p_args[0]; StringName method = (*p_args[1]).operator StringName(); - rpcp(peer_id, method, &p_args[2], p_argcount - 2); - + Error err = rpcp(peer_id, method, &p_args[2], p_argcount - 2); r_error.error = Callable::CallError::CALL_OK; + return err; } -void Node::rpcp(int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) { - ERR_FAIL_COND(!is_inside_tree()); - get_multiplayer()->rpcp(this, p_peer_id, p_method, p_arg, p_argcount); +Error Node::rpcp(int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) { + ERR_FAIL_COND_V(!is_inside_tree(), ERR_UNCONFIGURED); + return get_multiplayer()->rpcp(this, p_peer_id, p_method, p_arg, p_argcount); } Ref<MultiplayerAPI> Node::get_multiplayer() const { @@ -670,10 +659,6 @@ Ref<MultiplayerAPI> Node::get_multiplayer() const { return get_tree()->get_multiplayer(get_path()); } -Vector<Multiplayer::RPCConfig> Node::get_node_rpc_methods() const { - return data.rpc_methods; -} - //////////// end of rpc bool Node::can_process_notification(int p_what) const { @@ -957,6 +942,18 @@ String Node::validate_child_name(Node *p_child) { } #endif +String Node::adjust_name_casing(const String &p_name) { + switch (GLOBAL_GET("editor/node_naming/name_casing").operator int()) { + case NAME_CASING_PASCAL_CASE: + return p_name.to_pascal_case(); + case NAME_CASING_CAMEL_CASE: + return p_name.to_camel_case(); + case NAME_CASING_SNAKE_CASE: + return p_name.to_snake_case(); + } + return p_name; +} + void Node::_validate_child_name(Node *p_child, bool p_force_human_readable) { /* Make sure the name is unique */ @@ -1030,19 +1027,8 @@ void Node::_generate_serial_child_name(const Node *p_child, StringName &name) co //no name and a new name is needed, create one. name = p_child->get_class(); - // Adjust casing according to project setting. The current type name is expected to be in PascalCase. - switch (ProjectSettings::get_singleton()->get("editor/node_naming/name_casing").operator int()) { - case NAME_CASING_PASCAL_CASE: - break; - case NAME_CASING_CAMEL_CASE: { - String n = name; - n[0] = n.to_lower()[0]; - name = n; - } break; - case NAME_CASING_SNAKE_CASE: - name = String(name).camelcase_to_underscore(true); - break; - } + // Adjust casing according to project setting. + name = adjust_name_casing(name); } //quickly test if proposed name exists @@ -1139,7 +1125,7 @@ void Node::_add_child_nocheck(Node *p_child, const StringName &p_name) { add_child_notify(p_child); } -void Node::add_child(Node *p_child, bool p_legible_unique_name, InternalMode p_internal) { +void Node::add_child(Node *p_child, bool p_force_readable_name, InternalMode p_internal) { ERR_FAIL_NULL(p_child); ERR_FAIL_COND_MSG(p_child == this, vformat("Can't add child '%s' to itself.", p_child->get_name())); // adding to itself! ERR_FAIL_COND_MSG(p_child->data.parent, vformat("Can't add child '%s' to '%s', already has a parent '%s'.", p_child->get_name(), get_name(), p_child->data.parent->get_name())); //Fail if node has a parent @@ -1148,7 +1134,7 @@ void Node::add_child(Node *p_child, bool p_legible_unique_name, InternalMode p_i #endif ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, add_node() failed. Consider using call_deferred(\"add_child\", child) instead."); - _validate_child_name(p_child, p_legible_unique_name); + _validate_child_name(p_child, p_force_readable_name); _add_child_nocheck(p_child, p_child->data.name); if (p_internal == INTERNAL_MODE_FRONT) { @@ -1162,7 +1148,7 @@ void Node::add_child(Node *p_child, bool p_legible_unique_name, InternalMode p_i } } -void Node::add_sibling(Node *p_sibling, bool p_legible_unique_name) { +void Node::add_sibling(Node *p_sibling, bool p_force_readable_name) { ERR_FAIL_NULL(p_sibling); ERR_FAIL_NULL(data.parent); ERR_FAIL_COND_MSG(p_sibling == this, vformat("Can't add sibling '%s' to itself.", p_sibling->get_name())); // adding to itself! @@ -1175,7 +1161,7 @@ void Node::add_sibling(Node *p_sibling, bool p_legible_unique_name) { internal = INTERNAL_MODE_BACK; } - data.parent->add_child(p_sibling, p_legible_unique_name, internal); + data.parent->add_child(p_sibling, p_force_readable_name, internal); data.parent->_move_child(p_sibling, get_index() + 1); } @@ -1480,20 +1466,10 @@ bool Node::is_greater_than(const Node *p_node) const { ERR_FAIL_COND_V(data.depth < 0, false); ERR_FAIL_COND_V(p_node->data.depth < 0, false); -#ifdef NO_ALLOCA - - Vector<int> this_stack; - Vector<int> that_stack; - this_stack.resize(data.depth); - that_stack.resize(p_node->data.depth); - -#else int *this_stack = (int *)alloca(sizeof(int) * data.depth); int *that_stack = (int *)alloca(sizeof(int) * p_node->data.depth); -#endif - const Node *n = this; int idx = data.depth - 1; @@ -1787,8 +1763,8 @@ void Node::remove_from_group(const StringName &p_identifier) { data.grouped.remove(E); } -Array Node::_get_groups() const { - Array groups; +TypedArray<StringName> Node::_get_groups() const { + TypedArray<StringName> groups; List<GroupInfo> gi; get_groups(&gi); for (const GroupInfo &E : gi) { @@ -1930,43 +1906,6 @@ Ref<Tween> Node::create_tween() { return tween; } -void Node::remove_and_skip() { - ERR_FAIL_COND(!data.parent); - - Node *new_owner = get_owner(); - - List<Node *> children; - - while (true) { - bool clear = true; - for (int i = 0; i < data.children.size(); i++) { - Node *c_node = data.children[i]; - if (!c_node->get_owner()) { - continue; - } - - remove_child(c_node); - c_node->_propagate_replace_owner(this, nullptr); - children.push_back(c_node); - clear = false; - break; - } - - if (clear) { - break; - } - } - - while (!children.is_empty()) { - Node *c_node = children.front()->get(); - data.parent->add_child(c_node); - c_node->_propagate_replace_owner(nullptr, new_owner); - children.pop_front(); - } - - data.parent->remove_child(this); -} - void Node::set_scene_file_path(const String &p_scene_file_path) { data.scene_file_path = p_scene_file_path; } @@ -2404,7 +2343,7 @@ void Node::_duplicate_signals(const Node *p_original, Node *p_copy) const { if (copy && copytarget) { const Callable copy_callable = Callable(copytarget, E.callable.get_method()); if (!copy->is_connected(E.signal.get_name(), copy_callable)) { - copy->connect(E.signal.get_name(), copy_callable, E.binds, E.flags); + copy->connect(E.signal.get_name(), copy_callable, E.flags); } } } @@ -2490,7 +2429,7 @@ void Node::_replace_connections_target(Node *p_new_target) { c.signal.get_object()->disconnect(c.signal.get_name(), Callable(this, c.callable.get_method())); bool valid = p_new_target->has_method(c.callable.get_method()) || Ref<Script>(p_new_target->get_script()).is_null() || Ref<Script>(p_new_target->get_script())->has_method(c.callable.get_method()); ERR_CONTINUE_MSG(!valid, vformat("Attempt to connect signal '%s.%s' to nonexistent method '%s.%s'.", c.signal.get_object()->get_class(), c.signal.get_name(), c.callable.get_object()->get_class(), c.callable.get_method())); - c.signal.get_object()->connect(c.signal.get_name(), Callable(p_new_target, c.callable.get_method()), c.binds, c.flags); + c.signal.get_object()->connect(c.signal.get_name(), Callable(p_new_target, c.callable.get_method()), c.flags); } } } @@ -2691,21 +2630,19 @@ void Node::clear_internal_tree_resource_paths() { } } -TypedArray<String> Node::get_configuration_warnings() const { - TypedArray<String> ret; +PackedStringArray Node::get_configuration_warnings() const { + PackedStringArray ret; Vector<String> warnings; if (GDVIRTUAL_CALL(_get_configuration_warnings, warnings)) { - for (int i = 0; i < warnings.size(); i++) { - ret.push_back(warnings[i]); - } + ret.append_array(warnings); } return ret; } String Node::get_configuration_warnings_as_string() const { - TypedArray<String> warnings = get_configuration_warnings(); + PackedStringArray warnings = get_configuration_warnings(); String all_warnings = String(); for (int i = 0; i < warnings.size(); i++) { if (i > 0) { @@ -2713,7 +2650,7 @@ String Node::get_configuration_warnings_as_string() const { } // Format as a bullet point list to make multiple warnings easier to distinguish // from each other. - all_warnings += String::utf8("• ") + String(warnings[i]); + all_warnings += String::utf8("• ") + warnings[i]; } return all_warnings; } @@ -2795,11 +2732,11 @@ void Node::_bind_methods() { GLOBAL_DEF("editor/node_naming/name_casing", NAME_CASING_PASCAL_CASE); ProjectSettings::get_singleton()->set_custom_property_info("editor/node_naming/name_casing", PropertyInfo(Variant::INT, "editor/node_naming/name_casing", PROPERTY_HINT_ENUM, "PascalCase,camelCase,snake_case")); - ClassDB::bind_method(D_METHOD("add_sibling", "sibling", "legible_unique_name"), &Node::add_sibling, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("add_sibling", "sibling", "force_readable_name"), &Node::add_sibling, DEFVAL(false)); ClassDB::bind_method(D_METHOD("set_name", "name"), &Node::set_name); ClassDB::bind_method(D_METHOD("get_name"), &Node::get_name); - ClassDB::bind_method(D_METHOD("add_child", "node", "legible_unique_name", "internal"), &Node::add_child, DEFVAL(false), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("add_child", "node", "force_readable_name", "internal"), &Node::add_child, DEFVAL(false), DEFVAL(0)); ClassDB::bind_method(D_METHOD("remove_child", "node"), &Node::remove_child); ClassDB::bind_method(D_METHOD("get_child_count", "include_internal"), &Node::get_child_count, DEFVAL(false)); // Note that the default value bound for include_internal is false, while the method is declared with true. This is because internal nodes are irrelevant for GDSCript. ClassDB::bind_method(D_METHOD("get_children", "include_internal"), &Node::_get_children, DEFVAL(false)); @@ -2824,10 +2761,8 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("is_in_group", "group"), &Node::is_in_group); ClassDB::bind_method(D_METHOD("move_child", "child_node", "to_position"), &Node::move_child); ClassDB::bind_method(D_METHOD("get_groups"), &Node::_get_groups); - ClassDB::bind_method(D_METHOD("raise"), &Node::raise); ClassDB::bind_method(D_METHOD("set_owner", "owner"), &Node::set_owner); ClassDB::bind_method(D_METHOD("get_owner"), &Node::get_owner); - ClassDB::bind_method(D_METHOD("remove_and_skip"), &Node::remove_and_skip); ClassDB::bind_method(D_METHOD("get_index", "include_internal"), &Node::get_index, DEFVAL(false)); ClassDB::bind_method(D_METHOD("print_tree"), &Node::print_tree); ClassDB::bind_method(D_METHOD("print_tree_pretty"), &Node::print_tree_pretty); @@ -2888,7 +2823,7 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("is_multiplayer_authority"), &Node::is_multiplayer_authority); ClassDB::bind_method(D_METHOD("get_multiplayer"), &Node::get_multiplayer); - ClassDB::bind_method(D_METHOD("rpc_config", "method", "rpc_mode", "call_local", "transfer_mode", "channel"), &Node::rpc_config, DEFVAL(false), DEFVAL(Multiplayer::TRANSFER_MODE_RELIABLE), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("rpc_config", "method", "config"), &Node::rpc_config); ClassDB::bind_method(D_METHOD("set_editor_description", "editor_description"), &Node::set_editor_description); ClassDB::bind_method(D_METHOD("get_editor_description"), &Node::get_editor_description); @@ -2931,7 +2866,7 @@ void Node::_bind_methods() { BIND_CONSTANT(NOTIFICATION_PROCESS); BIND_CONSTANT(NOTIFICATION_PARENTED); BIND_CONSTANT(NOTIFICATION_UNPARENTED); - BIND_CONSTANT(NOTIFICATION_INSTANCED); + BIND_CONSTANT(NOTIFICATION_SCENE_INSTANTIATED); BIND_CONSTANT(NOTIFICATION_DRAG_BEGIN); BIND_CONSTANT(NOTIFICATION_DRAG_END); BIND_CONSTANT(NOTIFICATION_PATH_RENAMED); diff --git a/scene/main/node.h b/scene/main/node.h index 3c4727f11c..4e6530cccd 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -127,7 +127,7 @@ private: Node *process_owner = nullptr; int multiplayer_authority = 1; // Server by default. - Vector<Multiplayer::RPCConfig> rpc_methods; + Variant rpc_config; // Variables used to properly sort the node when processing, ignored otherwise. // TODO: Should move all the stuff below to bits. @@ -181,10 +181,10 @@ private: Node *_duplicate(int p_flags, HashMap<const Node *, Node *> *r_duplimap = nullptr) const; TypedArray<Node> _get_children(bool p_include_internal = true) const; - Array _get_groups() const; + TypedArray<StringName> _get_groups() const; - void _rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error); - void _rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error); + Error _rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error); + Error _rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error); _FORCE_INLINE_ bool _is_internal_front() const { return data.parent && data.pos < data.parent->data.internal_children_front; } _FORCE_INLINE_ bool _is_internal_back() const { return data.parent && data.pos >= data.parent->data.children.size() - data.parent->data.internal_children_back; } @@ -259,7 +259,7 @@ public: NOTIFICATION_PROCESS = 17, NOTIFICATION_PARENTED = 18, NOTIFICATION_UNPARENTED = 19, - NOTIFICATION_INSTANCED = 20, + NOTIFICATION_SCENE_INSTANTIATED = 20, NOTIFICATION_DRAG_BEGIN = 21, NOTIFICATION_DRAG_END = 22, NOTIFICATION_PATH_RENAMED = 23, @@ -303,8 +303,8 @@ public: StringName get_name() const; void set_name(const String &p_name); - void add_child(Node *p_child, bool p_legible_unique_name = false, InternalMode p_internal = INTERNAL_MODE_DISABLED); - void add_sibling(Node *p_sibling, bool p_legible_unique_name = false); + void add_child(Node *p_child, bool p_force_readable_name = false, InternalMode p_internal = INTERNAL_MODE_DISABLED); + void add_sibling(Node *p_sibling, bool p_force_readable_name = false); void remove_child(Node *p_child); int get_child_count(bool p_include_internal = true) const; @@ -348,7 +348,6 @@ public: void move_child(Node *p_child, int p_pos); void _move_child(Node *p_child, int p_pos, bool p_ignore_end = false); - void raise(); void set_owner(Node *p_owner); Node *get_owner() const; @@ -357,7 +356,6 @@ public: void set_unique_name_in_owner(bool p_enabled); bool is_unique_name_in_owner() const; - void remove_and_skip(); int get_index(bool p_include_internal = true) const; Ref<Tween> create_tween(); @@ -459,6 +457,7 @@ public: #ifdef TOOLS_ENABLED String validate_child_name(Node *p_child); #endif + static String adjust_name_casing(const String &p_name); void queue_delete(); @@ -478,7 +477,7 @@ public: _FORCE_INLINE_ Viewport *get_viewport() const { return data.viewport; } - virtual TypedArray<String> get_configuration_warnings() const; + virtual PackedStringArray get_configuration_warnings() const; String get_configuration_warnings_as_string() const; void update_configuration_warnings(); @@ -491,30 +490,16 @@ public: int get_multiplayer_authority() const; bool is_multiplayer_authority() const; - uint16_t rpc_config(const StringName &p_method, Multiplayer::RPCMode p_rpc_mode, bool p_call_local = false, Multiplayer::TransferMode p_transfer_mode = Multiplayer::TRANSFER_MODE_RELIABLE, int p_channel = 0); // config a local method for RPC - Vector<Multiplayer::RPCConfig> get_node_rpc_methods() const; + void rpc_config(const StringName &p_method, const Variant &p_config); // config a local method for RPC + const Variant get_node_rpc_config() const; template <typename... VarArgs> - void rpc(const StringName &p_method, VarArgs... p_args) { - Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported. - const Variant *argptrs[sizeof...(p_args) + 1]; - for (uint32_t i = 0; i < sizeof...(p_args); i++) { - argptrs[i] = &args[i]; - } - rpcp(0, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); - } + Error rpc(const StringName &p_method, VarArgs... p_args); template <typename... VarArgs> - void rpc_id(int p_peer_id, const StringName &p_method, VarArgs... p_args) { - Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported. - const Variant *argptrs[sizeof...(p_args) + 1]; - for (uint32_t i = 0; i < sizeof...(p_args); i++) { - argptrs[i] = &args[i]; - } - rpcp(p_peer_id, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); - } + Error rpc_id(int p_peer_id, const StringName &p_method, VarArgs... p_args); - void rpcp(int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount); + Error rpcp(int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount); Ref<MultiplayerAPI> get_multiplayer() const; @@ -526,4 +511,26 @@ VARIANT_ENUM_CAST(Node::DuplicateFlags); typedef HashSet<Node *, Node::Comparator> NodeSet; -#endif +// Template definitions must be in the header so they are always fully initialized before their usage. +// See this StackOverflow question for more information: https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file + +template <typename... VarArgs> +Error Node::rpc(const StringName &p_method, VarArgs... p_args) { + return rpc_id(0, p_method, p_args...); +} + +template <typename... VarArgs> +Error Node::rpc_id(int p_peer_id, const StringName &p_method, VarArgs... p_args) { + Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported. + const Variant *argptrs[sizeof...(p_args) + 1]; + for (uint32_t i = 0; i < sizeof...(p_args); i++) { + argptrs[i] = &args[i]; + } + return rpcp(p_peer_id, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); +} + +// Add these macro to your class's 'get_configuration_warnings' function to have warnings show up in the scene tree inspector. +#define DEPRECATED_NODE_WARNING warnings.push_back(RTR("This node is marked as deprecated and will be removed in future versions.\nPlease check the Godot documentation for information about migration.")); +#define EXPERIMENTAL_NODE_WARNING warnings.push_back(RTR("This node is marked as experimental and may be subject to removal or major changes in future versions.")); + +#endif // NODE_H diff --git a/scene/main/resource_preloader.cpp b/scene/main/resource_preloader.cpp index 5512d0a84e..a9b0285723 100644 --- a/scene/main/resource_preloader.cpp +++ b/scene/main/resource_preloader.cpp @@ -138,7 +138,7 @@ void ResourcePreloader::get_resource_list(List<StringName> *p_list) { } void ResourcePreloader::_bind_methods() { - ClassDB::bind_method(D_METHOD("_set_resources"), &ResourcePreloader::_set_resources); + ClassDB::bind_method(D_METHOD("_set_resources", "resources"), &ResourcePreloader::_set_resources); ClassDB::bind_method(D_METHOD("_get_resources"), &ResourcePreloader::_get_resources); ClassDB::bind_method(D_METHOD("add_resource", "name", "resource"), &ResourcePreloader::add_resource); diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index a76c00efcb..3f71de1b18 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -34,9 +34,9 @@ #include "core/debugger/engine_debugger.h" #include "core/input/input.h" #include "core/io/dir_access.h" +#include "core/io/image_loader.h" #include "core/io/marshalls.h" #include "core/io/resource_loader.h" -#include "core/multiplayer/multiplayer_api.h" #include "core/object/message_queue.h" #include "core/os/keyboard.h" #include "core/os/os.h" @@ -44,7 +44,9 @@ #include "node.h" #include "scene/animation/tween.h" #include "scene/debugger/scene_debugger.h" +#include "scene/main/multiplayer_api.h" #include "scene/main/viewport.h" +#include "scene/resources/environment.h" #include "scene/resources/font.h" #include "scene/resources/material.h" #include "scene/resources/mesh.h" @@ -86,6 +88,14 @@ bool SceneTreeTimer::is_process_always() { return process_always; } +void SceneTreeTimer::set_process_in_physics(bool p_process_in_physics) { + process_in_physics = p_process_in_physics; +} + +bool SceneTreeTimer::is_process_in_physics() { + return process_in_physics; +} + void SceneTreeTimer::set_ignore_time_scale(bool p_ignore) { ignore_time_scale = p_ignore; } @@ -418,6 +428,8 @@ bool SceneTree::physics_process(double p_time) { _flush_ugc(); MessageQueue::get_singleton()->flush(); //small little hack + process_timers(p_time, true); //go through timers + process_tweens(p_time, true); flush_transform_notifications(); @@ -460,37 +472,7 @@ bool SceneTree::process(double p_time) { _flush_delete_queue(); - //go through timers - - List<Ref<SceneTreeTimer>>::Element *L = timers.back(); //last element - - for (List<Ref<SceneTreeTimer>>::Element *E = timers.front(); E;) { - List<Ref<SceneTreeTimer>>::Element *N = E->next(); - if (paused && !E->get()->is_process_always()) { - if (E == L) { - break; //break on last, so if new timers were added during list traversal, ignore them. - } - E = N; - continue; - } - - double time_left = E->get()->get_time_left(); - if (E->get()->is_ignore_time_scale()) { - time_left -= Engine::get_singleton()->get_process_step(); - } else { - time_left -= p_time; - } - E->get()->set_time_left(time_left); - - if (time_left <= 0) { - E->get()->emit_signal(SNAME("timeout")); - timers.erase(E); - } - if (E == L) { - break; //break on last, so if new timers were added during list traversal, ignore them. - } - E = N; - } + process_timers(p_time, false); //go through timers process_tweens(p_time, false); @@ -528,7 +510,39 @@ bool SceneTree::process(double p_time) { return _quit; } -void SceneTree::process_tweens(float p_delta, bool p_physics) { +void SceneTree::process_timers(double p_delta, bool p_physics_frame) { + List<Ref<SceneTreeTimer>>::Element *L = timers.back(); //last element + + for (List<Ref<SceneTreeTimer>>::Element *E = timers.front(); E;) { + List<Ref<SceneTreeTimer>>::Element *N = E->next(); + if ((paused && !E->get()->is_process_always()) || (E->get()->is_process_in_physics() != p_physics_frame)) { + if (E == L) { + break; //break on last, so if new timers were added during list traversal, ignore them. + } + E = N; + continue; + } + + double time_left = E->get()->get_time_left(); + if (E->get()->is_ignore_time_scale()) { + time_left -= Engine::get_singleton()->get_process_step(); + } else { + time_left -= p_delta; + } + E->get()->set_time_left(time_left); + + if (time_left <= 0) { + E->get()->emit_signal(SNAME("timeout")); + timers.erase(E); + } + if (E == L) { + break; //break on last, so if new timers were added during list traversal, ignore them. + } + E = N; + } +} + +void SceneTree::process_tweens(double p_delta, bool p_physics) { // This methods works similarly to how SceneTreeTimers are handled. List<Ref<Tween>>::Element *L = tweens.back(); @@ -708,22 +722,6 @@ float SceneTree::get_debug_paths_width() const { return debug_paths_width; } -void SceneTree::set_debug_navigation_color(const Color &p_color) { - debug_navigation_color = p_color; -} - -Color SceneTree::get_debug_navigation_color() const { - return debug_navigation_color; -} - -void SceneTree::set_debug_navigation_disabled_color(const Color &p_color) { - debug_navigation_disabled_color = p_color; -} - -Color SceneTree::get_debug_navigation_disabled_color() const { - return debug_navigation_disabled_color; -} - Ref<Material> SceneTree::get_debug_paths_material() { if (debug_paths_material.is_valid()) { return debug_paths_material; @@ -741,40 +739,6 @@ Ref<Material> SceneTree::get_debug_paths_material() { return debug_paths_material; } -Ref<Material> SceneTree::get_debug_navigation_material() { - if (navigation_material.is_valid()) { - return navigation_material; - } - - Ref<StandardMaterial3D> line_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D)); - line_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); - line_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); - line_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); - line_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); - line_material->set_albedo(get_debug_navigation_color()); - - navigation_material = line_material; - - return navigation_material; -} - -Ref<Material> SceneTree::get_debug_navigation_disabled_material() { - if (navigation_disabled_material.is_valid()) { - return navigation_disabled_material; - } - - Ref<StandardMaterial3D> line_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D)); - line_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); - line_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); - line_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); - line_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); - line_material->set_albedo(get_debug_navigation_disabled_color()); - - navigation_disabled_material = line_material; - - return navigation_disabled_material; -} - Ref<Material> SceneTree::get_debug_collision_material() { if (collision_material.is_valid()) { return collision_material; @@ -999,8 +963,8 @@ int64_t SceneTree::get_frame() const { return current_frame; } -Array SceneTree::_get_nodes_in_group(const StringName &p_group) { - Array ret; +TypedArray<Node> SceneTree::_get_nodes_in_group(const StringName &p_group) { + TypedArray<Node> ret; HashMap<StringName, Group>::Iterator E = group_map.find(p_group); if (!E) { return ret; @@ -1121,19 +1085,20 @@ void SceneTree::_change_scene(Node *p_to) { if (p_to) { current_scene = p_to; root->add_child(p_to); + root->update_mouse_cursor_shape(); } } -Error SceneTree::change_scene(const String &p_path) { +Error SceneTree::change_scene_to_file(const String &p_path) { Ref<PackedScene> new_scene = ResourceLoader::load(p_path); if (new_scene.is_null()) { return ERR_CANT_OPEN; } - return change_scene_to(new_scene); + return change_scene_to_packed(new_scene); } -Error SceneTree::change_scene_to(const Ref<PackedScene> &p_scene) { +Error SceneTree::change_scene_to_packed(const Ref<PackedScene> &p_scene) { Node *new_scene = nullptr; if (p_scene.is_valid()) { new_scene = p_scene->instantiate(); @@ -1147,7 +1112,7 @@ Error SceneTree::change_scene_to(const Ref<PackedScene> &p_scene) { Error SceneTree::reload_current_scene() { ERR_FAIL_COND_V(!current_scene, ERR_UNCONFIGURED); String fname = current_scene->get_scene_file_path(); - return change_scene(fname); + return change_scene_to_file(fname); } void SceneTree::add_current_scene(Node *p_current) { @@ -1155,11 +1120,13 @@ void SceneTree::add_current_scene(Node *p_current) { root->add_child(p_current); } -Ref<SceneTreeTimer> SceneTree::create_timer(double p_delay_sec, bool p_process_always) { +Ref<SceneTreeTimer> SceneTree::create_timer(double p_delay_sec, bool p_process_always, bool p_process_in_physics, bool p_ignore_time_scale) { Ref<SceneTreeTimer> stt; stt.instantiate(); stt->set_process_always(p_process_always); stt->set_time_left(p_delay_sec); + stt->set_process_in_physics(p_process_in_physics); + stt->set_ignore_time_scale(p_ignore_time_scale); timers.push_back(stt); return stt; } @@ -1170,8 +1137,8 @@ Ref<Tween> SceneTree::create_tween() { return tween; } -Array SceneTree::get_processed_tweens() { - Array ret; +TypedArray<Tween> SceneTree::get_processed_tweens() { + TypedArray<Tween> ret; ret.resize(tweens.size()); int i = 0; @@ -1212,19 +1179,17 @@ void SceneTree::set_multiplayer(Ref<MultiplayerAPI> p_multiplayer, const NodePat if (p_root_path.is_empty()) { ERR_FAIL_COND(!p_multiplayer.is_valid()); if (multiplayer.is_valid()) { - multiplayer->set_root_path(NodePath()); + multiplayer->object_configuration_remove(nullptr, NodePath("/" + root->get_name())); } multiplayer = p_multiplayer; - multiplayer->set_root_path("/" + root->get_name()); + multiplayer->object_configuration_add(nullptr, NodePath("/" + root->get_name())); } else { + if (custom_multiplayers.has(p_root_path)) { + custom_multiplayers[p_root_path]->object_configuration_remove(nullptr, p_root_path); + } if (p_multiplayer.is_valid()) { custom_multiplayers[p_root_path] = p_multiplayer; - p_multiplayer->set_root_path(p_root_path); - } else { - if (custom_multiplayers.has(p_root_path)) { - custom_multiplayers[p_root_path]->set_root_path(NodePath()); - custom_multiplayers.erase(p_root_path); - } + p_multiplayer->object_configuration_add(nullptr, p_root_path); } } } @@ -1259,7 +1224,7 @@ void SceneTree::_bind_methods() { ClassDB::bind_method(D_METHOD("set_pause", "enable"), &SceneTree::set_pause); ClassDB::bind_method(D_METHOD("is_paused"), &SceneTree::is_paused); - ClassDB::bind_method(D_METHOD("create_timer", "time_sec", "process_always"), &SceneTree::create_timer, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("create_timer", "time_sec", "process_always", "process_in_physics", "ignore_time_scale"), &SceneTree::create_timer, DEFVAL(true), DEFVAL(false), DEFVAL(false)); ClassDB::bind_method(D_METHOD("create_tween"), &SceneTree::create_tween); ClassDB::bind_method(D_METHOD("get_processed_tweens"), &SceneTree::get_processed_tweens); @@ -1296,8 +1261,8 @@ void SceneTree::_bind_methods() { ClassDB::bind_method(D_METHOD("set_current_scene", "child_node"), &SceneTree::set_current_scene); ClassDB::bind_method(D_METHOD("get_current_scene"), &SceneTree::get_current_scene); - ClassDB::bind_method(D_METHOD("change_scene", "path"), &SceneTree::change_scene); - ClassDB::bind_method(D_METHOD("change_scene_to", "packed_scene"), &SceneTree::change_scene_to); + ClassDB::bind_method(D_METHOD("change_scene_to_file", "path"), &SceneTree::change_scene_to_file); + ClassDB::bind_method(D_METHOD("change_scene_to_packed", "packed_scene"), &SceneTree::change_scene_to_packed); ClassDB::bind_method(D_METHOD("reload_current_scene"), &SceneTree::reload_current_scene); @@ -1352,7 +1317,7 @@ void SceneTree::add_idle_callback(IdleCallback p_callback) { } void SceneTree::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { - if (p_function == "change_scene") { + if (p_function == "change_scene_to_file") { Ref<DirAccess> dir_access = DirAccess::create(DirAccess::ACCESS_RESOURCES); List<String> directories; directories.push_back(dir_access->get_current_dir()); @@ -1371,9 +1336,9 @@ void SceneTree::get_argument_options(const StringName &p_function, int p_idx, Li } if (dir_access->dir_exists(filename)) { - directories.push_back(dir_access->get_current_dir().plus_file(filename)); + directories.push_back(dir_access->get_current_dir().path_join(filename)); } else if (filename.ends_with(".tscn") || filename.ends_with(".scn")) { - r_options->push_back("\"" + dir_access->get_current_dir().plus_file(filename) + "\""); + r_options->push_back("\"" + dir_access->get_current_dir().path_join(filename) + "\""); } filename = dir_access->get_next(); @@ -1390,8 +1355,6 @@ SceneTree::SceneTree() { debug_collision_contact_color = GLOBAL_DEF("debug/shapes/collision/contact_color", Color(1.0, 0.2, 0.1, 0.8)); debug_paths_color = GLOBAL_DEF("debug/shapes/paths/geometry_color", Color(0.1, 1.0, 0.7, 0.4)); debug_paths_width = GLOBAL_DEF("debug/shapes/paths/geometry_width", 2.0); - debug_navigation_color = GLOBAL_DEF("debug/shapes/navigation/geometry_color", Color(0.1, 1.0, 0.7, 0.4)); - debug_navigation_disabled_color = GLOBAL_DEF("debug/shapes/navigation/disabled_geometry_color", Color(1.0, 0.7, 0.1, 0.4)); collision_debug_contacts = GLOBAL_DEF("debug/shapes/collision/max_contacts_displayed", 10000); ProjectSettings::get_singleton()->set_custom_property_info("debug/shapes/collision/max_contacts_displayed", PropertyInfo(Variant::INT, "debug/shapes/collision/max_contacts_displayed", PROPERTY_HINT_RANGE, "0,20000,1")); // No negative @@ -1414,14 +1377,18 @@ SceneTree::SceneTree() { #endif // _3D_DISABLED // Initialize network state. - set_multiplayer(Ref<MultiplayerAPI>(memnew(MultiplayerAPI))); + set_multiplayer(MultiplayerAPI::create_default_interface()); root->set_as_audio_listener_2d(true); current_scene = nullptr; - const int msaa_mode = GLOBAL_DEF_BASIC("rendering/anti_aliasing/quality/msaa", 0); - ProjectSettings::get_singleton()->set_custom_property_info("rendering/anti_aliasing/quality/msaa", PropertyInfo(Variant::INT, "rendering/anti_aliasing/quality/msaa", PROPERTY_HINT_ENUM, String::utf8("Disabled (Fastest),2× (Average),4× (Slow),8× (Slowest)"))); - root->set_msaa(Viewport::MSAA(msaa_mode)); + const int msaa_mode_2d = GLOBAL_DEF_BASIC("rendering/anti_aliasing/quality/msaa_2d", 0); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/anti_aliasing/quality/msaa_2d", PropertyInfo(Variant::INT, "rendering/anti_aliasing/quality/msaa_2d", PROPERTY_HINT_ENUM, String::utf8("Disabled (Fastest),2× (Average),4× (Slow),8× (Slowest)"))); + root->set_msaa_2d(Viewport::MSAA(msaa_mode_2d)); + + const int msaa_mode_3d = GLOBAL_DEF_BASIC("rendering/anti_aliasing/quality/msaa_3d", 0); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/anti_aliasing/quality/msaa_3d", PropertyInfo(Variant::INT, "rendering/anti_aliasing/quality/msaa_3d", PROPERTY_HINT_ENUM, String::utf8("Disabled (Fastest),2× (Average),4× (Slow),8× (Slowest)"))); + root->set_msaa_3d(Viewport::MSAA(msaa_mode_3d)); const int ssaa_mode = GLOBAL_DEF_BASIC("rendering/anti_aliasing/quality/screen_space_aa", 0); ProjectSettings::get_singleton()->set_custom_property_info("rendering/anti_aliasing/quality/screen_space_aa", PropertyInfo(Variant::INT, "rendering/anti_aliasing/quality/screen_space_aa", PROPERTY_HINT_ENUM, "Disabled (Fastest),FXAA (Fast)")); @@ -1446,25 +1413,48 @@ SceneTree::SceneTree() { bool snap_2d_vertices = GLOBAL_DEF("rendering/2d/snap/snap_2d_vertices_to_pixel", false); root->set_snap_2d_vertices_to_pixel(snap_2d_vertices); - int shadowmap_size = GLOBAL_DEF("rendering/shadows/shadow_atlas/size", 4096); - ProjectSettings::get_singleton()->set_custom_property_info("rendering/shadows/shadow_atlas/size", PropertyInfo(Variant::INT, "rendering/shadows/shadow_atlas/size", PROPERTY_HINT_RANGE, "256,16384")); - GLOBAL_DEF("rendering/shadows/shadow_atlas/size.mobile", 2048); - bool shadowmap_16_bits = GLOBAL_DEF("rendering/shadows/shadow_atlas/16_bits", true); - int atlas_q0 = GLOBAL_DEF("rendering/shadows/shadow_atlas/quadrant_0_subdiv", 2); - int atlas_q1 = GLOBAL_DEF("rendering/shadows/shadow_atlas/quadrant_1_subdiv", 2); - int atlas_q2 = GLOBAL_DEF("rendering/shadows/shadow_atlas/quadrant_2_subdiv", 3); - int atlas_q3 = GLOBAL_DEF("rendering/shadows/shadow_atlas/quadrant_3_subdiv", 4); - ProjectSettings::get_singleton()->set_custom_property_info("rendering/shadows/shadow_atlas/quadrant_0_subdiv", PropertyInfo(Variant::INT, "rendering/shadows/shadow_atlas/quadrant_0_subdiv", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows")); - ProjectSettings::get_singleton()->set_custom_property_info("rendering/shadows/shadow_atlas/quadrant_1_subdiv", PropertyInfo(Variant::INT, "rendering/shadows/shadow_atlas/quadrant_1_subdiv", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows")); - ProjectSettings::get_singleton()->set_custom_property_info("rendering/shadows/shadow_atlas/quadrant_2_subdiv", PropertyInfo(Variant::INT, "rendering/shadows/shadow_atlas/quadrant_2_subdiv", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows")); - ProjectSettings::get_singleton()->set_custom_property_info("rendering/shadows/shadow_atlas/quadrant_3_subdiv", PropertyInfo(Variant::INT, "rendering/shadows/shadow_atlas/quadrant_3_subdiv", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows")); - - root->set_shadow_atlas_size(shadowmap_size); - root->set_shadow_atlas_16_bits(shadowmap_16_bits); - root->set_shadow_atlas_quadrant_subdiv(0, Viewport::ShadowAtlasQuadrantSubdiv(atlas_q0)); - root->set_shadow_atlas_quadrant_subdiv(1, Viewport::ShadowAtlasQuadrantSubdiv(atlas_q1)); - root->set_shadow_atlas_quadrant_subdiv(2, Viewport::ShadowAtlasQuadrantSubdiv(atlas_q2)); - root->set_shadow_atlas_quadrant_subdiv(3, Viewport::ShadowAtlasQuadrantSubdiv(atlas_q3)); + // We setup VRS for the main viewport here, in the editor this will have little effect. + const int vrs_mode = GLOBAL_DEF("rendering/vrs/mode", 0); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/vrs/mode", PropertyInfo(Variant::INT, "rendering/vrs/mode", PROPERTY_HINT_ENUM, String::utf8("Disabled,Texture,XR"))); + root->set_vrs_mode(Viewport::VRSMode(vrs_mode)); + const String vrs_texture_path = String(GLOBAL_DEF("rendering/vrs/texture", String())).strip_edges(); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/vrs/texture", + PropertyInfo(Variant::STRING, + "rendering/vrs/texture", + PROPERTY_HINT_FILE, "*.png")); + if (vrs_mode == 1 && !vrs_texture_path.is_empty()) { + Ref<Image> vrs_image; + vrs_image.instantiate(); + Error load_err = ImageLoader::load_image(vrs_texture_path, vrs_image); + if (load_err) { + ERR_PRINT("Non-existing or invalid VRS texture at '" + vrs_texture_path + "'."); + } else { + Ref<ImageTexture> vrs_texture; + vrs_texture.instantiate(); + vrs_texture->create_from_image(vrs_image); + root->set_vrs_texture(vrs_texture); + } + } + + int shadowmap_size = GLOBAL_DEF("rendering/lights_and_shadows/positional_shadow/atlas_size", 4096); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/lights_and_shadows/positional_shadow/atlas_size", PropertyInfo(Variant::INT, "rendering/lights_and_shadows/positional_shadow/atlas_size", PROPERTY_HINT_RANGE, "256,16384")); + GLOBAL_DEF("rendering/lights_and_shadows/positional_shadow/atlas_size.mobile", 2048); + bool shadowmap_16_bits = GLOBAL_DEF("rendering/lights_and_shadows/positional_shadow/atlas_16_bits", true); + int atlas_q0 = GLOBAL_DEF("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_0_subdiv", 2); + int atlas_q1 = GLOBAL_DEF("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_1_subdiv", 2); + int atlas_q2 = GLOBAL_DEF("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_2_subdiv", 3); + int atlas_q3 = GLOBAL_DEF("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_3_subdiv", 4); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_0_subdiv", PropertyInfo(Variant::INT, "rendering/lights_and_shadows/positional_shadow/atlas_quadrant_0_subdiv", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows")); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_1_subdiv", PropertyInfo(Variant::INT, "rendering/lights_and_shadows/positional_shadow/atlas_quadrant_1_subdiv", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows")); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_2_subdiv", PropertyInfo(Variant::INT, "rendering/lights_and_shadows/positional_shadow/atlas_quadrant_2_subdiv", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows")); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_3_subdiv", PropertyInfo(Variant::INT, "rendering/lights_and_shadows/positional_shadow/atlas_quadrant_3_subdiv", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows")); + + root->set_positional_shadow_atlas_size(shadowmap_size); + root->set_positional_shadow_atlas_16_bits(shadowmap_16_bits); + root->set_positional_shadow_atlas_quadrant_subdiv(0, Viewport::PositionalShadowAtlasQuadrantSubdiv(atlas_q0)); + root->set_positional_shadow_atlas_quadrant_subdiv(1, Viewport::PositionalShadowAtlasQuadrantSubdiv(atlas_q1)); + root->set_positional_shadow_atlas_quadrant_subdiv(2, Viewport::PositionalShadowAtlasQuadrantSubdiv(atlas_q2)); + root->set_positional_shadow_atlas_quadrant_subdiv(3, Viewport::PositionalShadowAtlasQuadrantSubdiv(atlas_q3)); Viewport::SDFOversize sdf_oversize = Viewport::SDFOversize(int(GLOBAL_DEF("rendering/2d/sdf/oversize", 1))); root->set_sdf_oversize(sdf_oversize); diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h index a34aa8e2cd..a460e40597 100644 --- a/scene/main/scene_tree.h +++ b/scene/main/scene_tree.h @@ -53,6 +53,7 @@ class SceneTreeTimer : public RefCounted { double time_left = 0.0; bool process_always = true; + bool process_in_physics = false; bool ignore_time_scale = false; protected: @@ -65,6 +66,9 @@ public: void set_process_always(bool p_process_always); bool is_process_always(); + void set_process_in_physics(bool p_process_in_physics); + bool is_process_in_physics(); + void set_ignore_time_scale(bool p_ignore); bool is_ignore_time_scale(); @@ -90,8 +94,8 @@ private: Window *root = nullptr; uint64_t tree_version = 1; - double physics_process_time = 1.0; - double process_time = 1.0; + double physics_process_time = 0.0; + double process_time = 0.0; bool accept_quit = true; bool quit_on_go_back = true; @@ -141,7 +145,7 @@ private: _FORCE_INLINE_ void _update_group_order(Group &g, bool p_use_priority = false); - Array _get_nodes_in_group(const StringName &p_group); + TypedArray<Node> _get_nodes_in_group(const StringName &p_group); Node *current_scene = nullptr; @@ -176,7 +180,8 @@ private: void node_added(Node *p_node); void node_removed(Node *p_node); void node_renamed(Node *p_node); - void process_tweens(float p_delta, bool p_physics_frame); + void process_timers(double p_delta, bool p_physics_frame); + void process_tweens(double p_delta, bool p_physics_frame); Group *add_to_group(const StringName &p_group, Node *p_node); void remove_from_group(const StringName &p_group, Node *p_node); @@ -329,15 +334,7 @@ public: void set_debug_paths_width(float p_width); float get_debug_paths_width() const; - void set_debug_navigation_color(const Color &p_color); - Color get_debug_navigation_color() const; - - void set_debug_navigation_disabled_color(const Color &p_color); - Color get_debug_navigation_disabled_color() const; - Ref<Material> get_debug_paths_material(); - Ref<Material> get_debug_navigation_material(); - Ref<Material> get_debug_navigation_disabled_material(); Ref<Material> get_debug_collision_material(); Ref<ArrayMesh> get_debug_contact_mesh(); @@ -361,13 +358,13 @@ public: void set_current_scene(Node *p_scene); Node *get_current_scene() const; - Error change_scene(const String &p_path); - Error change_scene_to(const Ref<PackedScene> &p_scene); + Error change_scene_to_file(const String &p_path); + Error change_scene_to_packed(const Ref<PackedScene> &p_scene); Error reload_current_scene(); - Ref<SceneTreeTimer> create_timer(double p_delay_sec, bool p_process_always = true); + Ref<SceneTreeTimer> create_timer(double p_delay_sec, bool p_process_always = true, bool p_process_in_physics = false, bool p_ignore_time_scale = false); Ref<Tween> create_tween(); - Array get_processed_tweens(); + TypedArray<Tween> get_processed_tweens(); //used by Main::start, don't use otherwise void add_current_scene(Node *p_current); diff --git a/scene/main/shader_globals_override.cpp b/scene/main/shader_globals_override.cpp index 0049359cad..455b8c6866 100644 --- a/scene/main/shader_globals_override.cpp +++ b/scene/main/shader_globals_override.cpp @@ -64,9 +64,9 @@ bool ShaderGlobalsOverride::_set(const StringName &p_name, const Variant &p_valu if (active) { if (o->override.get_type() == Variant::OBJECT) { RID tex_rid = p_value; - RS::get_singleton()->global_variable_set_override(*r, tex_rid); + RS::get_singleton()->global_shader_parameter_set_override(*r, tex_rid); } else { - RS::get_singleton()->global_variable_set_override(*r, p_value); + RS::get_singleton()->global_shader_parameter_set_override(*r, p_value); } } o->in_use = p_value.get_type() != Variant::NIL; @@ -93,13 +93,13 @@ bool ShaderGlobalsOverride::_get(const StringName &p_name, Variant &r_ret) const void ShaderGlobalsOverride::_get_property_list(List<PropertyInfo> *p_list) const { Vector<StringName> variables; - variables = RS::get_singleton()->global_variable_get_list(); + variables = RS::get_singleton()->global_shader_parameter_get_list(); for (int i = 0; i < variables.size(); i++) { PropertyInfo pinfo; pinfo.name = "params/" + variables[i]; pinfo.usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CHECKABLE; - switch (RS::get_singleton()->global_variable_get_type(variables[i])) { + switch (RS::get_singleton()->global_shader_parameter_get_type(variables[i])) { case RS::GLOBAL_VAR_TYPE_BOOL: { pinfo.type = Variant::BOOL; } break; @@ -155,7 +155,7 @@ void ShaderGlobalsOverride::_get_property_list(List<PropertyInfo> *p_list) const pinfo.type = Variant::VECTOR3; } break; case RS::GLOBAL_VAR_TYPE_VEC4: { - pinfo.type = Variant::QUATERNION; + pinfo.type = Variant::VECTOR4; } break; case RS::GLOBAL_VAR_TYPE_RECT2: { pinfo.type = Variant::RECT2; @@ -169,15 +169,15 @@ void ShaderGlobalsOverride::_get_property_list(List<PropertyInfo> *p_list) const case RS::GLOBAL_VAR_TYPE_MAT3: { pinfo.type = Variant::BASIS; } break; + case RS::GLOBAL_VAR_TYPE_MAT4: { + pinfo.type = Variant::PROJECTION; + } break; case RS::GLOBAL_VAR_TYPE_TRANSFORM_2D: { pinfo.type = Variant::TRANSFORM2D; } break; case RS::GLOBAL_VAR_TYPE_TRANSFORM: { pinfo.type = Variant::TRANSFORM3D; } break; - case RS::GLOBAL_VAR_TYPE_MAT4: { - pinfo.type = Variant::PACKED_INT32_ARRAY; - } break; case RS::GLOBAL_VAR_TYPE_SAMPLER2D: { pinfo.type = Variant::OBJECT; pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE; @@ -234,9 +234,9 @@ void ShaderGlobalsOverride::_activate() { if (o->in_use && o->override.get_type() != Variant::NIL) { if (o->override.get_type() == Variant::OBJECT) { RID tex_rid = o->override; - RS::get_singleton()->global_variable_set_override(E.key, tex_rid); + RS::get_singleton()->global_shader_parameter_set_override(E.key, tex_rid); } else { - RS::get_singleton()->global_variable_set_override(E.key, o->override); + RS::get_singleton()->global_shader_parameter_set_override(E.key, o->override); } } @@ -258,7 +258,7 @@ void ShaderGlobalsOverride::_notification(int p_what) { for (const KeyValue<StringName, Override> &E : overrides) { const Override *o = &E.value; if (o->in_use) { - RS::get_singleton()->global_variable_set_override(E.key, Variant()); + RS::get_singleton()->global_shader_parameter_set_override(E.key, Variant()); } } } @@ -271,8 +271,8 @@ void ShaderGlobalsOverride::_notification(int p_what) { } } -TypedArray<String> ShaderGlobalsOverride::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray ShaderGlobalsOverride::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (!active) { warnings.push_back(RTR("ShaderGlobalsOverride is not active because another node of the same type is in the scene.")); diff --git a/scene/main/shader_globals_override.h b/scene/main/shader_globals_override.h index af99bf9aa7..f3d0074f28 100644 --- a/scene/main/shader_globals_override.h +++ b/scene/main/shader_globals_override.h @@ -58,7 +58,7 @@ protected: static void _bind_methods(); public: - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; ShaderGlobalsOverride(); }; diff --git a/scene/main/timer.cpp b/scene/main/timer.cpp index bb9359ef59..210b60171a 100644 --- a/scene/main/timer.cpp +++ b/scene/main/timer.cpp @@ -180,8 +180,8 @@ void Timer::_set_process(bool p_process, bool p_force) { processing = p_process; } -TypedArray<String> Timer::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray Timer::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (wait_time < 0.05 - CMP_EPSILON) { warnings.push_back(RTR("Very low timer wait times (< 0.05 seconds) may behave in significantly different ways depending on the rendered or physics frame rate.\nConsider using a script's process loop instead of relying on a Timer for very low wait times.")); diff --git a/scene/main/timer.h b/scene/main/timer.h index 8785d31a8a..53503e31b2 100644 --- a/scene/main/timer.h +++ b/scene/main/timer.h @@ -73,7 +73,7 @@ public: double get_time_left() const; - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; void set_timer_process_callback(TimerProcessCallback p_callback); TimerProcessCallback get_timer_process_callback() const; diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 0080e899c3..05b4f181c2 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -190,14 +190,7 @@ void Viewport::_sub_window_register(Window *p_window) { } void Viewport::_sub_window_update(Window *p_window) { - int index = -1; - for (int i = 0; i < gui.sub_windows.size(); i++) { - if (gui.sub_windows[i].window == p_window) { - index = i; - break; - } - } - + int index = _sub_window_find(p_window); ERR_FAIL_COND(index == -1); const SubWindow &sw = gui.sub_windows[index]; @@ -257,14 +250,7 @@ void Viewport::_sub_window_grab_focus(Window *p_window) { return; } - int index = -1; - for (int i = 0; i < gui.sub_windows.size(); i++) { - if (gui.sub_windows[i].window == p_window) { - index = i; - break; - } - } - + int index = _sub_window_find(p_window); ERR_FAIL_COND(index == -1); if (p_window->get_flag(Window::FLAG_NO_FOCUS)) { @@ -312,13 +298,11 @@ void Viewport::_sub_window_grab_focus(Window *p_window) { } void Viewport::_sub_window_remove(Window *p_window) { - for (int i = 0; i < gui.sub_windows.size(); i++) { - if (gui.sub_windows[i].window == p_window) { - RS::get_singleton()->free(gui.sub_windows[i].canvas_item); - gui.sub_windows.remove_at(i); - break; - } - } + int index = _sub_window_find(p_window); + ERR_FAIL_COND(index == -1); + + RS::get_singleton()->free(gui.sub_windows[index].canvas_item); + gui.sub_windows.remove_at(index); if (gui.sub_windows.size() == 0) { RS::get_singleton()->free(subwindow_canvas); @@ -326,27 +310,46 @@ void Viewport::_sub_window_remove(Window *p_window) { } if (gui.subwindow_focused == p_window) { + Window *new_focused_window; Window *parent_visible = p_window->get_parent_visible_window(); gui.subwindow_drag = SUB_WINDOW_DRAG_DISABLED; gui.subwindow_focused->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_OUT); - if (parent_visible && parent_visible != this) { - gui.subwindow_focused = parent_visible; - gui.subwindow_focused->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_IN); + if (parent_visible) { + new_focused_window = parent_visible; } else { - gui.subwindow_focused = nullptr; - Window *this_window = Object::cast_to<Window>(this); - if (this_window) { - this_window->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_IN); + new_focused_window = Object::cast_to<Window>(this); + } + + if (new_focused_window) { + int new_focused_index = _sub_window_find(new_focused_window); + if (new_focused_index != -1) { + gui.subwindow_focused = new_focused_window; + } else { + gui.subwindow_focused = nullptr; } + + new_focused_window->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_IN); + } else { + gui.subwindow_focused = nullptr; } } RenderingServer::get_singleton()->viewport_set_parent_viewport(p_window->viewport, p_window->parent ? p_window->parent->viewport : RID()); } +int Viewport::_sub_window_find(Window *p_window) { + for (int i = 0; i < gui.sub_windows.size(); i++) { + if (gui.sub_windows[i].window == p_window) { + return i; + } + } + + return -1; +} + void Viewport::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { @@ -792,19 +795,46 @@ void Viewport::_set_size(const Size2i &p_size, const Size2i &p_size_2d_override, stretch_transform = p_stretch_transform; to_screen_rect = p_to_screen_rect; - if (p_allocated) { - RS::get_singleton()->viewport_set_size(viewport, size.width, size.height); - } else { - RS::get_singleton()->viewport_set_size(viewport, 0, 0); - } +#ifndef _3D_DISABLED + if (!use_xr) { +#endif + + if (p_allocated) { + RS::get_singleton()->viewport_set_size(viewport, size.width, size.height); + } else { + RS::get_singleton()->viewport_set_size(viewport, 0, 0); + } + +#ifndef _3D_DISABLED + } // if (!use_xr) +#endif + _update_global_transform(); + update_configuration_warnings(); update_canvas_items(); + for (ViewportTexture *E : viewport_textures) { + E->emit_changed(); + } + emit_signal(SNAME("size_changed")); } Size2i Viewport::_get_size() const { +#ifndef _3D_DISABLED + if (use_xr) { + if (XRServer::get_singleton() != nullptr) { + Ref<XRInterface> xr_interface = XRServer::get_singleton()->get_primary_interface(); + if (xr_interface.is_valid() && xr_interface->is_initialized()) { + Size2 xr_size = xr_interface->get_render_target_size(); + return (Size2i)xr_size; + } + } + return Size2i(); + } +#endif // _3D_DISABLED + return size; } @@ -1022,7 +1052,7 @@ void Viewport::_update_canvas_items(Node *p_node) { CanvasItem *ci = Object::cast_to<CanvasItem>(p_node); if (ci) { - ci->update(); + ci->queue_redraw(); } } @@ -1037,44 +1067,44 @@ Ref<ViewportTexture> Viewport::get_texture() const { return default_texture; } -void Viewport::set_shadow_atlas_size(int p_size) { - shadow_atlas_size = p_size; - RS::get_singleton()->viewport_set_shadow_atlas_size(viewport, p_size, shadow_atlas_16_bits); +void Viewport::set_positional_shadow_atlas_size(int p_size) { + positional_shadow_atlas_size = p_size; + RS::get_singleton()->viewport_set_positional_shadow_atlas_size(viewport, p_size, positional_shadow_atlas_16_bits); } -int Viewport::get_shadow_atlas_size() const { - return shadow_atlas_size; +int Viewport::get_positional_shadow_atlas_size() const { + return positional_shadow_atlas_size; } -void Viewport::set_shadow_atlas_16_bits(bool p_16_bits) { - if (shadow_atlas_16_bits == p_16_bits) { +void Viewport::set_positional_shadow_atlas_16_bits(bool p_16_bits) { + if (positional_shadow_atlas_16_bits == p_16_bits) { return; } - shadow_atlas_16_bits = p_16_bits; - RS::get_singleton()->viewport_set_shadow_atlas_size(viewport, shadow_atlas_size, shadow_atlas_16_bits); + positional_shadow_atlas_16_bits = p_16_bits; + RS::get_singleton()->viewport_set_positional_shadow_atlas_size(viewport, positional_shadow_atlas_size, positional_shadow_atlas_16_bits); } -bool Viewport::get_shadow_atlas_16_bits() const { - return shadow_atlas_16_bits; +bool Viewport::get_positional_shadow_atlas_16_bits() const { + return positional_shadow_atlas_16_bits; } -void Viewport::set_shadow_atlas_quadrant_subdiv(int p_quadrant, ShadowAtlasQuadrantSubdiv p_subdiv) { +void Viewport::set_positional_shadow_atlas_quadrant_subdiv(int p_quadrant, PositionalShadowAtlasQuadrantSubdiv p_subdiv) { ERR_FAIL_INDEX(p_quadrant, 4); ERR_FAIL_INDEX(p_subdiv, SHADOW_ATLAS_QUADRANT_SUBDIV_MAX); - if (shadow_atlas_quadrant_subdiv[p_quadrant] == p_subdiv) { + if (positional_shadow_atlas_quadrant_subdiv[p_quadrant] == p_subdiv) { return; } - shadow_atlas_quadrant_subdiv[p_quadrant] = p_subdiv; + positional_shadow_atlas_quadrant_subdiv[p_quadrant] = p_subdiv; static const int subdiv[SHADOW_ATLAS_QUADRANT_SUBDIV_MAX] = { 0, 1, 4, 16, 64, 256, 1024 }; - RS::get_singleton()->viewport_set_shadow_atlas_quadrant_subdivision(viewport, p_quadrant, subdiv[p_subdiv]); + RS::get_singleton()->viewport_set_positional_shadow_atlas_quadrant_subdivision(viewport, p_quadrant, subdiv[p_subdiv]); } -Viewport::ShadowAtlasQuadrantSubdiv Viewport::get_shadow_atlas_quadrant_subdiv(int p_quadrant) const { +Viewport::PositionalShadowAtlasQuadrantSubdiv Viewport::get_positional_shadow_atlas_quadrant_subdiv(int p_quadrant) const { ERR_FAIL_INDEX_V(p_quadrant, 4, SHADOW_ATLAS_QUADRANT_SUBDIV_DISABLED); - return shadow_atlas_quadrant_subdiv[p_quadrant]; + return positional_shadow_atlas_quadrant_subdiv[p_quadrant]; } Transform2D Viewport::_get_input_pre_xform() const { @@ -1103,7 +1133,7 @@ Vector2 Viewport::get_mouse_position() const { void Viewport::warp_mouse(const Vector2 &p_position) { Transform2D xform = get_screen_transform(); - Vector2 gpos = xform.xform(p_position).round(); + Vector2 gpos = xform.xform(p_position); Input::get_singleton()->warp_mouse(gpos); } @@ -1215,7 +1245,7 @@ void Viewport::_gui_show_tooltip() { panel->connect("mouse_entered", callable_mp(this, &Viewport::_gui_cancel_tooltip)); } - base_tooltip->set_anchors_and_offsets_preset(Control::PRESET_WIDE); + base_tooltip->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); panel->set_transient(true); panel->set_flag(Window::FLAG_NO_FOCUS, true); @@ -1234,13 +1264,23 @@ void Viewport::_gui_show_tooltip() { Rect2i vr = window->get_usable_parent_rect(); if (r.size.x + r.position.x > vr.size.x + vr.position.x) { - r.position.x = vr.position.x + vr.size.x - r.size.x; + // Place it in the opposite direction. If it fails, just hug the border. + r.position.x = gui.tooltip_pos.x - r.size.x - tooltip_offset.x; + + if (r.position.x < vr.position.x) { + r.position.x = vr.position.x + vr.size.x - r.size.x; + } } else if (r.position.x < vr.position.x) { r.position.x = vr.position.x; } if (r.size.y + r.position.y > vr.size.y + vr.position.y) { - r.position.y = vr.position.y + vr.size.y - r.size.y; + // Same as above. + r.position.y = gui.tooltip_pos.y - r.size.y - tooltip_offset.y; + + if (r.position.y < vr.position.y) { + r.position.y = vr.position.y + vr.size.y - r.size.y; + } } else if (r.position.y < vr.position.y) { r.position.y = vr.position.y; } @@ -1536,7 +1576,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { gui.drag_preview_id = ObjectID(); } _propagate_viewport_notification(this, NOTIFICATION_DRAG_END); - // Change mouse accordingly. + get_base_window()->update_mouse_cursor_shape(); } _gui_cancel_tooltip(); @@ -1557,7 +1597,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { gui.dragging = false; gui.drag_mouse_over = nullptr; _propagate_viewport_notification(this, NOTIFICATION_DRAG_END); - // Change mouse accordingly. + get_base_window()->update_mouse_cursor_shape(); } gui.mouse_focus_mask &= ~mouse_button_to_mask(mb->get_button_index()); // Remove from mask. @@ -1698,15 +1738,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { is_tooltip_shown = true; } } else { - Variant t = gui.tooltip_popup->call("get_tooltip_text"); - - if (t.get_type() == Variant::STRING) { - if (tooltip == String(t)) { - is_tooltip_shown = true; - } - } else { - is_tooltip_shown = true; // Nothing to compare against, likely using custom control, so if it changes there is nothing we can do. - } + is_tooltip_shown = true; // Nothing to compare against, likely using custom control, so if it changes there is nothing we can do. } } else { _gui_cancel_tooltip(); @@ -1865,7 +1897,9 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { } } - DisplayServer::get_singleton()->cursor_set_shape(ds_cursor_shape); + if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CURSOR_SHAPE)) { + DisplayServer::get_singleton()->cursor_set_shape(ds_cursor_shape); + } } Ref<InputEventScreenTouch> touch_event = p_event; @@ -2091,7 +2125,7 @@ void Viewport::_gui_set_drag_preview(Control *p_base, Control *p_control) { p_control->set_as_top_level(true); p_control->set_position(gui.last_mouse_pos); p_base->get_root_parent_control()->add_child(p_control); // Add as child of viewport. - p_control->raise(); + p_control->move_to_front(); gui.drag_preview_id = p_control->get_instance_id(); } @@ -2192,7 +2226,7 @@ void Viewport::_gui_control_grab_focus(Control *p_control) { gui.key_focus = p_control; emit_signal(SNAME("gui_focus_changed"), p_control); p_control->notification(Control::NOTIFICATION_FOCUS_ENTER); - p_control->update(); + p_control->queue_redraw(); } void Viewport::_gui_accept_event() { @@ -2488,17 +2522,20 @@ bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) { if (gui.subwindow_drag == SUB_WINDOW_DRAG_RESIZE) { Vector2i diff = mm->get_position() - gui.subwindow_drag_from; Size2i min_size = gui.subwindow_focused->get_min_size(); + + Size2i min_size_adjusted = min_size; if (gui.subwindow_focused->is_wrapping_controls()) { Size2i cms = gui.subwindow_focused->get_contents_minimum_size(); - min_size.x = MAX(cms.x, min_size.x); - min_size.y = MAX(cms.y, min_size.y); + min_size_adjusted.x = MAX(cms.x, min_size.x); + min_size_adjusted.y = MAX(cms.y, min_size.y); } - min_size.x = MAX(min_size.x, 1); - min_size.y = MAX(min_size.y, 1); + + min_size_adjusted.x = MAX(min_size_adjusted.x, 1); + min_size_adjusted.y = MAX(min_size_adjusted.y, 1); Rect2i r = gui.subwindow_resize_from_rect; - Size2i limit = r.size - min_size; + Size2i limit = r.size - min_size_adjusted; switch (gui.subwindow_resize_mode) { case SUB_WINDOW_RESIZE_TOP_LEFT: { @@ -2553,6 +2590,19 @@ bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) { } } + Size2i max_size = gui.subwindow_focused->get_max_size(); + if ((max_size.x > 0 || max_size.y > 0) && (max_size.x >= min_size.x && max_size.y >= min_size.y)) { + max_size.x = MAX(max_size.x, 1); + max_size.y = MAX(max_size.y, 1); + + if (r.size.x > max_size.x) { + r.size.x = max_size.x; + } + if (r.size.y > max_size.y) { + r.size.y = max_size.y; + } + } + gui.subwindow_focused->_rect_changed_callback(r); } @@ -2590,7 +2640,7 @@ bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) { Ref<Texture2D> close_icon = sw.window->get_theme_icon(SNAME("close")); Rect2 close_rect; - close_rect.position = Vector2(r.position.x + r.size.x - close_v_ofs, r.position.y - close_h_ofs); + close_rect.position = Vector2(r.position.x + r.size.x - close_h_ofs, r.position.y - close_v_ofs); close_rect.size = close_icon->get_size(); if (gui.subwindow_focused != sw.window) { @@ -2658,7 +2708,9 @@ bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) { DisplayServer::CURSOR_FDIAGSIZE }; - DisplayServer::get_singleton()->cursor_set_shape(shapes[resize]); + if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CURSOR_SHAPE)) { + DisplayServer::get_singleton()->cursor_set_shape(shapes[resize]); + } return true; // Reserved for showing the resize cursor. } @@ -2747,7 +2799,7 @@ void Viewport::push_unhandled_input(const Ref<InputEvent> &p_event, bool p_local } // Shortcut Input. - if (Object::cast_to<InputEventKey>(*ev) != nullptr || Object::cast_to<InputEventShortcut>(*ev) != nullptr) { + if (Object::cast_to<InputEventKey>(*ev) != nullptr || Object::cast_to<InputEventShortcut>(*ev) != nullptr || Object::cast_to<InputEventJoypadButton>(*ev) != nullptr) { get_tree()->_call_input_pause(shortcut_input_group, SceneTree::CALL_INPUT_TYPE_SHORTCUT_INPUT, ev, this); } @@ -2820,11 +2872,11 @@ Variant Viewport::gui_get_drag_data() const { return gui.drag_data; } -TypedArray<String> Viewport::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray Viewport::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); - if (size.x == 0 || size.y == 0) { - warnings.push_back(RTR("Viewport size must be greater than 0 to render anything.")); + if (size.x <= 1 || size.y <= 1) { + warnings.push_back(RTR("The Viewport size must be greater than or equal to 2 pixels on both dimensions to render anything.")); } return warnings; } @@ -2842,7 +2894,7 @@ void Viewport::gui_release_focus() { Control *f = gui.key_focus; gui.key_focus = nullptr; f->notification(Control::NOTIFICATION_FOCUS_EXIT, true); - f->update(); + f->queue_redraw(); } } @@ -2850,17 +2902,30 @@ Control *Viewport::gui_get_focus_owner() { return gui.key_focus; } -void Viewport::set_msaa(MSAA p_msaa) { +void Viewport::set_msaa_2d(MSAA p_msaa) { ERR_FAIL_INDEX(p_msaa, MSAA_MAX); - if (msaa == p_msaa) { + if (msaa_2d == p_msaa) { return; } - msaa = p_msaa; - RS::get_singleton()->viewport_set_msaa(viewport, RS::ViewportMSAA(p_msaa)); + msaa_2d = p_msaa; + RS::get_singleton()->viewport_set_msaa_2d(viewport, RS::ViewportMSAA(p_msaa)); } -Viewport::MSAA Viewport::get_msaa() const { - return msaa; +Viewport::MSAA Viewport::get_msaa_2d() const { + return msaa_2d; +} + +void Viewport::set_msaa_3d(MSAA p_msaa) { + ERR_FAIL_INDEX(p_msaa, MSAA_MAX); + if (msaa_3d == p_msaa) { + return; + } + msaa_3d = p_msaa; + RS::get_singleton()->viewport_set_msaa_3d(viewport, RS::ViewportMSAA(p_msaa)); +} + +Viewport::MSAA Viewport::get_msaa_3d() const { + return msaa_3d; } void Viewport::set_screen_space_aa(ScreenSpaceAA p_screen_space_aa) { @@ -3080,6 +3145,41 @@ Viewport::DefaultCanvasItemTextureRepeat Viewport::get_default_canvas_item_textu return default_canvas_item_texture_repeat; } +void Viewport::set_vrs_mode(Viewport::VRSMode p_vrs_mode) { + // Note, set this even if not supported on this hardware, it will only be used if it is but we want to save the value as set by the user. + vrs_mode = p_vrs_mode; + + switch (p_vrs_mode) { + case VRS_TEXTURE: { + RS::get_singleton()->viewport_set_vrs_mode(viewport, RS::VIEWPORT_VRS_TEXTURE); + } break; + case VRS_XR: { + RS::get_singleton()->viewport_set_vrs_mode(viewport, RS::VIEWPORT_VRS_XR); + } break; + default: { + RS::get_singleton()->viewport_set_vrs_mode(viewport, RS::VIEWPORT_VRS_DISABLED); + } break; + } + + notify_property_list_changed(); +} + +Viewport::VRSMode Viewport::get_vrs_mode() const { + return vrs_mode; +} + +void Viewport::set_vrs_texture(Ref<Texture2D> p_texture) { + vrs_texture = p_texture; + + // TODO need to add something here in case the RID changes + RID tex = p_texture.is_valid() ? p_texture->get_rid() : RID(); + RS::get_singleton()->viewport_set_vrs_texture(viewport, tex); +} + +Ref<Texture2D> Viewport::get_vrs_texture() const { + return vrs_texture; +} + DisplayServer::WindowID Viewport::get_window_id() const { return DisplayServer::MAIN_WINDOW_ID; } @@ -3534,9 +3634,20 @@ void Viewport::_propagate_exit_world_3d(Node *p_node) { } void Viewport::set_use_xr(bool p_use_xr) { - use_xr = p_use_xr; + if (use_xr != p_use_xr) { + use_xr = p_use_xr; + + RS::get_singleton()->viewport_set_use_xr(viewport, use_xr); - RS::get_singleton()->viewport_set_use_xr(viewport, use_xr); + if (!use_xr) { + // Set viewport to previous size when exiting XR. + if (size_allocated) { + RS::get_singleton()->viewport_set_size(viewport, size.width, size.height); + } else { + RS::get_singleton()->viewport_set_size(viewport, 0, 0); + } + } + } } bool Viewport::is_using_xr() { @@ -3586,17 +3697,17 @@ float Viewport::get_fsr_sharpness() const { return fsr_sharpness; } -void Viewport::set_fsr_mipmap_bias(float p_fsr_mipmap_bias) { - if (fsr_mipmap_bias == p_fsr_mipmap_bias) { +void Viewport::set_texture_mipmap_bias(float p_texture_mipmap_bias) { + if (texture_mipmap_bias == p_texture_mipmap_bias) { return; } - fsr_mipmap_bias = p_fsr_mipmap_bias; - RS::get_singleton()->viewport_set_fsr_mipmap_bias(viewport, p_fsr_mipmap_bias); + texture_mipmap_bias = p_texture_mipmap_bias; + RS::get_singleton()->viewport_set_texture_mipmap_bias(viewport, p_texture_mipmap_bias); } -float Viewport::get_fsr_mipmap_bias() const { - return fsr_mipmap_bias; +float Viewport::get_texture_mipmap_bias() const { + return texture_mipmap_bias; } #endif // _3D_DISABLED @@ -3617,8 +3728,11 @@ void Viewport::_bind_methods() { ClassDB::bind_method(D_METHOD("set_transparent_background", "enable"), &Viewport::set_transparent_background); ClassDB::bind_method(D_METHOD("has_transparent_background"), &Viewport::has_transparent_background); - ClassDB::bind_method(D_METHOD("set_msaa", "msaa"), &Viewport::set_msaa); - ClassDB::bind_method(D_METHOD("get_msaa"), &Viewport::get_msaa); + ClassDB::bind_method(D_METHOD("set_msaa_2d", "msaa"), &Viewport::set_msaa_2d); + ClassDB::bind_method(D_METHOD("get_msaa_2d"), &Viewport::get_msaa_2d); + + ClassDB::bind_method(D_METHOD("set_msaa_3d", "msaa"), &Viewport::set_msaa_3d); + ClassDB::bind_method(D_METHOD("get_msaa_3d"), &Viewport::get_msaa_3d); ClassDB::bind_method(D_METHOD("set_screen_space_aa", "screen_space_aa"), &Viewport::set_screen_space_aa); ClassDB::bind_method(D_METHOD("get_screen_space_aa"), &Viewport::get_screen_space_aa); @@ -3667,11 +3781,11 @@ void Viewport::_bind_methods() { ClassDB::bind_method(D_METHOD("_gui_remove_focus_for_window"), &Viewport::_gui_remove_focus_for_window); ClassDB::bind_method(D_METHOD("_post_gui_grab_click_focus"), &Viewport::_post_gui_grab_click_focus); - ClassDB::bind_method(D_METHOD("set_shadow_atlas_size", "size"), &Viewport::set_shadow_atlas_size); - ClassDB::bind_method(D_METHOD("get_shadow_atlas_size"), &Viewport::get_shadow_atlas_size); + ClassDB::bind_method(D_METHOD("set_positional_shadow_atlas_size", "size"), &Viewport::set_positional_shadow_atlas_size); + ClassDB::bind_method(D_METHOD("get_positional_shadow_atlas_size"), &Viewport::get_positional_shadow_atlas_size); - ClassDB::bind_method(D_METHOD("set_shadow_atlas_16_bits", "enable"), &Viewport::set_shadow_atlas_16_bits); - ClassDB::bind_method(D_METHOD("get_shadow_atlas_16_bits"), &Viewport::get_shadow_atlas_16_bits); + ClassDB::bind_method(D_METHOD("set_positional_shadow_atlas_16_bits", "enable"), &Viewport::set_positional_shadow_atlas_16_bits); + ClassDB::bind_method(D_METHOD("get_positional_shadow_atlas_16_bits"), &Viewport::get_positional_shadow_atlas_16_bits); ClassDB::bind_method(D_METHOD("set_snap_controls_to_pixels", "enabled"), &Viewport::set_snap_controls_to_pixels); ClassDB::bind_method(D_METHOD("is_snap_controls_to_pixels_enabled"), &Viewport::is_snap_controls_to_pixels_enabled); @@ -3682,8 +3796,8 @@ void Viewport::_bind_methods() { ClassDB::bind_method(D_METHOD("set_snap_2d_vertices_to_pixel", "enabled"), &Viewport::set_snap_2d_vertices_to_pixel); ClassDB::bind_method(D_METHOD("is_snap_2d_vertices_to_pixel_enabled"), &Viewport::is_snap_2d_vertices_to_pixel_enabled); - ClassDB::bind_method(D_METHOD("set_shadow_atlas_quadrant_subdiv", "quadrant", "subdiv"), &Viewport::set_shadow_atlas_quadrant_subdiv); - ClassDB::bind_method(D_METHOD("get_shadow_atlas_quadrant_subdiv", "quadrant"), &Viewport::get_shadow_atlas_quadrant_subdiv); + ClassDB::bind_method(D_METHOD("set_positional_shadow_atlas_quadrant_subdiv", "quadrant", "subdiv"), &Viewport::set_positional_shadow_atlas_quadrant_subdiv); + ClassDB::bind_method(D_METHOD("get_positional_shadow_atlas_quadrant_subdiv", "quadrant"), &Viewport::get_positional_shadow_atlas_quadrant_subdiv); ClassDB::bind_method(D_METHOD("set_input_as_handled"), &Viewport::set_input_as_handled); ClassDB::bind_method(D_METHOD("is_input_handled"), &Viewport::is_input_handled); @@ -3738,8 +3852,14 @@ void Viewport::_bind_methods() { ClassDB::bind_method(D_METHOD("set_fsr_sharpness", "fsr_sharpness"), &Viewport::set_fsr_sharpness); ClassDB::bind_method(D_METHOD("get_fsr_sharpness"), &Viewport::get_fsr_sharpness); - ClassDB::bind_method(D_METHOD("set_fsr_mipmap_bias", "fsr_mipmap_bias"), &Viewport::set_fsr_mipmap_bias); - ClassDB::bind_method(D_METHOD("get_fsr_mipmap_bias"), &Viewport::get_fsr_mipmap_bias); + ClassDB::bind_method(D_METHOD("set_texture_mipmap_bias", "texture_mipmap_bias"), &Viewport::set_texture_mipmap_bias); + ClassDB::bind_method(D_METHOD("get_texture_mipmap_bias"), &Viewport::get_texture_mipmap_bias); + + ClassDB::bind_method(D_METHOD("set_vrs_mode", "mode"), &Viewport::set_vrs_mode); + ClassDB::bind_method(D_METHOD("get_vrs_mode"), &Viewport::get_vrs_mode); + + ClassDB::bind_method(D_METHOD("set_vrs_texture", "texture"), &Viewport::set_vrs_texture); + ClassDB::bind_method(D_METHOD("get_vrs_texture"), &Viewport::get_vrs_texture); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disable_3d"), "set_disable_3d", "is_3d_disabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_xr"), "set_use_xr", "is_using_xr"); @@ -3752,7 +3872,8 @@ void Viewport::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "snap_2d_transforms_to_pixel"), "set_snap_2d_transforms_to_pixel", "is_snap_2d_transforms_to_pixel_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "snap_2d_vertices_to_pixel"), "set_snap_2d_vertices_to_pixel", "is_snap_2d_vertices_to_pixel_enabled"); ADD_GROUP("Rendering", ""); - ADD_PROPERTY(PropertyInfo(Variant::INT, "msaa", PROPERTY_HINT_ENUM, String::utf8("Disabled (Fastest),2× (Average),4× (Slow),8× (Slowest)")), "set_msaa", "get_msaa"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "msaa_2d", PROPERTY_HINT_ENUM, String::utf8("Disabled (Fastest),2× (Average),4× (Slow),8× (Slowest)")), "set_msaa_2d", "get_msaa_2d"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "msaa_3d", PROPERTY_HINT_ENUM, String::utf8("Disabled (Fastest),2× (Average),4× (Slow),8× (Slowest)")), "set_msaa_3d", "get_msaa_3d"); ADD_PROPERTY(PropertyInfo(Variant::INT, "screen_space_aa", PROPERTY_HINT_ENUM, "Disabled (Fastest),FXAA (Fast)"), "set_screen_space_aa", "get_screen_space_aa"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_taa"), "set_use_taa", "is_using_taa"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_debanding"), "set_use_debanding", "is_using_debanding"); @@ -3763,9 +3884,12 @@ void Viewport::_bind_methods() { ADD_GROUP("Scaling 3D", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "scaling_3d_mode", PROPERTY_HINT_ENUM, "Bilinear (Fastest),FSR 1.0 (Fast)"), "set_scaling_3d_mode", "get_scaling_3d_mode"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scaling_3d_scale", PROPERTY_HINT_RANGE, "0.25,2.0,0.01"), "set_scaling_3d_scale", "get_scaling_3d_scale"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fsr_mipmap_bias", PROPERTY_HINT_RANGE, "-2,2,0.1"), "set_fsr_mipmap_bias", "get_fsr_mipmap_bias"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "texture_mipmap_bias", PROPERTY_HINT_RANGE, "-2,2,0.001"), "set_texture_mipmap_bias", "get_texture_mipmap_bias"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fsr_sharpness", PROPERTY_HINT_RANGE, "0,2,0.1"), "set_fsr_sharpness", "get_fsr_sharpness"); #endif + ADD_GROUP("Variable Rate Shading", "vrs_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "vrs_mode", PROPERTY_HINT_ENUM, "Disabled,Texture,Depth buffer,XR"), "set_vrs_mode", "get_vrs_mode"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "vrs_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_vrs_texture", "get_vrs_texture"); ADD_GROUP("Canvas Items", "canvas_item_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "canvas_item_default_texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,Linear Mipmap,Nearest Mipmap"), "set_default_canvas_item_texture_filter", "get_default_canvas_item_texture_filter"); ADD_PROPERTY(PropertyInfo(Variant::INT, "canvas_item_default_texture_repeat", PROPERTY_HINT_ENUM, "Disabled,Enabled,Mirror"), "set_default_canvas_item_texture_repeat", "get_default_canvas_item_texture_repeat"); @@ -3783,13 +3907,13 @@ void Viewport::_bind_methods() { ADD_GROUP("SDF", "sdf_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "sdf_oversize", PROPERTY_HINT_ENUM, "100%,120%,150%,200%"), "set_sdf_oversize", "get_sdf_oversize"); ADD_PROPERTY(PropertyInfo(Variant::INT, "sdf_scale", PROPERTY_HINT_ENUM, "100%,50%,25%"), "set_sdf_scale", "get_sdf_scale"); - ADD_GROUP("Shadow Atlas", "shadow_atlas_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "shadow_atlas_size"), "set_shadow_atlas_size", "get_shadow_atlas_size"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shadow_atlas_16_bits"), "set_shadow_atlas_16_bits", "get_shadow_atlas_16_bits"); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "shadow_atlas_quad_0", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows"), "set_shadow_atlas_quadrant_subdiv", "get_shadow_atlas_quadrant_subdiv", 0); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "shadow_atlas_quad_1", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows"), "set_shadow_atlas_quadrant_subdiv", "get_shadow_atlas_quadrant_subdiv", 1); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "shadow_atlas_quad_2", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows"), "set_shadow_atlas_quadrant_subdiv", "get_shadow_atlas_quadrant_subdiv", 2); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "shadow_atlas_quad_3", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows"), "set_shadow_atlas_quadrant_subdiv", "get_shadow_atlas_quadrant_subdiv", 3); + ADD_GROUP("Positional Shadow Atlas", "positional_shadow_atlas_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "positional_shadow_atlas_size"), "set_positional_shadow_atlas_size", "get_positional_shadow_atlas_size"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "positional_shadow_atlas_16_bits"), "set_positional_shadow_atlas_16_bits", "get_positional_shadow_atlas_16_bits"); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "positional_shadow_atlas_quad_0", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows"), "set_positional_shadow_atlas_quadrant_subdiv", "get_positional_shadow_atlas_quadrant_subdiv", 0); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "positional_shadow_atlas_quad_1", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows"), "set_positional_shadow_atlas_quadrant_subdiv", "get_positional_shadow_atlas_quadrant_subdiv", 1); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "positional_shadow_atlas_quad_2", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows"), "set_positional_shadow_atlas_quadrant_subdiv", "get_positional_shadow_atlas_quadrant_subdiv", 2); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "positional_shadow_atlas_quad_3", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows"), "set_positional_shadow_atlas_quadrant_subdiv", "get_positional_shadow_atlas_quadrant_subdiv", 3); ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "canvas_transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_canvas_transform", "get_canvas_transform"); ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "global_canvas_transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_global_canvas_transform", "get_global_canvas_transform"); @@ -3876,6 +4000,17 @@ void Viewport::_bind_methods() { BIND_ENUM_CONSTANT(SDF_SCALE_50_PERCENT); BIND_ENUM_CONSTANT(SDF_SCALE_25_PERCENT); BIND_ENUM_CONSTANT(SDF_SCALE_MAX); + + BIND_ENUM_CONSTANT(VRS_DISABLED); + BIND_ENUM_CONSTANT(VRS_TEXTURE); + BIND_ENUM_CONSTANT(VRS_XR); + BIND_ENUM_CONSTANT(VRS_MAX); +} + +void Viewport::_validate_property(PropertyInfo &p_property) const { + if (vrs_mode != VRS_TEXTURE && (p_property.name == "vrs_texture")) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } } Viewport::Viewport() { @@ -3891,15 +4026,15 @@ Viewport::Viewport() { canvas_layers.insert(nullptr); // This eases picking code (interpreted as the canvas of the Viewport). - set_shadow_atlas_size(shadow_atlas_size); + set_positional_shadow_atlas_size(positional_shadow_atlas_size); for (int i = 0; i < 4; i++) { - shadow_atlas_quadrant_subdiv[i] = SHADOW_ATLAS_QUADRANT_SUBDIV_MAX; + positional_shadow_atlas_quadrant_subdiv[i] = SHADOW_ATLAS_QUADRANT_SUBDIV_MAX; } - set_shadow_atlas_quadrant_subdiv(0, SHADOW_ATLAS_QUADRANT_SUBDIV_4); - set_shadow_atlas_quadrant_subdiv(1, SHADOW_ATLAS_QUADRANT_SUBDIV_4); - set_shadow_atlas_quadrant_subdiv(2, SHADOW_ATLAS_QUADRANT_SUBDIV_16); - set_shadow_atlas_quadrant_subdiv(3, SHADOW_ATLAS_QUADRANT_SUBDIV_64); + set_positional_shadow_atlas_quadrant_subdiv(0, SHADOW_ATLAS_QUADRANT_SUBDIV_4); + set_positional_shadow_atlas_quadrant_subdiv(1, SHADOW_ATLAS_QUADRANT_SUBDIV_4); + set_positional_shadow_atlas_quadrant_subdiv(2, SHADOW_ATLAS_QUADRANT_SUBDIV_16); + set_positional_shadow_atlas_quadrant_subdiv(3, SHADOW_ATLAS_QUADRANT_SUBDIV_64); set_mesh_lod_threshold(mesh_lod_threshold); @@ -3923,8 +4058,8 @@ Viewport::Viewport() { float fsr_sharpness = GLOBAL_GET("rendering/scaling_3d/fsr_sharpness"); set_fsr_sharpness(fsr_sharpness); - float fsr_mipmap_bias = GLOBAL_GET("rendering/scaling_3d/fsr_mipmap_bias"); - set_fsr_mipmap_bias(fsr_mipmap_bias); + float texture_mipmap_bias = GLOBAL_GET("rendering/textures/default_filters/texture_mipmap_bias"); + set_texture_mipmap_bias(texture_mipmap_bias); #endif // _3D_DISABLED set_sdf_oversize(sdf_oversize); // Set to server. diff --git a/scene/main/viewport.h b/scene/main/viewport.h index a22a2acf49..471dc41246 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -95,7 +95,7 @@ public: SCALING_3D_MODE_MAX }; - enum ShadowAtlasQuadrantSubdiv { + enum PositionalShadowAtlasQuadrantSubdiv { SHADOW_ATLAS_QUADRANT_SUBDIV_DISABLED, SHADOW_ATLAS_QUADRANT_SUBDIV_1, SHADOW_ATLAS_QUADRANT_SUBDIV_4, @@ -197,6 +197,13 @@ public: SUBWINDOW_CANVAS_LAYER = 1024 }; + enum VRSMode { + VRS_DISABLED, + VRS_TEXTURE, + VRS_XR, + VRS_MAX + }; + private: friend class ViewportTexture; @@ -286,18 +293,19 @@ private: DebugDraw debug_draw = DEBUG_DRAW_DISABLED; - int shadow_atlas_size = 2048; - bool shadow_atlas_16_bits = true; - ShadowAtlasQuadrantSubdiv shadow_atlas_quadrant_subdiv[4]; + int positional_shadow_atlas_size = 2048; + bool positional_shadow_atlas_16_bits = true; + PositionalShadowAtlasQuadrantSubdiv positional_shadow_atlas_quadrant_subdiv[4]; - MSAA msaa = MSAA_DISABLED; + MSAA msaa_2d = MSAA_DISABLED; + MSAA msaa_3d = MSAA_DISABLED; ScreenSpaceAA screen_space_aa = SCREEN_SPACE_AA_DISABLED; bool use_taa = false; Scaling3DMode scaling_3d_mode = SCALING_3D_MODE_BILINEAR; float scaling_3d_scale = 1.0; float fsr_sharpness = 0.2f; - float fsr_mipmap_bias = 0.0f; + float texture_mipmap_bias = 0.0f; bool use_debanding = false; float mesh_lod_threshold = 1.0; bool use_occlusion_culling = false; @@ -333,6 +341,10 @@ private: RID canvas_item; }; + // VRS + VRSMode vrs_mode = VRS_DISABLED; + Ref<Texture2D> vrs_texture; + struct GUI { // info used when this is a window @@ -449,6 +461,7 @@ private: void _sub_window_update(Window *p_window); void _sub_window_grab_focus(Window *p_window); void _sub_window_remove(Window *p_window); + int _sub_window_find(Window *p_window); bool _sub_windows_forward_input(const Ref<InputEvent> &p_event); SubWindowResize _sub_window_get_resize_margin(Window *p_subwindow, const Point2 &p_point); @@ -502,17 +515,20 @@ public: Ref<ViewportTexture> get_texture() const; - void set_shadow_atlas_size(int p_size); - int get_shadow_atlas_size() const; + void set_positional_shadow_atlas_size(int p_size); + int get_positional_shadow_atlas_size() const; + + void set_positional_shadow_atlas_16_bits(bool p_16_bits); + bool get_positional_shadow_atlas_16_bits() const; - void set_shadow_atlas_16_bits(bool p_16_bits); - bool get_shadow_atlas_16_bits() const; + void set_positional_shadow_atlas_quadrant_subdiv(int p_quadrant, PositionalShadowAtlasQuadrantSubdiv p_subdiv); + PositionalShadowAtlasQuadrantSubdiv get_positional_shadow_atlas_quadrant_subdiv(int p_quadrant) const; - void set_shadow_atlas_quadrant_subdiv(int p_quadrant, ShadowAtlasQuadrantSubdiv p_subdiv); - ShadowAtlasQuadrantSubdiv get_shadow_atlas_quadrant_subdiv(int p_quadrant) const; + void set_msaa_2d(MSAA p_msaa); + MSAA get_msaa_2d() const; - void set_msaa(MSAA p_msaa); - MSAA get_msaa() const; + void set_msaa_3d(MSAA p_msaa); + MSAA get_msaa_3d() const; void set_screen_space_aa(ScreenSpaceAA p_screen_space_aa); ScreenSpaceAA get_screen_space_aa() const; @@ -529,8 +545,8 @@ public: void set_fsr_sharpness(float p_fsr_sharpness); float get_fsr_sharpness() const; - void set_fsr_mipmap_bias(float p_fsr_mipmap_bias); - float get_fsr_mipmap_bias() const; + void set_texture_mipmap_bias(float p_texture_mipmap_bias); + float get_texture_mipmap_bias() const; void set_use_debanding(bool p_use_debanding); bool is_using_debanding() const; @@ -565,7 +581,7 @@ public: void gui_release_focus(); Control *gui_get_focus_owner(); - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; void set_debug_draw(DebugDraw p_debug_draw); DebugDraw get_debug_draw() const; @@ -604,6 +620,14 @@ public: void set_default_canvas_item_texture_repeat(DefaultCanvasItemTextureRepeat p_repeat); DefaultCanvasItemTextureRepeat get_default_canvas_item_texture_repeat() const; + // VRS + + void set_vrs_mode(VRSMode p_vrs_mode); + VRSMode get_vrs_mode() const; + + void set_vrs_texture(Ref<Texture2D> p_texture); + Ref<Texture2D> get_vrs_texture() const; + virtual DisplayServer::WindowID get_window_id() const = 0; void set_embedding_subwindows(bool p_embed); @@ -690,6 +714,7 @@ public: bool is_using_xr(); #endif // _3D_DISABLED + void _validate_property(PropertyInfo &p_property) const; Viewport(); ~Viewport(); }; @@ -746,16 +771,17 @@ public: }; VARIANT_ENUM_CAST(Viewport::Scaling3DMode); VARIANT_ENUM_CAST(SubViewport::UpdateMode); -VARIANT_ENUM_CAST(Viewport::ShadowAtlasQuadrantSubdiv); +VARIANT_ENUM_CAST(Viewport::PositionalShadowAtlasQuadrantSubdiv); VARIANT_ENUM_CAST(Viewport::MSAA); VARIANT_ENUM_CAST(Viewport::ScreenSpaceAA); VARIANT_ENUM_CAST(Viewport::DebugDraw); VARIANT_ENUM_CAST(Viewport::SDFScale); VARIANT_ENUM_CAST(Viewport::SDFOversize); +VARIANT_ENUM_CAST(Viewport::VRSMode); VARIANT_ENUM_CAST(SubViewport::ClearMode); VARIANT_ENUM_CAST(Viewport::RenderInfo); VARIANT_ENUM_CAST(Viewport::RenderInfoType); VARIANT_ENUM_CAST(Viewport::DefaultCanvasItemTextureFilter); VARIANT_ENUM_CAST(Viewport::DefaultCanvasItemTextureRepeat); -#endif +#endif // VIEWPORT_H diff --git a/scene/main/window.cpp b/scene/main/window.cpp index 73e8f537d9..64869b2936 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -32,9 +32,13 @@ #include "core/config/project_settings.h" #include "core/debugger/engine_debugger.h" +#include "core/input/shortcut.h" #include "core/string/translation.h" +#include "core/variant/variant_parser.h" #include "scene/gui/control.h" #include "scene/scene_string_names.h" +#include "scene/theme/theme_db.h" +#include "scene/theme/theme_owner.h" void Window::set_title(const String &p_title) { title = p_title; @@ -242,8 +246,8 @@ void Window::_make_window() { window_id = DisplayServer::get_singleton()->create_sub_window(DisplayServer::WindowMode(mode), vsync_mode, f, Rect2i(position, size)); ERR_FAIL_COND(window_id == DisplayServer::INVALID_WINDOW_ID); DisplayServer::get_singleton()->window_set_current_screen(current_screen, window_id); - DisplayServer::get_singleton()->window_set_max_size(max_size, window_id); - DisplayServer::get_singleton()->window_set_min_size(min_size, window_id); + DisplayServer::get_singleton()->window_set_max_size(Size2i(), window_id); + DisplayServer::get_singleton()->window_set_min_size(Size2i(), window_id); String tr_title = atr(title); #ifdef DEBUG_ENABLED if (window_id == DisplayServer::MAIN_WINDOW_ID) { @@ -349,7 +353,9 @@ void Window::_event_callback(DisplayServer::WindowEvent p_event) { _propagate_window_notification(this, NOTIFICATION_WM_MOUSE_ENTER); emit_signal(SNAME("mouse_entered")); notification(NOTIFICATION_VP_MOUSE_ENTER); - DisplayServer::get_singleton()->cursor_set_shape(DisplayServer::CURSOR_ARROW); //restore cursor shape + if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CURSOR_SHAPE)) { + DisplayServer::get_singleton()->cursor_set_shape(DisplayServer::CURSOR_ARROW); //restore cursor shape + } } break; case DisplayServer::WINDOW_EVENT_MOUSE_EXIT: { notification(NOTIFICATION_VP_MOUSE_EXIT); @@ -383,9 +389,24 @@ void Window::_event_callback(DisplayServer::WindowEvent p_event) { _propagate_window_notification(this, NOTIFICATION_WM_DPI_CHANGE); emit_signal(SNAME("dpi_changed")); } break; + case DisplayServer::WINDOW_EVENT_TITLEBAR_CHANGE: { + emit_signal(SNAME("titlebar_changed")); + } break; } } +void Window::update_mouse_cursor_shape() { + // The default shape is set in Viewport::_gui_input_event. To instantly + // see the shape in the viewport we need to trigger a mouse motion event. + Ref<InputEventMouseMotion> mm; + Vector2 pos = get_mouse_position(); + Transform2D xform = get_global_canvas_transform().affine_inverse(); + mm.instantiate(); + mm->set_position(pos); + mm->set_global_position(xform.xform(pos)); + push_input(mm); +} + void Window::show() { set_visible(true); } @@ -596,20 +617,43 @@ void Window::_update_window_size() { size.x = MAX(size_limit.x, size.x); size.y = MAX(size_limit.y, size.y); - if (max_size.x > 0 && max_size.x > min_size.x && size.x > max_size.x) { - size.x = max_size.x; - } + bool reset_min_first = false; + + bool max_size_valid = false; + if ((max_size.x > 0 || max_size.y > 0) && (max_size.x >= min_size.x && max_size.y >= min_size.y)) { + max_size_valid = true; - if (max_size.y > 0 && max_size.y > min_size.y && size.y > max_size.y) { - size.y = max_size.y; + if (size.x > max_size.x) { + size.x = max_size.x; + } + if (size_limit.x > max_size.x) { + size_limit.x = max_size.x; + reset_min_first = true; + } + + if (size.y > max_size.y) { + size.y = max_size.y; + } + if (size_limit.y > max_size.y) { + size_limit.y = max_size.y; + reset_min_first = true; + } } if (embedder) { + size.x = MAX(size.x, 1); + size.y = MAX(size.y, 1); + embedder->_sub_window_update(this); } else if (window_id != DisplayServer::INVALID_WINDOW_ID) { + if (reset_min_first && wrap_controls) { + // Avoid an error if setting max_size to a value between min_size and the previous size_limit. + DisplayServer::get_singleton()->window_set_min_size(Size2i(), window_id); + } + DisplayServer::get_singleton()->window_set_size(size, window_id); + DisplayServer::get_singleton()->window_set_max_size(max_size_valid ? max_size : Size2i(), window_id); DisplayServer::get_singleton()->window_set_min_size(size_limit, window_id); - DisplayServer::get_singleton()->window_set_max_size(max_size, window_id); } //update the viewport @@ -771,6 +815,19 @@ Viewport *Window::_get_embedder() const { void Window::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_POSTINITIALIZE: { + _invalidate_theme_cache(); + _update_theme_item_cache(); + } break; + + case NOTIFICATION_PARENTED: { + theme_owner->assign_theme_on_parented(this); + } break; + + case NOTIFICATION_UNPARENTED: { + theme_owner->clear_theme_on_unparented(this); + } break; + case NOTIFICATION_ENTER_TREE: { bool embedded = false; { @@ -823,21 +880,13 @@ void Window::_notification(int p_what) { RS::get_singleton()->viewport_set_active(get_viewport_rid(), true); } - if (theme.is_null()) { - Control *parent_c = cast_to<Control>(get_parent()); - if (parent_c && (parent_c->data.theme_owner || parent_c->data.theme_owner_window)) { - theme_owner = parent_c->data.theme_owner; - theme_owner_window = parent_c->data.theme_owner_window; - notification(NOTIFICATION_THEME_CHANGED); - } else { - Window *parent_w = cast_to<Window>(get_parent()); - if (parent_w && (parent_w->theme_owner || parent_w->theme_owner_window)) { - theme_owner = parent_w->theme_owner; - theme_owner_window = parent_w->theme_owner_window; - notification(NOTIFICATION_THEME_CHANGED); - } - } - } + notification(NOTIFICATION_THEME_CHANGED); + } break; + + case NOTIFICATION_THEME_CHANGED: { + emit_signal(SceneStringNames::get_singleton()->theme_changed); + _invalidate_theme_cache(); + _update_theme_item_cache(); } break; case NOTIFICATION_READY: { @@ -847,6 +896,9 @@ void Window::_notification(int p_what) { } break; case NOTIFICATION_TRANSLATION_CHANGED: { + _invalidate_theme_cache(); + _update_theme_item_cache(); + if (embedder) { embedder->_sub_window_update(this); } else if (window_id != DisplayServer::INVALID_WINDOW_ID) { @@ -953,6 +1005,8 @@ void Window::set_wrap_controls(bool p_enable) { wrap_controls = p_enable; if (wrap_controls) { child_controls_changed(); + } else { + _update_window_size(); } } @@ -1002,9 +1056,31 @@ bool Window::_can_consume_input_events() const { void Window::_window_input(const Ref<InputEvent> &p_ev) { if (EngineDebugger::is_active()) { - // Quit from game window using F8. + // Quit from game window using the stop shortcut (F8 by default). + // The custom shortcut is provided via environment variable when running from the editor. + if (debugger_stop_shortcut.is_null()) { + String shortcut_str = OS::get_singleton()->get_environment("__GODOT_EDITOR_STOP_SHORTCUT__"); + if (!shortcut_str.is_empty()) { + Variant shortcut_var; + + VariantParser::StreamString ss; + ss.s = shortcut_str; + + String errs; + int line; + VariantParser::parse(&ss, shortcut_var, errs, line); + debugger_stop_shortcut = shortcut_var; + } + + if (debugger_stop_shortcut.is_null()) { + // Define a default shortcut if it wasn't provided or is invalid. + debugger_stop_shortcut.instantiate(); + debugger_stop_shortcut->set_events({ (Variant)InputEventKey::create_reference(Key::F8) }); + } + } + Ref<InputEventKey> k = p_ev; - if (k.is_valid() && k->is_pressed() && !k->is_echo() && k->get_keycode() == Key::F8) { + if (k.is_valid() && k->is_pressed() && !k->is_echo() && debugger_stop_shortcut->matches_event(k)) { EngineDebugger::get_singleton()->send_message("request_quit", Array()); } } @@ -1080,7 +1156,7 @@ void Window::popup_centered_clamped(const Size2i &p_size, float p_fallback_ratio Rect2 parent_rect; if (is_embedded()) { - parent_rect = get_parent_viewport()->get_visible_rect(); + parent_rect = _get_embedder()->get_visible_rect(); } else { DisplayServer::WindowID parent_id = get_parent_visible_window()->get_window_id(); int parent_screen = DisplayServer::get_singleton()->window_get_current_screen(parent_id); @@ -1106,7 +1182,7 @@ void Window::popup_centered(const Size2i &p_minsize) { Rect2 parent_rect; if (is_embedded()) { - parent_rect = get_parent_viewport()->get_visible_rect(); + parent_rect = _get_embedder()->get_visible_rect(); } else { DisplayServer::WindowID parent_id = get_parent_visible_window()->get_window_id(); int parent_screen = DisplayServer::get_singleton()->window_get_current_screen(parent_id); @@ -1134,7 +1210,7 @@ void Window::popup_centered_ratio(float p_ratio) { Rect2 parent_rect; if (is_embedded()) { - parent_rect = get_parent_viewport()->get_visible_rect(); + parent_rect = _get_embedder()->get_visible_rect(); } else { DisplayServer::WindowID parent_id = get_parent_visible_window()->get_window_id(); int parent_screen = DisplayServer::get_singleton()->window_get_current_screen(parent_id); @@ -1187,6 +1263,15 @@ void Window::popup(const Rect2i &p_screen_rect) { set_transient(true); set_visible(true); + + int screen_id = DisplayServer::get_singleton()->window_get_current_screen(get_window_id()); + Rect2i screen_rect = DisplayServer::get_singleton()->screen_get_usable_rect(screen_id); + if (screen_rect != Rect2i() && !screen_rect.intersects(Rect2i(position, size))) { + ERR_PRINT(vformat("Window %d spawned at invalid position: %s.", get_window_id(), position)); + // Window appeared on unavailable screen area, so force it to the center. + set_position(screen_rect.size / 2 - size / 2); + } + _post_popup(); notification(NOTIFICATION_POST_POPUP); } @@ -1223,39 +1308,27 @@ Rect2i Window::get_usable_parent_rect() const { } void Window::add_child_notify(Node *p_child) { - Control *child_c = Object::cast_to<Control>(p_child); - - if (child_c && child_c->data.theme.is_null() && (theme_owner || theme_owner_window)) { - Control::_propagate_theme_changed(child_c, theme_owner, theme_owner_window); //need to propagate here, since many controls may require setting up stuff - } - - Window *child_w = Object::cast_to<Window>(p_child); - - if (child_w && child_w->theme.is_null() && (theme_owner || theme_owner_window)) { - Control::_propagate_theme_changed(child_w, theme_owner, theme_owner_window); //need to propagate here, since many controls may require setting up stuff - } - if (is_inside_tree() && wrap_controls) { child_controls_changed(); } } void Window::remove_child_notify(Node *p_child) { - Control *child_c = Object::cast_to<Control>(p_child); - - if (child_c && (child_c->data.theme_owner || child_c->data.theme_owner_window) && child_c->data.theme.is_null()) { - Control::_propagate_theme_changed(child_c, nullptr, nullptr); + if (is_inside_tree() && wrap_controls) { + child_controls_changed(); } +} - Window *child_w = Object::cast_to<Window>(p_child); +void Window::set_theme_owner_node(Node *p_node) { + theme_owner->set_owner_node(p_node); +} - if (child_w && (child_w->theme_owner || child_w->theme_owner_window) && child_w->theme.is_null()) { - Control::_propagate_theme_changed(child_w, nullptr, nullptr); - } +Node *Window::get_theme_owner_node() const { + return theme_owner->get_owner_node(); +} - if (is_inside_tree() && wrap_controls) { - child_controls_changed(); - } +bool Window::has_theme_owner_node() const { + return theme_owner->has_owner_node(); } void Window::set_theme(const Ref<Theme> &p_theme) { @@ -1263,134 +1336,183 @@ void Window::set_theme(const Ref<Theme> &p_theme) { return; } + if (theme.is_valid()) { + theme->disconnect("changed", callable_mp(this, &Window::_theme_changed)); + } + theme = p_theme; + if (theme.is_valid()) { + theme_owner->propagate_theme_changed(this, this, is_inside_tree(), true); + theme->connect("changed", callable_mp(this, &Window::_theme_changed), CONNECT_DEFERRED); + return; + } - if (!p_theme.is_null()) { - theme_owner = nullptr; - theme_owner_window = this; - Control::_propagate_theme_changed(this, nullptr, this); - } else { - Control *parent_c = cast_to<Control>(get_parent()); - if (parent_c && (parent_c->data.theme_owner || parent_c->data.theme_owner_window)) { - Control::_propagate_theme_changed(this, parent_c->data.theme_owner, parent_c->data.theme_owner_window); - } else { - Window *parent_w = cast_to<Window>(get_parent()); - if (parent_w && (parent_w->theme_owner || parent_w->theme_owner_window)) { - Control::_propagate_theme_changed(this, parent_w->theme_owner, parent_w->theme_owner_window); - } else { - Control::_propagate_theme_changed(this, nullptr, nullptr); - } - } + Control *parent_c = Object::cast_to<Control>(get_parent()); + if (parent_c && parent_c->has_theme_owner_node()) { + theme_owner->propagate_theme_changed(this, parent_c->get_theme_owner_node(), is_inside_tree(), true); + return; } + + Window *parent_w = cast_to<Window>(get_parent()); + if (parent_w && parent_w->has_theme_owner_node()) { + theme_owner->propagate_theme_changed(this, parent_w->get_theme_owner_node(), is_inside_tree(), true); + return; + } + + theme_owner->propagate_theme_changed(this, nullptr, is_inside_tree(), true); } Ref<Theme> Window::get_theme() const { return theme; } +void Window::_theme_changed() { + if (is_inside_tree()) { + theme_owner->propagate_theme_changed(this, this, true, false); + } +} + +void Window::_invalidate_theme_cache() { + theme_icon_cache.clear(); + theme_style_cache.clear(); + theme_font_cache.clear(); + theme_font_size_cache.clear(); + theme_color_cache.clear(); + theme_constant_cache.clear(); +} + +void Window::_update_theme_item_cache() { +} + void Window::set_theme_type_variation(const StringName &p_theme_type) { theme_type_variation = p_theme_type; - Control::_propagate_theme_changed(this, theme_owner, theme_owner_window); + if (is_inside_tree()) { + notification(NOTIFICATION_THEME_CHANGED); + } } StringName Window::get_theme_type_variation() const { return theme_type_variation; } -void Window::_get_theme_type_dependencies(const StringName &p_theme_type, List<StringName> *p_list) const { - if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == theme_type_variation) { - if (Theme::get_project_default().is_valid() && Theme::get_project_default()->get_type_variation_base(theme_type_variation) != StringName()) { - Theme::get_project_default()->get_type_dependencies(get_class_name(), theme_type_variation, p_list); - } else { - Theme::get_default()->get_type_dependencies(get_class_name(), theme_type_variation, p_list); - } - } else { - Theme::get_default()->get_type_dependencies(p_theme_type, StringName(), p_list); +Ref<Texture2D> Window::get_theme_icon(const StringName &p_name, const StringName &p_theme_type) const { + if (theme_icon_cache.has(p_theme_type) && theme_icon_cache[p_theme_type].has(p_name)) { + return theme_icon_cache[p_theme_type][p_name]; } -} -Ref<Texture2D> Window::get_theme_icon(const StringName &p_name, const StringName &p_theme_type) const { List<StringName> theme_types; - _get_theme_type_dependencies(p_theme_type, &theme_types); - return Control::get_theme_item_in_types<Ref<Texture2D>>(theme_owner, theme_owner_window, Theme::DATA_TYPE_ICON, p_name, theme_types); + theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types); + Ref<Texture2D> icon = theme_owner->get_theme_item_in_types(Theme::DATA_TYPE_ICON, p_name, theme_types); + theme_icon_cache[p_theme_type][p_name] = icon; + return icon; } Ref<StyleBox> Window::get_theme_stylebox(const StringName &p_name, const StringName &p_theme_type) const { + if (theme_style_cache.has(p_theme_type) && theme_style_cache[p_theme_type].has(p_name)) { + return theme_style_cache[p_theme_type][p_name]; + } + List<StringName> theme_types; - _get_theme_type_dependencies(p_theme_type, &theme_types); - return Control::get_theme_item_in_types<Ref<StyleBox>>(theme_owner, theme_owner_window, Theme::DATA_TYPE_STYLEBOX, p_name, theme_types); + theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types); + Ref<StyleBox> style = theme_owner->get_theme_item_in_types(Theme::DATA_TYPE_STYLEBOX, p_name, theme_types); + theme_style_cache[p_theme_type][p_name] = style; + return style; } Ref<Font> Window::get_theme_font(const StringName &p_name, const StringName &p_theme_type) const { + if (theme_font_cache.has(p_theme_type) && theme_font_cache[p_theme_type].has(p_name)) { + return theme_font_cache[p_theme_type][p_name]; + } + List<StringName> theme_types; - _get_theme_type_dependencies(p_theme_type, &theme_types); - return Control::get_theme_item_in_types<Ref<Font>>(theme_owner, theme_owner_window, Theme::DATA_TYPE_FONT, p_name, theme_types); + theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types); + Ref<Font> font = theme_owner->get_theme_item_in_types(Theme::DATA_TYPE_FONT, p_name, theme_types); + theme_font_cache[p_theme_type][p_name] = font; + return font; } int Window::get_theme_font_size(const StringName &p_name, const StringName &p_theme_type) const { + if (theme_font_size_cache.has(p_theme_type) && theme_font_size_cache[p_theme_type].has(p_name)) { + return theme_font_size_cache[p_theme_type][p_name]; + } + List<StringName> theme_types; - _get_theme_type_dependencies(p_theme_type, &theme_types); - return Control::get_theme_item_in_types<int>(theme_owner, theme_owner_window, Theme::DATA_TYPE_FONT_SIZE, p_name, theme_types); + theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types); + int font_size = theme_owner->get_theme_item_in_types(Theme::DATA_TYPE_FONT_SIZE, p_name, theme_types); + theme_font_size_cache[p_theme_type][p_name] = font_size; + return font_size; } Color Window::get_theme_color(const StringName &p_name, const StringName &p_theme_type) const { + if (theme_color_cache.has(p_theme_type) && theme_color_cache[p_theme_type].has(p_name)) { + return theme_color_cache[p_theme_type][p_name]; + } + List<StringName> theme_types; - _get_theme_type_dependencies(p_theme_type, &theme_types); - return Control::get_theme_item_in_types<Color>(theme_owner, theme_owner_window, Theme::DATA_TYPE_COLOR, p_name, theme_types); + theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types); + Color color = theme_owner->get_theme_item_in_types(Theme::DATA_TYPE_COLOR, p_name, theme_types); + theme_color_cache[p_theme_type][p_name] = color; + return color; } int Window::get_theme_constant(const StringName &p_name, const StringName &p_theme_type) const { + if (theme_constant_cache.has(p_theme_type) && theme_constant_cache[p_theme_type].has(p_name)) { + return theme_constant_cache[p_theme_type][p_name]; + } + List<StringName> theme_types; - _get_theme_type_dependencies(p_theme_type, &theme_types); - return Control::get_theme_item_in_types<int>(theme_owner, theme_owner_window, Theme::DATA_TYPE_CONSTANT, p_name, theme_types); + theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types); + int constant = theme_owner->get_theme_item_in_types(Theme::DATA_TYPE_CONSTANT, p_name, theme_types); + theme_constant_cache[p_theme_type][p_name] = constant; + return constant; } bool Window::has_theme_icon(const StringName &p_name, const StringName &p_theme_type) const { List<StringName> theme_types; - _get_theme_type_dependencies(p_theme_type, &theme_types); - return Control::has_theme_item_in_types(theme_owner, theme_owner_window, Theme::DATA_TYPE_ICON, p_name, theme_types); + theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types); + return theme_owner->has_theme_item_in_types(Theme::DATA_TYPE_ICON, p_name, theme_types); } bool Window::has_theme_stylebox(const StringName &p_name, const StringName &p_theme_type) const { List<StringName> theme_types; - _get_theme_type_dependencies(p_theme_type, &theme_types); - return Control::has_theme_item_in_types(theme_owner, theme_owner_window, Theme::DATA_TYPE_STYLEBOX, p_name, theme_types); + theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types); + return theme_owner->has_theme_item_in_types(Theme::DATA_TYPE_STYLEBOX, p_name, theme_types); } bool Window::has_theme_font(const StringName &p_name, const StringName &p_theme_type) const { List<StringName> theme_types; - _get_theme_type_dependencies(p_theme_type, &theme_types); - return Control::has_theme_item_in_types(theme_owner, theme_owner_window, Theme::DATA_TYPE_FONT, p_name, theme_types); + theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types); + return theme_owner->has_theme_item_in_types(Theme::DATA_TYPE_FONT, p_name, theme_types); } bool Window::has_theme_font_size(const StringName &p_name, const StringName &p_theme_type) const { List<StringName> theme_types; - _get_theme_type_dependencies(p_theme_type, &theme_types); - return Control::has_theme_item_in_types(theme_owner, theme_owner_window, Theme::DATA_TYPE_FONT_SIZE, p_name, theme_types); + theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types); + return theme_owner->has_theme_item_in_types(Theme::DATA_TYPE_FONT_SIZE, p_name, theme_types); } bool Window::has_theme_color(const StringName &p_name, const StringName &p_theme_type) const { List<StringName> theme_types; - _get_theme_type_dependencies(p_theme_type, &theme_types); - return Control::has_theme_item_in_types(theme_owner, theme_owner_window, Theme::DATA_TYPE_COLOR, p_name, theme_types); + theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types); + return theme_owner->has_theme_item_in_types(Theme::DATA_TYPE_COLOR, p_name, theme_types); } bool Window::has_theme_constant(const StringName &p_name, const StringName &p_theme_type) const { List<StringName> theme_types; - _get_theme_type_dependencies(p_theme_type, &theme_types); - return Control::has_theme_item_in_types(theme_owner, theme_owner_window, Theme::DATA_TYPE_CONSTANT, p_name, theme_types); + theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types); + return theme_owner->has_theme_item_in_types(Theme::DATA_TYPE_CONSTANT, p_name, theme_types); } float Window::get_theme_default_base_scale() const { - return Control::fetch_theme_default_base_scale(theme_owner, theme_owner_window); + return theme_owner->get_theme_default_base_scale(); } Ref<Font> Window::get_theme_default_font() const { - return Control::fetch_theme_default_font(theme_owner, theme_owner_window); + return theme_owner->get_theme_default_font(); } int Window::get_theme_default_font_size() const { - return Control::fetch_theme_default_font_size(theme_owner, theme_owner_window); + return theme_owner->get_theme_default_font_size(); } Rect2i Window::get_parent_rect() const { @@ -1485,15 +1607,15 @@ bool Window::is_auto_translating() const { return auto_translate; } -void Window::_validate_property(PropertyInfo &property) const { - if (property.name == "theme_type_variation") { +void Window::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "theme_type_variation") { List<StringName> names; // Only the default theme and the project theme are used for the list of options. // This is an imposed limitation to simplify the logic needed to leverage those options. - Theme::get_default()->get_type_variation_list(get_class_name(), &names); - if (Theme::get_project_default().is_valid()) { - Theme::get_project_default()->get_type_variation_list(get_class_name(), &names); + ThemeDB::get_singleton()->get_default_theme()->get_type_variation_list(get_class_name(), &names); + if (ThemeDB::get_singleton()->get_project_theme().is_valid()) { + ThemeDB::get_singleton()->get_project_theme()->get_type_variation_list(get_class_name(), &names); } names.sort_custom<StringName::AlphCompare>(); @@ -1509,14 +1631,14 @@ void Window::_validate_property(PropertyInfo &property) const { unique_names.append(E); } - property.hint_string = hint_string; + p_property.hint_string = hint_string; } } Transform2D Window::get_screen_transform() const { Transform2D embedder_transform = Transform2D(); if (_get_embedder()) { - embedder_transform.translate(get_position()); + embedder_transform.translate_local(get_position()); embedder_transform = _get_embedder()->get_screen_transform() * embedder_transform; } return embedder_transform * Viewport::get_screen_transform(); @@ -1654,6 +1776,7 @@ void Window::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "transparent"), "set_flag", "get_flag", FLAG_TRANSPARENT); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "unfocusable"), "set_flag", "get_flag", FLAG_NO_FOCUS); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "popup_window"), "set_flag", "get_flag", FLAG_POPUP); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "extend_to_title"), "set_flag", "get_flag", FLAG_EXTEND_TO_TITLE); ADD_GROUP("Limits", ""); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "min_size", PROPERTY_HINT_NONE, "suffix:px"), "set_min_size", "get_min_size"); @@ -1683,8 +1806,10 @@ void Window::_bind_methods() { ADD_SIGNAL(MethodInfo("visibility_changed")); ADD_SIGNAL(MethodInfo("about_to_popup")); ADD_SIGNAL(MethodInfo("theme_changed")); + ADD_SIGNAL(MethodInfo("titlebar_changed")); BIND_CONSTANT(NOTIFICATION_VISIBILITY_CHANGED); + BIND_CONSTANT(NOTIFICATION_THEME_CHANGED); BIND_ENUM_CONSTANT(MODE_WINDOWED); BIND_ENUM_CONSTANT(MODE_MINIMIZED); @@ -1698,6 +1823,7 @@ void Window::_bind_methods() { BIND_ENUM_CONSTANT(FLAG_TRANSPARENT); BIND_ENUM_CONSTANT(FLAG_NO_FOCUS); BIND_ENUM_CONSTANT(FLAG_POPUP); + BIND_ENUM_CONSTANT(FLAG_EXTEND_TO_TITLE); BIND_ENUM_CONSTANT(FLAG_MAX); BIND_ENUM_CONSTANT(CONTENT_SCALE_MODE_DISABLED); @@ -1717,8 +1843,10 @@ void Window::_bind_methods() { } Window::Window() { + theme_owner = memnew(ThemeOwner); RS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), RS::VIEWPORT_UPDATE_DISABLED); } Window::~Window() { + memdelete(theme_owner); } diff --git a/scene/main/window.h b/scene/main/window.h index c060f1d79d..786f0ada38 100644 --- a/scene/main/window.h +++ b/scene/main/window.h @@ -32,11 +32,13 @@ #define WINDOW_H #include "scene/main/viewport.h" +#include "scene/resources/theme.h" class Control; class Font; +class Shortcut; class StyleBox; -class Theme; +class ThemeOwner; class Window : public Viewport { GDCLASS(Window, Viewport) @@ -56,6 +58,7 @@ public: FLAG_TRANSPARENT = DisplayServer::WINDOW_FLAG_TRANSPARENT, FLAG_NO_FOCUS = DisplayServer::WINDOW_FLAG_NO_FOCUS, FLAG_POPUP = DisplayServer::WINDOW_FLAG_POPUP, + FLAG_EXTEND_TO_TITLE = DisplayServer::WINDOW_FLAG_EXTEND_TO_TITLE, FLAG_MAX = DisplayServer::WINDOW_FLAG_MAX, }; @@ -133,12 +136,20 @@ private: Window *exclusive_child = nullptr; HashSet<Window *> transient_children; - friend class Control; + ThemeOwner *theme_owner = nullptr; Ref<Theme> theme; - Control *theme_owner = nullptr; - Window *theme_owner_window = nullptr; StringName theme_type_variation; + mutable HashMap<StringName, Theme::ThemeIconMap> theme_icon_cache; + mutable HashMap<StringName, Theme::ThemeStyleMap> theme_style_cache; + mutable HashMap<StringName, Theme::ThemeFontMap> theme_font_cache; + mutable HashMap<StringName, Theme::ThemeFontSizeMap> theme_font_size_cache; + mutable HashMap<StringName, Theme::ThemeColorMap> theme_color_cache; + mutable HashMap<StringName, Theme::ThemeConstantMap> theme_constant_cache; + + void _theme_changed(); + void _invalidate_theme_cache(); + Viewport *embedder = nullptr; friend class Viewport; //friend back, can call the methods below @@ -150,15 +161,19 @@ private: void _event_callback(DisplayServer::WindowEvent p_event); virtual bool _can_consume_input_events() const override; + Ref<Shortcut> debugger_stop_shortcut; + protected: Viewport *_get_embedder() const; virtual Rect2i _popup_adjust_rect() const { return Rect2i(); } + virtual void _update_theme_item_cache(); + virtual void _post_popup() {} virtual Size2 _get_contents_minimum_size() const; static void _bind_methods(); void _notification(int p_what); - virtual void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; virtual void add_child_notify(Node *p_child) override; virtual void remove_child_notify(Node *p_child) override; @@ -205,6 +220,8 @@ public: void set_visible(bool p_visible); bool is_visible() const; + void update_mouse_cursor_shape(); + void show(); void hide(); @@ -251,12 +268,15 @@ public: void popup_centered(const Size2i &p_minsize = Size2i()); void popup_centered_clamped(const Size2i &p_size = Size2i(), float p_fallback_ratio = 0.75); + void set_theme_owner_node(Node *p_node); + Node *get_theme_owner_node() const; + bool has_theme_owner_node() const; + void set_theme(const Ref<Theme> &p_theme); Ref<Theme> get_theme() const; void set_theme_type_variation(const StringName &p_theme_type); StringName get_theme_type_variation() const; - _FORCE_INLINE_ void _get_theme_type_dependencies(const StringName &p_theme_type, List<StringName> *p_list) const; Size2 get_contents_minimum_size() const; diff --git a/scene/multiplayer/multiplayer_spawner.cpp b/scene/multiplayer/multiplayer_spawner.cpp deleted file mode 100644 index ddd01d0a43..0000000000 --- a/scene/multiplayer/multiplayer_spawner.cpp +++ /dev/null @@ -1,315 +0,0 @@ -/*************************************************************************/ -/* multiplayer_spawner.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "multiplayer_spawner.h" - -#include "core/io/marshalls.h" -#include "core/multiplayer/multiplayer_api.h" -#include "scene/main/window.h" -#include "scene/scene_string_names.h" - -#ifdef TOOLS_ENABLED -/* This is editor only */ -bool MultiplayerSpawner::_set(const StringName &p_name, const Variant &p_value) { - if (p_name == "_spawnable_scene_count") { - spawnable_scenes.resize(p_value); - notify_property_list_changed(); - return true; - } else { - String ns = p_name; - if (ns.begins_with("scenes/")) { - uint32_t index = ns.get_slicec('/', 1).to_int(); - ERR_FAIL_UNSIGNED_INDEX_V(index, spawnable_scenes.size(), false); - spawnable_scenes[index].path = p_value; - return true; - } - } - return false; -} - -bool MultiplayerSpawner::_get(const StringName &p_name, Variant &r_ret) const { - if (p_name == "_spawnable_scene_count") { - r_ret = spawnable_scenes.size(); - return true; - } else { - String ns = p_name; - if (ns.begins_with("scenes/")) { - uint32_t index = ns.get_slicec('/', 1).to_int(); - ERR_FAIL_UNSIGNED_INDEX_V(index, spawnable_scenes.size(), false); - r_ret = spawnable_scenes[index].path; - return true; - } - } - return false; -} - -void MultiplayerSpawner::_get_property_list(List<PropertyInfo> *p_list) const { - p_list->push_back(PropertyInfo(Variant::INT, "_spawnable_scene_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY, "Scenes,scenes/")); - List<String> exts; - ResourceLoader::get_recognized_extensions_for_type("PackedScene", &exts); - String ext_hint; - for (const String &E : exts) { - if (!ext_hint.is_empty()) { - ext_hint += ","; - } - ext_hint += "*." + E; - } - for (uint32_t i = 0; i < spawnable_scenes.size(); i++) { - p_list->push_back(PropertyInfo(Variant::STRING, "scenes/" + itos(i), PROPERTY_HINT_FILE, ext_hint, PROPERTY_USAGE_EDITOR)); - } -} -#endif -void MultiplayerSpawner::add_spawnable_scene(const String &p_path) { - SpawnableScene sc; - sc.path = p_path; - if (Engine::get_singleton()->is_editor_hint()) { - ERR_FAIL_COND(!FileAccess::exists(p_path)); - } else { - sc.cache = ResourceLoader::load(p_path); - ERR_FAIL_COND_MSG(sc.cache.is_null(), "Invalid spawnable scene: " + p_path); - } - spawnable_scenes.push_back(sc); -} -int MultiplayerSpawner::get_spawnable_scene_count() const { - return spawnable_scenes.size(); -} -String MultiplayerSpawner::get_spawnable_scene(int p_idx) const { - return spawnable_scenes[p_idx].path; -} -void MultiplayerSpawner::clear_spawnable_scenes() { - spawnable_scenes.clear(); -} - -Vector<String> MultiplayerSpawner::_get_spawnable_scenes() const { - Vector<String> ss; - ss.resize(spawnable_scenes.size()); - for (int i = 0; i < ss.size(); i++) { - ss.write[i] = spawnable_scenes[i].path; - } - return ss; -} - -void MultiplayerSpawner::_set_spawnable_scenes(const Vector<String> &p_scenes) { - clear_spawnable_scenes(); - for (int i = 0; i < p_scenes.size(); i++) { - add_spawnable_scene(p_scenes[i]); - } -} - -void MultiplayerSpawner::_bind_methods() { - ClassDB::bind_method(D_METHOD("add_spawnable_scene", "path"), &MultiplayerSpawner::add_spawnable_scene); - ClassDB::bind_method(D_METHOD("get_spawnable_scene_count"), &MultiplayerSpawner::get_spawnable_scene_count); - ClassDB::bind_method(D_METHOD("get_spawnable_scene", "path"), &MultiplayerSpawner::get_spawnable_scene); - ClassDB::bind_method(D_METHOD("clear_spawnable_scenes"), &MultiplayerSpawner::clear_spawnable_scenes); - - ClassDB::bind_method(D_METHOD("_get_spawnable_scenes"), &MultiplayerSpawner::_get_spawnable_scenes); - ClassDB::bind_method(D_METHOD("_set_spawnable_scenes", "scenes"), &MultiplayerSpawner::_set_spawnable_scenes); - - ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "_spawnable_scenes", PROPERTY_HINT_NONE, "", (PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL)), "_set_spawnable_scenes", "_get_spawnable_scenes"); - - ClassDB::bind_method(D_METHOD("spawn", "data"), &MultiplayerSpawner::spawn, DEFVAL(Variant())); - - ClassDB::bind_method(D_METHOD("get_spawn_path"), &MultiplayerSpawner::get_spawn_path); - ClassDB::bind_method(D_METHOD("set_spawn_path", "path"), &MultiplayerSpawner::set_spawn_path); - ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "spawn_path", PROPERTY_HINT_NONE, ""), "set_spawn_path", "get_spawn_path"); - - ClassDB::bind_method(D_METHOD("get_spawn_limit"), &MultiplayerSpawner::get_spawn_limit); - ClassDB::bind_method(D_METHOD("set_spawn_limit", "limit"), &MultiplayerSpawner::set_spawn_limit); - ADD_PROPERTY(PropertyInfo(Variant::INT, "spawn_limit", PROPERTY_HINT_RANGE, "0,1024,1,or_greater"), "set_spawn_limit", "get_spawn_limit"); - - ClassDB::bind_method(D_METHOD("set_auto_spawning", "enabled"), &MultiplayerSpawner::set_auto_spawning); - ClassDB::bind_method(D_METHOD("is_auto_spawning"), &MultiplayerSpawner::is_auto_spawning); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_spawn"), "set_auto_spawning", "is_auto_spawning"); - - GDVIRTUAL_BIND(_spawn_custom, "data"); - - ADD_SIGNAL(MethodInfo("despawned", PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); - ADD_SIGNAL(MethodInfo("spawned", PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); -} - -void MultiplayerSpawner::_update_spawn_node() { -#ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { - return; - } -#endif - if (spawn_node.is_valid()) { - Node *node = Object::cast_to<Node>(ObjectDB::get_instance(spawn_node)); - if (node && node->is_connected("child_entered_tree", callable_mp(this, &MultiplayerSpawner::_node_added))) { - node->disconnect("child_entered_tree", callable_mp(this, &MultiplayerSpawner::_node_added)); - } - } - Node *node = spawn_path.is_empty() && is_inside_tree() ? nullptr : get_node_or_null(spawn_path); - if (node) { - spawn_node = node->get_instance_id(); - if (auto_spawn) { - node->connect("child_entered_tree", callable_mp(this, &MultiplayerSpawner::_node_added)); - } - } else { - spawn_node = ObjectID(); - } -} - -void MultiplayerSpawner::_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_POST_ENTER_TREE: { - _update_spawn_node(); - } break; - - case NOTIFICATION_EXIT_TREE: { - _update_spawn_node(); - - for (const KeyValue<ObjectID, SpawnInfo> &E : tracked_nodes) { - Node *node = Object::cast_to<Node>(ObjectDB::get_instance(E.key)); - ERR_CONTINUE(!node); - node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &MultiplayerSpawner::_node_exit)); - // This is unlikely, but might still crash the engine. - if (node->is_connected(SceneStringNames::get_singleton()->ready, callable_mp(this, &MultiplayerSpawner::_node_ready))) { - node->disconnect(SceneStringNames::get_singleton()->ready, callable_mp(this, &MultiplayerSpawner::_node_ready)); - } - get_multiplayer()->despawn(node, this); - } - tracked_nodes.clear(); - } break; - } -} - -void MultiplayerSpawner::_node_added(Node *p_node) { - if (!get_multiplayer()->has_multiplayer_peer() || !is_multiplayer_authority()) { - return; - } - if (tracked_nodes.has(p_node->get_instance_id())) { - return; - } - const Node *parent = get_spawn_node(); - if (!parent || p_node->get_parent() != parent) { - return; - } - int id = find_spawnable_scene_index_from_path(p_node->get_scene_file_path()); - if (id == INVALID_ID) { - return; - } - const String name = p_node->get_name(); - ERR_FAIL_COND_MSG(name.validate_node_name() != name, vformat("Unable to auto-spawn node with reserved name: %s. Make sure to add your replicated scenes via 'add_child(node, true)' to produce valid names.", name)); - _track(p_node, Variant(), id); -} - -void MultiplayerSpawner::set_auto_spawning(bool p_enabled) { - auto_spawn = p_enabled; - _update_spawn_node(); -} - -bool MultiplayerSpawner::is_auto_spawning() const { - return auto_spawn; -} - -NodePath MultiplayerSpawner::get_spawn_path() const { - return spawn_path; -} - -void MultiplayerSpawner::set_spawn_path(const NodePath &p_path) { - spawn_path = p_path; - _update_spawn_node(); -} - -void MultiplayerSpawner::_track(Node *p_node, const Variant &p_argument, int p_scene_id) { - ObjectID oid = p_node->get_instance_id(); - if (!tracked_nodes.has(oid)) { - tracked_nodes[oid] = SpawnInfo(p_argument.duplicate(true), p_scene_id); - p_node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &MultiplayerSpawner::_node_exit), varray(p_node->get_instance_id()), CONNECT_ONESHOT); - p_node->connect(SceneStringNames::get_singleton()->ready, callable_mp(this, &MultiplayerSpawner::_node_ready), varray(p_node->get_instance_id()), CONNECT_ONESHOT); - } -} - -void MultiplayerSpawner::_node_ready(ObjectID p_id) { - get_multiplayer()->spawn(ObjectDB::get_instance(p_id), this); -} - -void MultiplayerSpawner::_node_exit(ObjectID p_id) { - Node *node = Object::cast_to<Node>(ObjectDB::get_instance(p_id)); - ERR_FAIL_COND(!node); - if (tracked_nodes.has(p_id)) { - tracked_nodes.erase(p_id); - get_multiplayer()->despawn(node, this); - } -} - -int MultiplayerSpawner::find_spawnable_scene_index_from_path(const String &p_scene) const { - for (uint32_t i = 0; i < spawnable_scenes.size(); i++) { - if (spawnable_scenes[i].path == p_scene) { - return i; - } - } - return INVALID_ID; -} - -int MultiplayerSpawner::find_spawnable_scene_index_from_object(const ObjectID &p_id) const { - const SpawnInfo *info = tracked_nodes.getptr(p_id); - return info ? info->id : INVALID_ID; -} - -const Variant MultiplayerSpawner::get_spawn_argument(const ObjectID &p_id) const { - const SpawnInfo *info = tracked_nodes.getptr(p_id); - return info ? info->args : Variant(); -} - -Node *MultiplayerSpawner::instantiate_scene(int p_id) { - ERR_FAIL_COND_V_MSG(spawn_limit && spawn_limit <= tracked_nodes.size(), nullptr, "Spawn limit reached!"); - ERR_FAIL_UNSIGNED_INDEX_V((uint32_t)p_id, spawnable_scenes.size(), nullptr); - Ref<PackedScene> scene = spawnable_scenes[p_id].cache; - ERR_FAIL_COND_V(scene.is_null(), nullptr); - return scene->instantiate(); -} - -Node *MultiplayerSpawner::instantiate_custom(const Variant &p_data) { - ERR_FAIL_COND_V_MSG(spawn_limit && spawn_limit <= tracked_nodes.size(), nullptr, "Spawn limit reached!"); - Object *obj = nullptr; - Node *node = nullptr; - if (GDVIRTUAL_CALL(_spawn_custom, p_data, obj)) { - node = Object::cast_to<Node>(obj); - } - return node; -} - -Node *MultiplayerSpawner::spawn(const Variant &p_data) { - ERR_FAIL_COND_V(!is_inside_tree() || !get_multiplayer()->has_multiplayer_peer() || !is_multiplayer_authority(), nullptr); - ERR_FAIL_COND_V_MSG(spawn_limit && spawn_limit <= tracked_nodes.size(), nullptr, "Spawn limit reached!"); - ERR_FAIL_COND_V_MSG(!GDVIRTUAL_IS_OVERRIDDEN(_spawn_custom), nullptr, "Custom spawn requires the '_spawn_custom' virtual method to be implemented via script."); - - Node *parent = get_spawn_node(); - ERR_FAIL_COND_V_MSG(!parent, nullptr, "Cannot find spawn node."); - - Node *node = instantiate_custom(p_data); - ERR_FAIL_COND_V_MSG(!node, nullptr, "The '_spawn_custom' implementation must return a valid Node."); - - _track(node, p_data); - parent->add_child(node, true); - return node; -} diff --git a/scene/multiplayer/multiplayer_spawner.h b/scene/multiplayer/multiplayer_spawner.h deleted file mode 100644 index e8abe702a0..0000000000 --- a/scene/multiplayer/multiplayer_spawner.h +++ /dev/null @@ -1,120 +0,0 @@ -/*************************************************************************/ -/* multiplayer_spawner.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef MULTIPLAYER_SPAWNER_H -#define MULTIPLAYER_SPAWNER_H - -#include "scene/main/node.h" - -#include "core/templates/local_vector.h" -#include "core/variant/typed_array.h" -#include "scene/resources/packed_scene.h" -#include "scene/resources/scene_replication_config.h" - -class MultiplayerSpawner : public Node { - GDCLASS(MultiplayerSpawner, Node); - -public: - enum { - INVALID_ID = 0xFF, - }; - -private: - struct SpawnableScene { - String path; - Ref<PackedScene> cache; - }; - - LocalVector<SpawnableScene> spawnable_scenes; - - HashSet<ResourceUID::ID> spawnable_ids; - NodePath spawn_path; - - struct SpawnInfo { - Variant args; - int id = INVALID_ID; - SpawnInfo(Variant p_args, int p_id) { - id = p_id; - args = p_args; - } - SpawnInfo() {} - }; - - ObjectID spawn_node; - HashMap<ObjectID, SpawnInfo> tracked_nodes; - bool auto_spawn = false; - uint32_t spawn_limit = 0; - - void _update_spawn_node(); - void _track(Node *p_node, const Variant &p_argument, int p_scene_id = INVALID_ID); - void _node_added(Node *p_node); - void _node_exit(ObjectID p_id); - void _node_ready(ObjectID p_id); - - Vector<String> _get_spawnable_scenes() const; - void _set_spawnable_scenes(const Vector<String> &p_scenes); - -protected: - static void _bind_methods(); - void _notification(int p_what); - -#ifdef TOOLS_ENABLED - bool _set(const StringName &p_name, const Variant &p_value); - bool _get(const StringName &p_name, Variant &r_ret) const; - void _get_property_list(List<PropertyInfo> *p_list) const; -#endif -public: - Node *get_spawn_node() const { return spawn_node.is_valid() ? Object::cast_to<Node>(ObjectDB::get_instance(spawn_node)) : nullptr; } - - void add_spawnable_scene(const String &p_path); - int get_spawnable_scene_count() const; - String get_spawnable_scene(int p_idx) const; - void clear_spawnable_scenes(); - - NodePath get_spawn_path() const; - void set_spawn_path(const NodePath &p_path); - uint32_t get_spawn_limit() const { return spawn_limit; } - void set_spawn_limit(uint32_t p_limit) { spawn_limit = p_limit; } - bool is_auto_spawning() const; - void set_auto_spawning(bool p_enabled); - - const Variant get_spawn_argument(const ObjectID &p_id) const; - int find_spawnable_scene_index_from_object(const ObjectID &p_id) const; - int find_spawnable_scene_index_from_path(const String &p_path) const; - Node *spawn(const Variant &p_data = Variant()); - Node *instantiate_custom(const Variant &p_data); - Node *instantiate_scene(int p_idx); - - GDVIRTUAL1R(Object *, _spawn_custom, const Variant &); - - MultiplayerSpawner() {} -}; - -#endif // MULTIPLAYER_SPAWNER_H diff --git a/scene/multiplayer/multiplayer_synchronizer.cpp b/scene/multiplayer/multiplayer_synchronizer.cpp deleted file mode 100644 index 68f6e54fa8..0000000000 --- a/scene/multiplayer/multiplayer_synchronizer.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/*************************************************************************/ -/* multiplayer_synchronizer.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "multiplayer_synchronizer.h" - -#include "core/config/engine.h" -#include "core/multiplayer/multiplayer_api.h" - -Object *MultiplayerSynchronizer::_get_prop_target(Object *p_obj, const NodePath &p_path) { - if (p_path.get_name_count() == 0) { - return p_obj; - } - Node *node = Object::cast_to<Node>(p_obj); - ERR_FAIL_COND_V_MSG(!node || !node->has_node(p_path), nullptr, vformat("Node '%s' not found.", p_path)); - return node->get_node(p_path); -} - -void MultiplayerSynchronizer::_stop() { - Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr; - if (node) { - get_multiplayer()->replication_stop(node, this); - } -} - -void MultiplayerSynchronizer::_start() { - Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr; - if (node) { - get_multiplayer()->replication_start(node, this); - } -} - -Error MultiplayerSynchronizer::get_state(const List<NodePath> &p_properties, Object *p_obj, Vector<Variant> &r_variant, Vector<const Variant *> &r_variant_ptrs) { - ERR_FAIL_COND_V(!p_obj, ERR_INVALID_PARAMETER); - r_variant.resize(p_properties.size()); - r_variant_ptrs.resize(r_variant.size()); - int i = 0; - for (const NodePath &prop : p_properties) { - bool valid = false; - const Object *obj = _get_prop_target(p_obj, prop); - ERR_FAIL_COND_V(!obj, FAILED); - r_variant.write[i] = obj->get(prop.get_concatenated_subnames(), &valid); - r_variant_ptrs.write[i] = &r_variant[i]; - ERR_FAIL_COND_V_MSG(!valid, ERR_INVALID_DATA, vformat("Property '%s' not found.", prop)); - i++; - } - return OK; -} - -Error MultiplayerSynchronizer::set_state(const List<NodePath> &p_properties, Object *p_obj, const Vector<Variant> &p_state) { - ERR_FAIL_COND_V(!p_obj, ERR_INVALID_PARAMETER); - int i = 0; - for (const NodePath &prop : p_properties) { - Object *obj = _get_prop_target(p_obj, prop); - ERR_FAIL_COND_V(!obj, FAILED); - obj->set(prop.get_concatenated_subnames(), p_state[i]); - i += 1; - } - return OK; -} - -void MultiplayerSynchronizer::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_root_path", "path"), &MultiplayerSynchronizer::set_root_path); - ClassDB::bind_method(D_METHOD("get_root_path"), &MultiplayerSynchronizer::get_root_path); - - ClassDB::bind_method(D_METHOD("set_replication_interval", "milliseconds"), &MultiplayerSynchronizer::set_replication_interval); - ClassDB::bind_method(D_METHOD("get_replication_interval"), &MultiplayerSynchronizer::get_replication_interval); - - ClassDB::bind_method(D_METHOD("set_replication_config", "config"), &MultiplayerSynchronizer::set_replication_config); - ClassDB::bind_method(D_METHOD("get_replication_config"), &MultiplayerSynchronizer::get_replication_config); - - ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_path"), "set_root_path", "get_root_path"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "replication_interval", PROPERTY_HINT_RANGE, "0,5,0.001,suffix:s"), "set_replication_interval", "get_replication_interval"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "replication_config", PROPERTY_HINT_RESOURCE_TYPE, "SceneReplicationConfig"), "set_replication_config", "get_replication_config"); -} - -void MultiplayerSynchronizer::_notification(int p_what) { -#ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { - return; - } -#endif - if (root_path.is_empty()) { - return; - } - - switch (p_what) { - case NOTIFICATION_ENTER_TREE: { - _start(); - } break; - - case NOTIFICATION_EXIT_TREE: { - _stop(); - } break; - } -} - -void MultiplayerSynchronizer::set_replication_interval(double p_interval) { - ERR_FAIL_COND_MSG(p_interval < 0, "Interval must be greater or equal to 0 (where 0 means default)"); - interval_msec = uint64_t(p_interval * 1000); -} - -double MultiplayerSynchronizer::get_replication_interval() const { - return double(interval_msec) / 1000.0; -} - -uint64_t MultiplayerSynchronizer::get_replication_interval_msec() const { - return interval_msec; -} - -void MultiplayerSynchronizer::set_replication_config(Ref<SceneReplicationConfig> p_config) { - replication_config = p_config; -} - -Ref<SceneReplicationConfig> MultiplayerSynchronizer::get_replication_config() { - return replication_config; -} - -void MultiplayerSynchronizer::set_root_path(const NodePath &p_path) { - _stop(); - root_path = p_path; - _start(); -} - -NodePath MultiplayerSynchronizer::get_root_path() const { - return root_path; -} - -void MultiplayerSynchronizer::set_multiplayer_authority(int p_peer_id, bool p_recursive) { - Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr; - if (!node) { - Node::set_multiplayer_authority(p_peer_id, p_recursive); - return; - } - get_multiplayer()->replication_stop(node, this); - Node::set_multiplayer_authority(p_peer_id, p_recursive); - get_multiplayer()->replication_start(node, this); -} diff --git a/scene/multiplayer/scene_cache_interface.cpp b/scene/multiplayer/scene_cache_interface.cpp deleted file mode 100644 index 7c271341db..0000000000 --- a/scene/multiplayer/scene_cache_interface.cpp +++ /dev/null @@ -1,263 +0,0 @@ -/*************************************************************************/ -/* scene_cache_interface.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "scene_cache_interface.h" - -#include "core/io/marshalls.h" -#include "scene/main/node.h" -#include "scene/main/window.h" - -MultiplayerCacheInterface *SceneCacheInterface::_create(MultiplayerAPI *p_multiplayer) { - return memnew(SceneCacheInterface(p_multiplayer)); -} - -void SceneCacheInterface::make_default() { - MultiplayerAPI::create_default_cache_interface = _create; -} - -void SceneCacheInterface::on_peer_change(int p_id, bool p_connected) { - if (p_connected) { - path_get_cache.insert(p_id, PathGetCache()); - } else { - // Cleanup get cache. - path_get_cache.erase(p_id); - // Cleanup sent cache. - // Some refactoring is needed to make this faster and do paths GC. - for (const KeyValue<NodePath, PathSentCache> &E : path_send_cache) { - PathSentCache *psc = path_send_cache.getptr(E.key); - psc->confirmed_peers.erase(p_id); - } - } -} - -void SceneCacheInterface::process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len) { - Node *root_node = SceneTree::get_singleton()->get_root()->get_node(multiplayer->get_root_path()); - ERR_FAIL_COND(!root_node); - ERR_FAIL_COND_MSG(p_packet_len < 38, "Invalid packet received. Size too small."); - int ofs = 1; - - String methods_md5; - methods_md5.parse_utf8((const char *)(p_packet + ofs), 32); - ofs += 33; - - int id = decode_uint32(&p_packet[ofs]); - ofs += 4; - - String paths; - paths.parse_utf8((const char *)(p_packet + ofs), p_packet_len - ofs); - - NodePath path = paths; - - if (!path_get_cache.has(p_from)) { - path_get_cache[p_from] = PathGetCache(); - } - - Node *node = root_node->get_node(path); - ERR_FAIL_COND(node == nullptr); - const bool valid_rpc_checksum = multiplayer->get_rpc_md5(node) == methods_md5; - if (valid_rpc_checksum == false) { - ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path); - } - - PathGetCache::NodeInfo ni; - ni.path = path; - - path_get_cache[p_from].nodes[id] = ni; - - // Encode path to send ack. - CharString pname = String(path).utf8(); - int len = encode_cstring(pname.get_data(), nullptr); - - Vector<uint8_t> packet; - - packet.resize(1 + 1 + len); - packet.write[0] = MultiplayerAPI::NETWORK_COMMAND_CONFIRM_PATH; - packet.write[1] = valid_rpc_checksum; - encode_cstring(pname.get_data(), &packet.write[2]); - - Ref<MultiplayerPeer> multiplayer_peer = multiplayer->get_multiplayer_peer(); - ERR_FAIL_COND(multiplayer_peer.is_null()); - -#ifdef DEBUG_ENABLED - multiplayer->profile_bandwidth("out", packet.size()); -#endif - - multiplayer_peer->set_transfer_channel(0); - multiplayer_peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE); - multiplayer_peer->set_target_peer(p_from); - multiplayer_peer->put_packet(packet.ptr(), packet.size()); -} - -void SceneCacheInterface::process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) { - ERR_FAIL_COND_MSG(p_packet_len < 3, "Invalid packet received. Size too small."); - - const bool valid_rpc_checksum = p_packet[1]; - - String paths; - paths.parse_utf8((const char *)&p_packet[2], p_packet_len - 2); - - NodePath path = paths; - - if (valid_rpc_checksum == false) { - ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path); - } - - PathSentCache *psc = path_send_cache.getptr(path); - ERR_FAIL_COND_MSG(!psc, "Invalid packet received. Tries to confirm a path which was not found in cache."); - - HashMap<int, bool>::Iterator E = psc->confirmed_peers.find(p_from); - ERR_FAIL_COND_MSG(!E, "Invalid packet received. Source peer was not found in cache for the given path."); - E->value = true; -} - -Error SceneCacheInterface::_send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, const List<int> &p_peers) { - // Encode function name. - const CharString path = String(p_path).utf8(); - const int path_len = encode_cstring(path.get_data(), nullptr); - - // Extract MD5 from rpc methods list. - const String methods_md5 = multiplayer->get_rpc_md5(p_node); - const int methods_md5_len = 33; // 32 + 1 for the `0` that is added by the encoder. - - Vector<uint8_t> packet; - packet.resize(1 + 4 + path_len + methods_md5_len); - int ofs = 0; - - packet.write[ofs] = MultiplayerAPI::NETWORK_COMMAND_SIMPLIFY_PATH; - ofs += 1; - - ofs += encode_cstring(methods_md5.utf8().get_data(), &packet.write[ofs]); - - ofs += encode_uint32(psc->id, &packet.write[ofs]); - - ofs += encode_cstring(path.get_data(), &packet.write[ofs]); - - Ref<MultiplayerPeer> multiplayer_peer = multiplayer->get_multiplayer_peer(); - ERR_FAIL_COND_V(multiplayer_peer.is_null(), ERR_BUG); - -#ifdef DEBUG_ENABLED - multiplayer->profile_bandwidth("out", packet.size() * p_peers.size()); -#endif - - Error err = OK; - for (int peer_id : p_peers) { - multiplayer_peer->set_target_peer(peer_id); - multiplayer_peer->set_transfer_channel(0); - multiplayer_peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE); - err = multiplayer_peer->put_packet(packet.ptr(), packet.size()); - ERR_FAIL_COND_V(err != OK, err); - // Insert into confirmed, but as false since it was not confirmed. - psc->confirmed_peers.insert(peer_id, false); - } - return err; -} - -bool SceneCacheInterface::is_cache_confirmed(NodePath p_path, int p_peer) { - const PathSentCache *psc = path_send_cache.getptr(p_path); - ERR_FAIL_COND_V(!psc, false); - HashMap<int, bool>::ConstIterator F = psc->confirmed_peers.find(p_peer); - ERR_FAIL_COND_V(!F, false); // Should never happen. - return F->value; -} - -bool SceneCacheInterface::send_object_cache(Object *p_obj, NodePath p_path, int p_peer_id, int &r_id) { - Node *node = Object::cast_to<Node>(p_obj); - ERR_FAIL_COND_V(!node, false); - // See if the path is cached. - PathSentCache *psc = path_send_cache.getptr(p_path); - if (!psc) { - // Path is not cached, create. - path_send_cache[p_path] = PathSentCache(); - psc = path_send_cache.getptr(p_path); - psc->id = last_send_cache_id++; - } - r_id = psc->id; - - bool has_all_peers = true; - List<int> peers_to_add; // If one is missing, take note to add it. - - if (p_peer_id > 0) { - // Fast single peer check. - HashMap<int, bool>::Iterator F = psc->confirmed_peers.find(p_peer_id); - if (!F) { - peers_to_add.push_back(p_peer_id); // Need to also be notified. - has_all_peers = false; - } else if (!F->value) { - has_all_peers = false; - } - } else { - // Long and painful. - for (const int &E : multiplayer->get_connected_peers()) { - if (p_peer_id < 0 && E == -p_peer_id) { - continue; // Continue, excluded. - } - if (p_peer_id > 0 && E != p_peer_id) { - continue; // Continue, not for this peer. - } - - HashMap<int, bool>::Iterator F = psc->confirmed_peers.find(E); - if (!F) { - peers_to_add.push_back(E); // Need to also be notified. - has_all_peers = false; - } else if (!F->value) { - has_all_peers = false; - } - } - } - - if (peers_to_add.size()) { - _send_confirm_path(node, p_path, psc, peers_to_add); - } - - return has_all_peers; -} - -Object *SceneCacheInterface::get_cached_object(int p_from, uint32_t p_cache_id) { - Node *root_node = SceneTree::get_singleton()->get_root()->get_node(multiplayer->get_root_path()); - ERR_FAIL_COND_V(!root_node, nullptr); - HashMap<int, PathGetCache>::Iterator E = path_get_cache.find(p_from); - ERR_FAIL_COND_V_MSG(!E, nullptr, vformat("No cache found for peer %d.", p_from)); - - HashMap<int, PathGetCache::NodeInfo>::Iterator F = E->value.nodes.find(p_cache_id); - ERR_FAIL_COND_V_MSG(!F, nullptr, vformat("ID %d not found in cache of peer %d.", p_cache_id, p_from)); - - PathGetCache::NodeInfo *ni = &F->value; - Node *node = root_node->get_node(ni->path); - if (!node) { - ERR_PRINT("Failed to get cached path: " + String(ni->path) + "."); - } - return node; -} - -void SceneCacheInterface::clear() { - path_get_cache.clear(); - path_send_cache.clear(); - last_send_cache_id = 1; -} diff --git a/scene/multiplayer/scene_cache_interface.h b/scene/multiplayer/scene_cache_interface.h deleted file mode 100644 index 3116233b5b..0000000000 --- a/scene/multiplayer/scene_cache_interface.h +++ /dev/null @@ -1,82 +0,0 @@ -/*************************************************************************/ -/* scene_cache_interface.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef SCENE_CACHE_INTERFACE_H -#define SCENE_CACHE_INTERFACE_H - -#include "core/multiplayer/multiplayer_api.h" - -class SceneCacheInterface : public MultiplayerCacheInterface { - GDCLASS(SceneCacheInterface, MultiplayerCacheInterface); - -private: - MultiplayerAPI *multiplayer = nullptr; - - //path sent caches - struct PathSentCache { - HashMap<int, bool> confirmed_peers; - int id; - }; - - //path get caches - struct PathGetCache { - struct NodeInfo { - NodePath path; - ObjectID instance; - }; - - HashMap<int, NodeInfo> nodes; - }; - - HashMap<NodePath, PathSentCache> path_send_cache; - HashMap<int, PathGetCache> path_get_cache; - int last_send_cache_id = 1; - -protected: - Error _send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, const List<int> &p_peers); - static MultiplayerCacheInterface *_create(MultiplayerAPI *p_multiplayer); - -public: - static void make_default(); - - virtual void clear() override; - virtual void on_peer_change(int p_id, bool p_connected) override; - virtual void process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len) override; - virtual void process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) override; - - // Returns true if all peers have cached path. - virtual bool send_object_cache(Object *p_obj, NodePath p_path, int p_target, int &p_id) override; - virtual Object *get_cached_object(int p_from, uint32_t p_cache_id) override; - virtual bool is_cache_confirmed(NodePath p_path, int p_peer) override; - - SceneCacheInterface(MultiplayerAPI *p_multiplayer) { multiplayer = p_multiplayer; } -}; - -#endif // SCENE_CACHE_INTERFACE_H diff --git a/scene/multiplayer/scene_replication_interface.cpp b/scene/multiplayer/scene_replication_interface.cpp deleted file mode 100644 index e4715ceb88..0000000000 --- a/scene/multiplayer/scene_replication_interface.cpp +++ /dev/null @@ -1,419 +0,0 @@ -/*************************************************************************/ -/* scene_replication_interface.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "scene_replication_interface.h" - -#include "core/io/marshalls.h" -#include "scene/main/node.h" -#include "scene/multiplayer/multiplayer_spawner.h" -#include "scene/multiplayer/multiplayer_synchronizer.h" - -#define MAKE_ROOM(m_amount) \ - if (packet_cache.size() < m_amount) \ - packet_cache.resize(m_amount); - -MultiplayerReplicationInterface *SceneReplicationInterface::_create(MultiplayerAPI *p_multiplayer) { - return memnew(SceneReplicationInterface(p_multiplayer)); -} - -void SceneReplicationInterface::make_default() { - MultiplayerAPI::create_default_replication_interface = _create; -} - -void SceneReplicationInterface::_free_remotes(int p_id) { - const HashMap<uint32_t, ObjectID> remotes = rep_state->peer_get_remotes(p_id); - for (const KeyValue<uint32_t, ObjectID> &E : remotes) { - Node *node = rep_state->get_node(E.value); - ERR_CONTINUE(!node); - node->queue_delete(); - } -} - -void SceneReplicationInterface::on_peer_change(int p_id, bool p_connected) { - if (p_connected) { - rep_state->on_peer_change(p_id, p_connected); - for (const ObjectID &oid : rep_state->get_spawned_nodes()) { - _send_spawn(rep_state->get_node(oid), rep_state->get_spawner(oid), p_id); - } - for (const ObjectID &oid : rep_state->get_path_only_nodes()) { - Node *node = rep_state->get_node(oid); - MultiplayerSynchronizer *sync = rep_state->get_synchronizer(oid); - ERR_CONTINUE(!node || !sync); - if (sync->is_multiplayer_authority()) { - rep_state->peer_add_node(p_id, oid); - } - } - } else { - _free_remotes(p_id); - rep_state->on_peer_change(p_id, p_connected); - } -} - -void SceneReplicationInterface::on_reset() { - for (int pid : rep_state->get_peers()) { - _free_remotes(pid); - } - rep_state->reset(); -} - -void SceneReplicationInterface::on_network_process() { - uint64_t msec = OS::get_singleton()->get_ticks_msec(); - for (int peer : rep_state->get_peers()) { - _send_sync(peer, msec); - } -} - -Error SceneReplicationInterface::on_spawn(Object *p_obj, Variant p_config) { - Node *node = Object::cast_to<Node>(p_obj); - ERR_FAIL_COND_V(!node || p_config.get_type() != Variant::OBJECT, ERR_INVALID_PARAMETER); - MultiplayerSpawner *spawner = Object::cast_to<MultiplayerSpawner>(p_config.get_validated_object()); - ERR_FAIL_COND_V(!spawner, ERR_INVALID_PARAMETER); - Error err = rep_state->config_add_spawn(node, spawner); - ERR_FAIL_COND_V(err != OK, err); - return _send_spawn(node, spawner, 0); -} - -Error SceneReplicationInterface::on_despawn(Object *p_obj, Variant p_config) { - Node *node = Object::cast_to<Node>(p_obj); - ERR_FAIL_COND_V(!node || p_config.get_type() != Variant::OBJECT, ERR_INVALID_PARAMETER); - MultiplayerSpawner *spawner = Object::cast_to<MultiplayerSpawner>(p_config.get_validated_object()); - ERR_FAIL_COND_V(!p_obj || !spawner, ERR_INVALID_PARAMETER); - Error err = rep_state->config_del_spawn(node, spawner); - ERR_FAIL_COND_V(err != OK, err); - return _send_despawn(node, 0); -} - -Error SceneReplicationInterface::on_replication_start(Object *p_obj, Variant p_config) { - Node *node = Object::cast_to<Node>(p_obj); - ERR_FAIL_COND_V(!node || p_config.get_type() != Variant::OBJECT, ERR_INVALID_PARAMETER); - MultiplayerSynchronizer *sync = Object::cast_to<MultiplayerSynchronizer>(p_config.get_validated_object()); - ERR_FAIL_COND_V(!sync, ERR_INVALID_PARAMETER); - rep_state->config_add_sync(node, sync); - // Try to apply initial state if spawning (hack to apply if before ready). - if (pending_spawn == p_obj->get_instance_id()) { - pending_spawn = ObjectID(); // Make sure this only happens once. - const List<NodePath> props = sync->get_replication_config()->get_spawn_properties(); - Vector<Variant> vars; - vars.resize(props.size()); - int consumed; - Error err = MultiplayerAPI::decode_and_decompress_variants(vars, pending_buffer, pending_buffer_size, consumed); - ERR_FAIL_COND_V(err, err); - err = MultiplayerSynchronizer::set_state(props, node, vars); - ERR_FAIL_COND_V(err, err); - } else if (multiplayer->has_multiplayer_peer() && sync->is_multiplayer_authority()) { - // Either it's a spawn or a static sync, in any case add it to the list of known nodes. - rep_state->peer_add_node(0, p_obj->get_instance_id()); - } - return OK; -} - -Error SceneReplicationInterface::on_replication_stop(Object *p_obj, Variant p_config) { - Node *node = Object::cast_to<Node>(p_obj); - ERR_FAIL_COND_V(!node || p_config.get_type() != Variant::OBJECT, ERR_INVALID_PARAMETER); - MultiplayerSynchronizer *sync = Object::cast_to<MultiplayerSynchronizer>(p_config.get_validated_object()); - ERR_FAIL_COND_V(!p_obj || !sync, ERR_INVALID_PARAMETER); - return rep_state->config_del_sync(node, sync); -} - -Error SceneReplicationInterface::_send_raw(const uint8_t *p_buffer, int p_size, int p_peer, bool p_reliable) { - ERR_FAIL_COND_V(!p_buffer || p_size < 1, ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V(!multiplayer, ERR_UNCONFIGURED); - ERR_FAIL_COND_V(!multiplayer->has_multiplayer_peer(), ERR_UNCONFIGURED); - -#ifdef DEBUG_ENABLED - multiplayer->profile_bandwidth("out", p_size); -#endif - - Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer(); - peer->set_target_peer(p_peer); - peer->set_transfer_channel(0); - peer->set_transfer_mode(p_reliable ? Multiplayer::TRANSFER_MODE_RELIABLE : Multiplayer::TRANSFER_MODE_UNRELIABLE); - return peer->put_packet(p_buffer, p_size); -} - -Error SceneReplicationInterface::_send_spawn(Node *p_node, MultiplayerSpawner *p_spawner, int p_peer) { - ERR_FAIL_COND_V(p_peer < 0, ERR_BUG); - ERR_FAIL_COND_V(!multiplayer, ERR_BUG); - ERR_FAIL_COND_V(!p_spawner || !p_node, ERR_BUG); - - const ObjectID oid = p_node->get_instance_id(); - uint32_t nid = rep_state->ensure_net_id(oid); - - // Prepare custom arg and scene_id - uint8_t scene_id = p_spawner->find_spawnable_scene_index_from_object(oid); - bool is_custom = scene_id == MultiplayerSpawner::INVALID_ID; - Variant spawn_arg = p_spawner->get_spawn_argument(oid); - int spawn_arg_size = 0; - if (is_custom) { - Error err = MultiplayerAPI::encode_and_compress_variant(spawn_arg, nullptr, spawn_arg_size, false); - ERR_FAIL_COND_V(err, err); - } - - // Prepare spawn state. - int state_size = 0; - Vector<Variant> state_vars; - Vector<const Variant *> state_varp; - MultiplayerSynchronizer *synchronizer = rep_state->get_synchronizer(oid); - if (synchronizer && synchronizer->get_replication_config().is_valid()) { - const List<NodePath> props = synchronizer->get_replication_config()->get_spawn_properties(); - Error err = MultiplayerSynchronizer::get_state(props, p_node, state_vars, state_varp); - ERR_FAIL_COND_V_MSG(err != OK, err, "Unable to retrieve spawn state."); - err = MultiplayerAPI::encode_and_compress_variants(state_varp.ptrw(), state_varp.size(), nullptr, state_size); - ERR_FAIL_COND_V_MSG(err != OK, err, "Unable to encode spawn state."); - } - - // Prepare simplified path. - NodePath rel_path = multiplayer->get_root_path().rel_path_to(p_spawner->get_path()); - - int path_id = 0; - multiplayer->send_object_cache(p_spawner, rel_path, p_peer, path_id); - - // Encode name and parent ID. - CharString cname = p_node->get_name().operator String().utf8(); - int nlen = encode_cstring(cname.get_data(), nullptr); - MAKE_ROOM(1 + 1 + 4 + 4 + 4 + nlen + (is_custom ? 4 + spawn_arg_size : 0) + state_size); - uint8_t *ptr = packet_cache.ptrw(); - ptr[0] = (uint8_t)MultiplayerAPI::NETWORK_COMMAND_SPAWN; - ptr[1] = scene_id; - int ofs = 2; - ofs += encode_uint32(path_id, &ptr[ofs]); - ofs += encode_uint32(nid, &ptr[ofs]); - ofs += encode_uint32(nlen, &ptr[ofs]); - ofs += encode_cstring(cname.get_data(), &ptr[ofs]); - // Write args - if (is_custom) { - ofs += encode_uint32(spawn_arg_size, &ptr[ofs]); - Error err = MultiplayerAPI::encode_and_compress_variant(spawn_arg, &ptr[ofs], spawn_arg_size, false); - ERR_FAIL_COND_V(err, err); - ofs += spawn_arg_size; - } - // Write state. - if (state_size) { - Error err = MultiplayerAPI::encode_and_compress_variants(state_varp.ptrw(), state_varp.size(), &ptr[ofs], state_size); - ERR_FAIL_COND_V(err, err); - ofs += state_size; - } - Error err = _send_raw(ptr, ofs, p_peer, true); - ERR_FAIL_COND_V(err, err); - return rep_state->peer_add_node(p_peer, oid); -} - -Error SceneReplicationInterface::_send_despawn(Node *p_node, int p_peer) { - const ObjectID oid = p_node->get_instance_id(); - MAKE_ROOM(5); - uint8_t *ptr = packet_cache.ptrw(); - ptr[0] = (uint8_t)MultiplayerAPI::NETWORK_COMMAND_DESPAWN; - int ofs = 1; - uint32_t nid = rep_state->get_net_id(oid); - ofs += encode_uint32(nid, &ptr[ofs]); - Error err = _send_raw(ptr, ofs, p_peer, true); - ERR_FAIL_COND_V(err, err); - return rep_state->peer_del_node(p_peer, oid); -} - -Error SceneReplicationInterface::on_spawn_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) { - ERR_FAIL_COND_V_MSG(p_buffer_len < 14, ERR_INVALID_DATA, "Invalid spawn packet received"); - int ofs = 1; // The spawn/despawn command. - uint8_t scene_id = p_buffer[ofs]; - ofs += 1; - uint32_t node_target = decode_uint32(&p_buffer[ofs]); - ofs += 4; - MultiplayerSpawner *spawner = Object::cast_to<MultiplayerSpawner>(multiplayer->get_cached_object(p_from, node_target)); - ERR_FAIL_COND_V(!spawner, ERR_DOES_NOT_EXIST); - ERR_FAIL_COND_V(p_from != spawner->get_multiplayer_authority(), ERR_UNAUTHORIZED); - - uint32_t net_id = decode_uint32(&p_buffer[ofs]); - ofs += 4; - uint32_t name_len = decode_uint32(&p_buffer[ofs]); - ofs += 4; - ERR_FAIL_COND_V_MSG(name_len > uint32_t(p_buffer_len - ofs), ERR_INVALID_DATA, vformat("Invalid spawn packet size: %d, wants: %d", p_buffer_len, ofs + name_len)); - ERR_FAIL_COND_V_MSG(name_len < 1, ERR_INVALID_DATA, "Zero spawn name size."); - - // We need to make sure no trickery happens here, but we want to allow autogenerated ("@") node names. - const String name = String::utf8((const char *)&p_buffer[ofs], name_len); - ERR_FAIL_COND_V_MSG(name.validate_node_name() != name, ERR_INVALID_DATA, vformat("Invalid node name received: '%s'. Make sure to add nodes via 'add_child(node, true)' remotely.", name)); - ofs += name_len; - - // Check that we can spawn. - Node *parent = spawner->get_node_or_null(spawner->get_spawn_path()); - ERR_FAIL_COND_V(!parent, ERR_UNCONFIGURED); - ERR_FAIL_COND_V(parent->has_node(name), ERR_INVALID_DATA); - - Node *node = nullptr; - if (scene_id == MultiplayerSpawner::INVALID_ID) { - // Custom spawn. - ERR_FAIL_COND_V(p_buffer_len - ofs < 4, ERR_INVALID_DATA); - uint32_t arg_size = decode_uint32(&p_buffer[ofs]); - ofs += 4; - ERR_FAIL_COND_V(arg_size > uint32_t(p_buffer_len - ofs), ERR_INVALID_DATA); - Variant v; - Error err = MultiplayerAPI::decode_and_decompress_variant(v, &p_buffer[ofs], arg_size, nullptr, false); - ERR_FAIL_COND_V(err != OK, err); - ofs += arg_size; - node = spawner->instantiate_custom(v); - } else { - // Scene based spawn. - node = spawner->instantiate_scene(scene_id); - } - ERR_FAIL_COND_V(!node, ERR_UNAUTHORIZED); - node->set_name(name); - rep_state->peer_add_remote(p_from, net_id, node, spawner); - // The initial state will be applied during the sync config (i.e. before _ready). - int state_len = p_buffer_len - ofs; - if (state_len) { - pending_spawn = node->get_instance_id(); - pending_buffer = &p_buffer[ofs]; - pending_buffer_size = state_len; - } - parent->add_child(node); - pending_spawn = ObjectID(); - pending_buffer = nullptr; - pending_buffer_size = 0; - return OK; -} - -Error SceneReplicationInterface::on_despawn_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) { - ERR_FAIL_COND_V_MSG(p_buffer_len < 5, ERR_INVALID_DATA, "Invalid spawn packet received"); - int ofs = 1; // The spawn/despawn command. - uint32_t net_id = decode_uint32(&p_buffer[ofs]); - ofs += 4; - Node *node = nullptr; - Error err = rep_state->peer_del_remote(p_from, net_id, &node); - ERR_FAIL_COND_V(err != OK, err); - ERR_FAIL_COND_V(!node, ERR_BUG); - if (node->get_parent() != nullptr) { - node->get_parent()->remove_child(node); - } - node->queue_delete(); - return OK; -} - -void SceneReplicationInterface::_send_sync(int p_peer, uint64_t p_msec) { - const HashSet<ObjectID> &known = rep_state->get_known_nodes(p_peer); - if (known.is_empty()) { - return; - } - MAKE_ROOM(sync_mtu); - uint8_t *ptr = packet_cache.ptrw(); - ptr[0] = MultiplayerAPI::NETWORK_COMMAND_SYNC; - int ofs = 1; - ofs += encode_uint16(rep_state->peer_sync_next(p_peer), &ptr[1]); - // Can only send updates for already notified nodes. - // This is a lazy implementation, we could optimize much more here with by grouping by replication config. - for (const ObjectID &oid : known) { - if (!rep_state->update_sync_time(oid, p_msec)) { - continue; // nothing to sync. - } - MultiplayerSynchronizer *sync = rep_state->get_synchronizer(oid); - ERR_CONTINUE(!sync); - Node *node = rep_state->get_node(oid); - ERR_CONTINUE(!node); - int size; - Vector<Variant> vars; - Vector<const Variant *> varp; - const List<NodePath> props = sync->get_replication_config()->get_sync_properties(); - Error err = MultiplayerSynchronizer::get_state(props, node, vars, varp); - ERR_CONTINUE_MSG(err != OK, "Unable to retrieve sync state."); - err = MultiplayerAPI::encode_and_compress_variants(varp.ptrw(), varp.size(), nullptr, size); - ERR_CONTINUE_MSG(err != OK, "Unable to encode sync state."); - // TODO Handle single state above MTU. - ERR_CONTINUE_MSG(size > 3 + 4 + 4 + sync_mtu, vformat("Node states bigger then MTU will not be sent (%d > %d): %s", size, sync_mtu, node->get_path())); - if (ofs + 4 + 4 + size > sync_mtu) { - // Send what we got, and reset write. - _send_raw(packet_cache.ptr(), ofs, p_peer, false); - ofs = 3; - } - if (size) { - uint32_t net_id = rep_state->get_net_id(oid); - if (net_id == 0 || (net_id & 0x80000000)) { - // First time path based ID. - NodePath rel_path = multiplayer->get_root_path().rel_path_to(sync->get_path()); - int path_id = 0; - multiplayer->send_object_cache(sync, rel_path, p_peer, path_id); - ERR_CONTINUE_MSG(net_id && net_id != (uint32_t(path_id) | 0x80000000), "This should never happen!"); - net_id = path_id; - rep_state->set_net_id(oid, net_id | 0x80000000); - } - ofs += encode_uint32(rep_state->get_net_id(oid), &ptr[ofs]); - ofs += encode_uint32(size, &ptr[ofs]); - MultiplayerAPI::encode_and_compress_variants(varp.ptrw(), varp.size(), &ptr[ofs], size); - ofs += size; - } - } - if (ofs > 3) { - // Got some left over to send. - _send_raw(packet_cache.ptr(), ofs, p_peer, false); - } -} - -Error SceneReplicationInterface::on_sync_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) { - ERR_FAIL_COND_V_MSG(p_buffer_len < 11, ERR_INVALID_DATA, "Invalid sync packet received"); - uint16_t time = decode_uint16(&p_buffer[1]); - int ofs = 3; - rep_state->peer_sync_recv(p_from, time); - while (ofs + 8 < p_buffer_len) { - uint32_t net_id = decode_uint32(&p_buffer[ofs]); - ofs += 4; - uint32_t size = decode_uint32(&p_buffer[ofs]); - ofs += 4; - Node *node = nullptr; - if (net_id & 0x80000000) { - MultiplayerSynchronizer *sync = Object::cast_to<MultiplayerSynchronizer>(multiplayer->get_cached_object(p_from, net_id & 0x7FFFFFFF)); - ERR_FAIL_COND_V(!sync || sync->get_multiplayer_authority() != p_from, ERR_UNAUTHORIZED); - node = sync->get_node(sync->get_root_path()); - } else { - node = rep_state->peer_get_remote(p_from, net_id); - } - if (!node) { - // Not received yet. - ofs += size; - continue; - } - const ObjectID oid = node->get_instance_id(); - if (!rep_state->update_last_node_sync(oid, time)) { - // State is too old. - ofs += size; - continue; - } - MultiplayerSynchronizer *sync = rep_state->get_synchronizer(oid); - ERR_FAIL_COND_V(!sync, ERR_BUG); - ERR_FAIL_COND_V(size > uint32_t(p_buffer_len - ofs), ERR_BUG); - const List<NodePath> props = sync->get_replication_config()->get_sync_properties(); - Vector<Variant> vars; - vars.resize(props.size()); - int consumed; - Error err = MultiplayerAPI::decode_and_decompress_variants(vars, &p_buffer[ofs], size, consumed); - ERR_FAIL_COND_V(err, err); - err = MultiplayerSynchronizer::set_state(props, node, vars); - ERR_FAIL_COND_V(err, err); - ofs += size; - } - return OK; -} diff --git a/scene/multiplayer/scene_replication_interface.h b/scene/multiplayer/scene_replication_interface.h deleted file mode 100644 index 60ac95c93c..0000000000 --- a/scene/multiplayer/scene_replication_interface.h +++ /dev/null @@ -1,84 +0,0 @@ -/*************************************************************************/ -/* scene_replication_interface.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef SCENE_TREE_REPLICATOR_INTERFACE_H -#define SCENE_TREE_REPLICATOR_INTERFACE_H - -#include "core/multiplayer/multiplayer_api.h" - -#include "scene/multiplayer/scene_replication_state.h" - -class SceneReplicationInterface : public MultiplayerReplicationInterface { - GDCLASS(SceneReplicationInterface, MultiplayerReplicationInterface); - -private: - void _send_sync(int p_peer, uint64_t p_msec); - Error _send_spawn(Node *p_node, MultiplayerSpawner *p_spawner, int p_peer); - Error _send_despawn(Node *p_node, int p_peer); - Error _send_raw(const uint8_t *p_buffer, int p_size, int p_peer, bool p_reliable); - - void _free_remotes(int p_peer); - - Ref<SceneReplicationState> rep_state; - MultiplayerAPI *multiplayer = nullptr; - PackedByteArray packet_cache; - int sync_mtu = 1350; // Highly dependent on underlying protocol. - - // An hack to apply the initial state before ready. - ObjectID pending_spawn; - const uint8_t *pending_buffer = nullptr; - int pending_buffer_size = 0; - -protected: - static MultiplayerReplicationInterface *_create(MultiplayerAPI *p_multiplayer); - -public: - static void make_default(); - - virtual void on_reset() override; - virtual void on_peer_change(int p_id, bool p_connected) override; - - virtual Error on_spawn(Object *p_obj, Variant p_config) override; - virtual Error on_despawn(Object *p_obj, Variant p_config) override; - virtual Error on_replication_start(Object *p_obj, Variant p_config) override; - virtual Error on_replication_stop(Object *p_obj, Variant p_config) override; - virtual void on_network_process() override; - - virtual Error on_spawn_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) override; - virtual Error on_despawn_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) override; - virtual Error on_sync_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) override; - - SceneReplicationInterface(MultiplayerAPI *p_multiplayer) { - rep_state.instantiate(); - multiplayer = p_multiplayer; - } -}; - -#endif // SCENE_TREE_REPLICATOR_INTERFACE_H diff --git a/scene/multiplayer/scene_replication_state.cpp b/scene/multiplayer/scene_replication_state.cpp deleted file mode 100644 index 937b30cb36..0000000000 --- a/scene/multiplayer/scene_replication_state.cpp +++ /dev/null @@ -1,254 +0,0 @@ -/*************************************************************************/ -/* scene_replication_state.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "scene/multiplayer/scene_replication_state.h" - -#include "core/multiplayer/multiplayer_api.h" -#include "scene/multiplayer/multiplayer_spawner.h" -#include "scene/multiplayer/multiplayer_synchronizer.h" -#include "scene/scene_string_names.h" - -SceneReplicationState::TrackedNode &SceneReplicationState::_track(const ObjectID &p_id) { - if (!tracked_nodes.has(p_id)) { - tracked_nodes[p_id] = TrackedNode(p_id); - Node *node = Object::cast_to<Node>(ObjectDB::get_instance(p_id)); - node->connect(SceneStringNames::get_singleton()->tree_exited, callable_mp(this, &SceneReplicationState::_untrack), varray(p_id), Node::CONNECT_ONESHOT); - } - return tracked_nodes[p_id]; -} - -void SceneReplicationState::_untrack(const ObjectID &p_id) { - if (tracked_nodes.has(p_id)) { - uint32_t net_id = tracked_nodes[p_id].net_id; - uint32_t peer = tracked_nodes[p_id].remote_peer; - tracked_nodes.erase(p_id); - // If it was spawned by a remote, remove it from the received nodes. - if (peer && peers_info.has(peer)) { - peers_info[peer].recv_nodes.erase(net_id); - } - // If we spawned or synced it, we need to remove it from any peer it was sent to. - if (net_id || peer == 0) { - for (KeyValue<int, PeerInfo> &E : peers_info) { - E.value.known_nodes.erase(p_id); - } - } - } -} - -const HashMap<uint32_t, ObjectID> SceneReplicationState::peer_get_remotes(int p_peer) const { - return peers_info.has(p_peer) ? peers_info[p_peer].recv_nodes : HashMap<uint32_t, ObjectID>(); -} - -bool SceneReplicationState::update_last_node_sync(const ObjectID &p_id, uint16_t p_time) { - TrackedNode *tnode = tracked_nodes.getptr(p_id); - ERR_FAIL_COND_V(!tnode, false); - if (p_time <= tnode->last_sync && tnode->last_sync - p_time < 32767) { - return false; - } - tnode->last_sync = p_time; - return true; -} - -bool SceneReplicationState::update_sync_time(const ObjectID &p_id, uint64_t p_msec) { - TrackedNode *tnode = tracked_nodes.getptr(p_id); - ERR_FAIL_COND_V(!tnode, false); - MultiplayerSynchronizer *sync = get_synchronizer(p_id); - if (!sync) { - return false; - } - if (tnode->last_sync_msec == p_msec) { - return true; - } - if (p_msec >= tnode->last_sync_msec + sync->get_replication_interval_msec()) { - tnode->last_sync_msec = p_msec; - return true; - } - return false; -} - -const HashSet<ObjectID> SceneReplicationState::get_known_nodes(int p_peer) { - ERR_FAIL_COND_V(!peers_info.has(p_peer), HashSet<ObjectID>()); - return peers_info[p_peer].known_nodes; -} - -uint32_t SceneReplicationState::get_net_id(const ObjectID &p_id) const { - const TrackedNode *tnode = tracked_nodes.getptr(p_id); - ERR_FAIL_COND_V(!tnode, 0); - return tnode->net_id; -} - -void SceneReplicationState::set_net_id(const ObjectID &p_id, uint32_t p_net_id) { - TrackedNode *tnode = tracked_nodes.getptr(p_id); - ERR_FAIL_COND(!tnode); - tnode->net_id = p_net_id; -} - -uint32_t SceneReplicationState::ensure_net_id(const ObjectID &p_id) { - TrackedNode *tnode = tracked_nodes.getptr(p_id); - ERR_FAIL_COND_V(!tnode, 0); - if (tnode->net_id == 0) { - tnode->net_id = ++last_net_id; - } - return tnode->net_id; -} - -void SceneReplicationState::on_peer_change(int p_peer, bool p_connected) { - if (p_connected) { - peers_info[p_peer] = PeerInfo(); - known_peers.insert(p_peer); - } else { - peers_info.erase(p_peer); - known_peers.erase(p_peer); - } -} - -void SceneReplicationState::reset() { - peers_info.clear(); - known_peers.clear(); - // Tracked nodes are cleared on deletion, here we only reset the ids so they can be later re-assigned. - for (KeyValue<ObjectID, TrackedNode> &E : tracked_nodes) { - TrackedNode &tobj = E.value; - tobj.net_id = 0; - tobj.remote_peer = 0; - tobj.last_sync = 0; - } -} - -Error SceneReplicationState::config_add_spawn(Node *p_node, MultiplayerSpawner *p_spawner) { - const ObjectID oid = p_node->get_instance_id(); - TrackedNode &tobj = _track(oid); - ERR_FAIL_COND_V(tobj.spawner != ObjectID(), ERR_ALREADY_IN_USE); - tobj.spawner = p_spawner->get_instance_id(); - spawned_nodes.insert(oid); - // The spawner may be notified after the synchronizer. - path_only_nodes.erase(oid); - return OK; -} - -Error SceneReplicationState::config_del_spawn(Node *p_node, MultiplayerSpawner *p_spawner) { - const ObjectID oid = p_node->get_instance_id(); - ERR_FAIL_COND_V(!is_tracked(oid), ERR_INVALID_PARAMETER); - TrackedNode &tobj = _track(oid); - ERR_FAIL_COND_V(tobj.spawner != p_spawner->get_instance_id(), ERR_INVALID_PARAMETER); - tobj.spawner = ObjectID(); - spawned_nodes.erase(oid); - return OK; -} - -Error SceneReplicationState::config_add_sync(Node *p_node, MultiplayerSynchronizer *p_sync) { - const ObjectID oid = p_node->get_instance_id(); - TrackedNode &tobj = _track(oid); - ERR_FAIL_COND_V(tobj.synchronizer != ObjectID(), ERR_ALREADY_IN_USE); - tobj.synchronizer = p_sync->get_instance_id(); - // If it doesn't have a spawner, we might need to assign ID for this node using it's path. - if (tobj.spawner.is_null()) { - path_only_nodes.insert(oid); - } - return OK; -} - -Error SceneReplicationState::config_del_sync(Node *p_node, MultiplayerSynchronizer *p_sync) { - const ObjectID oid = p_node->get_instance_id(); - ERR_FAIL_COND_V(!is_tracked(oid), ERR_INVALID_PARAMETER); - TrackedNode &tobj = _track(oid); - ERR_FAIL_COND_V(tobj.synchronizer != p_sync->get_instance_id(), ERR_INVALID_PARAMETER); - tobj.synchronizer = ObjectID(); - if (path_only_nodes.has(oid)) { - p_node->disconnect(SceneStringNames::get_singleton()->tree_exited, callable_mp(this, &SceneReplicationState::_untrack)); - _untrack(oid); - path_only_nodes.erase(oid); - } - return OK; -} - -Error SceneReplicationState::peer_add_node(int p_peer, const ObjectID &p_id) { - if (p_peer) { - ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER); - peers_info[p_peer].known_nodes.insert(p_id); - } else { - for (KeyValue<int, PeerInfo> &E : peers_info) { - E.value.known_nodes.insert(p_id); - } - } - return OK; -} - -Error SceneReplicationState::peer_del_node(int p_peer, const ObjectID &p_id) { - if (p_peer) { - ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER); - peers_info[p_peer].known_nodes.erase(p_id); - } else { - for (KeyValue<int, PeerInfo> &E : peers_info) { - E.value.known_nodes.erase(p_id); - } - } - return OK; -} - -Node *SceneReplicationState::peer_get_remote(int p_peer, uint32_t p_net_id) { - PeerInfo *info = peers_info.getptr(p_peer); - return info && info->recv_nodes.has(p_net_id) ? Object::cast_to<Node>(ObjectDB::get_instance(info->recv_nodes[p_net_id])) : nullptr; -} - -Error SceneReplicationState::peer_add_remote(int p_peer, uint32_t p_net_id, Node *p_node, MultiplayerSpawner *p_spawner) { - ERR_FAIL_COND_V(!p_node || !p_spawner, ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_UNAVAILABLE); - PeerInfo &pinfo = peers_info[p_peer]; - ObjectID oid = p_node->get_instance_id(); - TrackedNode &tobj = _track(oid); - tobj.spawner = p_spawner->get_instance_id(); - tobj.net_id = p_net_id; - tobj.remote_peer = p_peer; - tobj.last_sync = pinfo.last_recv_sync; - // Also track as a remote. - ERR_FAIL_COND_V(pinfo.recv_nodes.has(p_net_id), ERR_ALREADY_IN_USE); - pinfo.recv_nodes[p_net_id] = oid; - return OK; -} - -Error SceneReplicationState::peer_del_remote(int p_peer, uint32_t p_net_id, Node **r_node) { - ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_UNAUTHORIZED); - PeerInfo &info = peers_info[p_peer]; - ERR_FAIL_COND_V(!info.recv_nodes.has(p_net_id), ERR_UNAUTHORIZED); - *r_node = Object::cast_to<Node>(ObjectDB::get_instance(info.recv_nodes[p_net_id])); - info.recv_nodes.erase(p_net_id); - return OK; -} - -uint16_t SceneReplicationState::peer_sync_next(int p_peer) { - ERR_FAIL_COND_V(!peers_info.has(p_peer), 0); - PeerInfo &info = peers_info[p_peer]; - return ++info.last_sent_sync; -} - -void SceneReplicationState::peer_sync_recv(int p_peer, uint16_t p_time) { - ERR_FAIL_COND(!peers_info.has(p_peer)); - peers_info[p_peer].last_recv_sync = p_time; -} diff --git a/scene/multiplayer/scene_replication_state.h b/scene/multiplayer/scene_replication_state.h deleted file mode 100644 index 60a6c5d70c..0000000000 --- a/scene/multiplayer/scene_replication_state.h +++ /dev/null @@ -1,121 +0,0 @@ -/*************************************************************************/ -/* scene_replication_state.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef SCENE_REPLICATON_STATE_H -#define SCENE_REPLICATON_STATE_H - -#include "core/object/ref_counted.h" - -class MultiplayerSpawner; -class MultiplayerSynchronizer; -class Node; - -class SceneReplicationState : public RefCounted { -private: - struct TrackedNode { - ObjectID id; - uint32_t net_id = 0; - uint32_t remote_peer = 0; - ObjectID spawner; - ObjectID synchronizer; - uint16_t last_sync = 0; - uint64_t last_sync_msec = 0; - - bool operator==(const ObjectID &p_other) { return id == p_other; } - - Node *get_node() const { return id.is_valid() ? Object::cast_to<Node>(ObjectDB::get_instance(id)) : nullptr; } - MultiplayerSpawner *get_spawner() const { return spawner.is_valid() ? Object::cast_to<MultiplayerSpawner>(ObjectDB::get_instance(spawner)) : nullptr; } - MultiplayerSynchronizer *get_synchronizer() const { return synchronizer.is_valid() ? Object::cast_to<MultiplayerSynchronizer>(ObjectDB::get_instance(synchronizer)) : nullptr; } - TrackedNode() {} - TrackedNode(const ObjectID &p_id) { id = p_id; } - TrackedNode(const ObjectID &p_id, uint32_t p_net_id) { - id = p_id; - net_id = p_net_id; - } - }; - - struct PeerInfo { - HashSet<ObjectID> known_nodes; - HashMap<uint32_t, ObjectID> recv_nodes; - uint16_t last_sent_sync = 0; - uint16_t last_recv_sync = 0; - }; - - HashSet<int> known_peers; - uint32_t last_net_id = 0; - HashMap<ObjectID, TrackedNode> tracked_nodes; - HashMap<int, PeerInfo> peers_info; - HashSet<ObjectID> spawned_nodes; - HashSet<ObjectID> path_only_nodes; - - TrackedNode &_track(const ObjectID &p_id); - void _untrack(const ObjectID &p_id); - bool is_tracked(const ObjectID &p_id) const { return tracked_nodes.has(p_id); } - -public: - const HashSet<int> get_peers() const { return known_peers; } - const HashSet<ObjectID> &get_spawned_nodes() const { return spawned_nodes; } - const HashSet<ObjectID> &get_path_only_nodes() const { return path_only_nodes; } - - MultiplayerSynchronizer *get_synchronizer(const ObjectID &p_id) { return tracked_nodes.has(p_id) ? tracked_nodes[p_id].get_synchronizer() : nullptr; } - MultiplayerSpawner *get_spawner(const ObjectID &p_id) { return tracked_nodes.has(p_id) ? tracked_nodes[p_id].get_spawner() : nullptr; } - Node *get_node(const ObjectID &p_id) { return tracked_nodes.has(p_id) ? tracked_nodes[p_id].get_node() : nullptr; } - bool update_last_node_sync(const ObjectID &p_id, uint16_t p_time); - bool update_sync_time(const ObjectID &p_id, uint64_t p_msec); - - const HashSet<ObjectID> get_known_nodes(int p_peer); - uint32_t get_net_id(const ObjectID &p_id) const; - void set_net_id(const ObjectID &p_id, uint32_t p_net_id); - uint32_t ensure_net_id(const ObjectID &p_id); - - void reset(); - void on_peer_change(int p_peer, bool p_connected); - - Error config_add_spawn(Node *p_node, MultiplayerSpawner *p_spawner); - Error config_del_spawn(Node *p_node, MultiplayerSpawner *p_spawner); - - Error config_add_sync(Node *p_node, MultiplayerSynchronizer *p_sync); - Error config_del_sync(Node *p_node, MultiplayerSynchronizer *p_sync); - - Error peer_add_node(int p_peer, const ObjectID &p_id); - Error peer_del_node(int p_peer, const ObjectID &p_id); - - const HashMap<uint32_t, ObjectID> peer_get_remotes(int p_peer) const; - Node *peer_get_remote(int p_peer, uint32_t p_net_id); - Error peer_add_remote(int p_peer, uint32_t p_net_id, Node *p_node, MultiplayerSpawner *p_spawner); - Error peer_del_remote(int p_peer, uint32_t p_net_id, Node **r_node); - - uint16_t peer_sync_next(int p_peer); - void peer_sync_recv(int p_peer, uint16_t p_time); - - SceneReplicationState() {} -}; - -#endif // SCENE_REPLICATON_STATE_H diff --git a/scene/multiplayer/scene_rpc_interface.cpp b/scene/multiplayer/scene_rpc_interface.cpp deleted file mode 100644 index 84700a82f3..0000000000 --- a/scene/multiplayer/scene_rpc_interface.cpp +++ /dev/null @@ -1,511 +0,0 @@ -/*************************************************************************/ -/* scene_rpc_interface.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "scene/multiplayer/scene_rpc_interface.h" - -#include "core/debugger/engine_debugger.h" -#include "core/io/marshalls.h" -#include "core/multiplayer/multiplayer_api.h" -#include "scene/main/node.h" -#include "scene/main/window.h" - -MultiplayerRPCInterface *SceneRPCInterface::_create(MultiplayerAPI *p_multiplayer) { - return memnew(SceneRPCInterface(p_multiplayer)); -} - -void SceneRPCInterface::make_default() { - MultiplayerAPI::create_default_rpc_interface = _create; -} - -#ifdef DEBUG_ENABLED -_FORCE_INLINE_ void SceneRPCInterface::_profile_node_data(const String &p_what, ObjectID p_id) { - if (EngineDebugger::is_profiling("rpc")) { - Array values; - values.push_back(p_id); - values.push_back(p_what); - EngineDebugger::profiler_add_frame_data("rpc", values); - } -} -#else -_FORCE_INLINE_ void SceneRPCInterface::_profile_node_data(const String &p_what, ObjectID p_id) {} -#endif - -// Returns the packet size stripping the node path added when the node is not yet cached. -int get_packet_len(uint32_t p_node_target, int p_packet_len) { - if (p_node_target & 0x80000000) { - int ofs = p_node_target & 0x7FFFFFFF; - return p_packet_len - (p_packet_len - ofs); - } else { - return p_packet_len; - } -} - -const Multiplayer::RPCConfig _get_rpc_config(const Node *p_node, const StringName &p_method, uint16_t &r_id) { - const Vector<Multiplayer::RPCConfig> node_config = p_node->get_node_rpc_methods(); - for (int i = 0; i < node_config.size(); i++) { - if (node_config[i].name == p_method) { - r_id = ((uint16_t)i) | (1 << 15); - return node_config[i]; - } - } - if (p_node->get_script_instance()) { - const Vector<Multiplayer::RPCConfig> script_config = p_node->get_script_instance()->get_rpc_methods(); - for (int i = 0; i < script_config.size(); i++) { - if (script_config[i].name == p_method) { - r_id = (uint16_t)i; - return script_config[i]; - } - } - } - return Multiplayer::RPCConfig(); -} - -const Multiplayer::RPCConfig _get_rpc_config_by_id(Node *p_node, uint16_t p_id) { - Vector<Multiplayer::RPCConfig> config; - uint16_t id = p_id; - if (id & (1 << 15)) { - id = id & ~(1 << 15); - config = p_node->get_node_rpc_methods(); - } else if (p_node->get_script_instance()) { - config = p_node->get_script_instance()->get_rpc_methods(); - } - if (id < config.size()) { - return config[id]; - } - return Multiplayer::RPCConfig(); -} - -_FORCE_INLINE_ bool _can_call_mode(Node *p_node, Multiplayer::RPCMode mode, int p_remote_id) { - switch (mode) { - case Multiplayer::RPC_MODE_DISABLED: { - return false; - } break; - case Multiplayer::RPC_MODE_ANY_PEER: { - return true; - } break; - case Multiplayer::RPC_MODE_AUTHORITY: { - return !p_node->is_multiplayer_authority() && p_remote_id == p_node->get_multiplayer_authority(); - } break; - } - - return false; -} - -String SceneRPCInterface::get_rpc_md5(const Object *p_obj) const { - const Node *node = Object::cast_to<Node>(p_obj); - ERR_FAIL_COND_V(!node, ""); - String rpc_list; - const Vector<Multiplayer::RPCConfig> node_config = node->get_node_rpc_methods(); - for (int i = 0; i < node_config.size(); i++) { - rpc_list += String(node_config[i].name); - } - if (node->get_script_instance()) { - const Vector<Multiplayer::RPCConfig> script_config = node->get_script_instance()->get_rpc_methods(); - for (int i = 0; i < script_config.size(); i++) { - rpc_list += String(script_config[i].name); - } - } - return rpc_list.md5_text(); -} - -Node *SceneRPCInterface::_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len) { - Node *root_node = SceneTree::get_singleton()->get_root()->get_node(multiplayer->get_root_path()); - ERR_FAIL_COND_V(!root_node, nullptr); - Node *node = nullptr; - - if (p_node_target & 0x80000000) { - // Use full path (not cached yet). - int ofs = p_node_target & 0x7FFFFFFF; - - ERR_FAIL_COND_V_MSG(ofs >= p_packet_len, nullptr, "Invalid packet received. Size smaller than declared."); - - String paths; - paths.parse_utf8((const char *)&p_packet[ofs], p_packet_len - ofs); - - NodePath np = paths; - - node = root_node->get_node(np); - - if (!node) { - ERR_PRINT("Failed to get path from RPC: " + String(np) + "."); - } - return node; - } else { - // Use cached path. - return Object::cast_to<Node>(multiplayer->get_cached_object(p_from, p_node_target)); - } -} - -void SceneRPCInterface::process_rpc(int p_from, const uint8_t *p_packet, int p_packet_len) { - // Extract packet meta - int packet_min_size = 1; - int name_id_offset = 1; - ERR_FAIL_COND_MSG(p_packet_len < packet_min_size, "Invalid packet received. Size too small."); - // Compute the meta size, which depends on the compression level. - int node_id_compression = (p_packet[0] & NODE_ID_COMPRESSION_FLAG) >> NODE_ID_COMPRESSION_SHIFT; - int name_id_compression = (p_packet[0] & NAME_ID_COMPRESSION_FLAG) >> NAME_ID_COMPRESSION_SHIFT; - - switch (node_id_compression) { - case NETWORK_NODE_ID_COMPRESSION_8: - packet_min_size += 1; - name_id_offset += 1; - break; - case NETWORK_NODE_ID_COMPRESSION_16: - packet_min_size += 2; - name_id_offset += 2; - break; - case NETWORK_NODE_ID_COMPRESSION_32: - packet_min_size += 4; - name_id_offset += 4; - break; - default: - ERR_FAIL_MSG("Was not possible to extract the node id compression mode."); - } - switch (name_id_compression) { - case NETWORK_NAME_ID_COMPRESSION_8: - packet_min_size += 1; - break; - case NETWORK_NAME_ID_COMPRESSION_16: - packet_min_size += 2; - break; - default: - ERR_FAIL_MSG("Was not possible to extract the name id compression mode."); - } - ERR_FAIL_COND_MSG(p_packet_len < packet_min_size, "Invalid packet received. Size too small."); - - uint32_t node_target = 0; - switch (node_id_compression) { - case NETWORK_NODE_ID_COMPRESSION_8: - node_target = p_packet[1]; - break; - case NETWORK_NODE_ID_COMPRESSION_16: - node_target = decode_uint16(p_packet + 1); - break; - case NETWORK_NODE_ID_COMPRESSION_32: - node_target = decode_uint32(p_packet + 1); - break; - default: - // Unreachable, checked before. - CRASH_NOW(); - } - - Node *node = _process_get_node(p_from, p_packet, node_target, p_packet_len); - ERR_FAIL_COND_MSG(node == nullptr, "Invalid packet received. Requested node was not found."); - - uint16_t name_id = 0; - switch (name_id_compression) { - case NETWORK_NAME_ID_COMPRESSION_8: - name_id = p_packet[name_id_offset]; - break; - case NETWORK_NAME_ID_COMPRESSION_16: - name_id = decode_uint16(p_packet + name_id_offset); - break; - default: - // Unreachable, checked before. - CRASH_NOW(); - } - - const int packet_len = get_packet_len(node_target, p_packet_len); - _process_rpc(node, name_id, p_from, p_packet, packet_len, packet_min_size); -} - -void SceneRPCInterface::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) { - ERR_FAIL_COND_MSG(p_offset > p_packet_len, "Invalid packet received. Size too small."); - - // Check that remote can call the RPC on this node. - const Multiplayer::RPCConfig config = _get_rpc_config_by_id(p_node, p_rpc_method_id); - ERR_FAIL_COND(config.name == StringName()); - - bool can_call = _can_call_mode(p_node, config.rpc_mode, p_from); - ERR_FAIL_COND_MSG(!can_call, "RPC '" + String(config.name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)config.rpc_mode) + ", authority is " + itos(p_node->get_multiplayer_authority()) + "."); - - int argc = 0; - - const bool byte_only_or_no_args = p_packet[0] & BYTE_ONLY_OR_NO_ARGS_FLAG; - if (byte_only_or_no_args) { - if (p_offset < p_packet_len) { - // This packet contains only bytes. - argc = 1; - } - } else { - // Normal variant, takes the argument count from the packet. - ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small."); - argc = p_packet[p_offset]; - p_offset += 1; - } - - Vector<Variant> args; - Vector<const Variant *> argp; - args.resize(argc); - argp.resize(argc); - -#ifdef DEBUG_ENABLED - _profile_node_data("rpc_in", p_node->get_instance_id()); -#endif - - int out; - MultiplayerAPI::decode_and_decompress_variants(args, &p_packet[p_offset], p_packet_len - p_offset, out, byte_only_or_no_args, multiplayer->is_object_decoding_allowed()); - for (int i = 0; i < argc; i++) { - argp.write[i] = &args[i]; - } - - Callable::CallError ce; - - p_node->callp(config.name, (const Variant **)argp.ptr(), argc, ce); - if (ce.error != Callable::CallError::CALL_OK) { - String error = Variant::get_call_error_text(p_node, config.name, (const Variant **)argp.ptr(), argc, ce); - error = "RPC - " + error; - ERR_PRINT(error); - } -} - -void SceneRPCInterface::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const Multiplayer::RPCConfig &p_config, const StringName &p_name, const Variant **p_arg, int p_argcount) { - Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer(); - ERR_FAIL_COND_MSG(peer.is_null(), "Attempt to call RPC without active multiplayer peer."); - - ERR_FAIL_COND_MSG(peer->get_connection_status() == MultiplayerPeer::CONNECTION_CONNECTING, "Attempt to call RPC while multiplayer peer is not connected yet."); - - ERR_FAIL_COND_MSG(peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED, "Attempt to call RPC while multiplayer peer is disconnected."); - - ERR_FAIL_COND_MSG(p_argcount > 255, "Too many arguments (>255)."); - - if (p_to != 0 && !multiplayer->get_connected_peers().has(ABS(p_to))) { - ERR_FAIL_COND_MSG(p_to == peer->get_unique_id(), "Attempt to call RPC on yourself! Peer unique ID: " + itos(peer->get_unique_id()) + "."); - - ERR_FAIL_MSG("Attempt to call RPC with unknown peer ID: " + itos(p_to) + "."); - } - - NodePath from_path = multiplayer->get_root_path().rel_path_to(p_from->get_path()); - ERR_FAIL_COND_MSG(from_path.is_empty(), "Unable to send RPC. Relative path is empty. THIS IS LIKELY A BUG IN THE ENGINE!"); - - // See if all peers have cached path (if so, call can be fast). - int psc_id; - const bool has_all_peers = multiplayer->send_object_cache(p_from, from_path, p_to, psc_id); - - // Create base packet, lots of hardcode because it must be tight. - - int ofs = 0; - -#define MAKE_ROOM(m_amount) \ - if (packet_cache.size() < m_amount) \ - packet_cache.resize(m_amount); - - // Encode meta. - uint8_t command_type = MultiplayerAPI::NETWORK_COMMAND_REMOTE_CALL; - uint8_t node_id_compression = UINT8_MAX; - uint8_t name_id_compression = UINT8_MAX; - bool byte_only_or_no_args = false; - - MAKE_ROOM(1); - // The meta is composed along the way, so just set 0 for now. - packet_cache.write[0] = 0; - ofs += 1; - - // Encode Node ID. - if (has_all_peers) { - // Compress the node ID only if all the target peers already know it. - if (psc_id >= 0 && psc_id <= 255) { - // We can encode the id in 1 byte - node_id_compression = NETWORK_NODE_ID_COMPRESSION_8; - MAKE_ROOM(ofs + 1); - packet_cache.write[ofs] = static_cast<uint8_t>(psc_id); - ofs += 1; - } else if (psc_id >= 0 && psc_id <= 65535) { - // We can encode the id in 2 bytes - node_id_compression = NETWORK_NODE_ID_COMPRESSION_16; - MAKE_ROOM(ofs + 2); - encode_uint16(static_cast<uint16_t>(psc_id), &(packet_cache.write[ofs])); - ofs += 2; - } else { - // Too big, let's use 4 bytes. - node_id_compression = NETWORK_NODE_ID_COMPRESSION_32; - MAKE_ROOM(ofs + 4); - encode_uint32(psc_id, &(packet_cache.write[ofs])); - ofs += 4; - } - } else { - // The targets don't know the node yet, so we need to use 32 bits int. - node_id_compression = NETWORK_NODE_ID_COMPRESSION_32; - MAKE_ROOM(ofs + 4); - encode_uint32(psc_id, &(packet_cache.write[ofs])); - ofs += 4; - } - - // Encode method ID - if (p_rpc_id <= UINT8_MAX) { - // The ID fits in 1 byte - name_id_compression = NETWORK_NAME_ID_COMPRESSION_8; - MAKE_ROOM(ofs + 1); - packet_cache.write[ofs] = static_cast<uint8_t>(p_rpc_id); - ofs += 1; - } else { - // The ID is larger, let's use 2 bytes - name_id_compression = NETWORK_NAME_ID_COMPRESSION_16; - MAKE_ROOM(ofs + 2); - encode_uint16(p_rpc_id, &(packet_cache.write[ofs])); - ofs += 2; - } - - int len; - Error err = MultiplayerAPI::encode_and_compress_variants(p_arg, p_argcount, nullptr, len, &byte_only_or_no_args, multiplayer->is_object_decoding_allowed()); - ERR_FAIL_COND_MSG(err != OK, "Unable to encode RPC arguments. THIS IS LIKELY A BUG IN THE ENGINE!"); - if (byte_only_or_no_args) { - MAKE_ROOM(ofs + len); - } else { - MAKE_ROOM(ofs + 1 + len); - packet_cache.write[ofs] = p_argcount; - ofs += 1; - } - if (len) { - MultiplayerAPI::encode_and_compress_variants(p_arg, p_argcount, &packet_cache.write[ofs], len, &byte_only_or_no_args, multiplayer->is_object_decoding_allowed()); - ofs += len; - } - - ERR_FAIL_COND(command_type > 7); - ERR_FAIL_COND(node_id_compression > 3); - ERR_FAIL_COND(name_id_compression > 1); - - // We can now set the meta - packet_cache.write[0] = command_type + (node_id_compression << NODE_ID_COMPRESSION_SHIFT) + (name_id_compression << NAME_ID_COMPRESSION_SHIFT) + (byte_only_or_no_args ? BYTE_ONLY_OR_NO_ARGS_FLAG : 0); - -#ifdef DEBUG_ENABLED - multiplayer->profile_bandwidth("out", ofs); -#endif - - // Take chance and set transfer mode, since all send methods will use it. - peer->set_transfer_channel(p_config.channel); - peer->set_transfer_mode(p_config.transfer_mode); - - if (has_all_peers) { - // They all have verified paths, so send fast. - peer->set_target_peer(p_to); // To all of you. - peer->put_packet(packet_cache.ptr(), ofs); // A message with love. - } else { - // Unreachable because the node ID is never compressed if the peers doesn't know it. - CRASH_COND(node_id_compression != NETWORK_NODE_ID_COMPRESSION_32); - - // Not all verified path, so send one by one. - - // Append path at the end, since we will need it for some packets. - CharString pname = String(from_path).utf8(); - int path_len = encode_cstring(pname.get_data(), nullptr); - MAKE_ROOM(ofs + path_len); - encode_cstring(pname.get_data(), &(packet_cache.write[ofs])); - - for (const int &P : multiplayer->get_connected_peers()) { - if (p_to < 0 && P == -p_to) { - continue; // Continue, excluded. - } - - if (p_to > 0 && P != p_to) { - continue; // Continue, not for this peer. - } - - bool confirmed = multiplayer->is_cache_confirmed(from_path, P); - - peer->set_target_peer(P); // To this one specifically. - - if (confirmed) { - // This one confirmed path, so use id. - encode_uint32(psc_id, &(packet_cache.write[1])); - peer->put_packet(packet_cache.ptr(), ofs); - } else { - // This one did not confirm path yet, so use entire path (sorry!). - encode_uint32(0x80000000 | ofs, &(packet_cache.write[1])); // Offset to path and flag. - peer->put_packet(packet_cache.ptr(), ofs + path_len); - } - } - } -} - -void SceneRPCInterface::rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) { - Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer(); - ERR_FAIL_COND_MSG(!peer.is_valid(), "Trying to call an RPC while no multiplayer peer is active."); - Node *node = Object::cast_to<Node>(p_obj); - ERR_FAIL_COND(!node); - ERR_FAIL_COND_MSG(!node->is_inside_tree(), "Trying to call an RPC on a node which is not inside SceneTree."); - ERR_FAIL_COND_MSG(peer->get_connection_status() != MultiplayerPeer::CONNECTION_CONNECTED, "Trying to call an RPC via a multiplayer peer which is not connected."); - - int node_id = peer->get_unique_id(); - bool call_local_native = false; - bool call_local_script = false; - uint16_t rpc_id = UINT16_MAX; - const Multiplayer::RPCConfig config = _get_rpc_config(node, p_method, rpc_id); - ERR_FAIL_COND_MSG(config.name == StringName(), - vformat("Unable to get the RPC configuration for the function \"%s\" at path: \"%s\". This happens when the method is missing or not marked for RPCs in the local script.", p_method, node->get_path())); - if (p_peer_id == 0 || p_peer_id == node_id || (p_peer_id < 0 && p_peer_id != -node_id)) { - if (rpc_id & (1 << 15)) { - call_local_native = config.call_local; - } else { - call_local_script = config.call_local; - } - } - - if (p_peer_id != node_id) { -#ifdef DEBUG_ENABLED - _profile_node_data("rpc_out", node->get_instance_id()); -#endif - - _send_rpc(node, p_peer_id, rpc_id, config, p_method, p_arg, p_argcount); - } - - if (call_local_native) { - Callable::CallError ce; - - multiplayer->set_remote_sender_override(peer->get_unique_id()); - node->callp(p_method, p_arg, p_argcount, ce); - multiplayer->set_remote_sender_override(0); - - if (ce.error != Callable::CallError::CALL_OK) { - String error = Variant::get_call_error_text(node, p_method, p_arg, p_argcount, ce); - error = "rpc() aborted in local call: - " + error + "."; - ERR_PRINT(error); - return; - } - } - - if (call_local_script) { - Callable::CallError ce; - ce.error = Callable::CallError::CALL_OK; - - multiplayer->set_remote_sender_override(peer->get_unique_id()); - node->get_script_instance()->callp(p_method, p_arg, p_argcount, ce); - multiplayer->set_remote_sender_override(0); - - if (ce.error != Callable::CallError::CALL_OK) { - String error = Variant::get_call_error_text(node, p_method, p_arg, p_argcount, ce); - error = "rpc() aborted in script local call: - " + error + "."; - ERR_PRINT(error); - return; - } - } - - ERR_FAIL_COND_MSG(p_peer_id == node_id && !config.call_local, "RPC '" + p_method + "' on yourself is not allowed by selected mode."); -} diff --git a/scene/multiplayer/scene_rpc_interface.h b/scene/multiplayer/scene_rpc_interface.h deleted file mode 100644 index 86e1d0d280..0000000000 --- a/scene/multiplayer/scene_rpc_interface.h +++ /dev/null @@ -1,91 +0,0 @@ -/*************************************************************************/ -/* scene_rpc_interface.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef SCENE_RPC_INTERFACE_H -#define SCENE_RPC_INTERFACE_H - -#include "core/multiplayer/multiplayer.h" -#include "core/multiplayer/multiplayer_api.h" - -class SceneRPCInterface : public MultiplayerRPCInterface { - GDCLASS(SceneRPCInterface, MultiplayerRPCInterface); - -private: - enum NetworkNodeIdCompression { - NETWORK_NODE_ID_COMPRESSION_8 = 0, - NETWORK_NODE_ID_COMPRESSION_16, - NETWORK_NODE_ID_COMPRESSION_32, - }; - - enum NetworkNameIdCompression { - NETWORK_NAME_ID_COMPRESSION_8 = 0, - NETWORK_NAME_ID_COMPRESSION_16, - }; - - // The RPC meta is composed by a single byte that contains (starting from the least significant bit): - // - `NetworkCommands` in the first four bits. - // - `NetworkNodeIdCompression` in the next 2 bits. - // - `NetworkNameIdCompression` in the next 1 bit. - // - `byte_only_or_no_args` in the next 1 bit. - enum { - NODE_ID_COMPRESSION_SHIFT = MultiplayerAPI::CMD_FLAG_0_SHIFT, // 2 bits for this. - NAME_ID_COMPRESSION_SHIFT = MultiplayerAPI::CMD_FLAG_2_SHIFT, - BYTE_ONLY_OR_NO_ARGS_SHIFT = MultiplayerAPI::CMD_FLAG_3_SHIFT, - }; - - enum { - NODE_ID_COMPRESSION_FLAG = (1 << NODE_ID_COMPRESSION_SHIFT) | (1 << (NODE_ID_COMPRESSION_SHIFT + 1)), // 2 bits for this. - NAME_ID_COMPRESSION_FLAG = (1 << NAME_ID_COMPRESSION_SHIFT), - BYTE_ONLY_OR_NO_ARGS_FLAG = (1 << BYTE_ONLY_OR_NO_ARGS_SHIFT), - }; - - MultiplayerAPI *multiplayer = nullptr; - Vector<uint8_t> packet_cache; - -protected: - static MultiplayerRPCInterface *_create(MultiplayerAPI *p_multiplayer); - - _FORCE_INLINE_ void _profile_node_data(const String &p_what, ObjectID p_id); - void _process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset); - - void _send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const Multiplayer::RPCConfig &p_config, const StringName &p_name, const Variant **p_arg, int p_argcount); - Node *_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len); - -public: - static void make_default(); - - virtual void rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) override; - virtual void process_rpc(int p_from, const uint8_t *p_packet, int p_packet_len) override; - virtual String get_rpc_md5(const Object *p_obj) const override; - - SceneRPCInterface(MultiplayerAPI *p_multiplayer) { multiplayer = p_multiplayer; } -}; - -#endif // SCENE_RPC_INTERFACE_H diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 5c5b60df63..e536aeee51 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -50,9 +50,11 @@ #include "scene/2d/light_2d.h" #include "scene/2d/light_occluder_2d.h" #include "scene/2d/line_2d.h" +#include "scene/2d/marker_2d.h" #include "scene/2d/mesh_instance_2d.h" #include "scene/2d/multimesh_instance_2d.h" #include "scene/2d/navigation_agent_2d.h" +#include "scene/2d/navigation_link_2d.h" #include "scene/2d/navigation_obstacle_2d.h" #include "scene/2d/parallax_background.h" #include "scene/2d/parallax_layer.h" @@ -60,7 +62,6 @@ #include "scene/2d/physical_bone_2d.h" #include "scene/2d/physics_body_2d.h" #include "scene/2d/polygon_2d.h" -#include "scene/2d/position_2d.h" #include "scene/2d/ray_cast_2d.h" #include "scene/2d/remote_transform_2d.h" #include "scene/2d/shape_cast_2d.h" @@ -100,6 +101,7 @@ #include "scene/gui/line_edit.h" #include "scene/gui/link_button.h" #include "scene/gui/margin_container.h" +#include "scene/gui/menu_bar.h" #include "scene/gui/menu_button.h" #include "scene/gui/nine_patch_rect.h" #include "scene/gui/option_button.h" @@ -130,22 +132,18 @@ #include "scene/main/http_request.h" #include "scene/main/instance_placeholder.h" #include "scene/main/missing_node.h" +#include "scene/main/multiplayer_api.h" #include "scene/main/resource_preloader.h" #include "scene/main/scene_tree.h" #include "scene/main/timer.h" #include "scene/main/viewport.h" #include "scene/main/window.h" -#include "scene/multiplayer/multiplayer_spawner.h" -#include "scene/multiplayer/multiplayer_synchronizer.h" -#include "scene/multiplayer/scene_cache_interface.h" -#include "scene/multiplayer/scene_replication_interface.h" -#include "scene/multiplayer/scene_rpc_interface.h" #include "scene/resources/animation_library.h" -#include "scene/resources/audio_stream_sample.h" +#include "scene/resources/audio_stream_wav.h" #include "scene/resources/bit_map.h" #include "scene/resources/bone_map.h" #include "scene/resources/box_shape_3d.h" -#include "scene/resources/camera_effects.h" +#include "scene/resources/camera_attributes.h" #include "scene/resources/capsule_shape_2d.h" #include "scene/resources/capsule_shape_3d.h" #include "scene/resources/circle_shape_2d.h" @@ -155,16 +153,18 @@ #include "scene/resources/convex_polygon_shape_3d.h" #include "scene/resources/cylinder_shape_3d.h" #include "scene/resources/default_theme/default_theme.h" +#include "scene/resources/environment.h" #include "scene/resources/font.h" #include "scene/resources/gradient.h" #include "scene/resources/height_map_shape_3d.h" #include "scene/resources/immediate_mesh.h" +#include "scene/resources/label_settings.h" #include "scene/resources/material.h" -#include "scene/resources/mesh.h" #include "scene/resources/mesh_data_tool.h" +#include "scene/resources/multimesh.h" #include "scene/resources/navigation_mesh.h" #include "scene/resources/packed_scene.h" -#include "scene/resources/particles_material.h" +#include "scene/resources/particle_process_material.h" #include "scene/resources/physics_material.h" #include "scene/resources/polygon_path_finder.h" #include "scene/resources/primitive_meshes.h" @@ -173,6 +173,7 @@ #include "scene/resources/segment_shape_2d.h" #include "scene/resources/separation_ray_shape_2d.h" #include "scene/resources/separation_ray_shape_3d.h" +#include "scene/resources/shader_include.h" #include "scene/resources/skeleton_modification_2d.h" #include "scene/resources/skeleton_modification_2d_ccdik.h" #include "scene/resources/skeleton_modification_2d_fabrik.h" @@ -194,12 +195,14 @@ #include "scene/resources/sky.h" #include "scene/resources/sky_material.h" #include "scene/resources/sphere_shape_3d.h" +#include "scene/resources/style_box.h" #include "scene/resources/surface_tool.h" #include "scene/resources/syntax_highlighter.h" #include "scene/resources/text_file.h" #include "scene/resources/text_line.h" #include "scene/resources/text_paragraph.h" #include "scene/resources/texture.h" +#include "scene/resources/theme.h" #include "scene/resources/tile_set.h" #include "scene/resources/video_stream.h" #include "scene/resources/visual_shader.h" @@ -211,6 +214,7 @@ #include "scene/resources/world_boundary_shape_2d.h" #include "scene/resources/world_boundary_shape_3d.h" #include "scene/scene_string_names.h" +#include "scene/theme/theme_db.h" #include "scene/main/shader_globals_override.h" @@ -233,22 +237,24 @@ #include "scene/3d/light_3d.h" #include "scene/3d/lightmap_gi.h" #include "scene/3d/lightmap_probe.h" +#include "scene/3d/marker_3d.h" #include "scene/3d/mesh_instance_3d.h" #include "scene/3d/multimesh_instance_3d.h" #include "scene/3d/navigation_agent_3d.h" +#include "scene/3d/navigation_link_3d.h" #include "scene/3d/navigation_obstacle_3d.h" #include "scene/3d/navigation_region_3d.h" #include "scene/3d/node_3d.h" #include "scene/3d/occluder_instance_3d.h" #include "scene/3d/path_3d.h" #include "scene/3d/physics_body_3d.h" -#include "scene/3d/position_3d.h" #include "scene/3d/ray_cast_3d.h" #include "scene/3d/reflection_probe.h" #include "scene/3d/remote_transform_3d.h" +#include "scene/3d/shape_cast_3d.h" #include "scene/3d/skeleton_3d.h" #include "scene/3d/skeleton_ik_3d.h" -#include "scene/3d/soft_dynamic_body_3d.h" +#include "scene/3d/soft_body_3d.h" #include "scene/3d/spring_arm_3d.h" #include "scene/3d/sprite_3d.h" #include "scene/3d/vehicle_body_3d.h" @@ -260,7 +266,7 @@ #include "scene/resources/fog_material.h" #include "scene/resources/importer_mesh.h" #include "scene/resources/mesh_library.h" -#endif +#endif // _3D_DISABLED static Ref<ResourceFormatSaverText> resource_saver_text; static Ref<ResourceFormatLoaderText> resource_loader_text; @@ -272,6 +278,9 @@ static Ref<ResourceFormatLoaderCompressedTexture3D> resource_loader_texture_3d; static Ref<ResourceFormatSaverShader> resource_saver_shader; static Ref<ResourceFormatLoaderShader> resource_loader_shader; +static Ref<ResourceFormatSaverShaderInclude> resource_saver_shader_include; +static Ref<ResourceFormatLoaderShaderInclude> resource_loader_shader_include; + void register_scene_types() { SceneStringNames::create(); @@ -300,6 +309,12 @@ void register_scene_types() { resource_loader_shader.instantiate(); ResourceLoader::add_resource_format_loader(resource_loader_shader, true); + resource_saver_shader_include.instantiate(); + ResourceSaver::add_resource_format_saver(resource_saver_shader_include, true); + + resource_loader_shader_include.instantiate(); + ResourceLoader::add_resource_format_loader(resource_loader_shader_include, true); + OS::get_singleton()->yield(); // may take time to init GDREGISTER_CLASS(Object); @@ -311,9 +326,13 @@ void register_scene_types() { GDREGISTER_ABSTRACT_CLASS(Viewport); GDREGISTER_CLASS(SubViewport); GDREGISTER_CLASS(ViewportTexture); + + GDREGISTER_ABSTRACT_CLASS(MultiplayerPeer); + GDREGISTER_CLASS(MultiplayerPeerExtension); + GDREGISTER_ABSTRACT_CLASS(MultiplayerAPI); + GDREGISTER_CLASS(MultiplayerAPIExtension); + GDREGISTER_CLASS(HTTPRequest); - GDREGISTER_CLASS(MultiplayerSpawner); - GDREGISTER_CLASS(MultiplayerSynchronizer); GDREGISTER_CLASS(Timer); GDREGISTER_CLASS(CanvasLayer); GDREGISTER_CLASS(CanvasModulate); @@ -339,6 +358,7 @@ void register_scene_types() { GDREGISTER_CLASS(VSlider); GDREGISTER_CLASS(Popup); GDREGISTER_CLASS(PopupPanel); + GDREGISTER_CLASS(MenuBar); GDREGISTER_CLASS(MenuButton); GDREGISTER_CLASS(CheckBox); GDREGISTER_CLASS(CheckButton); @@ -360,14 +380,14 @@ void register_scene_types() { GDREGISTER_CLASS(VSeparator); GDREGISTER_CLASS(TextureButton); GDREGISTER_CLASS(Container); - GDREGISTER_ABSTRACT_CLASS(BoxContainer); + GDREGISTER_CLASS(BoxContainer); GDREGISTER_CLASS(HBoxContainer); GDREGISTER_CLASS(VBoxContainer); GDREGISTER_CLASS(GridContainer); GDREGISTER_CLASS(CenterContainer); GDREGISTER_CLASS(ScrollContainer); GDREGISTER_CLASS(PanelContainer); - GDREGISTER_ABSTRACT_CLASS(FlowContainer); + GDREGISTER_CLASS(FlowContainer); GDREGISTER_CLASS(HFlowContainer); GDREGISTER_CLASS(VFlowContainer); @@ -404,7 +424,7 @@ void register_scene_types() { GDREGISTER_CLASS(MarginContainer); GDREGISTER_CLASS(SubViewportContainer); - GDREGISTER_ABSTRACT_CLASS(SplitContainer); + GDREGISTER_CLASS(SplitContainer); GDREGISTER_CLASS(HSplitContainer); GDREGISTER_CLASS(VSplitContainer); @@ -439,6 +459,7 @@ void register_scene_types() { GDREGISTER_CLASS(AnimationNodeStateMachine); GDREGISTER_CLASS(AnimationNodeStateMachinePlayback); + GDREGISTER_CLASS(AnimationNodeSync); GDREGISTER_CLASS(AnimationNodeStateMachineTransition); GDREGISTER_CLASS(AnimationNodeOutput); GDREGISTER_CLASS(AnimationNodeOneShot); @@ -509,10 +530,8 @@ void register_scene_types() { GDREGISTER_CLASS(GPUParticlesAttractorSphere3D); GDREGISTER_CLASS(GPUParticlesAttractorVectorField3D); GDREGISTER_CLASS(CPUParticles3D); - GDREGISTER_CLASS(Position3D); - + GDREGISTER_CLASS(Marker3D); GDREGISTER_CLASS(RootMotionView); - ClassDB::set_class_enabled("RootMotionView", false); // disabled by default, enabled by editor OS::get_singleton()->yield(); // may take time to init @@ -520,13 +539,13 @@ void register_scene_types() { GDREGISTER_ABSTRACT_CLASS(PhysicsBody3D); GDREGISTER_CLASS(StaticBody3D); GDREGISTER_CLASS(AnimatableBody3D); - GDREGISTER_CLASS(RigidDynamicBody3D); + GDREGISTER_CLASS(RigidBody3D); GDREGISTER_CLASS(KinematicCollision3D); GDREGISTER_CLASS(CharacterBody3D); GDREGISTER_CLASS(SpringArm3D); GDREGISTER_CLASS(PhysicalBone3D); - GDREGISTER_CLASS(SoftDynamicBody3D); + GDREGISTER_CLASS(SoftBody3D); GDREGISTER_CLASS(SkeletonIK3D); GDREGISTER_CLASS(BoneAttachment3D); @@ -537,6 +556,7 @@ void register_scene_types() { GDREGISTER_CLASS(CollisionShape3D); GDREGISTER_CLASS(CollisionPolygon3D); GDREGISTER_CLASS(RayCast3D); + GDREGISTER_CLASS(ShapeCast3D); GDREGISTER_CLASS(MultiMeshInstance3D); GDREGISTER_CLASS(Curve3D); @@ -559,14 +579,16 @@ void register_scene_types() { GDREGISTER_CLASS(NavigationRegion3D); GDREGISTER_CLASS(NavigationAgent3D); GDREGISTER_CLASS(NavigationObstacle3D); + GDREGISTER_CLASS(NavigationLink3D); OS::get_singleton()->yield(); // may take time to init -#endif +#endif // _3D_DISABLED /* REGISTER SHADER */ GDREGISTER_CLASS(Shader); GDREGISTER_CLASS(VisualShader); + GDREGISTER_CLASS(ShaderInclude); GDREGISTER_ABSTRACT_CLASS(VisualShaderNode); GDREGISTER_CLASS(VisualShaderNodeCustom); GDREGISTER_CLASS(VisualShaderNodeInput); @@ -596,6 +618,7 @@ void register_scene_types() { GDREGISTER_CLASS(VisualShaderNodeColorFunc); GDREGISTER_CLASS(VisualShaderNodeTransformFunc); GDREGISTER_CLASS(VisualShaderNodeUVFunc); + GDREGISTER_CLASS(VisualShaderNodeUVPolarCoord); GDREGISTER_CLASS(VisualShaderNodeDotProduct); GDREGISTER_CLASS(VisualShaderNodeVectorLen); GDREGISTER_CLASS(VisualShaderNodeDeterminant); @@ -619,21 +642,23 @@ void register_scene_types() { GDREGISTER_CLASS(VisualShaderNodeTexture2DArray); GDREGISTER_CLASS(VisualShaderNodeTexture3D); GDREGISTER_CLASS(VisualShaderNodeCubemap); - GDREGISTER_ABSTRACT_CLASS(VisualShaderNodeUniform); - GDREGISTER_CLASS(VisualShaderNodeUniformRef); - GDREGISTER_CLASS(VisualShaderNodeFloatUniform); - GDREGISTER_CLASS(VisualShaderNodeIntUniform); - GDREGISTER_CLASS(VisualShaderNodeBooleanUniform); - GDREGISTER_CLASS(VisualShaderNodeColorUniform); - GDREGISTER_CLASS(VisualShaderNodeVec2Uniform); - GDREGISTER_CLASS(VisualShaderNodeVec3Uniform); - GDREGISTER_CLASS(VisualShaderNodeVec4Uniform); - GDREGISTER_CLASS(VisualShaderNodeTransformUniform); - GDREGISTER_CLASS(VisualShaderNodeTextureUniform); - GDREGISTER_CLASS(VisualShaderNodeTextureUniformTriplanar); - GDREGISTER_CLASS(VisualShaderNodeTexture2DArrayUniform); - GDREGISTER_CLASS(VisualShaderNodeTexture3DUniform); - GDREGISTER_CLASS(VisualShaderNodeCubemapUniform); + GDREGISTER_ABSTRACT_CLASS(VisualShaderNodeParameter); + GDREGISTER_CLASS(VisualShaderNodeParameterRef); + GDREGISTER_CLASS(VisualShaderNodeFloatParameter); + GDREGISTER_CLASS(VisualShaderNodeIntParameter); + GDREGISTER_CLASS(VisualShaderNodeBooleanParameter); + GDREGISTER_CLASS(VisualShaderNodeColorParameter); + GDREGISTER_CLASS(VisualShaderNodeVec2Parameter); + GDREGISTER_CLASS(VisualShaderNodeVec3Parameter); + GDREGISTER_CLASS(VisualShaderNodeVec4Parameter); + GDREGISTER_CLASS(VisualShaderNodeTransformParameter); + GDREGISTER_ABSTRACT_CLASS(VisualShaderNodeTextureParameter); + GDREGISTER_CLASS(VisualShaderNodeTexture2DParameter); + GDREGISTER_CLASS(VisualShaderNodeTextureParameterTriplanar); + GDREGISTER_CLASS(VisualShaderNodeTexture2DArrayParameter); + GDREGISTER_CLASS(VisualShaderNodeTexture3DParameter); + GDREGISTER_CLASS(VisualShaderNodeCubemapParameter); + GDREGISTER_CLASS(VisualShaderNodeLinearSceneDepth); GDREGISTER_CLASS(VisualShaderNodeIf); GDREGISTER_CLASS(VisualShaderNodeSwitch); GDREGISTER_CLASS(VisualShaderNodeFresnel); @@ -643,6 +668,10 @@ void register_scene_types() { GDREGISTER_CLASS(VisualShaderNodeCompare); GDREGISTER_CLASS(VisualShaderNodeMultiplyAdd); GDREGISTER_CLASS(VisualShaderNodeBillboard); + GDREGISTER_CLASS(VisualShaderNodeDistanceFade); + GDREGISTER_CLASS(VisualShaderNodeProximityFade); + GDREGISTER_CLASS(VisualShaderNodeRandomRange); + GDREGISTER_CLASS(VisualShaderNodeRemap); GDREGISTER_ABSTRACT_CLASS(VisualShaderNodeVarying); GDREGISTER_CLASS(VisualShaderNodeVaryingSetter); GDREGISTER_CLASS(VisualShaderNodeVaryingGetter); @@ -681,7 +710,7 @@ void register_scene_types() { GDREGISTER_CLASS(Sprite2D); GDREGISTER_CLASS(SpriteFrames); GDREGISTER_CLASS(AnimatedSprite2D); - GDREGISTER_CLASS(Position2D); + GDREGISTER_CLASS(Marker2D); GDREGISTER_CLASS(Line2D); GDREGISTER_CLASS(MeshInstance2D); GDREGISTER_CLASS(MultiMeshInstance2D); @@ -689,7 +718,7 @@ void register_scene_types() { GDREGISTER_ABSTRACT_CLASS(PhysicsBody2D); GDREGISTER_CLASS(StaticBody2D); GDREGISTER_CLASS(AnimatableBody2D); - GDREGISTER_CLASS(RigidDynamicBody2D); + GDREGISTER_CLASS(RigidBody2D); GDREGISTER_CLASS(CharacterBody2D); GDREGISTER_CLASS(KinematicCollision2D); GDREGISTER_CLASS(Area2D); @@ -746,13 +775,9 @@ void register_scene_types() { /* REGISTER RESOURCES */ GDREGISTER_ABSTRACT_CLASS(Shader); - GDREGISTER_CLASS(ParticlesMaterial); - SceneTree::add_idle_callback(ParticlesMaterial::flush_changes); - ParticlesMaterial::init_shaders(); - - GDREGISTER_CLASS(ProceduralSkyMaterial); - GDREGISTER_CLASS(PanoramaSkyMaterial); - GDREGISTER_CLASS(PhysicalSkyMaterial); + GDREGISTER_CLASS(ParticleProcessMaterial); + SceneTree::add_idle_callback(ParticleProcessMaterial::flush_changes); + ParticleProcessMaterial::init_shaders(); GDREGISTER_VIRTUAL_CLASS(Mesh); GDREGISTER_CLASS(ArrayMesh); @@ -772,6 +797,7 @@ void register_scene_types() { GDREGISTER_CLASS(QuadMesh); GDREGISTER_CLASS(SphereMesh); GDREGISTER_CLASS(TextMesh); + GDREGISTER_CLASS(TorusMesh); GDREGISTER_CLASS(TubeTrailMesh); GDREGISTER_CLASS(RibbonTrailMesh); GDREGISTER_CLASS(PointMesh); @@ -780,6 +806,9 @@ void register_scene_types() { GDREGISTER_CLASS(StandardMaterial3D); GDREGISTER_CLASS(ORMMaterial3D); GDREGISTER_CLASS(PlaceholderMaterial); + GDREGISTER_CLASS(ProceduralSkyMaterial); + GDREGISTER_CLASS(PanoramaSkyMaterial); + GDREGISTER_CLASS(PhysicalSkyMaterial); SceneTree::add_idle_callback(BaseMaterial3D::flush_changes); BaseMaterial3D::init_shaders(); @@ -808,14 +837,14 @@ void register_scene_types() { ClassDB::register_class<SkeletonModification3DStackHolder>(); OS::get_singleton()->yield(); // may take time to init - - GDREGISTER_CLASS(VelocityTracker3D); -#endif +#endif // _3D_DISABLED GDREGISTER_CLASS(PhysicsMaterial); GDREGISTER_CLASS(World3D); GDREGISTER_CLASS(Environment); - GDREGISTER_CLASS(CameraEffects); + GDREGISTER_VIRTUAL_CLASS(CameraAttributes); + GDREGISTER_CLASS(CameraAttributesPhysical); + GDREGISTER_CLASS(CameraAttributesPractical); GDREGISTER_CLASS(World2D); GDREGISTER_VIRTUAL_CLASS(Texture); GDREGISTER_VIRTUAL_CLASS(Texture2D); @@ -829,7 +858,6 @@ void register_scene_types() { GDREGISTER_CLASS(CurveXYZTexture); GDREGISTER_CLASS(GradientTexture1D); GDREGISTER_CLASS(GradientTexture2D); - GDREGISTER_CLASS(ProxyTexture); GDREGISTER_CLASS(AnimatedTexture); GDREGISTER_CLASS(CameraTexture); GDREGISTER_VIRTUAL_CLASS(TextureLayered); @@ -857,10 +885,11 @@ void register_scene_types() { GDREGISTER_ABSTRACT_CLASS(Font); GDREGISTER_CLASS(FontFile); GDREGISTER_CLASS(FontVariation); + GDREGISTER_CLASS(SystemFont); GDREGISTER_CLASS(Curve); - GDREGISTER_CLASS(SceneReplicationConfig); + GDREGISTER_CLASS(LabelSettings); GDREGISTER_CLASS(TextLine); GDREGISTER_CLASS(TextParagraph); @@ -888,7 +917,7 @@ void register_scene_types() { GDREGISTER_CLASS(AudioStreamPlayer3D); #endif GDREGISTER_ABSTRACT_CLASS(VideoStream); - GDREGISTER_CLASS(AudioStreamSample); + GDREGISTER_CLASS(AudioStreamWAV); OS::get_singleton()->yield(); // may take time to init @@ -910,6 +939,7 @@ void register_scene_types() { GDREGISTER_CLASS(NavigationRegion2D); GDREGISTER_CLASS(NavigationAgent2D); GDREGISTER_CLASS(NavigationObstacle2D); + GDREGISTER_CLASS(NavigationLink2D); OS::get_singleton()->yield(); // may take time to init @@ -1006,6 +1036,7 @@ void register_scene_types() { ClassDB::add_compatibility_class("PanoramaSky", "Sky"); ClassDB::add_compatibility_class("Particles", "GPUParticles3D"); ClassDB::add_compatibility_class("Particles2D", "GPUParticles2D"); + ClassDB::add_compatibility_class("ParticlesMaterial", "ParticleProcessMaterial"); ClassDB::add_compatibility_class("Path", "Path3D"); ClassDB::add_compatibility_class("PathFollow", "PathFollow3D"); ClassDB::add_compatibility_class("PhysicalBone", "PhysicalBone3D"); @@ -1021,23 +1052,26 @@ void register_scene_types() { ClassDB::add_compatibility_class("PhysicsShapeQueryParameters", "PhysicsShapeQueryParameters3D"); ClassDB::add_compatibility_class("PinJoint", "PinJoint3D"); ClassDB::add_compatibility_class("PlaneShape", "WorldBoundaryShape3D"); + ClassDB::add_compatibility_class("Position2D", "Marker2D"); + ClassDB::add_compatibility_class("Position3D", "Marker3D"); ClassDB::add_compatibility_class("ProceduralSky", "Sky"); ClassDB::add_compatibility_class("RayCast", "RayCast3D"); ClassDB::add_compatibility_class("RayShape", "SeparationRayShape3D"); ClassDB::add_compatibility_class("RayShape2D", "SeparationRayShape2D"); ClassDB::add_compatibility_class("RemoteTransform", "RemoteTransform3D"); - ClassDB::add_compatibility_class("RigidBody", "RigidDynamicBody3D"); - ClassDB::add_compatibility_class("RigidBody2D", "RigidDynamicBody2D"); + ClassDB::add_compatibility_class("RigidBody", "RigidBody3D"); + ClassDB::add_compatibility_class("RigidDynamicBody2D", "RigidBody2D"); + ClassDB::add_compatibility_class("RigidDynamicBody3D", "RigidBody3D"); ClassDB::add_compatibility_class("Shape", "Shape3D"); ClassDB::add_compatibility_class("ShortCut", "Shortcut"); ClassDB::add_compatibility_class("Skeleton", "Skeleton3D"); ClassDB::add_compatibility_class("SkeletonIK", "SkeletonIK3D"); ClassDB::add_compatibility_class("SliderJoint", "SliderJoint3D"); - ClassDB::add_compatibility_class("SoftBody", "SoftDynamicBody3D"); + ClassDB::add_compatibility_class("SoftBody", "SoftBody3D"); + ClassDB::add_compatibility_class("SoftDynamicBody3D", "SoftBody3D"); ClassDB::add_compatibility_class("Spatial", "Node3D"); ClassDB::add_compatibility_class("SpatialGizmo", "Node3DGizmo"); ClassDB::add_compatibility_class("SpatialMaterial", "StandardMaterial3D"); - ClassDB::add_compatibility_class("SpatialVelocityTracker", "VelocityTracker3D"); ClassDB::add_compatibility_class("SphereShape", "SphereShape3D"); ClassDB::add_compatibility_class("SpotLight", "SpotLight3D"); ClassDB::add_compatibility_class("SpringArm", "SpringArm3D"); @@ -1055,10 +1089,12 @@ void register_scene_types() { ClassDB::add_compatibility_class("VisibilityNotifier2D", "VisibleOnScreenNotifier2D"); ClassDB::add_compatibility_class("VisibilityNotifier3D", "VisibleOnScreenNotifier3D"); ClassDB::add_compatibility_class("VisualServer", "RenderingServer"); + ClassDB::add_compatibility_class("World", "World3D"); + + // VisualShader classes. ClassDB::add_compatibility_class("VisualShaderNodeScalarConstant", "VisualShaderNodeFloatConstant"); ClassDB::add_compatibility_class("VisualShaderNodeScalarFunc", "VisualShaderNodeFloatFunc"); ClassDB::add_compatibility_class("VisualShaderNodeScalarOp", "VisualShaderNodeFloatOp"); - ClassDB::add_compatibility_class("VisualShaderNodeScalarUniform", "VisualShaderNodeFloatUniform"); ClassDB::add_compatibility_class("VisualShaderNodeScalarClamp", "VisualShaderNodeClamp"); ClassDB::add_compatibility_class("VisualShaderNodeVectorClamp", "VisualShaderNodeClamp"); ClassDB::add_compatibility_class("VisualShaderNodeScalarInterp", "VisualShaderNodeMix"); @@ -1072,15 +1108,29 @@ void register_scene_types() { ClassDB::add_compatibility_class("VisualShaderNodeScalarTransformMult", "VisualShaderNodeTransformOp"); ClassDB::add_compatibility_class("VisualShaderNodeScalarDerivativeFunc", "VisualShaderNodeDerivativeFunc"); ClassDB::add_compatibility_class("VisualShaderNodeVectorDerivativeFunc", "VisualShaderNodeDerivativeFunc"); - ClassDB::add_compatibility_class("World", "World3D"); + + ClassDB::add_compatibility_class("VisualShaderNodeBooleanUniform", "VisualShaderNodeBooleanParameter"); + ClassDB::add_compatibility_class("VisualShaderNodeColorUniform", "VisualShaderNodeColorParameter"); + ClassDB::add_compatibility_class("VisualShaderNodeScalarUniform", "VisualShaderNodeFloatParameter"); + ClassDB::add_compatibility_class("VisualShaderNodeCubeMapUniform", "VisualShaderNodeCubeMapParameter"); + ClassDB::add_compatibility_class("VisualShaderNodeTextureUniform", "VisualShaderNodeTexture2DParameter"); + ClassDB::add_compatibility_class("VisualShaderNodeTextureUniformTriplanar", "VisualShaderNodeTextureParameterTriplanar"); + ClassDB::add_compatibility_class("VisualShaderNodeTransformUniform", "VisualShaderNodeTransformParameter"); + ClassDB::add_compatibility_class("VisualShaderNodeVec3Uniform", "VisualShaderNodeVec3Parameter"); + ClassDB::add_compatibility_class("VisualShaderNodeUniform", "VisualShaderNodeParameter"); + ClassDB::add_compatibility_class("VisualShaderNodeUniformRef", "VisualShaderNodeParameterRef"); // Renamed during 4.0 alpha, added to ease transition between alphas. + ClassDB::add_compatibility_class("AudioStreamOGGVorbis", "AudioStreamOggVorbis"); + ClassDB::add_compatibility_class("AudioStreamSample", "AudioStreamWAV"); + ClassDB::add_compatibility_class("OGGPacketSequence", "OggPacketSequence"); ClassDB::add_compatibility_class("StreamCubemap", "CompressedCubemap"); ClassDB::add_compatibility_class("StreamCubemapArray", "CompressedCubemapArray"); ClassDB::add_compatibility_class("StreamTexture2D", "CompressedTexture2D"); ClassDB::add_compatibility_class("StreamTexture2DArray", "CompressedTexture2DArray"); ClassDB::add_compatibility_class("StreamTexture3D", "CompressedTexture3D"); ClassDB::add_compatibility_class("StreamTextureLayered", "CompressedTextureLayered"); + ClassDB::add_compatibility_class("VisualShaderNodeFloatUniform", "VisualShaderNodeFloatParameter"); #endif /* DISABLE_DEPRECATED */ OS::get_singleton()->yield(); // may take time to init @@ -1102,63 +1152,10 @@ void register_scene_types() { } SceneDebugger::initialize(); - SceneReplicationInterface::make_default(); - SceneRPCInterface::make_default(); - SceneCacheInterface::make_default(); -} - -void initialize_theme() { - // Allow creating the default theme at a different scale to suit higher/lower base resolutions. - float default_theme_scale = GLOBAL_DEF("gui/theme/default_theme_scale", 1.0); - ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/default_theme_scale", PropertyInfo(Variant::FLOAT, "gui/theme/default_theme_scale", PROPERTY_HINT_RANGE, "0.5,8,0.01", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)); - - String theme_path = GLOBAL_DEF_RST("gui/theme/custom", ""); - ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/custom", PropertyInfo(Variant::STRING, "gui/theme/custom", PROPERTY_HINT_FILE, "*.tres,*.res,*.theme", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)); - - String font_path = GLOBAL_DEF_RST("gui/theme/custom_font", ""); - ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/custom_font", PropertyInfo(Variant::STRING, "gui/theme/custom_font", PROPERTY_HINT_FILE, "*.tres,*.res", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)); - - bool font_antialiased = (bool)GLOBAL_DEF_RST("gui/theme/default_font_antialiased", true); - ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/default_font_antialiased", PropertyInfo(Variant::BOOL, "gui/theme/default_font_antialiased", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)); - - TextServer::Hinting font_hinting = (TextServer::Hinting)(int)GLOBAL_DEF_RST("gui/theme/default_font_hinting", TextServer::HINTING_LIGHT); - ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/default_font_hinting", PropertyInfo(Variant::INT, "gui/theme/default_font_hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)); - - TextServer::SubpixelPositioning font_subpixel_positioning = (TextServer::SubpixelPositioning)(int)GLOBAL_DEF_RST("gui/theme/default_font_subpixel_positioning", TextServer::SUBPIXEL_POSITIONING_AUTO); - ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/default_font_subpixel_positioning", PropertyInfo(Variant::INT, "gui/theme/default_font_subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One half of a pixel,One quarter of a pixel", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)); - - const bool font_msdf = GLOBAL_DEF_RST("gui/theme/default_font_multichannel_signed_distance_field", false); - const bool font_generate_mipmaps = GLOBAL_DEF_RST("gui/theme/default_font_generate_mipmaps", false); - - Ref<Font> font; - if (!font_path.is_empty()) { - font = ResourceLoader::load(font_path); - if (!font.is_valid()) { - ERR_PRINT("Error loading custom font '" + font_path + "'"); - } - } - - // Always make the default theme to avoid invalid default font/icon/style in the given theme. - if (RenderingServer::get_singleton()) { - make_default_theme(default_theme_scale, font, font_subpixel_positioning, font_hinting, font_antialiased, font_msdf, font_generate_mipmaps); - } - - if (!theme_path.is_empty()) { - Ref<Theme> theme = ResourceLoader::load(theme_path); - if (theme.is_valid()) { - Theme::set_project_default(theme); - if (font.is_valid()) { - Theme::set_fallback_font(font); - } - } else { - ERR_PRINT("Error loading custom theme '" + theme_path + "'"); - } - } } void unregister_scene_types() { SceneDebugger::deinitialize(); - clear_default_theme(); ResourceLoader::remove_resource_format_loader(resource_loader_texture_layered); resource_loader_texture_layered.unref(); @@ -1181,17 +1178,28 @@ void unregister_scene_types() { ResourceLoader::remove_resource_format_loader(resource_loader_shader); resource_loader_shader.unref(); + ResourceSaver::remove_resource_format_saver(resource_saver_shader_include); + resource_saver_shader_include.unref(); + + ResourceLoader::remove_resource_format_loader(resource_loader_shader_include); + resource_loader_shader_include.unref(); + // StandardMaterial3D is not initialised when 3D is disabled, so it shouldn't be cleaned up either #ifndef _3D_DISABLED BaseMaterial3D::finish_shaders(); -#endif // _3D_DISABLED - PhysicalSkyMaterial::cleanup_shader(); PanoramaSkyMaterial::cleanup_shader(); ProceduralSkyMaterial::cleanup_shader(); +#endif // _3D_DISABLED - ParticlesMaterial::finish_shaders(); + ParticleProcessMaterial::finish_shaders(); CanvasItemMaterial::finish_shaders(); ColorPicker::finish_shaders(); SceneStringNames::free(); } + +void register_scene_singletons() { + GDREGISTER_CLASS(ThemeDB); + + Engine::get_singleton()->add_singleton(Engine::Singleton("ThemeDB", ThemeDB::get_singleton())); +} diff --git a/scene/register_scene_types.h b/scene/register_scene_types.h index f0a14387c1..cb3249c5d7 100644 --- a/scene/register_scene_types.h +++ b/scene/register_scene_types.h @@ -33,7 +33,6 @@ void register_scene_types(); void unregister_scene_types(); +void register_scene_singletons(); -void initialize_theme(); - -#endif +#endif // REGISTER_SCENE_TYPES_H diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index 7183accc66..f2ac1c2e58 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -313,29 +313,37 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) { Dictionary d = p_value; ERR_FAIL_COND_V(!d.has("times"), false); ERR_FAIL_COND_V(!d.has("points"), false); - Vector<real_t> times = d["times"]; Vector<real_t> values = d["points"]; +#ifdef TOOLS_ENABLED + ERR_FAIL_COND_V(!d.has("handle_modes"), false); + Vector<int> handle_modes = d["handle_modes"]; +#endif // TOOLS_ENABLED - ERR_FAIL_COND_V(times.size() * 6 != values.size(), false); + ERR_FAIL_COND_V(times.size() * 5 != values.size(), false); if (times.size()) { int valcount = times.size(); const real_t *rt = times.ptr(); const real_t *rv = values.ptr(); +#ifdef TOOLS_ENABLED + const int *rh = handle_modes.ptr(); +#endif // TOOLS_ENABLED bt->values.resize(valcount); for (int i = 0; i < valcount; i++) { bt->values.write[i].time = rt[i]; bt->values.write[i].transition = 0; //unused in bezier - bt->values.write[i].value.value = rv[i * 6 + 0]; - bt->values.write[i].value.in_handle.x = rv[i * 6 + 1]; - bt->values.write[i].value.in_handle.y = rv[i * 6 + 2]; - bt->values.write[i].value.out_handle.x = rv[i * 6 + 3]; - bt->values.write[i].value.out_handle.y = rv[i * 6 + 4]; - bt->values.write[i].value.handle_mode = static_cast<HandleMode>((int)rv[i * 6 + 5]); + bt->values.write[i].value.value = rv[i * 5 + 0]; + bt->values.write[i].value.in_handle.x = rv[i * 5 + 1]; + bt->values.write[i].value.in_handle.y = rv[i * 5 + 2]; + bt->values.write[i].value.out_handle.x = rv[i * 5 + 3]; + bt->values.write[i].value.out_handle.y = rv[i * 5 + 4]; +#ifdef TOOLS_ENABLED + bt->values.write[i].value.handle_mode = static_cast<HandleMode>(rh[i]); +#endif // TOOLS_ENABLED } } @@ -699,28 +707,39 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const { int kk = bt->values.size(); key_times.resize(kk); - key_points.resize(kk * 6); + key_points.resize(kk * 5); real_t *wti = key_times.ptrw(); real_t *wpo = key_points.ptrw(); +#ifdef TOOLS_ENABLED + Vector<int> handle_modes; + handle_modes.resize(kk); + int *whm = handle_modes.ptrw(); +#endif // TOOLS_ENABLED + int idx = 0; const TKey<BezierKey> *vls = bt->values.ptr(); for (int i = 0; i < kk; i++) { wti[idx] = vls[i].time; - wpo[idx * 6 + 0] = vls[i].value.value; - wpo[idx * 6 + 1] = vls[i].value.in_handle.x; - wpo[idx * 6 + 2] = vls[i].value.in_handle.y; - wpo[idx * 6 + 3] = vls[i].value.out_handle.x; - wpo[idx * 6 + 4] = vls[i].value.out_handle.y; - wpo[idx * 6 + 5] = (double)vls[i].value.handle_mode; + wpo[idx * 5 + 0] = vls[i].value.value; + wpo[idx * 5 + 1] = vls[i].value.in_handle.x; + wpo[idx * 5 + 2] = vls[i].value.in_handle.y; + wpo[idx * 5 + 3] = vls[i].value.out_handle.x; + wpo[idx * 5 + 4] = vls[i].value.out_handle.y; +#ifdef TOOLS_ENABLED + whm[idx] = static_cast<int>(vls[i].value.handle_mode); +#endif // TOOLS_ENABLED idx++; } d["times"] = key_times; d["points"] = key_points; +#ifdef TOOLS_ENABLED + d["handle_modes"] = handle_modes; +#endif // TOOLS_ENABLED r_ret = d; @@ -967,7 +986,6 @@ int Animation::find_track(const NodePath &p_path, const TrackType p_type) const void Animation::track_set_interpolation_type(int p_track, InterpolationType p_interp) { ERR_FAIL_INDEX(p_track, tracks.size()); - ERR_FAIL_INDEX(p_interp, 3); tracks[p_track]->interpolation = p_interp; emit_changed(); } @@ -1563,33 +1581,35 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const { return -1; } -void Animation::track_insert_key(int p_track, double p_time, const Variant &p_key, real_t p_transition) { - ERR_FAIL_INDEX(p_track, tracks.size()); +int Animation::track_insert_key(int p_track, double p_time, const Variant &p_key, real_t p_transition) { + ERR_FAIL_INDEX_V(p_track, tracks.size(), -1); Track *t = tracks[p_track]; + int ret = -1; + switch (t->type) { case TYPE_POSITION_3D: { - ERR_FAIL_COND((p_key.get_type() != Variant::VECTOR3) && (p_key.get_type() != Variant::VECTOR3I)); - int idx = position_track_insert_key(p_track, p_time, p_key); - track_set_key_transition(p_track, idx, p_transition); + ERR_FAIL_COND_V((p_key.get_type() != Variant::VECTOR3) && (p_key.get_type() != Variant::VECTOR3I), -1); + ret = position_track_insert_key(p_track, p_time, p_key); + track_set_key_transition(p_track, ret, p_transition); } break; case TYPE_ROTATION_3D: { - ERR_FAIL_COND((p_key.get_type() != Variant::QUATERNION) && (p_key.get_type() != Variant::BASIS)); - int idx = rotation_track_insert_key(p_track, p_time, p_key); - track_set_key_transition(p_track, idx, p_transition); + ERR_FAIL_COND_V((p_key.get_type() != Variant::QUATERNION) && (p_key.get_type() != Variant::BASIS), -1); + ret = rotation_track_insert_key(p_track, p_time, p_key); + track_set_key_transition(p_track, ret, p_transition); } break; case TYPE_SCALE_3D: { - ERR_FAIL_COND((p_key.get_type() != Variant::VECTOR3) && (p_key.get_type() != Variant::VECTOR3I)); - int idx = scale_track_insert_key(p_track, p_time, p_key); - track_set_key_transition(p_track, idx, p_transition); + ERR_FAIL_COND_V((p_key.get_type() != Variant::VECTOR3) && (p_key.get_type() != Variant::VECTOR3I), -1); + ret = scale_track_insert_key(p_track, p_time, p_key); + track_set_key_transition(p_track, ret, p_transition); } break; case TYPE_BLEND_SHAPE: { - ERR_FAIL_COND((p_key.get_type() != Variant::FLOAT) && (p_key.get_type() != Variant::INT)); - int idx = blend_shape_track_insert_key(p_track, p_time, p_key); - track_set_key_transition(p_track, idx, p_transition); + ERR_FAIL_COND_V((p_key.get_type() != Variant::FLOAT) && (p_key.get_type() != Variant::INT), -1); + ret = blend_shape_track_insert_key(p_track, p_time, p_key); + track_set_key_transition(p_track, ret, p_transition); } break; case TYPE_VALUE: { @@ -1599,17 +1619,17 @@ void Animation::track_insert_key(int p_track, double p_time, const Variant &p_ke k.time = p_time; k.transition = p_transition; k.value = p_key; - _insert(p_time, vt->values, k); + ret = _insert(p_time, vt->values, k); } break; case TYPE_METHOD: { MethodTrack *mt = static_cast<MethodTrack *>(t); - ERR_FAIL_COND(p_key.get_type() != Variant::DICTIONARY); + ERR_FAIL_COND_V(p_key.get_type() != Variant::DICTIONARY, -1); Dictionary d = p_key; - ERR_FAIL_COND(!d.has("method") || (d["method"].get_type() != Variant::STRING_NAME && d["method"].get_type() != Variant::STRING)); - ERR_FAIL_COND(!d.has("args") || !d["args"].is_array()); + ERR_FAIL_COND_V(!d.has("method") || (d["method"].get_type() != Variant::STRING_NAME && d["method"].get_type() != Variant::STRING), -1); + ERR_FAIL_COND_V(!d.has("args") || !d["args"].is_array(), -1); MethodKey k; @@ -1618,14 +1638,14 @@ void Animation::track_insert_key(int p_track, double p_time, const Variant &p_ke k.method = d["method"]; k.params = d["args"]; - _insert(p_time, mt->methods, k); + ret = _insert(p_time, mt->methods, k); } break; case TYPE_BEZIER: { BezierTrack *bt = static_cast<BezierTrack *>(t); Array arr = p_key; - ERR_FAIL_COND(arr.size() != 6); + ERR_FAIL_COND_V(arr.size() != 5, -1); TKey<BezierKey> k; k.time = p_time; @@ -1634,24 +1654,31 @@ void Animation::track_insert_key(int p_track, double p_time, const Variant &p_ke k.value.in_handle.y = arr[2]; k.value.out_handle.x = arr[3]; k.value.out_handle.y = arr[4]; - k.value.handle_mode = static_cast<HandleMode>((int)arr[5]); - _insert(p_time, bt->values, k); + ret = _insert(p_time, bt->values, k); + Vector<int> key_neighborhood; + key_neighborhood.push_back(ret); + if (ret > 0) { + key_neighborhood.push_back(ret - 1); + } + if (ret < track_get_key_count(p_track) - 1) { + key_neighborhood.push_back(ret + 1); + } } break; case TYPE_AUDIO: { AudioTrack *at = static_cast<AudioTrack *>(t); Dictionary k = p_key; - ERR_FAIL_COND(!k.has("start_offset")); - ERR_FAIL_COND(!k.has("end_offset")); - ERR_FAIL_COND(!k.has("stream")); + ERR_FAIL_COND_V(!k.has("start_offset"), -1); + ERR_FAIL_COND_V(!k.has("end_offset"), -1); + ERR_FAIL_COND_V(!k.has("stream"), -1); TKey<AudioKey> ak; ak.time = p_time; ak.value.start_offset = k["start_offset"]; ak.value.end_offset = k["end_offset"]; ak.value.stream = k["stream"]; - _insert(p_time, at->values, ak); + ret = _insert(p_time, at->values, ak); } break; case TYPE_ANIMATION: { @@ -1661,12 +1688,14 @@ void Animation::track_insert_key(int p_track, double p_time, const Variant &p_ke ak.time = p_time; ak.value = p_key; - _insert(p_time, at->values, ak); + ret = _insert(p_time, at->values, ak); } break; } emit_changed(); + + return ret; } int Animation::track_get_key_count(int p_track) const { @@ -1773,13 +1802,12 @@ Variant Animation::track_get_key_value(int p_track, int p_key_idx) const { ERR_FAIL_INDEX_V(p_key_idx, bt->values.size(), Variant()); Array arr; - arr.resize(6); + arr.resize(5); arr[0] = bt->values[p_key_idx].value.value; arr[1] = bt->values[p_key_idx].value.in_handle.x; arr[2] = bt->values[p_key_idx].value.in_handle.y; arr[3] = bt->values[p_key_idx].value.out_handle.x; arr[4] = bt->values[p_key_idx].value.out_handle.y; - arr[5] = (double)bt->values[p_key_idx].value.handle_mode; return arr; } break; @@ -2074,11 +2102,9 @@ bool Animation::track_is_compressed(int p_track) const { return bst->compressed_track >= 0; } break; default: { - return false; //animation does not really use transitions + return false; // Animation does not really use transitions. } break; } - - ERR_FAIL_V(false); } void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p_value) { @@ -2148,14 +2174,13 @@ void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p ERR_FAIL_INDEX(p_key_idx, bt->values.size()); Array arr = p_value; - ERR_FAIL_COND(arr.size() != 6); + ERR_FAIL_COND(arr.size() != 5); bt->values.write[p_key_idx].value.value = arr[0]; bt->values.write[p_key_idx].value.in_handle.x = arr[1]; bt->values.write[p_key_idx].value.in_handle.y = arr[2]; bt->values.write[p_key_idx].value.out_handle.x = arr[3]; bt->values.write[p_key_idx].value.out_handle.y = arr[4]; - bt->values.write[p_key_idx].value.handle_mode = static_cast<HandleMode>((int)arr[5]); } break; case TYPE_AUDIO: { @@ -2279,6 +2304,8 @@ int Animation::_find(const Vector<K> &p_keys, double p_time, bool p_backward) co return middle; } +// Linear interpolation for anytype. + Vector3 Animation::_interpolate(const Vector3 &p_a, const Vector3 &p_b, real_t p_c) const { return p_a.lerp(p_b, p_c); } @@ -2288,24 +2315,37 @@ Quaternion Animation::_interpolate(const Quaternion &p_a, const Quaternion &p_b, } Variant Animation::_interpolate(const Variant &p_a, const Variant &p_b, real_t p_c) const { - Variant dst; - Variant::interpolate(p_a, p_b, p_c, dst); - return dst; + return interpolate_variant(p_a, p_b, p_c); } real_t Animation::_interpolate(const real_t &p_a, const real_t &p_b, real_t p_c) const { - return p_a * (1.0 - p_c) + p_b * p_c; + return Math::lerp(p_a, p_b, p_c); } -Vector3 Animation::_cubic_interpolate(const Vector3 &p_pre_a, const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_post_b, real_t p_c) const { - return p_a.cubic_interpolate(p_b, p_pre_a, p_post_b, p_c); +Variant Animation::_interpolate_angle(const Variant &p_a, const Variant &p_b, real_t p_c) const { + Variant::Type type_a = p_a.get_type(); + Variant::Type type_b = p_b.get_type(); + uint32_t vformat = 1 << type_a; + vformat |= 1 << type_b; + if (vformat == ((1 << Variant::INT) | (1 << Variant::FLOAT)) || vformat == (1 << Variant::FLOAT)) { + real_t a = p_a; + real_t b = p_b; + return Math::fposmod((float)Math::lerp_angle(a, b, p_c), (float)Math_TAU); + } + return _interpolate(p_a, p_b, p_c); } -Quaternion Animation::_cubic_interpolate(const Quaternion &p_pre_a, const Quaternion &p_a, const Quaternion &p_b, const Quaternion &p_post_b, real_t p_c) const { - return p_a.cubic_slerp(p_b, p_pre_a, p_post_b, p_c); +// Cubic interpolation for anytype. + +Vector3 Animation::_cubic_interpolate_in_time(const Vector3 &p_pre_a, const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_post_b, real_t p_c, real_t p_pre_a_t, real_t p_b_t, real_t p_post_b_t) const { + return p_a.cubic_interpolate_in_time(p_b, p_pre_a, p_post_b, p_c, p_b_t, p_pre_a_t, p_post_b_t); } -Variant Animation::_cubic_interpolate(const Variant &p_pre_a, const Variant &p_a, const Variant &p_b, const Variant &p_post_b, real_t p_c) const { +Quaternion Animation::_cubic_interpolate_in_time(const Quaternion &p_pre_a, const Quaternion &p_a, const Quaternion &p_b, const Quaternion &p_post_b, real_t p_c, real_t p_pre_a_t, real_t p_b_t, real_t p_post_b_t) const { + return p_a.spherical_cubic_interpolate_in_time(p_b, p_pre_a, p_post_b, p_c, p_b_t, p_pre_a_t, p_post_b_t); +} + +Variant Animation::_cubic_interpolate_in_time(const Variant &p_pre_a, const Variant &p_a, const Variant &p_b, const Variant &p_post_b, real_t p_c, real_t p_pre_a_t, real_t p_b_t, real_t p_post_b_t) const { Variant::Type type_a = p_a.get_type(); Variant::Type type_b = p_b.get_type(); Variant::Type type_pa = p_pre_a.get_type(); @@ -2325,7 +2365,7 @@ Variant Animation::_cubic_interpolate(const Variant &p_pre_a, const Variant &p_a real_t pa = p_pre_a; real_t pb = p_post_b; - return Math::cubic_interpolate(a, b, pa, pb, p_c); + return Math::cubic_interpolate_in_time(a, b, pa, pb, p_c, p_b_t, p_pre_a_t, p_post_b_t); } else if ((vformat & (vformat - 1))) { return p_a; //can't interpolate, mix of types } @@ -2337,7 +2377,7 @@ Variant Animation::_cubic_interpolate(const Variant &p_pre_a, const Variant &p_a Vector2 pa = p_pre_a; Vector2 pb = p_post_b; - return a.cubic_interpolate(b, pa, pb, p_c); + return a.cubic_interpolate_in_time(b, pa, pb, p_c, p_b_t, p_pre_a_t, p_post_b_t); } case Variant::RECT2: { Rect2 a = p_a; @@ -2346,8 +2386,8 @@ Variant Animation::_cubic_interpolate(const Variant &p_pre_a, const Variant &p_a Rect2 pb = p_post_b; return Rect2( - a.position.cubic_interpolate(b.position, pa.position, pb.position, p_c), - a.size.cubic_interpolate(b.size, pa.size, pb.size, p_c)); + a.position.cubic_interpolate_in_time(b.position, pa.position, pb.position, p_c, p_b_t, p_pre_a_t, p_post_b_t), + a.size.cubic_interpolate_in_time(b.size, pa.size, pb.size, p_c, p_b_t, p_pre_a_t, p_post_b_t)); } case Variant::VECTOR3: { Vector3 a = p_a; @@ -2355,7 +2395,7 @@ Variant Animation::_cubic_interpolate(const Variant &p_pre_a, const Variant &p_a Vector3 pa = p_pre_a; Vector3 pb = p_post_b; - return a.cubic_interpolate(b, pa, pb, p_c); + return a.cubic_interpolate_in_time(b, pa, pb, p_c, p_b_t, p_pre_a_t, p_post_b_t); } case Variant::QUATERNION: { Quaternion a = p_a; @@ -2363,7 +2403,7 @@ Variant Animation::_cubic_interpolate(const Variant &p_pre_a, const Variant &p_a Quaternion pa = p_pre_a; Quaternion pb = p_post_b; - return a.cubic_slerp(b, pa, pb, p_c); + return a.spherical_cubic_interpolate_in_time(b, pa, pb, p_c, p_b_t, p_pre_a_t, p_post_b_t); } case Variant::AABB: { AABB a = p_a; @@ -2372,8 +2412,8 @@ Variant Animation::_cubic_interpolate(const Variant &p_pre_a, const Variant &p_a AABB pb = p_post_b; return AABB( - a.position.cubic_interpolate(b.position, pa.position, pb.position, p_c), - a.size.cubic_interpolate(b.size, pa.size, pb.size, p_c)); + a.position.cubic_interpolate_in_time(b.position, pa.position, pb.position, p_c, p_b_t, p_pre_a_t, p_post_b_t), + a.size.cubic_interpolate_in_time(b.size, pa.size, pb.size, p_c, p_b_t, p_pre_a_t, p_post_b_t)); } default: { return _interpolate(p_a, p_b, p_c); @@ -2381,7 +2421,26 @@ Variant Animation::_cubic_interpolate(const Variant &p_pre_a, const Variant &p_a } } -real_t Animation::_cubic_interpolate(const real_t &p_pre_a, const real_t &p_a, const real_t &p_b, const real_t &p_post_b, real_t p_c) const { +real_t Animation::_cubic_interpolate_in_time(const real_t &p_pre_a, const real_t &p_a, const real_t &p_b, const real_t &p_post_b, real_t p_c, real_t p_pre_a_t, real_t p_b_t, real_t p_post_b_t) const { + return Math::cubic_interpolate_in_time(p_a, p_b, p_pre_a, p_post_b, p_c, p_b_t, p_pre_a_t, p_post_b_t); +} + +Variant Animation::_cubic_interpolate_angle_in_time(const Variant &p_pre_a, const Variant &p_a, const Variant &p_b, const Variant &p_post_b, real_t p_c, real_t p_pre_a_t, real_t p_b_t, real_t p_post_b_t) const { + Variant::Type type_a = p_a.get_type(); + Variant::Type type_b = p_b.get_type(); + Variant::Type type_pa = p_pre_a.get_type(); + Variant::Type type_pb = p_post_b.get_type(); + uint32_t vformat = 1 << type_a; + vformat |= 1 << type_b; + vformat |= 1 << type_pa; + vformat |= 1 << type_pb; + if (vformat == ((1 << Variant::INT) | (1 << Variant::FLOAT)) || vformat == (1 << Variant::FLOAT)) { + real_t a = p_a; + real_t b = p_b; + real_t pa = p_pre_a; + real_t pb = p_post_b; + return Math::fposmod((float)Math::cubic_interpolate_angle_in_time(a, b, pa, pb, p_c, p_b_t, p_pre_a_t, p_post_b_t), (float)Math_TAU); + } return _interpolate(p_a, p_b, p_c); } @@ -2564,26 +2623,70 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, Interpol case INTERPOLATION_LINEAR: { return _interpolate(p_keys[idx].value, p_keys[next].value, c); } break; - case INTERPOLATION_CUBIC: { - int pre = idx - 1; - if (pre < 0) { - if (loop_mode == LOOP_LINEAR && p_loop_wrap) { - pre = len - 1; - } else { - pre = 0; + case INTERPOLATION_LINEAR_ANGLE: { + return _interpolate_angle(p_keys[idx].value, p_keys[next].value, c); + } break; + case INTERPOLATION_CUBIC: + case INTERPOLATION_CUBIC_ANGLE: { + int pre = 0; + int post = 0; + if (!p_backward) { + pre = idx - 1; + if (pre < 0) { + if (loop_mode == LOOP_LINEAR && p_loop_wrap) { + pre = len - 1; + } else { + pre = 0; + } } - } - int post = next + 1; - if (post >= len) { - if (loop_mode == LOOP_LINEAR && p_loop_wrap) { - post = 0; - } else { - post = next; + post = next + 1; + if (post >= len) { + if (loop_mode == LOOP_LINEAR && p_loop_wrap) { + post = 0; + } else { + post = next; + } + } + } else { + pre = idx + 1; + if (pre >= len) { + if (loop_mode == LOOP_LINEAR && p_loop_wrap) { + pre = 0; + } else { + pre = idx; + } + } + post = next - 1; + if (post < 0) { + if (loop_mode == LOOP_LINEAR && p_loop_wrap) { + post = len - 1; + } else { + post = 0; + } } } - return _cubic_interpolate(p_keys[pre].value, p_keys[idx].value, p_keys[next].value, p_keys[post].value, c); + real_t pre_t = 0.0; + real_t to_t = 0.0; + real_t post_t = 0.0; + if (loop_mode == LOOP_LINEAR && p_loop_wrap) { + pre_t = pre > idx ? -length + p_keys[pre].time - p_keys[idx].time : p_keys[pre].time - p_keys[idx].time; + to_t = next < idx ? length + p_keys[next].time - p_keys[idx].time : p_keys[next].time - p_keys[idx].time; + post_t = next < idx || post <= idx ? length + p_keys[post].time - p_keys[idx].time : p_keys[post].time - p_keys[idx].time; + } else { + pre_t = p_keys[pre].time - p_keys[idx].time; + to_t = p_keys[next].time - p_keys[idx].time; + post_t = p_keys[post].time - p_keys[idx].time; + } + if (p_interp == INTERPOLATION_CUBIC_ANGLE) { + return _cubic_interpolate_angle_in_time( + p_keys[pre].value, p_keys[idx].value, p_keys[next].value, p_keys[post].value, c, + pre_t, to_t, post_t); + } + return _cubic_interpolate_in_time( + p_keys[pre].value, p_keys[idx].value, p_keys[next].value, p_keys[post].value, c, + pre_t, to_t, post_t); } break; default: return p_keys[idx].value; @@ -3211,7 +3314,7 @@ StringName Animation::method_track_get_name(int p_track, int p_key_idx) const { return pm->methods[p_key_idx].method; } -int Animation::bezier_track_insert_key(int p_track, double p_time, real_t p_value, const Vector2 &p_in_handle, const Vector2 &p_out_handle, const HandleMode p_handle_mode) { +int Animation::bezier_track_insert_key(int p_track, double p_time, real_t p_value, const Vector2 &p_in_handle, const Vector2 &p_out_handle) { ERR_FAIL_INDEX_V(p_track, tracks.size(), -1); Track *t = tracks[p_track]; ERR_FAIL_COND_V(t->type != TYPE_BEZIER, -1); @@ -3229,7 +3332,6 @@ int Animation::bezier_track_insert_key(int p_track, double p_time, real_t p_valu if (k.value.out_handle.x < 0) { k.value.out_handle.x = 0; } - k.value.handle_mode = p_handle_mode; int key = _insert(p_time, bt->values, k); @@ -3238,30 +3340,6 @@ int Animation::bezier_track_insert_key(int p_track, double p_time, real_t p_valu return key; } -void Animation::bezier_track_set_key_handle_mode(int p_track, int p_index, HandleMode p_mode, double p_balanced_value_time_ratio) { - ERR_FAIL_INDEX(p_track, tracks.size()); - Track *t = tracks[p_track]; - ERR_FAIL_COND(t->type != TYPE_BEZIER); - - BezierTrack *bt = static_cast<BezierTrack *>(t); - - ERR_FAIL_INDEX(p_index, bt->values.size()); - - bt->values.write[p_index].value.handle_mode = p_mode; - - if (p_mode == HANDLE_MODE_BALANCED) { - Transform2D xform; - xform.set_scale(Vector2(1.0, 1.0 / p_balanced_value_time_ratio)); - - Vector2 vec_in = xform.xform(bt->values[p_index].value.in_handle); - Vector2 vec_out = xform.xform(bt->values[p_index].value.out_handle); - - bt->values.write[p_index].value.in_handle = xform.affine_inverse().xform(-vec_out.normalized() * vec_in.length()); - } - - emit_changed(); -} - void Animation::bezier_track_set_key_value(int p_track, int p_index, real_t p_value) { ERR_FAIL_INDEX(p_track, tracks.size()); Track *t = tracks[p_track]; @@ -3272,10 +3350,11 @@ void Animation::bezier_track_set_key_value(int p_track, int p_index, real_t p_va ERR_FAIL_INDEX(p_index, bt->values.size()); bt->values.write[p_index].value.value = p_value; + emit_changed(); } -void Animation::bezier_track_set_key_in_handle(int p_track, int p_index, const Vector2 &p_handle, double p_balanced_value_time_ratio) { +void Animation::bezier_track_set_key_in_handle(int p_track, int p_index, const Vector2 &p_handle, real_t p_balanced_value_time_ratio) { ERR_FAIL_INDEX(p_track, tracks.size()); Track *t = tracks[p_track]; ERR_FAIL_COND(t->type != TYPE_BEZIER); @@ -3290,7 +3369,11 @@ void Animation::bezier_track_set_key_in_handle(int p_track, int p_index, const V } bt->values.write[p_index].value.in_handle = in_handle; - if (bt->values[p_index].value.handle_mode == HANDLE_MODE_BALANCED) { +#ifdef TOOLS_ENABLED + if (bt->values[p_index].value.handle_mode == HANDLE_MODE_LINEAR) { + bt->values.write[p_index].value.in_handle = Vector2(); + bt->values.write[p_index].value.out_handle = Vector2(); + } else if (bt->values[p_index].value.handle_mode == HANDLE_MODE_BALANCED) { Transform2D xform; xform.set_scale(Vector2(1.0, 1.0 / p_balanced_value_time_ratio)); @@ -3298,12 +3381,15 @@ void Animation::bezier_track_set_key_in_handle(int p_track, int p_index, const V Vector2 vec_in = xform.xform(in_handle); bt->values.write[p_index].value.out_handle = xform.affine_inverse().xform(-vec_in.normalized() * vec_out.length()); + } else if (bt->values[p_index].value.handle_mode == HANDLE_MODE_MIRRORED) { + bt->values.write[p_index].value.out_handle = -in_handle; } +#endif // TOOLS_ENABLED emit_changed(); } -void Animation::bezier_track_set_key_out_handle(int p_track, int p_index, const Vector2 &p_handle, double p_balanced_value_time_ratio) { +void Animation::bezier_track_set_key_out_handle(int p_track, int p_index, const Vector2 &p_handle, real_t p_balanced_value_time_ratio) { ERR_FAIL_INDEX(p_track, tracks.size()); Track *t = tracks[p_track]; ERR_FAIL_COND(t->type != TYPE_BEZIER); @@ -3318,7 +3404,11 @@ void Animation::bezier_track_set_key_out_handle(int p_track, int p_index, const } bt->values.write[p_index].value.out_handle = out_handle; - if (bt->values[p_index].value.handle_mode == HANDLE_MODE_BALANCED) { +#ifdef TOOLS_ENABLED + if (bt->values[p_index].value.handle_mode == HANDLE_MODE_LINEAR) { + bt->values.write[p_index].value.in_handle = Vector2(); + bt->values.write[p_index].value.out_handle = Vector2(); + } else if (bt->values[p_index].value.handle_mode == HANDLE_MODE_BALANCED) { Transform2D xform; xform.set_scale(Vector2(1.0, 1.0 / p_balanced_value_time_ratio)); @@ -3326,7 +3416,10 @@ void Animation::bezier_track_set_key_out_handle(int p_track, int p_index, const Vector2 vec_out = xform.xform(out_handle); bt->values.write[p_index].value.in_handle = xform.affine_inverse().xform(-vec_out.normalized() * vec_in.length()); + } else if (bt->values[p_index].value.handle_mode == HANDLE_MODE_MIRRORED) { + bt->values.write[p_index].value.in_handle = -out_handle; } +#endif // TOOLS_ENABLED emit_changed(); } @@ -3343,18 +3436,6 @@ real_t Animation::bezier_track_get_key_value(int p_track, int p_index) const { return bt->values[p_index].value.value; } -int Animation::bezier_track_get_key_handle_mode(int p_track, int p_index) const { - ERR_FAIL_INDEX_V(p_track, tracks.size(), 0); - Track *t = tracks[p_track]; - ERR_FAIL_COND_V(t->type != TYPE_BEZIER, 0); - - BezierTrack *bt = static_cast<BezierTrack *>(t); - - ERR_FAIL_INDEX_V(p_index, bt->values.size(), 0); - - return bt->values[p_index].value.handle_mode; -} - Vector2 Animation::bezier_track_get_key_in_handle(int p_track, int p_index) const { ERR_FAIL_INDEX_V(p_track, tracks.size(), Vector2()); Track *t = tracks[p_track]; @@ -3379,6 +3460,109 @@ Vector2 Animation::bezier_track_get_key_out_handle(int p_track, int p_index) con return bt->values[p_index].value.out_handle; } +#ifdef TOOLS_ENABLED +void Animation::bezier_track_set_key_handle_mode(int p_track, int p_index, HandleMode p_mode, HandleSetMode p_set_mode) { + ERR_FAIL_INDEX(p_track, tracks.size()); + Track *t = tracks[p_track]; + ERR_FAIL_COND(t->type != TYPE_BEZIER); + + BezierTrack *bt = static_cast<BezierTrack *>(t); + + ERR_FAIL_INDEX(p_index, bt->values.size()); + + bt->values.write[p_index].value.handle_mode = p_mode; + + switch (p_mode) { + case HANDLE_MODE_LINEAR: { + bt->values.write[p_index].value.in_handle = Vector2(0, 0); + bt->values.write[p_index].value.out_handle = Vector2(0, 0); + } break; + case HANDLE_MODE_BALANCED: + case HANDLE_MODE_MIRRORED: { + int prev_key = MAX(0, p_index - 1); + int next_key = MIN(bt->values.size() - 1, p_index + 1); + if (prev_key == next_key) { + break; // Exists only one key. + } + real_t in_handle_x = 0; + real_t in_handle_y = 0; + real_t out_handle_x = 0; + real_t out_handle_y = 0; + if (p_mode == HANDLE_MODE_BALANCED) { + // Note: + // If p_set_mode == HANDLE_SET_MODE_NONE, I don't know if it should change the Tangent implicitly. + // At the least, we need to avoid corrupting the handles when loading animation from the resource. + // However, changes made by the Inspector do not go through the BezierEditor, + // so if you change from Free to Balanced or Mirrored in Inspector, there is no guarantee that + // it is Balanced or Mirrored until there is a handle operation. + if (p_set_mode == HANDLE_SET_MODE_RESET) { + real_t handle_length = 1.0 / 3.0; + in_handle_x = (bt->values[prev_key].time - bt->values[p_index].time) * handle_length; + in_handle_y = 0; + out_handle_x = (bt->values[next_key].time - bt->values[p_index].time) * handle_length; + out_handle_y = 0; + bt->values.write[p_index].value.in_handle = Vector2(in_handle_x, in_handle_y); + bt->values.write[p_index].value.out_handle = Vector2(out_handle_x, out_handle_y); + } else if (p_set_mode == HANDLE_SET_MODE_AUTO) { + real_t handle_length = 1.0 / 6.0; + real_t tangent = (bt->values[next_key].value.value - bt->values[prev_key].value.value) / (bt->values[next_key].time - bt->values[prev_key].time); + in_handle_x = (bt->values[prev_key].time - bt->values[p_index].time) * handle_length; + in_handle_y = in_handle_x * tangent; + out_handle_x = (bt->values[next_key].time - bt->values[p_index].time) * handle_length; + out_handle_y = out_handle_x * tangent; + bt->values.write[p_index].value.in_handle = Vector2(in_handle_x, in_handle_y); + bt->values.write[p_index].value.out_handle = Vector2(out_handle_x, out_handle_y); + } + } else { + real_t handle_length = 1.0 / 4.0; + real_t prev_interval = Math::abs(bt->values[p_index].time - bt->values[prev_key].time); + real_t next_interval = Math::abs(bt->values[p_index].time - bt->values[next_key].time); + real_t min_time = 0; + if (Math::is_zero_approx(prev_interval)) { + min_time = next_interval; + } else if (Math::is_zero_approx(next_interval)) { + min_time = prev_interval; + } else { + min_time = MIN(prev_interval, next_interval); + } + if (p_set_mode == HANDLE_SET_MODE_RESET) { + in_handle_x = -min_time * handle_length; + in_handle_y = 0; + out_handle_x = min_time * handle_length; + out_handle_y = 0; + bt->values.write[p_index].value.in_handle = Vector2(in_handle_x, in_handle_y); + bt->values.write[p_index].value.out_handle = Vector2(out_handle_x, out_handle_y); + } else if (p_set_mode == HANDLE_SET_MODE_AUTO) { + real_t tangent = (bt->values[next_key].value.value - bt->values[prev_key].value.value) / min_time; + in_handle_x = -min_time * handle_length; + in_handle_y = in_handle_x * tangent; + out_handle_x = min_time * handle_length; + out_handle_y = out_handle_x * tangent; + bt->values.write[p_index].value.in_handle = Vector2(in_handle_x, in_handle_y); + bt->values.write[p_index].value.out_handle = Vector2(out_handle_x, out_handle_y); + } + } + } break; + default: { + } break; + } + + emit_changed(); +} + +Animation::HandleMode Animation::bezier_track_get_key_handle_mode(int p_track, int p_index) const { + ERR_FAIL_INDEX_V(p_track, tracks.size(), HANDLE_MODE_FREE); + Track *t = tracks[p_track]; + ERR_FAIL_COND_V(t->type != TYPE_BEZIER, HANDLE_MODE_FREE); + + BezierTrack *bt = static_cast<BezierTrack *>(t); + + ERR_FAIL_INDEX_V(p_index, bt->values.size(), HANDLE_MODE_FREE); + + return bt->values[p_index].value.handle_mode; +} +#endif // TOOLS_ENABLED + real_t Animation::bezier_track_interpolate(int p_track, double p_time) const { //this uses a different interpolation scheme ERR_FAIL_INDEX_V(p_track, tracks.size(), 0); @@ -3775,7 +3959,7 @@ void Animation::_bind_methods() { ClassDB::bind_method(D_METHOD("method_track_get_name", "track_idx", "key_idx"), &Animation::method_track_get_name); ClassDB::bind_method(D_METHOD("method_track_get_params", "track_idx", "key_idx"), &Animation::method_track_get_params); - ClassDB::bind_method(D_METHOD("bezier_track_insert_key", "track_idx", "time", "value", "in_handle", "out_handle", "handle_mode"), &Animation::bezier_track_insert_key, DEFVAL(Vector2()), DEFVAL(Vector2()), DEFVAL(Animation::HandleMode::HANDLE_MODE_BALANCED)); + ClassDB::bind_method(D_METHOD("bezier_track_insert_key", "track_idx", "time", "value", "in_handle", "out_handle"), &Animation::bezier_track_insert_key, DEFVAL(Vector2()), DEFVAL(Vector2())); ClassDB::bind_method(D_METHOD("bezier_track_set_key_value", "track_idx", "key_idx", "value"), &Animation::bezier_track_set_key_value); ClassDB::bind_method(D_METHOD("bezier_track_set_key_in_handle", "track_idx", "key_idx", "in_handle", "balanced_value_time_ratio"), &Animation::bezier_track_set_key_in_handle, DEFVAL(1.0)); @@ -3795,9 +3979,6 @@ void Animation::_bind_methods() { ClassDB::bind_method(D_METHOD("audio_track_get_key_start_offset", "track_idx", "key_idx"), &Animation::audio_track_get_key_start_offset); ClassDB::bind_method(D_METHOD("audio_track_get_key_end_offset", "track_idx", "key_idx"), &Animation::audio_track_get_key_end_offset); - ClassDB::bind_method(D_METHOD("bezier_track_set_key_handle_mode", "track_idx", "key_idx", "key_handle_mode", "balanced_value_time_ratio"), &Animation::bezier_track_set_key_handle_mode, DEFVAL(1.0)); - ClassDB::bind_method(D_METHOD("bezier_track_get_key_handle_mode", "track_idx", "key_idx"), &Animation::bezier_track_get_key_handle_mode); - ClassDB::bind_method(D_METHOD("animation_track_insert_key", "track_idx", "time", "animation"), &Animation::animation_track_insert_key); ClassDB::bind_method(D_METHOD("animation_track_set_key_animation", "track_idx", "key_idx", "animation"), &Animation::animation_track_set_key_animation); ClassDB::bind_method(D_METHOD("animation_track_get_key_animation", "track_idx", "key_idx"), &Animation::animation_track_get_key_animation); @@ -3835,6 +4016,8 @@ void Animation::_bind_methods() { BIND_ENUM_CONSTANT(INTERPOLATION_NEAREST); BIND_ENUM_CONSTANT(INTERPOLATION_LINEAR); BIND_ENUM_CONSTANT(INTERPOLATION_CUBIC); + BIND_ENUM_CONSTANT(INTERPOLATION_LINEAR_ANGLE); + BIND_ENUM_CONSTANT(INTERPOLATION_CUBIC_ANGLE); BIND_ENUM_CONSTANT(UPDATE_CONTINUOUS); BIND_ENUM_CONSTANT(UPDATE_DISCRETE); @@ -3844,9 +4027,6 @@ void Animation::_bind_methods() { BIND_ENUM_CONSTANT(LOOP_NONE); BIND_ENUM_CONSTANT(LOOP_LINEAR); BIND_ENUM_CONSTANT(LOOP_PINGPONG); - - BIND_ENUM_CONSTANT(HANDLE_MODE_FREE); - BIND_ENUM_CONSTANT(HANDLE_MODE_BALANCED); } void Animation::clear() { @@ -3864,316 +4044,369 @@ void Animation::clear() { emit_signal(SceneStringNames::get_singleton()->tracks_changed); } -bool Animation::_position_track_optimize_key(const TKey<Vector3> &t0, const TKey<Vector3> &t1, const TKey<Vector3> &t2, real_t p_allowed_linear_err, real_t p_allowed_angular_error, const Vector3 &p_norm) { - const Vector3 &v0 = t0.value; - const Vector3 &v1 = t1.value; - const Vector3 &v2 = t2.value; - - if (v0.is_equal_approx(v2)) { - //0 and 2 are close, let's see if 1 is close - if (!v0.is_equal_approx(v1)) { - //not close, not optimizable - return false; - } - - } else { - Vector3 pd = (v2 - v0); - real_t d0 = pd.dot(v0); - real_t d1 = pd.dot(v1); - real_t d2 = pd.dot(v2); - if (d1 < d0 || d1 > d2) { - return false; - } - - Vector3 s[2] = { v0, v2 }; - real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1); - - if (d > pd.length() * p_allowed_linear_err) { - return false; //beyond allowed error for collinearity - } - - if (p_norm != Vector3() && Math::acos(pd.normalized().dot(p_norm)) > p_allowed_angular_error) { - return false; +bool Animation::_float_track_optimize_key(const TKey<float> t0, const TKey<float> t1, const TKey<float> t2, real_t p_allowed_velocity_err, real_t p_allowed_precision_error) { + // Remove overlapping keys. + if (Math::is_equal_approx(t0.time, t1.time) || Math::is_equal_approx(t1.time, t2.time)) { + return true; + } + if (abs(t0.value - t1.value) < p_allowed_precision_error && abs(t1.value - t2.value) < p_allowed_precision_error) { + return true; + } + // Calc velocities. + double v0 = (t1.value - t0.value) / (t1.time - t0.time); + double v1 = (t2.value - t1.value) / (t2.time - t1.time); + // Avoid zero div but check equality. + if (abs(v0 - v1) < p_allowed_precision_error) { + return true; + } else if (abs(v0) < p_allowed_precision_error || abs(v1) < p_allowed_precision_error) { + return false; + } + if (!signbit(v0 * v1)) { + v0 = abs(v0); + v1 = abs(v1); + double ratio = v0 < v1 ? v0 / v1 : v1 / v0; + if (ratio >= 1.0 - p_allowed_velocity_err) { + return true; } } - - return true; + return false; } -bool Animation::_rotation_track_optimize_key(const TKey<Quaternion> &t0, const TKey<Quaternion> &t1, const TKey<Quaternion> &t2, real_t p_allowed_angular_error, float p_max_optimizable_angle) { - const Quaternion &q0 = t0.value; - const Quaternion &q1 = t1.value; - const Quaternion &q2 = t2.value; - - //localize both to rotation from q0 - - if (q0.is_equal_approx(q2)) { - if (!q0.is_equal_approx(q1)) { - return false; - } - - } else { - Quaternion r02 = (q0.inverse() * q2).normalized(); - Quaternion r01 = (q0.inverse() * q1).normalized(); - - Vector3 v02, v01; - real_t a02, a01; - - r02.get_axis_angle(v02, a02); - r01.get_axis_angle(v01, a01); - - if (Math::abs(a02) > p_max_optimizable_angle) { - return false; - } - - if (v01.dot(v02) < 0) { - //make sure both rotations go the same way to compare - v02 = -v02; - a02 = -a02; - } - - real_t err_01 = Math::acos(v01.normalized().dot(v02.normalized())) / Math_PI; - if (err_01 > p_allowed_angular_error) { - //not rotating in the same axis - return false; - } - - if (a01 * a02 < 0) { - //not rotating in the same direction - return false; - } - - real_t tr = a01 / a02; - if (tr < 0 || tr > 1) { - return false; //rotating too much or too less +bool Animation::_vector2_track_optimize_key(const TKey<Vector2> t0, const TKey<Vector2> t1, const TKey<Vector2> t2, real_t p_allowed_velocity_err, real_t p_allowed_angular_error, real_t p_allowed_precision_error) { + // Remove overlapping keys. + if (Math::is_equal_approx(t0.time, t1.time) || Math::is_equal_approx(t1.time, t2.time)) { + return true; + } + if ((t0.value - t1.value).length() < p_allowed_precision_error && (t1.value - t2.value).length() < p_allowed_precision_error) { + return true; + } + // Calc velocities. + Vector2 vc0 = (t1.value - t0.value) / (t1.time - t0.time); + Vector2 vc1 = (t2.value - t1.value) / (t2.time - t1.time); + double v0 = vc0.length(); + double v1 = vc1.length(); + // Avoid zero div but check equality. + if (abs(v0 - v1) < p_allowed_precision_error) { + return true; + } else if (abs(v0) < p_allowed_precision_error || abs(v1) < p_allowed_precision_error) { + return false; + } + // Check axis. + if (vc0.normalized().dot(vc1.normalized()) >= 1.0 - p_allowed_angular_error * 2.0) { + v0 = abs(v0); + v1 = abs(v1); + double ratio = v0 < v1 ? v0 / v1 : v1 / v0; + if (ratio >= 1.0 - p_allowed_velocity_err) { + return true; } } - - return true; + return false; } -bool Animation::_scale_track_optimize_key(const TKey<Vector3> &t0, const TKey<Vector3> &t1, const TKey<Vector3> &t2, real_t p_allowed_linear_error) { - const Vector3 &v0 = t0.value; - const Vector3 &v1 = t1.value; - const Vector3 &v2 = t2.value; - - if (v0.is_equal_approx(v2)) { - //0 and 2 are close, let's see if 1 is close - if (!v0.is_equal_approx(v1)) { - //not close, not optimizable - return false; - } - - } else { - Vector3 pd = (v2 - v0); - real_t d0 = pd.dot(v0); - real_t d1 = pd.dot(v1); - real_t d2 = pd.dot(v2); - if (d1 < d0 || d1 > d2) { - return false; //beyond segment range - } - - Vector3 s[2] = { v0, v2 }; - real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1); - - if (d > pd.length() * p_allowed_linear_error) { - return false; //beyond allowed error for colinearity +bool Animation::_vector3_track_optimize_key(const TKey<Vector3> t0, const TKey<Vector3> t1, const TKey<Vector3> t2, real_t p_allowed_velocity_err, real_t p_allowed_angular_error, real_t p_allowed_precision_error) { + // Remove overlapping keys. + if (Math::is_equal_approx(t0.time, t1.time) || Math::is_equal_approx(t1.time, t2.time)) { + return true; + } + if ((t0.value - t1.value).length() < p_allowed_precision_error && (t1.value - t2.value).length() < p_allowed_precision_error) { + return true; + } + // Calc velocities. + Vector3 vc0 = (t1.value - t0.value) / (t1.time - t0.time); + Vector3 vc1 = (t2.value - t1.value) / (t2.time - t1.time); + double v0 = vc0.length(); + double v1 = vc1.length(); + // Avoid zero div but check equality. + if (abs(v0 - v1) < p_allowed_precision_error) { + return true; + } else if (abs(v0) < p_allowed_precision_error || abs(v1) < p_allowed_precision_error) { + return false; + } + // Check axis. + if (vc0.normalized().dot(vc1.normalized()) >= 1.0 - p_allowed_angular_error * 2.0) { + v0 = abs(v0); + v1 = abs(v1); + double ratio = v0 < v1 ? v0 / v1 : v1 / v0; + if (ratio >= 1.0 - p_allowed_velocity_err) { + return true; } } - - return true; + return false; } -bool Animation::_blend_shape_track_optimize_key(const TKey<float> &t0, const TKey<float> &t1, const TKey<float> &t2, real_t p_allowed_unit_error) { - float v0 = t0.value; - float v1 = t1.value; - float v2 = t2.value; - - if (Math::is_equal_approx(v1, v2, (float)p_allowed_unit_error)) { - //0 and 2 are close, let's see if 1 is close - if (!Math::is_equal_approx(v0, v1, (float)p_allowed_unit_error)) { - //not close, not optimizable - return false; +bool Animation::_quaternion_track_optimize_key(const TKey<Quaternion> t0, const TKey<Quaternion> t1, const TKey<Quaternion> t2, real_t p_allowed_velocity_err, real_t p_allowed_angular_error, real_t p_allowed_precision_error) { + // Remove overlapping keys. + if (Math::is_equal_approx(t0.time, t1.time) || Math::is_equal_approx(t1.time, t2.time)) { + return true; + } + if ((t0.value - t1.value).length() < p_allowed_precision_error && (t1.value - t2.value).length() < p_allowed_precision_error) { + return true; + } + // Check axis. + Quaternion q0 = t0.value * t1.value * t0.value.inverse(); + Quaternion q1 = t1.value * t2.value * t1.value.inverse(); + if (q0.get_axis().dot(q1.get_axis()) >= 1.0 - p_allowed_angular_error * 2.0) { + double a0 = Math::acos(t0.value.dot(t1.value)); + double a1 = Math::acos(t1.value.dot(t2.value)); + if (a0 + a1 >= Math_PI) { + return false; // Rotation is more than 180 deg, keep key. } - } else { - /* - TODO eventually discuss a way to optimize these better. - float pd = (v2 - v0); - real_t d0 = pd.dot(v0); - real_t d1 = pd.dot(v1); - real_t d2 = pd.dot(v2); - if (d1 < d0 || d1 > d2) { - return false; //beyond segment range + // Calc velocities. + double v0 = a0 / (t1.time - t0.time); + double v1 = a1 / (t2.time - t1.time); + // Avoid zero div but check equality. + if (abs(v0 - v1) < p_allowed_precision_error) { + return true; + } else if (abs(v0) < p_allowed_precision_error || abs(v1) < p_allowed_precision_error) { + return false; } - - float s[2] = { v0, v2 }; - real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1); - - if (d > pd.length() * p_allowed_linear_error) { - return false; //beyond allowed error for colinearity + double ratio = v0 < v1 ? v0 / v1 : v1 / v0; + if (ratio >= 1.0 - p_allowed_velocity_err) { + return true; } -*/ } - - return true; + return false; } -void Animation::_position_track_optimize(int p_idx, real_t p_allowed_linear_err, real_t p_allowed_angular_err) { +void Animation::_position_track_optimize(int p_idx, real_t p_allowed_velocity_err, real_t p_allowed_angular_err, real_t p_allowed_precision_error) { ERR_FAIL_INDEX(p_idx, tracks.size()); ERR_FAIL_COND(tracks[p_idx]->type != TYPE_POSITION_3D); PositionTrack *tt = static_cast<PositionTrack *>(tracks[p_idx]); - bool prev_erased = false; - TKey<Vector3> first_erased; - - Vector3 norm; - for (int i = 1; i < tt->positions.size() - 1; i++) { - TKey<Vector3> &t0 = tt->positions.write[i - 1]; - TKey<Vector3> &t1 = tt->positions.write[i]; - TKey<Vector3> &t2 = tt->positions.write[i + 1]; - - bool erase = _position_track_optimize_key(t0, t1, t2, p_allowed_linear_err, p_allowed_angular_err, norm); - if (erase && !prev_erased) { - norm = (t2.value - t1.value).normalized(); - } - - if (prev_erased && !_position_track_optimize_key(t0, first_erased, t2, p_allowed_linear_err, p_allowed_angular_err, norm)) { - //avoid error to go beyond first erased key - erase = false; - } + int i = 0; + while (i < tt->positions.size() - 2) { + TKey<Vector3> t0 = tt->positions[i]; + TKey<Vector3> t1 = tt->positions[i + 1]; + TKey<Vector3> t2 = tt->positions[i + 2]; + bool erase = _vector3_track_optimize_key(t0, t1, t2, p_allowed_velocity_err, p_allowed_angular_err, p_allowed_precision_error); if (erase) { - if (!prev_erased) { - first_erased = t1; - prev_erased = true; - } - - tt->positions.remove_at(i); - i--; - + tt->positions.remove_at(i + 1); } else { - prev_erased = false; - norm = Vector3(); + i++; + } + } + + if (tt->positions.size() == 2) { + if ((tt->positions[0].value - tt->positions[1].value).length() < p_allowed_precision_error) { + tt->positions.remove_at(1); } } } -void Animation::_rotation_track_optimize(int p_idx, real_t p_allowed_angular_err, real_t p_max_optimizable_angle) { +void Animation::_rotation_track_optimize(int p_idx, real_t p_allowed_velocity_err, real_t p_allowed_angular_err, real_t p_allowed_precision_error) { ERR_FAIL_INDEX(p_idx, tracks.size()); ERR_FAIL_COND(tracks[p_idx]->type != TYPE_ROTATION_3D); - RotationTrack *tt = static_cast<RotationTrack *>(tracks[p_idx]); - bool prev_erased = false; - TKey<Quaternion> first_erased; - - for (int i = 1; i < tt->rotations.size() - 1; i++) { - TKey<Quaternion> &t0 = tt->rotations.write[i - 1]; - TKey<Quaternion> &t1 = tt->rotations.write[i]; - TKey<Quaternion> &t2 = tt->rotations.write[i + 1]; + RotationTrack *rt = static_cast<RotationTrack *>(tracks[p_idx]); - bool erase = _rotation_track_optimize_key(t0, t1, t2, p_allowed_angular_err, p_max_optimizable_angle); - - if (prev_erased && !_rotation_track_optimize_key(t0, first_erased, t2, p_allowed_angular_err, p_max_optimizable_angle)) { - //avoid error to go beyond first erased key - erase = false; - } + int i = 0; + while (i < rt->rotations.size() - 2) { + TKey<Quaternion> t0 = rt->rotations[i]; + TKey<Quaternion> t1 = rt->rotations[i + 1]; + TKey<Quaternion> t2 = rt->rotations[i + 2]; + bool erase = _quaternion_track_optimize_key(t0, t1, t2, p_allowed_velocity_err, p_allowed_angular_err, p_allowed_precision_error); if (erase) { - if (!prev_erased) { - first_erased = t1; - prev_erased = true; - } - - tt->rotations.remove_at(i); - i--; - + rt->rotations.remove_at(i + 1); } else { - prev_erased = false; + i++; + } + } + + if (rt->rotations.size() == 2) { + if ((rt->rotations[0].value - rt->rotations[1].value).length() < p_allowed_precision_error) { + rt->rotations.remove_at(1); } } } -void Animation::_scale_track_optimize(int p_idx, real_t p_allowed_linear_err) { +void Animation::_scale_track_optimize(int p_idx, real_t p_allowed_velocity_err, real_t p_allowed_angular_err, real_t p_allowed_precision_error) { ERR_FAIL_INDEX(p_idx, tracks.size()); ERR_FAIL_COND(tracks[p_idx]->type != TYPE_SCALE_3D); - ScaleTrack *tt = static_cast<ScaleTrack *>(tracks[p_idx]); - bool prev_erased = false; - TKey<Vector3> first_erased; - - for (int i = 1; i < tt->scales.size() - 1; i++) { - TKey<Vector3> &t0 = tt->scales.write[i - 1]; - TKey<Vector3> &t1 = tt->scales.write[i]; - TKey<Vector3> &t2 = tt->scales.write[i + 1]; + ScaleTrack *st = static_cast<ScaleTrack *>(tracks[p_idx]); - bool erase = _scale_track_optimize_key(t0, t1, t2, p_allowed_linear_err); - - if (prev_erased && !_scale_track_optimize_key(t0, first_erased, t2, p_allowed_linear_err)) { - //avoid error to go beyond first erased key - erase = false; - } + int i = 0; + while (i < st->scales.size() - 2) { + TKey<Vector3> t0 = st->scales[i]; + TKey<Vector3> t1 = st->scales[i + 1]; + TKey<Vector3> t2 = st->scales[i + 2]; + bool erase = _vector3_track_optimize_key(t0, t1, t2, p_allowed_velocity_err, p_allowed_angular_err, p_allowed_precision_error); if (erase) { - if (!prev_erased) { - first_erased = t1; - prev_erased = true; - } - - tt->scales.remove_at(i); - i--; - + st->scales.remove_at(i + 1); } else { - prev_erased = false; + i++; + } + } + + if (st->scales.size() == 2) { + if ((st->scales[0].value - st->scales[1].value).length() < p_allowed_precision_error) { + st->scales.remove_at(1); } } } -void Animation::_blend_shape_track_optimize(int p_idx, real_t p_allowed_linear_err) { +void Animation::_blend_shape_track_optimize(int p_idx, real_t p_allowed_velocity_err, real_t p_allowed_precision_error) { ERR_FAIL_INDEX(p_idx, tracks.size()); ERR_FAIL_COND(tracks[p_idx]->type != TYPE_BLEND_SHAPE); - BlendShapeTrack *tt = static_cast<BlendShapeTrack *>(tracks[p_idx]); - bool prev_erased = false; - TKey<float> first_erased; - first_erased.value = 0.0; - - for (int i = 1; i < tt->blend_shapes.size() - 1; i++) { - TKey<float> &t0 = tt->blend_shapes.write[i - 1]; - TKey<float> &t1 = tt->blend_shapes.write[i]; - TKey<float> &t2 = tt->blend_shapes.write[i + 1]; + BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(tracks[p_idx]); - bool erase = _blend_shape_track_optimize_key(t0, t1, t2, p_allowed_linear_err); + int i = 0; + while (i < bst->blend_shapes.size() - 2) { + TKey<float> t0 = bst->blend_shapes[i]; + TKey<float> t1 = bst->blend_shapes[i + 1]; + TKey<float> t2 = bst->blend_shapes[i + 2]; - if (prev_erased && !_blend_shape_track_optimize_key(t0, first_erased, t2, p_allowed_linear_err)) { - //avoid error to go beyond first erased key - erase = false; + bool erase = _float_track_optimize_key(t0, t1, t2, p_allowed_velocity_err, p_allowed_precision_error); + if (erase) { + bst->blend_shapes.remove_at(i + 1); + } else { + i++; } + } - if (erase) { - if (!prev_erased) { - first_erased = t1; - prev_erased = true; - } + if (bst->blend_shapes.size() == 2) { + if (abs(bst->blend_shapes[0].value - bst->blend_shapes[1].value) < p_allowed_precision_error) { + bst->blend_shapes.remove_at(1); + } + } +} - tt->blend_shapes.remove_at(i); - i--; +void Animation::_value_track_optimize(int p_idx, real_t p_allowed_velocity_err, real_t p_allowed_angular_err, real_t p_allowed_precision_error) { + ERR_FAIL_INDEX(p_idx, tracks.size()); + ERR_FAIL_COND(tracks[p_idx]->type != TYPE_VALUE); + ValueTrack *vt = static_cast<ValueTrack *>(tracks[p_idx]); + if (vt->values.size() == 0) { + return; + } + Variant::Type type = vt->values[0].value.get_type(); + + // Special case for angle interpolation. + bool is_using_angle = vt->interpolation == Animation::INTERPOLATION_LINEAR_ANGLE || vt->interpolation == Animation::INTERPOLATION_CUBIC_ANGLE; + int i = 0; + while (i < vt->values.size() - 2) { + bool erase = false; + switch (type) { + case Variant::FLOAT: { + TKey<float> t0; + TKey<float> t1; + TKey<float> t2; + t0.time = vt->values[i].time; + t1.time = vt->values[i + 1].time; + t2.time = vt->values[i + 2].time; + t0.value = vt->values[i].value; + t1.value = vt->values[i + 1].value; + t2.value = vt->values[i + 2].value; + if (is_using_angle) { + float diff1 = fmod(t1.value - t0.value, Math_TAU); + t1.value = t0.value + fmod(2.0 * diff1, Math_TAU) - diff1; + float diff2 = fmod(t2.value - t1.value, Math_TAU); + t2.value = t1.value + fmod(2.0 * diff2, Math_TAU) - diff2; + if (abs(abs(diff1) + abs(diff2)) >= Math_PI) { + break; // Rotation is more than 180 deg, keep key. + } + } + erase = _float_track_optimize_key(t0, t1, t2, p_allowed_velocity_err, p_allowed_precision_error); + } break; + case Variant::VECTOR2: { + TKey<Vector2> t0; + TKey<Vector2> t1; + TKey<Vector2> t2; + t0.time = vt->values[i].time; + t1.time = vt->values[i + 1].time; + t2.time = vt->values[i + 2].time; + t0.value = vt->values[i].value; + t1.value = vt->values[i + 1].value; + t2.value = vt->values[i + 2].value; + erase = _vector2_track_optimize_key(t0, t1, t2, p_allowed_velocity_err, p_allowed_angular_err, p_allowed_precision_error); + } break; + case Variant::VECTOR3: { + TKey<Vector3> t0; + TKey<Vector3> t1; + TKey<Vector3> t2; + t0.time = vt->values[i].time; + t1.time = vt->values[i + 1].time; + t2.time = vt->values[i + 2].time; + t0.value = vt->values[i].value; + t1.value = vt->values[i + 1].value; + t2.value = vt->values[i + 2].value; + erase = _vector3_track_optimize_key(t0, t1, t2, p_allowed_velocity_err, p_allowed_angular_err, p_allowed_precision_error); + } break; + case Variant::QUATERNION: { + TKey<Quaternion> t0; + TKey<Quaternion> t1; + TKey<Quaternion> t2; + t0.time = vt->values[i].time; + t1.time = vt->values[i + 1].time; + t2.time = vt->values[i + 2].time; + t0.value = vt->values[i].value; + t1.value = vt->values[i + 1].value; + t2.value = vt->values[i + 2].value; + erase = _quaternion_track_optimize_key(t0, t1, t2, p_allowed_velocity_err, p_allowed_angular_err, p_allowed_precision_error); + } break; + default: { + } break; + } + if (erase) { + vt->values.remove_at(i + 1); } else { - prev_erased = false; + i++; + } + } + + if (vt->values.size() == 2) { + bool single_key = false; + switch (type) { + case Variant::FLOAT: { + float val_0 = vt->values[0].value; + float val_1 = vt->values[1].value; + if (is_using_angle) { + float diff1 = fmod(val_1 - val_0, Math_TAU); + val_1 = val_0 + fmod(2.0 * diff1, Math_TAU) - diff1; + } + single_key = abs(val_0 - val_1) < p_allowed_precision_error; + } break; + case Variant::VECTOR2: { + Vector2 val_0 = vt->values[0].value; + Vector2 val_1 = vt->values[1].value; + single_key = (val_0 - val_1).length() < p_allowed_precision_error; + } break; + case Variant::VECTOR3: { + Vector3 val_0 = vt->values[0].value; + Vector3 val_1 = vt->values[1].value; + single_key = (val_0 - val_1).length() < p_allowed_precision_error; + } break; + case Variant::QUATERNION: { + Quaternion val_0 = vt->values[0].value; + Quaternion val_1 = vt->values[1].value; + single_key = (val_0 - val_1).length() < p_allowed_precision_error; + } break; + default: { + } break; + } + if (single_key) { + vt->values.remove_at(1); } } } -void Animation::optimize(real_t p_allowed_linear_err, real_t p_allowed_angular_err, real_t p_max_optimizable_angle) { +void Animation::optimize(real_t p_allowed_velocity_err, real_t p_allowed_angular_err, int p_precision) { + real_t precision = Math::pow(0.1, p_precision); for (int i = 0; i < tracks.size(); i++) { if (track_is_compressed(i)) { continue; //not possible to optimize compressed track } if (tracks[i]->type == TYPE_POSITION_3D) { - _position_track_optimize(i, p_allowed_linear_err, p_allowed_angular_err); + _position_track_optimize(i, p_allowed_velocity_err, p_allowed_angular_err, precision); } else if (tracks[i]->type == TYPE_ROTATION_3D) { - _rotation_track_optimize(i, p_allowed_angular_err, p_max_optimizable_angle); + _rotation_track_optimize(i, p_allowed_velocity_err, p_allowed_angular_err, precision); } else if (tracks[i]->type == TYPE_SCALE_3D) { - _scale_track_optimize(i, p_allowed_linear_err); + _scale_track_optimize(i, p_allowed_velocity_err, p_allowed_angular_err, precision); } else if (tracks[i]->type == TYPE_BLEND_SHAPE) { - _blend_shape_track_optimize(i, p_allowed_linear_err); + _blend_shape_track_optimize(i, p_allowed_velocity_err, precision); + } else if (tracks[i]->type == TYPE_VALUE) { + _value_track_optimize(i, p_allowed_velocity_err, p_allowed_angular_err, precision); } } } @@ -5326,6 +5559,466 @@ bool Animation::_fetch_compressed_by_index(uint32_t p_compressed_track, int p_in return false; } +// Helper math functions for Variant. +Variant Animation::add_variant(const Variant &a, const Variant &b) { + if (a.get_type() != b.get_type()) { + return a; + } + + switch (a.get_type()) { + case Variant::NIL: { + return Variant(); + } + case Variant::BOOL: { + return (a.operator real_t()) + (b.operator real_t()); // It is cast for interpolation. + } + case Variant::RECT2: { + const Rect2 ra = a.operator Rect2(); + const Rect2 rb = b.operator Rect2(); + return Rect2(ra.position + rb.position, ra.size + rb.size); + } + case Variant::RECT2I: { + const Rect2i ra = a.operator Rect2i(); + const Rect2i rb = b.operator Rect2i(); + return Rect2i(ra.position + rb.position, ra.size + rb.size); + } + case Variant::PLANE: { + const Plane pa = a.operator Plane(); + const Plane pb = b.operator Plane(); + return Plane(pa.normal + pb.normal, pa.d + pb.d); + } + case Variant::AABB: { + const ::AABB aa = a.operator ::AABB(); + const ::AABB ab = b.operator ::AABB(); + return ::AABB(aa.position + ab.position, aa.size + ab.size); + } + case Variant::QUATERNION: { + return (a.operator Quaternion()) * (b.operator Quaternion()); + } + case Variant::TRANSFORM2D: { + return (a.operator Transform2D()) * (b.operator Transform2D()); + } + case Variant::TRANSFORM3D: { + return (a.operator Transform3D()) * (b.operator Transform3D()); + } + default: { + return Variant::evaluate(Variant::OP_ADD, a, b); + } + } +} + +Variant Animation::subtract_variant(const Variant &a, const Variant &b) { + if (a.get_type() != b.get_type()) { + return a; + } + + switch (a.get_type()) { + case Variant::NIL: { + return Variant(); + } + case Variant::BOOL: { + return (a.operator real_t()) - (b.operator real_t()); // It is cast for interpolation. + } + case Variant::RECT2: { + const Rect2 ra = a.operator Rect2(); + const Rect2 rb = b.operator Rect2(); + return Rect2(ra.position - rb.position, ra.size - rb.size); + } + case Variant::RECT2I: { + const Rect2i ra = a.operator Rect2i(); + const Rect2i rb = b.operator Rect2i(); + return Rect2i(ra.position - rb.position, ra.size - rb.size); + } + case Variant::PLANE: { + const Plane pa = a.operator Plane(); + const Plane pb = b.operator Plane(); + return Plane(pa.normal - pb.normal, pa.d - pb.d); + } + case Variant::AABB: { + const ::AABB aa = a.operator ::AABB(); + const ::AABB ab = b.operator ::AABB(); + return ::AABB(aa.position - ab.position, aa.size - ab.size); + } + case Variant::QUATERNION: { + return (b.operator Quaternion()).inverse() * (a.operator Quaternion()); + } + case Variant::TRANSFORM2D: { + return (b.operator Transform2D()).inverse() * (a.operator Transform2D()); + } + case Variant::TRANSFORM3D: { + return (b.operator Transform3D()).inverse() * (a.operator Transform3D()); + } + default: { + return Variant::evaluate(Variant::OP_SUBTRACT, a, b); + } + } +} + +Variant Animation::blend_variant(const Variant &a, const Variant &b, float c) { + if (a.get_type() != b.get_type()) { + if (a.is_num() && b.is_num()) { + real_t va = a; + real_t vb = b; + return va + vb * c; + } + return a; + } + + switch (a.get_type()) { + case Variant::NIL: { + return Variant(); + } + case Variant::INT: { + return int((a.operator int64_t()) + (b.operator int64_t()) * c + 0.5); + } + case Variant::FLOAT: { + return (a.operator double()) + (b.operator double()) * c; + } + case Variant::VECTOR2: { + return (a.operator Vector2()) + (b.operator Vector2()) * c; + } + case Variant::VECTOR2I: { + const Vector2i va = a.operator Vector2i(); + const Vector2i vb = b.operator Vector2i(); + return Vector2i(int32_t(va.x + vb.x * c + 0.5), int32_t(va.y + vb.y * c + 0.5)); + } + case Variant::RECT2: { + const Rect2 ra = a.operator Rect2(); + const Rect2 rb = b.operator Rect2(); + return Rect2(ra.position + rb.position * c, ra.size + rb.size * c); + } + case Variant::RECT2I: { + const Rect2i ra = a.operator Rect2i(); + const Rect2i rb = b.operator Rect2i(); + return Rect2i(int32_t(ra.position.x + rb.position.x * c + 0.5), int32_t(ra.position.y + rb.position.y * c + 0.5), int32_t(ra.size.x + rb.size.x * c + 0.5), int32_t(ra.size.y + rb.size.y * c + 0.5)); + } + case Variant::VECTOR3: { + return (a.operator Vector3()) + (b.operator Vector3()) * c; + } + case Variant::VECTOR3I: { + const Vector3i va = a.operator Vector3i(); + const Vector3i vb = b.operator Vector3i(); + return Vector3i(int32_t(va.x + vb.x * c + 0.5), int32_t(va.y + vb.y * c + 0.5), int32_t(va.z + vb.z * c + 0.5)); + } + case Variant::VECTOR4: { + return (a.operator Vector4()) + (b.operator Vector4()) * c; + } + case Variant::VECTOR4I: { + const Vector4i va = a.operator Vector4i(); + const Vector4i vb = b.operator Vector4i(); + return Vector4i(int32_t(va.x + vb.x * c + 0.5), int32_t(va.y + vb.y * c + 0.5), int32_t(va.z + vb.z * c + 0.5), int32_t(va.w + vb.w * c + 0.5)); + } + case Variant::PLANE: { + const Plane pa = a.operator Plane(); + const Plane pb = b.operator Plane(); + return Plane(pa.normal + pb.normal * c, pa.d + pb.d * c); + } + case Variant::COLOR: { + return (a.operator Color()) + (b.operator Color()) * c; + } + case Variant::AABB: { + const ::AABB aa = a.operator ::AABB(); + const ::AABB ab = b.operator ::AABB(); + return ::AABB(aa.position + ab.position * c, aa.size + ab.size * c); + } + case Variant::BASIS: { + return (a.operator Basis()) + (b.operator Basis()) * c; + } + case Variant::QUATERNION: { + return (a.operator Quaternion()) * Quaternion().slerp((b.operator Quaternion()), c); + } + case Variant::TRANSFORM2D: { + return (a.operator Transform2D()) * Transform2D().interpolate_with((b.operator Transform2D()), c); + } + case Variant::TRANSFORM3D: { + return (a.operator Transform3D()) * Transform3D().interpolate_with((b.operator Transform3D()), c); + } + default: { + return c < 0.5 ? a : b; + } + } +} + +Variant Animation::interpolate_variant(const Variant &a, const Variant &b, float c) { + if (a.get_type() != b.get_type()) { + if (a.is_num() && b.is_num()) { + real_t va = a; + real_t vb = b; + return va + (vb - va) * c; + } + return a; + } + + switch (a.get_type()) { + case Variant::NIL: { + return Variant(); + } + case Variant::INT: { + const int64_t va = a.operator int64_t(); + return int(va + ((b.operator int64_t()) - va) * c); + } + case Variant::FLOAT: { + const real_t va = a.operator real_t(); + return va + ((b.operator real_t()) - va) * c; + } + case Variant::VECTOR2: { + return (a.operator Vector2()).lerp(b.operator Vector2(), c); + } + case Variant::VECTOR2I: { + const Vector2i va = a.operator Vector2i(); + const Vector2i vb = b.operator Vector2i(); + return Vector2i(int32_t(va.x + (vb.x - va.x) * c), int32_t(va.y + (vb.y - va.y) * c)); + } + case Variant::RECT2: { + const Rect2 ra = a.operator Rect2(); + const Rect2 rb = b.operator Rect2(); + return Rect2(ra.position.lerp(rb.position, c), ra.size.lerp(rb.size, c)); + } + case Variant::RECT2I: { + const Rect2i ra = a.operator Rect2i(); + const Rect2i rb = b.operator Rect2i(); + return Rect2i(int32_t(ra.position.x + (rb.position.x - ra.position.x) * c), int32_t(ra.position.y + (rb.position.y - ra.position.y) * c), int32_t(ra.size.x + (rb.size.x - ra.size.x) * c), int32_t(ra.size.y + (rb.size.y - ra.size.y) * c)); + } + case Variant::VECTOR3: { + return (a.operator Vector3()).lerp(b.operator Vector3(), c); + } + case Variant::VECTOR3I: { + const Vector3i va = a.operator Vector3i(); + const Vector3i vb = b.operator Vector3i(); + return Vector3i(int32_t(va.x + (vb.x - va.x) * c), int32_t(va.y + (vb.y - va.y) * c), int32_t(va.z + (vb.z - va.z) * c)); + } + case Variant::VECTOR4: { + return (a.operator Vector4()).lerp(b.operator Vector4(), c); + } + case Variant::VECTOR4I: { + const Vector4i va = a.operator Vector4i(); + const Vector4i vb = b.operator Vector4i(); + return Vector4i(int32_t(va.x + (vb.x - va.x) * c), int32_t(va.y + (vb.y - va.y) * c), int32_t(va.z + (vb.z - va.z) * c), int32_t(va.w + (vb.w - va.w) * c)); + } + case Variant::PLANE: { + const Plane pa = a.operator Plane(); + const Plane pb = b.operator Plane(); + return Plane(pa.normal.lerp(pb.normal, c), pa.d + (pb.d - pa.d) * c); + } + case Variant::COLOR: { + return (a.operator Color()).lerp(b.operator Color(), c); + } + case Variant::AABB: { + const ::AABB aa = a.operator ::AABB(); + const ::AABB ab = b.operator ::AABB(); + return ::AABB(aa.position.lerp(ab.position, c), aa.size.lerp(ab.size, c)); + } + case Variant::BASIS: { + return (a.operator Basis()).lerp(b.operator Basis(), c); + } + case Variant::QUATERNION: { + return (a.operator Quaternion()).slerp(b.operator Quaternion(), c); + } + case Variant::TRANSFORM2D: { + return (a.operator Transform2D()).interpolate_with(b.operator Transform2D(), c); + } + case Variant::TRANSFORM3D: { + return (a.operator Transform3D()).interpolate_with(b.operator Transform3D(), c); + } + case Variant::STRING: { + // This is pretty funny and bizarre, but artists like to use it for typewriter effects. + const String sa = a.operator String(); + const String sb = b.operator String(); + String dst; + int sa_len = sa.length(); + int sb_len = sb.length(); + int csize = sa_len + (sb_len - sa_len) * c; + if (csize == 0) { + return ""; + } + dst.resize(csize + 1); + dst[csize] = 0; + int split = csize / 2; + + for (int i = 0; i < csize; i++) { + char32_t chr = ' '; + + if (i < split) { + if (i < sa.length()) { + chr = sa[i]; + } else if (i < sb.length()) { + chr = sb[i]; + } + + } else { + if (i < sb.length()) { + chr = sb[i]; + } else if (i < sa.length()) { + chr = sa[i]; + } + } + + dst[i] = chr; + } + + return dst; + } + case Variant::PACKED_INT32_ARRAY: { + const Vector<int32_t> *arr_a = Object::cast_to<Vector<int32_t>>(a); + const Vector<int32_t> *arr_b = Object::cast_to<Vector<int32_t>>(b); + int32_t sz = arr_a->size(); + if (sz == 0 || arr_b->size() != sz) { + return a; + } else { + Vector<int32_t> v; + v.resize(sz); + { + int32_t *vw = v.ptrw(); + const int32_t *ar = arr_a->ptr(); + const int32_t *br = arr_b->ptr(); + + Variant va; + for (int32_t i = 0; i < sz; i++) { + va = interpolate_variant(ar[i], br[i], c); + vw[i] = va; + } + } + return v; + } + } + case Variant::PACKED_INT64_ARRAY: { + const Vector<int64_t> *arr_a = Object::cast_to<Vector<int64_t>>(a); + const Vector<int64_t> *arr_b = Object::cast_to<Vector<int64_t>>(b); + int64_t sz = arr_a->size(); + if (sz == 0 || arr_b->size() != sz) { + return a; + } else { + Vector<int64_t> v; + v.resize(sz); + { + int64_t *vw = v.ptrw(); + const int64_t *ar = arr_a->ptr(); + const int64_t *br = arr_b->ptr(); + + Variant va; + for (int64_t i = 0; i < sz; i++) { + va = interpolate_variant(ar[i], br[i], c); + vw[i] = va; + } + } + return v; + } + } + case Variant::PACKED_FLOAT32_ARRAY: { + const Vector<float> *arr_a = Object::cast_to<Vector<float>>(a); + const Vector<float> *arr_b = Object::cast_to<Vector<float>>(b); + int sz = arr_a->size(); + if (sz == 0 || arr_b->size() != sz) { + return a; + } else { + Vector<float> v; + v.resize(sz); + { + float *vw = v.ptrw(); + const float *ar = arr_a->ptr(); + const float *br = arr_b->ptr(); + + Variant va; + for (int i = 0; i < sz; i++) { + va = interpolate_variant(ar[i], br[i], c); + vw[i] = va; + } + } + return v; + } + } + case Variant::PACKED_FLOAT64_ARRAY: { + const Vector<double> *arr_a = Object::cast_to<Vector<double>>(a); + const Vector<double> *arr_b = Object::cast_to<Vector<double>>(b); + int sz = arr_a->size(); + if (sz == 0 || arr_b->size() != sz) { + return a; + } else { + Vector<double> v; + v.resize(sz); + { + double *vw = v.ptrw(); + const double *ar = arr_a->ptr(); + const double *br = arr_b->ptr(); + + Variant va; + for (int i = 0; i < sz; i++) { + va = interpolate_variant(ar[i], br[i], c); + vw[i] = va; + } + } + return v; + } + } + case Variant::PACKED_VECTOR2_ARRAY: { + const Vector<Vector2> *arr_a = Object::cast_to<Vector<Vector2>>(a); + const Vector<Vector2> *arr_b = Object::cast_to<Vector<Vector2>>(b); + int sz = arr_a->size(); + if (sz == 0 || arr_b->size() != sz) { + return a; + } else { + Vector<Vector2> v; + v.resize(sz); + { + Vector2 *vw = v.ptrw(); + const Vector2 *ar = arr_a->ptr(); + const Vector2 *br = arr_b->ptr(); + + for (int i = 0; i < sz; i++) { + vw[i] = ar[i].lerp(br[i], c); + } + } + return v; + } + } + case Variant::PACKED_VECTOR3_ARRAY: { + const Vector<Vector3> *arr_a = Object::cast_to<Vector<Vector3>>(a); + const Vector<Vector3> *arr_b = Object::cast_to<Vector<Vector3>>(b); + int sz = arr_a->size(); + if (sz == 0 || arr_b->size() != sz) { + return a; + } else { + Vector<Vector3> v; + v.resize(sz); + { + Vector3 *vw = v.ptrw(); + const Vector3 *ar = arr_a->ptr(); + const Vector3 *br = arr_b->ptr(); + + for (int i = 0; i < sz; i++) { + vw[i] = ar[i].lerp(br[i], c); + } + } + return v; + } + } + case Variant::PACKED_COLOR_ARRAY: { + const Vector<Color> *arr_a = Object::cast_to<Vector<Color>>(a); + const Vector<Color> *arr_b = Object::cast_to<Vector<Color>>(b); + int sz = arr_a->size(); + if (sz == 0 || arr_b->size() != sz) { + return a; + } else { + Vector<Color> v; + v.resize(sz); + { + Color *vw = v.ptrw(); + const Color *ar = arr_a->ptr(); + const Color *br = arr_b->ptr(); + + for (int i = 0; i < sz; i++) { + vw[i] = ar[i].lerp(br[i], c); + } + } + return v; + } + } + default: { + return c < 0.5 ? a : b; + } + } +} + Animation::Animation() {} Animation::~Animation() { diff --git a/scene/resources/animation.h b/scene/resources/animation.h index b4528ccd3a..49c8fa4c22 100644 --- a/scene/resources/animation.h +++ b/scene/resources/animation.h @@ -56,7 +56,9 @@ public: enum InterpolationType { INTERPOLATION_NEAREST, INTERPOLATION_LINEAR, - INTERPOLATION_CUBIC + INTERPOLATION_CUBIC, + INTERPOLATION_LINEAR_ANGLE, + INTERPOLATION_CUBIC_ANGLE, }; enum UpdateMode { @@ -72,10 +74,19 @@ public: LOOP_PINGPONG, }; +#ifdef TOOLS_ENABLED enum HandleMode { HANDLE_MODE_FREE, + HANDLE_MODE_LINEAR, HANDLE_MODE_BALANCED, + HANDLE_MODE_MIRRORED, }; + enum HandleSetMode { + HANDLE_SET_MODE_NONE, + HANDLE_SET_MODE_RESET, + HANDLE_SET_MODE_AUTO, + }; +#endif // TOOLS_ENABLED private: struct Track { @@ -165,8 +176,10 @@ private: struct BezierKey { Vector2 in_handle; //relative (x always <0) Vector2 out_handle; //relative (x always >0) - HandleMode handle_mode = HANDLE_MODE_BALANCED; real_t value = 0.0; +#ifdef TOOLS_ENABLED + HandleMode handle_mode = HANDLE_MODE_FREE; +#endif // TOOLS_ENABLED }; struct BezierTrack : public Track { @@ -225,11 +238,13 @@ private: _FORCE_INLINE_ Quaternion _interpolate(const Quaternion &p_a, const Quaternion &p_b, real_t p_c) const; _FORCE_INLINE_ Variant _interpolate(const Variant &p_a, const Variant &p_b, real_t p_c) const; _FORCE_INLINE_ real_t _interpolate(const real_t &p_a, const real_t &p_b, real_t p_c) const; + _FORCE_INLINE_ Variant _interpolate_angle(const Variant &p_a, const Variant &p_b, real_t p_c) const; - _FORCE_INLINE_ Vector3 _cubic_interpolate(const Vector3 &p_pre_a, const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_post_b, real_t p_c) const; - _FORCE_INLINE_ Quaternion _cubic_interpolate(const Quaternion &p_pre_a, const Quaternion &p_a, const Quaternion &p_b, const Quaternion &p_post_b, real_t p_c) const; - _FORCE_INLINE_ Variant _cubic_interpolate(const Variant &p_pre_a, const Variant &p_a, const Variant &p_b, const Variant &p_post_b, real_t p_c) const; - _FORCE_INLINE_ real_t _cubic_interpolate(const real_t &p_pre_a, const real_t &p_a, const real_t &p_b, const real_t &p_post_b, real_t p_c) const; + _FORCE_INLINE_ Vector3 _cubic_interpolate_in_time(const Vector3 &p_pre_a, const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_post_b, real_t p_c, real_t p_pre_a_t, real_t p_b_t, real_t p_post_b_t) const; + _FORCE_INLINE_ Quaternion _cubic_interpolate_in_time(const Quaternion &p_pre_a, const Quaternion &p_a, const Quaternion &p_b, const Quaternion &p_post_b, real_t p_c, real_t p_pre_a_t, real_t p_b_t, real_t p_post_b_t) const; + _FORCE_INLINE_ Variant _cubic_interpolate_in_time(const Variant &p_pre_a, const Variant &p_a, const Variant &p_b, const Variant &p_post_b, real_t p_c, real_t p_pre_a_t, real_t p_b_t, real_t p_post_b_t) const; + _FORCE_INLINE_ real_t _cubic_interpolate_in_time(const real_t &p_pre_a, const real_t &p_a, const real_t &p_b, const real_t &p_post_b, real_t p_c, real_t p_pre_a_t, real_t p_b_t, real_t p_post_b_t) const; + _FORCE_INLINE_ Variant _cubic_interpolate_angle_in_time(const Variant &p_pre_a, const Variant &p_a, const Variant &p_b, const Variant &p_post_b, real_t p_c, real_t p_pre_a_t, real_t p_b_t, real_t p_post_b_t) const; template <class T> _FORCE_INLINE_ T _interpolate(const Vector<TKey<T>> &p_keys, double p_time, InterpolationType p_interp, bool p_loop_wrap, bool *p_ok, bool p_backward = false) const; @@ -351,15 +366,16 @@ private: return idxr; } - bool _position_track_optimize_key(const TKey<Vector3> &t0, const TKey<Vector3> &t1, const TKey<Vector3> &t2, real_t p_alowed_linear_err, real_t p_allowed_angular_error, const Vector3 &p_norm); - bool _rotation_track_optimize_key(const TKey<Quaternion> &t0, const TKey<Quaternion> &t1, const TKey<Quaternion> &t2, real_t p_allowed_angular_error, float p_max_optimizable_angle); - bool _scale_track_optimize_key(const TKey<Vector3> &t0, const TKey<Vector3> &t1, const TKey<Vector3> &t2, real_t p_allowed_linear_error); - bool _blend_shape_track_optimize_key(const TKey<float> &t0, const TKey<float> &t1, const TKey<float> &t2, real_t p_allowed_unit_error); + bool _float_track_optimize_key(const TKey<float> t0, const TKey<float> t1, const TKey<float> t2, real_t p_allowed_velocity_err, real_t p_allowed_precision_error); + bool _vector2_track_optimize_key(const TKey<Vector2> t0, const TKey<Vector2> t1, const TKey<Vector2> t2, real_t p_alowed_velocity_err, real_t p_allowed_angular_error, real_t p_allowed_precision_error); + bool _vector3_track_optimize_key(const TKey<Vector3> t0, const TKey<Vector3> t1, const TKey<Vector3> t2, real_t p_alowed_velocity_err, real_t p_allowed_angular_error, real_t p_allowed_precision_error); + bool _quaternion_track_optimize_key(const TKey<Quaternion> t0, const TKey<Quaternion> t1, const TKey<Quaternion> t2, real_t p_allowed_velocity_err, real_t p_allowed_angular_error, real_t p_allowed_precision_error); - void _position_track_optimize(int p_idx, real_t p_allowed_linear_err, real_t p_allowed_angular_err); - void _rotation_track_optimize(int p_idx, real_t p_allowed_angular_err, real_t p_max_optimizable_angle); - void _scale_track_optimize(int p_idx, real_t p_allowed_linear_err); - void _blend_shape_track_optimize(int p_idx, real_t p_allowed_unit_error); + void _position_track_optimize(int p_idx, real_t p_allowed_velocity_err, real_t p_allowed_angular_err, real_t p_allowed_precision_error); + void _rotation_track_optimize(int p_idx, real_t p_allowed_velocity_err, real_t p_allowed_angular_error, real_t p_allowed_precision_error); + void _scale_track_optimize(int p_idx, real_t p_allowed_velocity_err, real_t p_allowed_angular_err, real_t p_allowed_precision_error); + void _blend_shape_track_optimize(int p_idx, real_t p_allowed_velocity_err, real_t p_allowed_precision_error); + void _value_track_optimize(int p_idx, real_t p_allowed_velocity_err, real_t p_allowed_angular_err, real_t p_allowed_precision_error); protected: bool _set(const StringName &p_name, const Variant &p_value); @@ -392,7 +408,7 @@ public: void track_set_enabled(int p_track, bool p_enabled); bool track_is_enabled(int p_track) const; - void track_insert_key(int p_track, double p_time, const Variant &p_key, real_t p_transition = 1); + int track_insert_key(int p_track, double p_time, const Variant &p_key, real_t p_transition = 1); void track_set_key_transition(int p_track, int p_key_idx, real_t p_transition); void track_set_key_value(int p_track, int p_key_idx, const Variant &p_value); void track_set_key_time(int p_track, int p_key_idx, double p_time); @@ -424,15 +440,17 @@ public: void track_set_interpolation_type(int p_track, InterpolationType p_interp); InterpolationType track_get_interpolation_type(int p_track) const; - int bezier_track_insert_key(int p_track, double p_time, real_t p_value, const Vector2 &p_in_handle, const Vector2 &p_out_handle, const HandleMode p_handle_mode = HandleMode::HANDLE_MODE_BALANCED); - void bezier_track_set_key_handle_mode(int p_track, int p_index, HandleMode p_mode, double p_balanced_value_time_ratio = 1.0); + int bezier_track_insert_key(int p_track, double p_time, real_t p_value, const Vector2 &p_in_handle, const Vector2 &p_out_handle); void bezier_track_set_key_value(int p_track, int p_index, real_t p_value); - void bezier_track_set_key_in_handle(int p_track, int p_index, const Vector2 &p_handle, double p_balanced_value_time_ratio = 1.0); - void bezier_track_set_key_out_handle(int p_track, int p_index, const Vector2 &p_handle, double p_balanced_value_time_ratio = 1.0); + void bezier_track_set_key_in_handle(int p_track, int p_index, const Vector2 &p_handle, real_t p_balanced_value_time_ratio = 1.0); + void bezier_track_set_key_out_handle(int p_track, int p_index, const Vector2 &p_handle, real_t p_balanced_value_time_ratio = 1.0); real_t bezier_track_get_key_value(int p_track, int p_index) const; - int bezier_track_get_key_handle_mode(int p_track, int p_index) const; Vector2 bezier_track_get_key_in_handle(int p_track, int p_index) const; Vector2 bezier_track_get_key_out_handle(int p_track, int p_index) const; +#ifdef TOOLS_ENABLED + void bezier_track_set_key_handle_mode(int p_track, int p_index, HandleMode p_mode, HandleSetMode p_set_mode = HANDLE_SET_MODE_NONE); + HandleMode bezier_track_get_key_handle_mode(int p_track, int p_index) const; +#endif // TOOLS_ENABLED real_t bezier_track_interpolate(int p_track, double p_time) const; @@ -475,9 +493,15 @@ public: void clear(); - void optimize(real_t p_allowed_linear_err = 0.05, real_t p_allowed_angular_err = 0.01, real_t p_max_optimizable_angle = Math_PI * 0.125); + void optimize(real_t p_allowed_velocity_err = 0.01, real_t p_allowed_angular_err = 0.01, int p_precision = 3); void compress(uint32_t p_page_size = 8192, uint32_t p_fps = 120, float p_split_tolerance = 4.0); // 4.0 seems to be the split tolerance sweet spot from many tests + // Helper math functions for Variant. + static Variant add_variant(const Variant &a, const Variant &b); + static Variant subtract_variant(const Variant &a, const Variant &b); + static Variant blend_variant(const Variant &a, const Variant &b, float c); + static Variant interpolate_variant(const Variant &a, const Variant &b, float c); + Animation(); ~Animation(); }; @@ -485,7 +509,10 @@ public: VARIANT_ENUM_CAST(Animation::TrackType); VARIANT_ENUM_CAST(Animation::InterpolationType); VARIANT_ENUM_CAST(Animation::UpdateMode); -VARIANT_ENUM_CAST(Animation::HandleMode); VARIANT_ENUM_CAST(Animation::LoopMode); +#ifdef TOOLS_ENABLED +VARIANT_ENUM_CAST(Animation::HandleMode); +VARIANT_ENUM_CAST(Animation::HandleSetMode); +#endif // TOOLS_ENABLED -#endif +#endif // ANIMATION_H diff --git a/scene/resources/animation_library.cpp b/scene/resources/animation_library.cpp index 5f725b2fbe..427d418551 100644 --- a/scene/resources/animation_library.cpp +++ b/scene/resources/animation_library.cpp @@ -85,7 +85,7 @@ bool AnimationLibrary::has_animation(const StringName &p_name) const { } Ref<Animation> AnimationLibrary::get_animation(const StringName &p_name) const { - ERR_FAIL_COND_V(!animations.has(p_name), Ref<Animation>()); + ERR_FAIL_COND_V_MSG(!animations.has(p_name), Ref<Animation>(), vformat("Animation not found: \"%s\".", p_name)); return animations[p_name]; } diff --git a/scene/resources/animation_library.h b/scene/resources/animation_library.h index 7a69cd140a..d63807b6d7 100644 --- a/scene/resources/animation_library.h +++ b/scene/resources/animation_library.h @@ -63,4 +63,4 @@ public: AnimationLibrary(); }; -#endif // ANIMATIONLIBRARY_H +#endif // ANIMATION_LIBRARY_H diff --git a/scene/resources/audio_stream_sample.cpp b/scene/resources/audio_stream_wav.cpp index 30c222bdff..26204583ef 100644 --- a/scene/resources/audio_stream_sample.cpp +++ b/scene/resources/audio_stream_wav.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* audio_stream_sample.cpp */ +/* audio_stream_wav.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,13 +28,13 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "audio_stream_sample.h" +#include "audio_stream_wav.h" #include "core/io/file_access.h" #include "core/io/marshalls.h" -void AudioStreamPlaybackSample::start(float p_from_pos) { - if (base->format == AudioStreamSample::FORMAT_IMA_ADPCM) { +void AudioStreamPlaybackWAV::start(double p_from_pos) { + if (base->format == AudioStreamWAV::FORMAT_IMA_ADPCM) { //no seeking in IMA_ADPCM for (int i = 0; i < 2; i++) { ima_adpcm[i].step_index = 0; @@ -55,28 +55,28 @@ void AudioStreamPlaybackSample::start(float p_from_pos) { active = true; } -void AudioStreamPlaybackSample::stop() { +void AudioStreamPlaybackWAV::stop() { active = false; } -bool AudioStreamPlaybackSample::is_playing() const { +bool AudioStreamPlaybackWAV::is_playing() const { return active; } -int AudioStreamPlaybackSample::get_loop_count() const { +int AudioStreamPlaybackWAV::get_loop_count() const { return 0; } -float AudioStreamPlaybackSample::get_playback_position() const { +double AudioStreamPlaybackWAV::get_playback_position() const { return float(offset >> MIX_FRAC_BITS) / base->mix_rate; } -void AudioStreamPlaybackSample::seek(float p_time) { - if (base->format == AudioStreamSample::FORMAT_IMA_ADPCM) { +void AudioStreamPlaybackWAV::seek(double p_time) { + if (base->format == AudioStreamWAV::FORMAT_IMA_ADPCM) { return; //no seeking in ima-adpcm } - float max = base->get_length(); + double max = base->get_length(); if (p_time < 0) { p_time = 0; } else if (p_time >= max) { @@ -87,7 +87,7 @@ void AudioStreamPlaybackSample::seek(float p_time) { } template <class Depth, bool is_stereo, bool is_ima_adpcm> -void AudioStreamPlaybackSample::do_resample(const Depth *p_src, AudioFrame *p_dst, int64_t &offset, int32_t &increment, uint32_t amount, IMA_ADPCM_State *ima_adpcm) { +void AudioStreamPlaybackWAV::do_resample(const Depth *p_src, AudioFrame *p_dst, int64_t &offset, int32_t &increment, uint32_t amount, IMA_ADPCM_State *ima_adpcm) { // this function will be compiled branchless by any decent compiler int32_t final, final_r, next, next_r; @@ -124,7 +124,7 @@ void AudioStreamPlaybackSample::do_resample(const Depth *p_src, AudioFrame *p_ds ima_adpcm[i].last_nibble++; const uint8_t *src_ptr = (const uint8_t *)base->data; - src_ptr += AudioStreamSample::DATA_PAD; + src_ptr += AudioStreamWAV::DATA_PAD; uint8_t nbb = src_ptr[(ima_adpcm[i].last_nibble >> 1) * (is_stereo ? 2 : 1) + i]; nibble = (ima_adpcm[i].last_nibble & 1) ? (nbb >> 4) : (nbb & 0xF); @@ -180,7 +180,7 @@ void AudioStreamPlaybackSample::do_resample(const Depth *p_src, AudioFrame *p_ds final_r = p_src[pos + 1]; } - if (sizeof(Depth) == 1) { /* conditions will not exist anymore when compiled! */ + if constexpr (sizeof(Depth) == 1) { /* conditions will not exist anymore when compiled! */ final <<= 8; if (is_stereo) { final_r <<= 8; @@ -194,7 +194,7 @@ void AudioStreamPlaybackSample::do_resample(const Depth *p_src, AudioFrame *p_ds next = p_src[pos + 1]; } - if (sizeof(Depth) == 1) { + if constexpr (sizeof(Depth) == 1) { next <<= 8; if (is_stereo) { next_r <<= 8; @@ -221,7 +221,7 @@ void AudioStreamPlaybackSample::do_resample(const Depth *p_src, AudioFrame *p_ds } } -int AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) { +int AudioStreamPlaybackWAV::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) { if (!base->data || !active) { for (int i = 0; i < p_frames; i++) { p_buffer[i] = AudioFrame(0, 0); @@ -231,13 +231,13 @@ int AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, int int len = base->data_bytes; switch (base->format) { - case AudioStreamSample::FORMAT_8_BITS: + case AudioStreamWAV::FORMAT_8_BITS: len /= 1; break; - case AudioStreamSample::FORMAT_16_BITS: + case AudioStreamWAV::FORMAT_16_BITS: len /= 2; break; - case AudioStreamSample::FORMAT_IMA_ADPCM: + case AudioStreamWAV::FORMAT_IMA_ADPCM: len *= 2; break; } @@ -251,13 +251,13 @@ int AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, int int64_t loop_begin_fp = ((int64_t)base->loop_begin << MIX_FRAC_BITS); int64_t loop_end_fp = ((int64_t)base->loop_end << MIX_FRAC_BITS); int64_t length_fp = ((int64_t)len << MIX_FRAC_BITS); - int64_t begin_limit = (base->loop_mode != AudioStreamSample::LOOP_DISABLED) ? loop_begin_fp : 0; - int64_t end_limit = (base->loop_mode != AudioStreamSample::LOOP_DISABLED) ? loop_end_fp : length_fp; + int64_t begin_limit = (base->loop_mode != AudioStreamWAV::LOOP_DISABLED) ? loop_begin_fp : 0; + int64_t end_limit = (base->loop_mode != AudioStreamWAV::LOOP_DISABLED) ? loop_end_fp : length_fp; bool is_stereo = base->stereo; int32_t todo = p_frames; - if (base->loop_mode == AudioStreamSample::LOOP_BACKWARD) { + if (base->loop_mode == AudioStreamWAV::LOOP_BACKWARD) { sign = -1; } @@ -271,20 +271,20 @@ int AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, int //looping - AudioStreamSample::LoopMode loop_format = base->loop_mode; - AudioStreamSample::Format format = base->format; + AudioStreamWAV::LoopMode loop_format = base->loop_mode; + AudioStreamWAV::Format format = base->format; /* audio data */ uint8_t *dataptr = (uint8_t *)base->data; - const void *data = dataptr + AudioStreamSample::DATA_PAD; + const void *data = dataptr + AudioStreamWAV::DATA_PAD; AudioFrame *dst_buff = p_buffer; - if (format == AudioStreamSample::FORMAT_IMA_ADPCM) { - if (loop_format != AudioStreamSample::LOOP_DISABLED) { + if (format == AudioStreamWAV::FORMAT_IMA_ADPCM) { + if (loop_format != AudioStreamWAV::LOOP_DISABLED) { ima_adpcm[0].loop_pos = loop_begin_fp >> MIX_FRAC_BITS; ima_adpcm[1].loop_pos = loop_begin_fp >> MIX_FRAC_BITS; - loop_format = AudioStreamSample::LOOP_FORWARD; + loop_format = AudioStreamWAV::LOOP_FORWARD; } } @@ -297,9 +297,9 @@ int AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, int if (increment < 0) { /* going backwards */ - if (loop_format != AudioStreamSample::LOOP_DISABLED && offset < loop_begin_fp) { + if (loop_format != AudioStreamWAV::LOOP_DISABLED && offset < loop_begin_fp) { /* loopstart reached */ - if (loop_format == AudioStreamSample::LOOP_PINGPONG) { + if (loop_format == AudioStreamWAV::LOOP_PINGPONG) { /* bounce ping pong */ offset = loop_begin_fp + (loop_begin_fp - offset); increment = -increment; @@ -317,10 +317,10 @@ int AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, int } } else { /* going forward */ - if (loop_format != AudioStreamSample::LOOP_DISABLED && offset >= loop_end_fp) { + if (loop_format != AudioStreamWAV::LOOP_DISABLED && offset >= loop_end_fp) { /* loopend reached */ - if (loop_format == AudioStreamSample::LOOP_PINGPONG) { + if (loop_format == AudioStreamWAV::LOOP_PINGPONG) { /* bounce ping pong */ offset = loop_end_fp - (offset - loop_end_fp); increment = -increment; @@ -328,7 +328,7 @@ int AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, int } else { /* go to loop-begin */ - if (format == AudioStreamSample::FORMAT_IMA_ADPCM) { + if (format == AudioStreamWAV::FORMAT_IMA_ADPCM) { for (int i = 0; i < 2; i++) { ima_adpcm[i].step_index = ima_adpcm[i].loop_step_index; ima_adpcm[i].predictor = ima_adpcm[i].loop_predictor; @@ -366,14 +366,14 @@ int AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, int todo -= target; switch (base->format) { - case AudioStreamSample::FORMAT_8_BITS: { + case AudioStreamWAV::FORMAT_8_BITS: { if (is_stereo) { do_resample<int8_t, true, false>((int8_t *)data, dst_buff, offset, increment, target, ima_adpcm); } else { do_resample<int8_t, false, false>((int8_t *)data, dst_buff, offset, increment, target, ima_adpcm); } } break; - case AudioStreamSample::FORMAT_16_BITS: { + case AudioStreamWAV::FORMAT_16_BITS: { if (is_stereo) { do_resample<int16_t, true, false>((int16_t *)data, dst_buff, offset, increment, target, ima_adpcm); } else { @@ -381,7 +381,7 @@ int AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, int } } break; - case AudioStreamSample::FORMAT_IMA_ADPCM: { + case AudioStreamWAV::FORMAT_IMA_ADPCM: { if (is_stereo) { do_resample<int8_t, true, true>((int8_t *)data, dst_buff, offset, increment, target, ima_adpcm); } else { @@ -406,69 +406,73 @@ int AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, int return p_frames; } -AudioStreamPlaybackSample::AudioStreamPlaybackSample() {} +void AudioStreamPlaybackWAV::tag_used_streams() { + base->tag_used(get_playback_position()); +} + +AudioStreamPlaybackWAV::AudioStreamPlaybackWAV() {} ///////////////////// -void AudioStreamSample::set_format(Format p_format) { +void AudioStreamWAV::set_format(Format p_format) { format = p_format; } -AudioStreamSample::Format AudioStreamSample::get_format() const { +AudioStreamWAV::Format AudioStreamWAV::get_format() const { return format; } -void AudioStreamSample::set_loop_mode(LoopMode p_loop_mode) { +void AudioStreamWAV::set_loop_mode(LoopMode p_loop_mode) { loop_mode = p_loop_mode; } -AudioStreamSample::LoopMode AudioStreamSample::get_loop_mode() const { +AudioStreamWAV::LoopMode AudioStreamWAV::get_loop_mode() const { return loop_mode; } -void AudioStreamSample::set_loop_begin(int p_frame) { +void AudioStreamWAV::set_loop_begin(int p_frame) { loop_begin = p_frame; } -int AudioStreamSample::get_loop_begin() const { +int AudioStreamWAV::get_loop_begin() const { return loop_begin; } -void AudioStreamSample::set_loop_end(int p_frame) { +void AudioStreamWAV::set_loop_end(int p_frame) { loop_end = p_frame; } -int AudioStreamSample::get_loop_end() const { +int AudioStreamWAV::get_loop_end() const { return loop_end; } -void AudioStreamSample::set_mix_rate(int p_hz) { +void AudioStreamWAV::set_mix_rate(int p_hz) { ERR_FAIL_COND(p_hz == 0); mix_rate = p_hz; } -int AudioStreamSample::get_mix_rate() const { +int AudioStreamWAV::get_mix_rate() const { return mix_rate; } -void AudioStreamSample::set_stereo(bool p_enable) { +void AudioStreamWAV::set_stereo(bool p_enable) { stereo = p_enable; } -bool AudioStreamSample::is_stereo() const { +bool AudioStreamWAV::is_stereo() const { return stereo; } -float AudioStreamSample::get_length() const { +double AudioStreamWAV::get_length() const { int len = data_bytes; switch (format) { - case AudioStreamSample::FORMAT_8_BITS: + case AudioStreamWAV::FORMAT_8_BITS: len /= 1; break; - case AudioStreamSample::FORMAT_16_BITS: + case AudioStreamWAV::FORMAT_16_BITS: len /= 2; break; - case AudioStreamSample::FORMAT_IMA_ADPCM: + case AudioStreamWAV::FORMAT_IMA_ADPCM: len *= 2; break; } @@ -477,14 +481,14 @@ float AudioStreamSample::get_length() const { len /= 2; } - return float(len) / mix_rate; + return double(len) / mix_rate; } -bool AudioStreamSample::is_monophonic() const { +bool AudioStreamWAV::is_monophonic() const { return false; } -void AudioStreamSample::set_data(const Vector<uint8_t> &p_data) { +void AudioStreamWAV::set_data(const Vector<uint8_t> &p_data) { AudioServer::get_singleton()->lock(); if (data) { memfree(data); @@ -506,7 +510,7 @@ void AudioStreamSample::set_data(const Vector<uint8_t> &p_data) { AudioServer::get_singleton()->unlock(); } -Vector<uint8_t> AudioStreamSample::get_data() const { +Vector<uint8_t> AudioStreamWAV::get_data() const { Vector<uint8_t> pv; if (data) { @@ -521,8 +525,8 @@ Vector<uint8_t> AudioStreamSample::get_data() const { return pv; } -Error AudioStreamSample::save_to_wav(const String &p_path) { - if (format == AudioStreamSample::FORMAT_IMA_ADPCM) { +Error AudioStreamWAV::save_to_wav(const String &p_path) { + if (format == AudioStreamWAV::FORMAT_IMA_ADPCM) { WARN_PRINT("Saving IMA_ADPC samples are not supported yet"); return ERR_UNAVAILABLE; } @@ -540,13 +544,13 @@ Error AudioStreamSample::save_to_wav(const String &p_path) { int byte_pr_sample = 0; switch (format) { - case AudioStreamSample::FORMAT_8_BITS: + case AudioStreamWAV::FORMAT_8_BITS: byte_pr_sample = 1; break; - case AudioStreamSample::FORMAT_16_BITS: + case AudioStreamWAV::FORMAT_16_BITS: byte_pr_sample = 2; break; - case AudioStreamSample::FORMAT_IMA_ADPCM: + case AudioStreamWAV::FORMAT_IMA_ADPCM: byte_pr_sample = 4; break; } @@ -579,19 +583,19 @@ Error AudioStreamSample::save_to_wav(const String &p_path) { Vector<uint8_t> data = get_data(); const uint8_t *read_data = data.ptr(); switch (format) { - case AudioStreamSample::FORMAT_8_BITS: + case AudioStreamWAV::FORMAT_8_BITS: for (unsigned int i = 0; i < data_bytes; i++) { uint8_t data_point = (read_data[i] + 128); file->store_8(data_point); } break; - case AudioStreamSample::FORMAT_16_BITS: + case AudioStreamWAV::FORMAT_16_BITS: for (unsigned int i = 0; i < data_bytes / 2; i++) { uint16_t data_point = decode_uint16(&read_data[i * 2]); file->store_16(data_point); } break; - case AudioStreamSample::FORMAT_IMA_ADPCM: + case AudioStreamWAV::FORMAT_IMA_ADPCM: //Unimplemented break; } @@ -599,40 +603,40 @@ Error AudioStreamSample::save_to_wav(const String &p_path) { return OK; } -Ref<AudioStreamPlayback> AudioStreamSample::instance_playback() { - Ref<AudioStreamPlaybackSample> sample; +Ref<AudioStreamPlayback> AudioStreamWAV::instantiate_playback() { + Ref<AudioStreamPlaybackWAV> sample; sample.instantiate(); - sample->base = Ref<AudioStreamSample>(this); + sample->base = Ref<AudioStreamWAV>(this); return sample; } -String AudioStreamSample::get_stream_name() const { +String AudioStreamWAV::get_stream_name() const { return ""; } -void AudioStreamSample::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_data", "data"), &AudioStreamSample::set_data); - ClassDB::bind_method(D_METHOD("get_data"), &AudioStreamSample::get_data); +void AudioStreamWAV::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_data", "data"), &AudioStreamWAV::set_data); + ClassDB::bind_method(D_METHOD("get_data"), &AudioStreamWAV::get_data); - ClassDB::bind_method(D_METHOD("set_format", "format"), &AudioStreamSample::set_format); - ClassDB::bind_method(D_METHOD("get_format"), &AudioStreamSample::get_format); + ClassDB::bind_method(D_METHOD("set_format", "format"), &AudioStreamWAV::set_format); + ClassDB::bind_method(D_METHOD("get_format"), &AudioStreamWAV::get_format); - ClassDB::bind_method(D_METHOD("set_loop_mode", "loop_mode"), &AudioStreamSample::set_loop_mode); - ClassDB::bind_method(D_METHOD("get_loop_mode"), &AudioStreamSample::get_loop_mode); + ClassDB::bind_method(D_METHOD("set_loop_mode", "loop_mode"), &AudioStreamWAV::set_loop_mode); + ClassDB::bind_method(D_METHOD("get_loop_mode"), &AudioStreamWAV::get_loop_mode); - ClassDB::bind_method(D_METHOD("set_loop_begin", "loop_begin"), &AudioStreamSample::set_loop_begin); - ClassDB::bind_method(D_METHOD("get_loop_begin"), &AudioStreamSample::get_loop_begin); + ClassDB::bind_method(D_METHOD("set_loop_begin", "loop_begin"), &AudioStreamWAV::set_loop_begin); + ClassDB::bind_method(D_METHOD("get_loop_begin"), &AudioStreamWAV::get_loop_begin); - ClassDB::bind_method(D_METHOD("set_loop_end", "loop_end"), &AudioStreamSample::set_loop_end); - ClassDB::bind_method(D_METHOD("get_loop_end"), &AudioStreamSample::get_loop_end); + ClassDB::bind_method(D_METHOD("set_loop_end", "loop_end"), &AudioStreamWAV::set_loop_end); + ClassDB::bind_method(D_METHOD("get_loop_end"), &AudioStreamWAV::get_loop_end); - ClassDB::bind_method(D_METHOD("set_mix_rate", "mix_rate"), &AudioStreamSample::set_mix_rate); - ClassDB::bind_method(D_METHOD("get_mix_rate"), &AudioStreamSample::get_mix_rate); + ClassDB::bind_method(D_METHOD("set_mix_rate", "mix_rate"), &AudioStreamWAV::set_mix_rate); + ClassDB::bind_method(D_METHOD("get_mix_rate"), &AudioStreamWAV::get_mix_rate); - ClassDB::bind_method(D_METHOD("set_stereo", "stereo"), &AudioStreamSample::set_stereo); - ClassDB::bind_method(D_METHOD("is_stereo"), &AudioStreamSample::is_stereo); + ClassDB::bind_method(D_METHOD("set_stereo", "stereo"), &AudioStreamWAV::set_stereo); + ClassDB::bind_method(D_METHOD("is_stereo"), &AudioStreamWAV::is_stereo); - ClassDB::bind_method(D_METHOD("save_to_wav", "path"), &AudioStreamSample::save_to_wav); + ClassDB::bind_method(D_METHOD("save_to_wav", "path"), &AudioStreamWAV::save_to_wav); ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_data", "get_data"); ADD_PROPERTY(PropertyInfo(Variant::INT, "format", PROPERTY_HINT_ENUM, "8-Bit,16-Bit,IMA-ADPCM"), "set_format", "get_format"); @@ -652,9 +656,9 @@ void AudioStreamSample::_bind_methods() { BIND_ENUM_CONSTANT(LOOP_BACKWARD); } -AudioStreamSample::AudioStreamSample() {} +AudioStreamWAV::AudioStreamWAV() {} -AudioStreamSample::~AudioStreamSample() { +AudioStreamWAV::~AudioStreamWAV() { if (data) { memfree(data); data = nullptr; diff --git a/scene/resources/audio_stream_sample.h b/scene/resources/audio_stream_wav.h index 357febc27a..d0edc52031 100644 --- a/scene/resources/audio_stream_sample.h +++ b/scene/resources/audio_stream_wav.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* audio_stream_sample.h */ +/* audio_stream_wav.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,15 +28,15 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef AUDIO_STREAM_SAMPLE_H -#define AUDIO_STREAM_SAMPLE_H +#ifndef AUDIO_STREAM_WAV_H +#define AUDIO_STREAM_WAV_H #include "servers/audio/audio_stream.h" -class AudioStreamSample; +class AudioStreamWAV; -class AudioStreamPlaybackSample : public AudioStreamPlayback { - GDCLASS(AudioStreamPlaybackSample, AudioStreamPlayback); +class AudioStreamPlaybackWAV : public AudioStreamPlayback { + GDCLASS(AudioStreamPlaybackWAV, AudioStreamPlayback); enum { MIX_FRAC_BITS = 13, MIX_FRAC_LEN = (1 << MIX_FRAC_BITS), @@ -57,29 +57,31 @@ class AudioStreamPlaybackSample : public AudioStreamPlayback { int64_t offset = 0; int sign = 1; bool active = false; - friend class AudioStreamSample; - Ref<AudioStreamSample> base; + friend class AudioStreamWAV; + Ref<AudioStreamWAV> base; template <class Depth, bool is_stereo, bool is_ima_adpcm> void do_resample(const Depth *p_src, AudioFrame *p_dst, int64_t &offset, int32_t &increment, uint32_t amount, IMA_ADPCM_State *ima_adpcm); public: - virtual void start(float p_from_pos = 0.0) override; + virtual void start(double p_from_pos = 0.0) override; virtual void stop() override; virtual bool is_playing() const override; virtual int get_loop_count() const override; //times it looped - virtual float get_playback_position() const override; - virtual void seek(float p_time) override; + virtual double get_playback_position() const override; + virtual void seek(double p_time) override; virtual int mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) override; - AudioStreamPlaybackSample(); + virtual void tag_used_streams() override; + + AudioStreamPlaybackWAV(); }; -class AudioStreamSample : public AudioStream { - GDCLASS(AudioStreamSample, AudioStream); +class AudioStreamWAV : public AudioStream { + GDCLASS(AudioStreamWAV, AudioStream); RES_BASE_EXTENSION("sample") public: @@ -98,7 +100,7 @@ public: }; private: - friend class AudioStreamPlaybackSample; + friend class AudioStreamPlaybackWAV; enum { DATA_PAD = 16 //padding for interpolation @@ -135,7 +137,7 @@ public: void set_stereo(bool p_enable); bool is_stereo() const; - virtual float get_length() const override; //if supported, otherwise return 0 + virtual double get_length() const override; //if supported, otherwise return 0 virtual bool is_monophonic() const override; @@ -144,14 +146,14 @@ public: Error save_to_wav(const String &p_path); - virtual Ref<AudioStreamPlayback> instance_playback() override; + virtual Ref<AudioStreamPlayback> instantiate_playback() override; virtual String get_stream_name() const override; - AudioStreamSample(); - ~AudioStreamSample(); + AudioStreamWAV(); + ~AudioStreamWAV(); }; -VARIANT_ENUM_CAST(AudioStreamSample::Format) -VARIANT_ENUM_CAST(AudioStreamSample::LoopMode) +VARIANT_ENUM_CAST(AudioStreamWAV::Format) +VARIANT_ENUM_CAST(AudioStreamWAV::LoopMode) -#endif // AUDIO_STREAM_SAMPLE_H +#endif // AUDIO_STREAM_WAV_H diff --git a/scene/resources/bit_map.cpp b/scene/resources/bit_map.cpp index 1ff72825ac..0505f6b559 100644 --- a/scene/resources/bit_map.cpp +++ b/scene/resources/bit_map.cpp @@ -31,14 +31,20 @@ #include "bit_map.h" #include "core/io/image_loader.h" +#include "core/variant/typed_array.h" -void BitMap::create(const Size2 &p_size) { +void BitMap::create(const Size2i &p_size) { ERR_FAIL_COND(p_size.width < 1); ERR_FAIL_COND(p_size.height < 1); + ERR_FAIL_COND(static_cast<int64_t>(p_size.width) * static_cast<int64_t>(p_size.height) > INT32_MAX); + + Error err = bitmask.resize((((p_size.width * p_size.height) - 1) / 8) + 1); + ERR_FAIL_COND(err != OK); + width = p_size.width; height = p_size.height; - bitmask.resize((((width * height) - 1) / 8) + 1); + memset(bitmask.ptrw(), 0, bitmask.size()); } @@ -48,7 +54,7 @@ void BitMap::create_from_image_alpha(const Ref<Image> &p_image, float p_threshol img->convert(Image::FORMAT_LA8); ERR_FAIL_COND(img->get_format() != Image::FORMAT_LA8); - create(img->get_size()); + create(Size2i(img->get_width(), img->get_height())); const uint8_t *r = img->get_data().ptr(); uint8_t *w = bitmask.ptrw(); @@ -62,7 +68,7 @@ void BitMap::create_from_image_alpha(const Ref<Image> &p_image, float p_threshol } } -void BitMap::set_bit_rect(const Rect2 &p_rect, bool p_value) { +void BitMap::set_bit_rect(const Rect2i &p_rect, bool p_value) { Rect2i current = Rect2i(0, 0, width, height).intersection(p_rect); uint8_t *data = bitmask.ptrw(); @@ -90,7 +96,7 @@ int BitMap::get_true_bit_count() const { const uint8_t *d = bitmask.ptr(); int c = 0; - //fast, almost branchless version + // Fast, almost branchless version. for (int i = 0; i < ds; i++) { c += (d[i] & (1 << 7)) >> 7; @@ -106,14 +112,15 @@ int BitMap::get_true_bit_count() const { return c; } -void BitMap::set_bit(const Point2 &p_pos, bool p_value) { - int x = p_pos.x; - int y = p_pos.y; +void BitMap::set_bitv(const Point2i &p_pos, bool p_value) { + set_bit(p_pos.x, p_pos.y, p_value); +} - ERR_FAIL_INDEX(x, width); - ERR_FAIL_INDEX(y, height); +void BitMap::set_bit(int p_x, int p_y, bool p_value) { + ERR_FAIL_INDEX(p_x, width); + ERR_FAIL_INDEX(p_y, height); - int ofs = width * y + x; + int ofs = width * p_y + p_x; int bbyte = ofs / 8; int bbit = ofs % 8; @@ -128,21 +135,23 @@ void BitMap::set_bit(const Point2 &p_pos, bool p_value) { bitmask.write[bbyte] = b; } -bool BitMap::get_bit(const Point2 &p_pos) const { - int x = Math::fast_ftoi(p_pos.x); - int y = Math::fast_ftoi(p_pos.y); - ERR_FAIL_INDEX_V(x, width, false); - ERR_FAIL_INDEX_V(y, height, false); +bool BitMap::get_bitv(const Point2i &p_pos) const { + return get_bit(p_pos.x, p_pos.y); +} + +bool BitMap::get_bit(int p_x, int p_y) const { + ERR_FAIL_INDEX_V(p_x, width, false); + ERR_FAIL_INDEX_V(p_y, height, false); - int ofs = width * y + x; + int ofs = width * p_y + p_x; int bbyte = ofs / 8; int bbit = ofs % 8; return (bitmask[bbyte] & (1 << bbit)) != 0; } -Size2 BitMap::get_size() const { - return Size2(width, height); +Size2i BitMap::get_size() const { + return Size2i(width, height); } void BitMap::_set_data(const Dictionary &p_d) { @@ -160,13 +169,13 @@ Dictionary BitMap::_get_data() const { return d; } -Vector<Vector2> BitMap::_march_square(const Rect2i &rect, const Point2i &start) const { +Vector<Vector2> BitMap::_march_square(const Rect2i &p_rect, const Point2i &p_start) const { int stepx = 0; int stepy = 0; int prevx = 0; int prevy = 0; - int startx = start.x; - int starty = start.y; + int startx = p_start.x; + int starty = p_start.y; int curx = startx; int cury = starty; unsigned int count = 0; @@ -175,7 +184,7 @@ Vector<Vector2> BitMap::_march_square(const Rect2i &rect, const Point2i &start) Vector<Vector2> _points; do { int sv = 0; - { //square value + { // Square value /* checking the 2x2 pixel grid, assigning these values to each pixel, if not transparent @@ -186,13 +195,13 @@ Vector<Vector2> BitMap::_march_square(const Rect2i &rect, const Point2i &start) +---+---+ */ Point2i tl = Point2i(curx - 1, cury - 1); - sv += (rect.has_point(tl) && get_bit(tl)) ? 1 : 0; + sv += (p_rect.has_point(tl) && get_bitv(tl)) ? 1 : 0; Point2i tr = Point2i(curx, cury - 1); - sv += (rect.has_point(tr) && get_bit(tr)) ? 2 : 0; + sv += (p_rect.has_point(tr) && get_bitv(tr)) ? 2 : 0; Point2i bl = Point2i(curx - 1, cury); - sv += (rect.has_point(bl) && get_bit(bl)) ? 4 : 0; + sv += (p_rect.has_point(bl) && get_bitv(bl)) ? 4 : 0; Point2i br = Point2i(curx, cury); - sv += (rect.has_point(br) && get_bit(br)) ? 8 : 0; + sv += (p_rect.has_point(br) && get_bitv(br)) ? 8 : 0; ERR_FAIL_COND_V(sv == 0 || sv == 15, Vector<Vector2>()); } @@ -302,16 +311,16 @@ Vector<Vector2> BitMap::_march_square(const Rect2i &rect, const Point2i &start) default: ERR_PRINT("this shouldn't happen."); } - //little optimization - // if previous direction is same as current direction, - // then we should modify the last vec to current + // Small optimization: + // If the previous direction is same as the current direction, + // then we should modify the last vector to current. curx += stepx; cury += stepy; if (stepx == prevx && stepy == prevy) { - _points.write[_points.size() - 1].x = (float)(curx - rect.position.x); - _points.write[_points.size() - 1].y = (float)(cury + rect.position.y); + _points.write[_points.size() - 1].x = (float)(curx - p_rect.position.x); + _points.write[_points.size() - 1].y = (float)(cury + p_rect.position.y); } else { - _points.push_back(Vector2((float)(curx - rect.position.x), (float)(cury + rect.position.y))); + _points.push_back(Vector2((float)(curx - p_rect.position.x), (float)(cury + p_rect.position.y))); } count++; @@ -347,7 +356,7 @@ static Vector<Vector2> rdp(const Vector<Vector2> &v, float optimization) { int index = -1; float dist = 0.0; - //not looping first and last point + // Not looping first and last point. for (size_t i = 1, size = v.size(); i < size - 1; ++i) { float cdist = perpendicular_distance(v[i], v[0], v[v.size() - 1]); if (cdist > dist) { @@ -384,9 +393,9 @@ static Vector<Vector2> rdp(const Vector<Vector2> &v, float optimization) { static Vector<Vector2> reduce(const Vector<Vector2> &points, const Rect2i &rect, float epsilon) { int size = points.size(); - // if there are less than 3 points, then we have nothing + // If there are less than 3 points, then we have nothing. ERR_FAIL_COND_V(size < 3, Vector<Vector2>()); - // if there are less than 9 points (but more than 3), then we don't need to reduce it + // If there are less than 9 points (but more than 3), then we don't need to reduce it. if (size < 9) { return points; } @@ -411,9 +420,9 @@ struct FillBitsStackEntry { }; static void fill_bits(const BitMap *p_src, Ref<BitMap> &p_map, const Point2i &p_pos, const Rect2i &rect) { - // Using a custom stack to work iteratively to avoid stack overflow on big bitmaps + // Using a custom stack to work iteratively to avoid stack overflow on big bitmaps. Vector<FillBitsStackEntry> stack; - // Tracking size since we won't be shrinking the stack vector + // Tracking size since we won't be shrinking the stack vector. int stack_size = 0; Point2i pos = p_pos; @@ -432,10 +441,10 @@ static void fill_bits(const BitMap *p_src, Ref<BitMap> &p_map, const Point2i &p_ for (int i = next_i; i <= pos.x + 1; i++) { for (int j = next_j; j <= pos.y + 1; j++) { if (popped) { - // The next loop over j must start normally + // The next loop over j must start normally. next_j = pos.y; popped = false; - // Skip because an iteration was already executed with current counter values + // Skip because an iteration was already executed with current counter values. continue; } @@ -446,11 +455,11 @@ static void fill_bits(const BitMap *p_src, Ref<BitMap> &p_map, const Point2i &p_ continue; } - if (p_map->get_bit(Vector2(i, j))) { + if (p_map->get_bit(i, j)) { continue; - } else if (p_src->get_bit(Vector2(i, j))) { - p_map->set_bit(Vector2(i, j), true); + } else if (p_src->get_bit(i, j)) { + p_map->set_bit(i, j, true); FillBitsStackEntry se = { pos, i, j }; stack.resize(MAX(stack_size + 1, stack.size())); @@ -481,7 +490,7 @@ static void fill_bits(const BitMap *p_src, Ref<BitMap> &p_map, const Point2i &p_ print_verbose("BitMap: Max stack size: " + itos(stack.size())); } -Vector<Vector<Vector2>> BitMap::clip_opaque_to_polygons(const Rect2 &p_rect, float p_epsilon) const { +Vector<Vector<Vector2>> BitMap::clip_opaque_to_polygons(const Rect2i &p_rect, float p_epsilon) const { Rect2i r = Rect2i(0, 0, width, height).intersection(p_rect); print_verbose("BitMap: Rect: " + r); @@ -493,7 +502,7 @@ Vector<Vector<Vector2>> BitMap::clip_opaque_to_polygons(const Rect2 &p_rect, flo Vector<Vector<Vector2>> polygons; for (int i = r.position.y; i < r.position.y + r.size.height; i++) { for (int j = r.position.x; j < r.position.x + r.size.width; j++) { - if (!fill->get_bit(Point2(j, i)) && get_bit(Point2(j, i))) { + if (!fill->get_bit(j, i) && get_bit(j, i)) { fill_bits(this, fill, Point2i(j, i), r); Vector<Vector2> polygon = _march_square(r, Point2i(j, i)); @@ -514,7 +523,7 @@ Vector<Vector<Vector2>> BitMap::clip_opaque_to_polygons(const Rect2 &p_rect, flo return polygons; } -void BitMap::grow_mask(int p_pixels, const Rect2 &p_rect) { +void BitMap::grow_mask(int p_pixels, const Rect2i &p_rect) { if (p_pixels == 0) { return; } @@ -531,7 +540,7 @@ void BitMap::grow_mask(int p_pixels, const Rect2 &p_rect) { for (int i = r.position.y; i < r.position.y + r.size.height; i++) { for (int j = r.position.x; j < r.position.x + r.size.width; j++) { - if (bit_value == get_bit(Point2(j, i))) { + if (bit_value == get_bit(j, i)) { continue; } @@ -542,7 +551,7 @@ void BitMap::grow_mask(int p_pixels, const Rect2 &p_rect) { bool outside = false; if ((x < p_rect.position.x) || (x >= p_rect.position.x + p_rect.size.x) || (y < p_rect.position.y) || (y >= p_rect.position.y + p_rect.size.y)) { - // outside of rectangle counts as bit not set + // Outside of rectangle counts as bit not set. if (!bit_value) { outside = true; } else { @@ -555,7 +564,7 @@ void BitMap::grow_mask(int p_pixels, const Rect2 &p_rect) { continue; } - if (outside || (bit_value == copy->get_bit(Point2(x, y)))) { + if (outside || (bit_value == copy->get_bit(x, y))) { found = true; break; } @@ -566,22 +575,22 @@ void BitMap::grow_mask(int p_pixels, const Rect2 &p_rect) { } if (found) { - set_bit(Point2(j, i), bit_value); + set_bit(j, i, bit_value); } } } } -void BitMap::shrink_mask(int p_pixels, const Rect2 &p_rect) { +void BitMap::shrink_mask(int p_pixels, const Rect2i &p_rect) { grow_mask(-p_pixels, p_rect); } -Array BitMap::_opaque_to_polygons_bind(const Rect2 &p_rect, float p_epsilon) const { +TypedArray<PackedVector2Array> BitMap::_opaque_to_polygons_bind(const Rect2i &p_rect, float p_epsilon) const { Vector<Vector<Vector2>> result = clip_opaque_to_polygons(p_rect, p_epsilon); - // Convert result to bindable types + // Convert result to bindable types. - Array result_array; + TypedArray<PackedVector2Array> result_array; result_array.resize(result.size()); for (int i = 0; i < result.size(); i++) { const Vector<Vector2> &polygon = result[i]; @@ -602,15 +611,25 @@ 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) { +void BitMap::resize(const Size2i &p_new_size) { + ERR_FAIL_COND(p_new_size.width < 0 || p_new_size.height < 0); + if (p_new_size == get_size()) { + return; + } + Ref<BitMap> new_bitmap; new_bitmap.instantiate(); new_bitmap->create(p_new_size); - int lw = MIN(width, p_new_size.width); - int lh = MIN(height, p_new_size.height); + // also allow for upscaling + int lw = (width == 0) ? 0 : p_new_size.width; + int lh = (height == 0) ? 0 : p_new_size.height; + + float scale_x = ((float)width / p_new_size.width); + float scale_y = ((float)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))); + bool new_bit = get_bit(x * scale_x, y * scale_y); + new_bitmap->set_bit(x, y, new_bit); } } @@ -626,14 +645,16 @@ Ref<Image> BitMap::convert_to_image() const { 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->set_pixel(i, j, get_bit(i, j) ? Color(1, 1, 1) : Color(0, 0, 0)); } } return image; } -void BitMap::blit(const Vector2 &p_pos, const Ref<BitMap> &p_bitmap) { +void BitMap::blit(const Vector2i &p_pos, const Ref<BitMap> &p_bitmap) { + ERR_FAIL_COND_MSG(p_bitmap.is_null(), "It's not a reference to a valid BitMap object."); + int x = p_pos.x; int y = p_pos.y; int w = p_bitmap->get_size().width; @@ -649,8 +670,8 @@ void BitMap::blit(const Vector2 &p_pos, const Ref<BitMap> &p_bitmap) { if (py < 0 || py >= height) { continue; } - if (p_bitmap->get_bit(Vector2(i, j))) { - set_bit(Vector2(x, y), true); + if (p_bitmap->get_bit(i, j)) { + set_bit(px, py, true); } } } @@ -660,8 +681,10 @@ void BitMap::_bind_methods() { ClassDB::bind_method(D_METHOD("create", "size"), &BitMap::create); ClassDB::bind_method(D_METHOD("create_from_image_alpha", "image", "threshold"), &BitMap::create_from_image_alpha, DEFVAL(0.1)); - ClassDB::bind_method(D_METHOD("set_bit", "position", "bit"), &BitMap::set_bit); - ClassDB::bind_method(D_METHOD("get_bit", "position"), &BitMap::get_bit); + ClassDB::bind_method(D_METHOD("set_bitv", "position", "bit"), &BitMap::set_bitv); + ClassDB::bind_method(D_METHOD("set_bit", "x", "y", "bit"), &BitMap::set_bit); + ClassDB::bind_method(D_METHOD("get_bitv", "position"), &BitMap::get_bitv); + ClassDB::bind_method(D_METHOD("get_bit", "x", "y"), &BitMap::get_bit); ClassDB::bind_method(D_METHOD("set_bit_rect", "rect", "bit"), &BitMap::set_bit_rect); ClassDB::bind_method(D_METHOD("get_true_bit_count"), &BitMap::get_true_bit_count); @@ -669,7 +692,7 @@ void BitMap::_bind_methods() { ClassDB::bind_method(D_METHOD("get_size"), &BitMap::get_size); ClassDB::bind_method(D_METHOD("resize", "new_size"), &BitMap::resize); - ClassDB::bind_method(D_METHOD("_set_data"), &BitMap::_set_data); + ClassDB::bind_method(D_METHOD("_set_data", "data"), &BitMap::_set_data); ClassDB::bind_method(D_METHOD("_get_data"), &BitMap::_get_data); ClassDB::bind_method(D_METHOD("grow_mask", "pixels", "rect"), &BitMap::grow_mask); @@ -680,5 +703,3 @@ void BitMap::_bind_methods() { } BitMap::BitMap() {} - -////////////////////////////////////// diff --git a/scene/resources/bit_map.h b/scene/resources/bit_map.h index 0d0d779c32..291ed8c4d0 100644 --- a/scene/resources/bit_map.h +++ b/scene/resources/bit_map.h @@ -35,6 +35,9 @@ #include "core/io/resource.h" #include "core/io/resource_loader.h" +template <typename T> +class TypedArray; + class BitMap : public Resource { GDCLASS(BitMap, Resource); OBJ_SAVE_TYPE(BitMap); @@ -43,9 +46,9 @@ class BitMap : public Resource { int width = 0; int height = 0; - Vector<Vector2> _march_square(const Rect2i &rect, const Point2i &start) const; + Vector<Vector2> _march_square(const Rect2i &p_rect, const Point2i &p_start) const; - Array _opaque_to_polygons_bind(const Rect2 &p_rect, float p_epsilon) const; + TypedArray<PackedVector2Array> _opaque_to_polygons_bind(const Rect2i &p_rect, float p_epsilon) const; protected: void _set_data(const Dictionary &p_d); @@ -54,24 +57,27 @@ protected: static void _bind_methods(); public: - void create(const Size2 &p_size); + void create(const Size2i &p_size); void create_from_image_alpha(const Ref<Image> &p_image, float p_threshold = 0.1); - void set_bit(const Point2 &p_pos, bool p_value); - bool get_bit(const Point2 &p_pos) const; - void set_bit_rect(const Rect2 &p_rect, bool p_value); + void set_bitv(const Point2i &p_pos, bool p_value); + void set_bit(int p_x, int p_y, bool p_value); + void set_bit_rect(const Rect2i &p_rect, bool p_value); + bool get_bitv(const Point2i &p_pos) const; + bool get_bit(int p_x, int p_y) const; + int get_true_bit_count() const; - Size2 get_size() const; - void resize(const Size2 &p_new_size); + Size2i get_size() const; + void resize(const Size2i &p_new_size); - void grow_mask(int p_pixels, const Rect2 &p_rect); - void shrink_mask(int p_pixels, const Rect2 &p_rect); + void grow_mask(int p_pixels, const Rect2i &p_rect); + void shrink_mask(int p_pixels, const Rect2i &p_rect); - void blit(const Vector2 &p_pos, const Ref<BitMap> &p_bitmap); + void blit(const Vector2i &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; + Vector<Vector<Vector2>> clip_opaque_to_polygons(const Rect2i &p_rect, float p_epsilon = 2.0) const; BitMap(); }; diff --git a/scene/resources/bone_map.cpp b/scene/resources/bone_map.cpp index ce030934fa..dfaf82f36a 100644 --- a/scene/resources/bone_map.cpp +++ b/scene/resources/bone_map.cpp @@ -50,6 +50,14 @@ bool BoneMap::_get(const StringName &p_path, Variant &r_ret) const { return true; } +void BoneMap::_get_property_list(List<PropertyInfo> *p_list) const { + HashMap<StringName, StringName>::ConstIterator E = bone_map.begin(); + while (E) { + p_list->push_back(PropertyInfo(Variant::STRING_NAME, "bone_map/" + E->key, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + ++E; + } +} + Ref<SkeletonProfile> BoneMap::get_profile() const { return profile; } @@ -74,9 +82,13 @@ StringName BoneMap::get_skeleton_bone_name(StringName p_profile_bone_name) const return bone_map.get(p_profile_bone_name); } -void BoneMap::set_skeleton_bone_name(StringName p_profile_bone_name, const StringName p_skeleton_bone_name) { +void BoneMap::_set_skeleton_bone_name(StringName p_profile_bone_name, const StringName p_skeleton_bone_name) { ERR_FAIL_COND(!bone_map.has(p_profile_bone_name)); bone_map.insert(p_profile_bone_name, p_skeleton_bone_name); +} + +void BoneMap::set_skeleton_bone_name(StringName p_profile_bone_name, const StringName p_skeleton_bone_name) { + _set_skeleton_bone_name(p_profile_bone_name, p_skeleton_bone_name); emit_signal("bone_map_updated"); } @@ -153,13 +165,16 @@ void BoneMap::_bind_methods() { ClassDB::bind_method(D_METHOD("find_profile_bone_name", "skeleton_bone_name"), &BoneMap::find_profile_bone_name); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "profile", PROPERTY_HINT_RESOURCE_TYPE, "SkeletonProfile"), "set_profile", "get_profile"); + ADD_ARRAY("bonemap", "bonemap"); ADD_SIGNAL(MethodInfo("bone_map_updated")); ADD_SIGNAL(MethodInfo("profile_updated")); } void BoneMap::_validate_property(PropertyInfo &property) const { - // + if (property.name == "bonemap" || property.name == "profile") { + property.usage = PROPERTY_USAGE_NO_EDITOR; + } } BoneMap::BoneMap() { diff --git a/scene/resources/bone_map.h b/scene/resources/bone_map.h index 4b7928015d..a07a776e27 100644 --- a/scene/resources/bone_map.h +++ b/scene/resources/bone_map.h @@ -45,13 +45,11 @@ class BoneMap : public Resource { protected: bool _get(const StringName &p_path, Variant &r_ret) const; bool _set(const StringName &p_path, const Variant &p_value); - virtual void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; + void _get_property_list(List<PropertyInfo> *p_list) const; static void _bind_methods(); public: - int get_profile_type() const; - void set_profile_type(const int p_profile_type); - Ref<SkeletonProfile> get_profile() const; void set_profile(const Ref<SkeletonProfile> &p_profile); @@ -59,6 +57,7 @@ public: StringName get_skeleton_bone_name(StringName p_profile_bone_name) const; void set_skeleton_bone_name(StringName p_profile_bone_name, const StringName p_skeleton_bone_name); + void _set_skeleton_bone_name(StringName p_profile_bone_name, const StringName p_skeleton_bone_name); // Avoid to emit signal for editor. StringName find_profile_bone_name(StringName p_skeleton_bone_name) const; diff --git a/scene/resources/box_shape_3d.h b/scene/resources/box_shape_3d.h index 4e6db893af..3dd0bb6cb7 100644 --- a/scene/resources/box_shape_3d.h +++ b/scene/resources/box_shape_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef BOX_SHAPE_H -#define BOX_SHAPE_H +#ifndef BOX_SHAPE_3D_H +#define BOX_SHAPE_3D_H #include "scene/resources/shape_3d.h" @@ -56,4 +56,4 @@ public: BoxShape3D(); }; -#endif // BOX_SHAPE_H +#endif // BOX_SHAPE_3D_H diff --git a/scene/resources/camera_attributes.cpp b/scene/resources/camera_attributes.cpp new file mode 100644 index 0000000000..3c322f32b6 --- /dev/null +++ b/scene/resources/camera_attributes.cpp @@ -0,0 +1,493 @@ +/*************************************************************************/ +/* camera_attributes.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "camera_attributes.h" + +#include "core/config/project_settings.h" +#include "servers/rendering_server.h" + +void CameraAttributes::set_exposure_multiplier(float p_multiplier) { + exposure_multiplier = p_multiplier; + _update_exposure(); + emit_changed(); +} + +float CameraAttributes::get_exposure_multiplier() const { + return exposure_multiplier; +} + +void CameraAttributes::set_exposure_sensitivity(float p_sensitivity) { + exposure_sensitivity = p_sensitivity; + _update_exposure(); + emit_changed(); +} + +float CameraAttributes::get_exposure_sensitivity() const { + return exposure_sensitivity; +} + +void CameraAttributes::_update_exposure() { + float exposure_normalization = 1.0; + // Ignore physical properties if not using physical light units. + if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + exposure_normalization = calculate_exposure_normalization(); + } + + RS::get_singleton()->camera_attributes_set_exposure(camera_attributes, exposure_multiplier, exposure_normalization); +} + +void CameraAttributes::set_auto_exposure_enabled(bool p_enabled) { + auto_exposure_enabled = p_enabled; + _update_auto_exposure(); + notify_property_list_changed(); +} + +bool CameraAttributes::is_auto_exposure_enabled() const { + return auto_exposure_enabled; +} + +void CameraAttributes::set_auto_exposure_speed(float p_auto_exposure_speed) { + auto_exposure_speed = p_auto_exposure_speed; + _update_auto_exposure(); +} + +float CameraAttributes::get_auto_exposure_speed() const { + return auto_exposure_speed; +} + +void CameraAttributes::set_auto_exposure_scale(float p_auto_exposure_scale) { + auto_exposure_scale = p_auto_exposure_scale; + _update_auto_exposure(); +} + +float CameraAttributes::get_auto_exposure_scale() const { + return auto_exposure_scale; +} + +RID CameraAttributes::get_rid() const { + return camera_attributes; +} + +void CameraAttributes::_validate_property(PropertyInfo &p_property) const { + if (!GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units") && p_property.name == "exposure_sensitivity") { + p_property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + return; + } + + if (p_property.name.begins_with("auto_exposure_") && p_property.name != "auto_exposure_enabled" && !auto_exposure_enabled) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + return; + } +} + +void CameraAttributes::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_exposure_multiplier", "multiplier"), &CameraAttributes::set_exposure_multiplier); + ClassDB::bind_method(D_METHOD("get_exposure_multiplier"), &CameraAttributes::get_exposure_multiplier); + ClassDB::bind_method(D_METHOD("set_exposure_sensitivity", "sensitivity"), &CameraAttributes::set_exposure_sensitivity); + ClassDB::bind_method(D_METHOD("get_exposure_sensitivity"), &CameraAttributes::get_exposure_sensitivity); + + ClassDB::bind_method(D_METHOD("set_auto_exposure_enabled", "enabled"), &CameraAttributes::set_auto_exposure_enabled); + ClassDB::bind_method(D_METHOD("is_auto_exposure_enabled"), &CameraAttributes::is_auto_exposure_enabled); + ClassDB::bind_method(D_METHOD("set_auto_exposure_speed", "exposure_speed"), &CameraAttributes::set_auto_exposure_speed); + ClassDB::bind_method(D_METHOD("get_auto_exposure_speed"), &CameraAttributes::get_auto_exposure_speed); + ClassDB::bind_method(D_METHOD("set_auto_exposure_scale", "exposure_grey"), &CameraAttributes::set_auto_exposure_scale); + ClassDB::bind_method(D_METHOD("get_auto_exposure_scale"), &CameraAttributes::get_auto_exposure_scale); + + ADD_GROUP("Exposure", "exposure"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "exposure_sensitivity", PROPERTY_HINT_RANGE, "0.1,32000.0,0.1,suffix:ISO"), "set_exposure_sensitivity", "get_exposure_sensitivity"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "exposure_multiplier", PROPERTY_HINT_RANGE, "0.0,2048.0,0.001"), "set_exposure_multiplier", "get_exposure_multiplier"); + + ADD_GROUP("Auto Exposure", "auto_exposure_"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_exposure_enabled"), "set_auto_exposure_enabled", "is_auto_exposure_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "auto_exposure_scale", PROPERTY_HINT_RANGE, "0.01,64,0.01"), "set_auto_exposure_scale", "get_auto_exposure_scale"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "auto_exposure_speed", PROPERTY_HINT_RANGE, "0.01,64,0.01"), "set_auto_exposure_speed", "get_auto_exposure_speed"); +} + +CameraAttributes::CameraAttributes() { + camera_attributes = RS::get_singleton()->camera_attributes_create(); +} + +CameraAttributes::~CameraAttributes() { + RS::get_singleton()->free(camera_attributes); +} + +////////////////////////////////////////////////////// +/* CameraAttributesPractical */ + +void CameraAttributesPractical::set_dof_blur_far_enabled(bool p_enabled) { + dof_blur_far_enabled = p_enabled; + _update_dof_blur(); + notify_property_list_changed(); +} + +bool CameraAttributesPractical::is_dof_blur_far_enabled() const { + return dof_blur_far_enabled; +} + +void CameraAttributesPractical::set_dof_blur_far_distance(float p_distance) { + dof_blur_far_distance = p_distance; + _update_dof_blur(); +} + +float CameraAttributesPractical::get_dof_blur_far_distance() const { + return dof_blur_far_distance; +} + +void CameraAttributesPractical::set_dof_blur_far_transition(float p_distance) { + dof_blur_far_transition = p_distance; + _update_dof_blur(); +} + +float CameraAttributesPractical::get_dof_blur_far_transition() const { + return dof_blur_far_transition; +} + +void CameraAttributesPractical::set_dof_blur_near_enabled(bool p_enabled) { + dof_blur_near_enabled = p_enabled; + _update_dof_blur(); + notify_property_list_changed(); +} + +bool CameraAttributesPractical::is_dof_blur_near_enabled() const { + return dof_blur_near_enabled; +} + +void CameraAttributesPractical::set_dof_blur_near_distance(float p_distance) { + dof_blur_near_distance = p_distance; + _update_dof_blur(); +} + +float CameraAttributesPractical::get_dof_blur_near_distance() const { + return dof_blur_near_distance; +} + +void CameraAttributesPractical::set_dof_blur_near_transition(float p_distance) { + dof_blur_near_transition = p_distance; + _update_dof_blur(); +} + +float CameraAttributesPractical::get_dof_blur_near_transition() const { + return dof_blur_near_transition; +} + +void CameraAttributesPractical::set_dof_blur_amount(float p_amount) { + dof_blur_amount = p_amount; + _update_dof_blur(); +} + +float CameraAttributesPractical::get_dof_blur_amount() const { + return dof_blur_amount; +} + +void CameraAttributesPractical::_update_dof_blur() { + RS::get_singleton()->camera_attributes_set_dof_blur( + get_rid(), + dof_blur_far_enabled, + dof_blur_far_distance, + dof_blur_far_transition, + dof_blur_near_enabled, + dof_blur_near_distance, + dof_blur_near_transition, + dof_blur_amount); +} + +float CameraAttributesPractical::calculate_exposure_normalization() const { + return exposure_sensitivity / 3072007.0; // Matches exposure normalization for default CameraAttributesPhysical at ISO 100. +} + +void CameraAttributesPractical::set_auto_exposure_min_sensitivity(float p_min) { + auto_exposure_min = p_min; + _update_auto_exposure(); +} + +float CameraAttributesPractical::get_auto_exposure_min_sensitivity() const { + return auto_exposure_min; +} + +void CameraAttributesPractical::set_auto_exposure_max_sensitivity(float p_max) { + auto_exposure_max = p_max; + _update_auto_exposure(); +} + +float CameraAttributesPractical::get_auto_exposure_max_sensitivity() const { + return auto_exposure_max; +} + +void CameraAttributesPractical::_update_auto_exposure() { + RS::get_singleton()->camera_attributes_set_auto_exposure( + get_rid(), + auto_exposure_enabled, + auto_exposure_min * ((12.5 / 100.0) / exposure_sensitivity), // Convert from Sensitivity to Luminance + auto_exposure_max * ((12.5 / 100.0) / exposure_sensitivity), // Convert from Sensitivity to Luminance + auto_exposure_speed, + auto_exposure_scale); + emit_changed(); +} + +void CameraAttributesPractical::_validate_property(PropertyInfo &p_property) const { + if ((!dof_blur_far_enabled && (p_property.name == "dof_blur_far_distance" || p_property.name == "dof_blur_far_transition")) || + (!dof_blur_near_enabled && (p_property.name == "dof_blur_near_distance" || p_property.name == "dof_blur_near_transition"))) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + } +} + +void CameraAttributesPractical::_bind_methods() { + // DOF blur + + ClassDB::bind_method(D_METHOD("set_dof_blur_far_enabled", "enabled"), &CameraAttributesPractical::set_dof_blur_far_enabled); + ClassDB::bind_method(D_METHOD("is_dof_blur_far_enabled"), &CameraAttributesPractical::is_dof_blur_far_enabled); + ClassDB::bind_method(D_METHOD("set_dof_blur_far_distance", "distance"), &CameraAttributesPractical::set_dof_blur_far_distance); + ClassDB::bind_method(D_METHOD("get_dof_blur_far_distance"), &CameraAttributesPractical::get_dof_blur_far_distance); + ClassDB::bind_method(D_METHOD("set_dof_blur_far_transition", "distance"), &CameraAttributesPractical::set_dof_blur_far_transition); + ClassDB::bind_method(D_METHOD("get_dof_blur_far_transition"), &CameraAttributesPractical::get_dof_blur_far_transition); + + ClassDB::bind_method(D_METHOD("set_dof_blur_near_enabled", "enabled"), &CameraAttributesPractical::set_dof_blur_near_enabled); + ClassDB::bind_method(D_METHOD("is_dof_blur_near_enabled"), &CameraAttributesPractical::is_dof_blur_near_enabled); + ClassDB::bind_method(D_METHOD("set_dof_blur_near_distance", "distance"), &CameraAttributesPractical::set_dof_blur_near_distance); + ClassDB::bind_method(D_METHOD("get_dof_blur_near_distance"), &CameraAttributesPractical::get_dof_blur_near_distance); + ClassDB::bind_method(D_METHOD("set_dof_blur_near_transition", "distance"), &CameraAttributesPractical::set_dof_blur_near_transition); + ClassDB::bind_method(D_METHOD("get_dof_blur_near_transition"), &CameraAttributesPractical::get_dof_blur_near_transition); + ClassDB::bind_method(D_METHOD("set_dof_blur_amount", "amount"), &CameraAttributesPractical::set_dof_blur_amount); + ClassDB::bind_method(D_METHOD("get_dof_blur_amount"), &CameraAttributesPractical::get_dof_blur_amount); + + ClassDB::bind_method(D_METHOD("set_auto_exposure_max_sensitivity", "max_sensitivity"), &CameraAttributesPractical::set_auto_exposure_max_sensitivity); + ClassDB::bind_method(D_METHOD("get_auto_exposure_max_sensitivity"), &CameraAttributesPractical::get_auto_exposure_max_sensitivity); + ClassDB::bind_method(D_METHOD("set_auto_exposure_min_sensitivity", "min_sensitivity"), &CameraAttributesPractical::set_auto_exposure_min_sensitivity); + ClassDB::bind_method(D_METHOD("get_auto_exposure_min_sensitivity"), &CameraAttributesPractical::get_auto_exposure_min_sensitivity); + + ADD_GROUP("DOF Blur", "dof_blur_"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dof_blur_far_enabled"), "set_dof_blur_far_enabled", "is_dof_blur_far_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_far_distance", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp,suffix:m"), "set_dof_blur_far_distance", "get_dof_blur_far_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_far_transition", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp"), "set_dof_blur_far_transition", "get_dof_blur_far_transition"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dof_blur_near_enabled"), "set_dof_blur_near_enabled", "is_dof_blur_near_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_near_distance", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp,suffix:m"), "set_dof_blur_near_distance", "get_dof_blur_near_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_near_transition", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp"), "set_dof_blur_near_transition", "get_dof_blur_near_transition"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_amount", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_dof_blur_amount", "get_dof_blur_amount"); + + ADD_GROUP("Auto Exposure", "auto_exposure_"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "auto_exposure_min_sensitivity", PROPERTY_HINT_RANGE, "0,1600,0.01,or_greater,suffic:ISO"), "set_auto_exposure_min_sensitivity", "get_auto_exposure_min_sensitivity"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "auto_exposure_max_sensitivity", PROPERTY_HINT_RANGE, "0,64000,0.1,or_greater,suffic:ISO"), "set_auto_exposure_max_sensitivity", "get_auto_exposure_max_sensitivity"); +} + +CameraAttributesPractical::CameraAttributesPractical() { + _update_dof_blur(); + _update_exposure(); + set_auto_exposure_min_sensitivity(0.0); + set_auto_exposure_max_sensitivity(800.0); + notify_property_list_changed(); +} + +CameraAttributesPractical::~CameraAttributesPractical() { +} + +////////////////////////////////////////////////////// +/* CameraAttributesPhysical */ + +void CameraAttributesPhysical::set_aperture(float p_aperture) { + exposure_aperture = p_aperture; + _update_exposure(); + _update_frustum(); +} + +float CameraAttributesPhysical::get_aperture() const { + return exposure_aperture; +} + +void CameraAttributesPhysical::set_shutter_speed(float p_shutter_speed) { + exposure_shutter_speed = p_shutter_speed; + _update_exposure(); +} + +float CameraAttributesPhysical::get_shutter_speed() const { + return exposure_shutter_speed; +} + +void CameraAttributesPhysical::set_focal_length(float p_focal_length) { + frustum_focal_length = p_focal_length; + _update_frustum(); + emit_changed(); +} + +float CameraAttributesPhysical::get_focal_length() const { + return frustum_focal_length; +} + +void CameraAttributesPhysical::set_focus_distance(float p_focus_distance) { + frustum_focus_distance = p_focus_distance; + _update_frustum(); +} + +float CameraAttributesPhysical::get_focus_distance() const { + return frustum_focus_distance; +} + +void CameraAttributesPhysical::set_near(real_t p_near) { + frustum_near = p_near; + _update_frustum(); + emit_changed(); +} + +real_t CameraAttributesPhysical::get_near() const { + return frustum_near; +} + +void CameraAttributesPhysical::set_far(real_t p_far) { + frustum_far = p_far; + _update_frustum(); + emit_changed(); +} + +real_t CameraAttributesPhysical::get_far() const { + return frustum_far; +} + +real_t CameraAttributesPhysical::get_fov() const { + return frustum_fov; +} + +void CameraAttributesPhysical::_update_frustum() { + //https://en.wikipedia.org/wiki/Circle_of_confusion#Circle_of_confusion_diameter_limit_based_on_d/1500 + Vector2i sensor_size = Vector2i(36, 24); // Matches high-end DSLR, could be made variable if there is demand. + float CoC = sensor_size.length() / 1500.0; + + frustum_fov = Math::rad_to_deg(2 * atan(sensor_size.height / (2 * frustum_focal_length))); + + // Based on https://en.wikipedia.org/wiki/Depth_of_field. + float u = MAX(frustum_focus_distance * 1000.0, frustum_focal_length + 1.0); // Focus distance expressed in mm and clamped to at least 1 mm away from lens. + float hyperfocal_length = frustum_focal_length + ((frustum_focal_length * frustum_focal_length) / (exposure_aperture * CoC)); + + // This computes the start and end of the depth of field. Anything between these two points has a Circle of Confusino so small + // that it is not picked up by the camera sensors. + // To be properly physically-based, we would run the DoF shader at all depths. To be efficient, we are only running it where the CoC + // will be visible, this introduces some value shifts in the near field that we have to compensate for below. + float near = ((hyperfocal_length * u) / (hyperfocal_length + (u - frustum_focal_length))) / 1000.0; // In meters. + float far = ((hyperfocal_length * u) / (hyperfocal_length - (u - frustum_focal_length))) / 1000.0; // In meters. + float scale = (frustum_focal_length / (u - frustum_focal_length)) * (frustum_focal_length / exposure_aperture); + + bool use_far = (far < frustum_far) && (far > 0.0); + bool use_near = near > frustum_near; + RS::get_singleton()->camera_attributes_set_dof_blur( + get_rid(), + use_far, + u / 1000.0, // Focus distance clampd to focal length expressed in meters. + -1.0, // Negative to tell Bokeh effect to use physically-based scaling. + use_near, + u / 1000.0, + -1.0, + scale / 5.0); // Arbitrary scaling to get close to how much blur there should be. +} + +float CameraAttributesPhysical::calculate_exposure_normalization() const { + const float e = (exposure_aperture * exposure_aperture) * exposure_shutter_speed * (100.0 / exposure_sensitivity); + return 1.0 / (e * 1.2); +} + +void CameraAttributesPhysical::set_auto_exposure_min_exposure_value(float p_min) { + auto_exposure_min = p_min; + _update_auto_exposure(); +} + +float CameraAttributesPhysical::get_auto_exposure_min_exposure_value() const { + return auto_exposure_min; +} + +void CameraAttributesPhysical::set_auto_exposure_max_exposure_value(float p_max) { + auto_exposure_max = p_max; + _update_auto_exposure(); +} + +float CameraAttributesPhysical::get_auto_exposure_max_exposure_value() const { + return auto_exposure_max; +} + +void CameraAttributesPhysical::_update_auto_exposure() { + RS::get_singleton()->camera_attributes_set_auto_exposure( + get_rid(), + auto_exposure_enabled, + pow(2.0, auto_exposure_min) * (12.5 / exposure_sensitivity), // Convert from EV100 to Luminance + pow(2.0, auto_exposure_max) * (12.5 / exposure_sensitivity), // Convert from EV100 to Luminance + auto_exposure_speed, + auto_exposure_scale); + emit_changed(); +} + +void CameraAttributesPhysical::_validate_property(PropertyInfo &property) const { + if (!GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units") && (property.name == "exposure_aperture" || property.name == "exposure_shutter_speed")) { + property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + return; + } +} + +void CameraAttributesPhysical::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_aperture", "aperture"), &CameraAttributesPhysical::set_aperture); + ClassDB::bind_method(D_METHOD("get_aperture"), &CameraAttributesPhysical::get_aperture); + ClassDB::bind_method(D_METHOD("set_shutter_speed", "shutter_speed"), &CameraAttributesPhysical::set_shutter_speed); + ClassDB::bind_method(D_METHOD("get_shutter_speed"), &CameraAttributesPhysical::get_shutter_speed); + + ClassDB::bind_method(D_METHOD("set_focal_length", "focal_length"), &CameraAttributesPhysical::set_focal_length); + ClassDB::bind_method(D_METHOD("get_focal_length"), &CameraAttributesPhysical::get_focal_length); + ClassDB::bind_method(D_METHOD("set_focus_distance", "focus_distance"), &CameraAttributesPhysical::set_focus_distance); + ClassDB::bind_method(D_METHOD("get_focus_distance"), &CameraAttributesPhysical::get_focus_distance); + ClassDB::bind_method(D_METHOD("set_near", "near"), &CameraAttributesPhysical::set_near); + ClassDB::bind_method(D_METHOD("get_near"), &CameraAttributesPhysical::get_near); + ClassDB::bind_method(D_METHOD("set_far", "far"), &CameraAttributesPhysical::set_far); + ClassDB::bind_method(D_METHOD("get_far"), &CameraAttributesPhysical::get_far); + ClassDB::bind_method(D_METHOD("get_fov"), &CameraAttributesPhysical::get_fov); + + ClassDB::bind_method(D_METHOD("set_auto_exposure_max_exposure_value", "exposure_value_max"), &CameraAttributesPhysical::set_auto_exposure_max_exposure_value); + ClassDB::bind_method(D_METHOD("get_auto_exposure_max_exposure_value"), &CameraAttributesPhysical::get_auto_exposure_max_exposure_value); + ClassDB::bind_method(D_METHOD("set_auto_exposure_min_exposure_value", "exposure_value_min"), &CameraAttributesPhysical::set_auto_exposure_min_exposure_value); + ClassDB::bind_method(D_METHOD("get_auto_exposure_min_exposure_value"), &CameraAttributesPhysical::get_auto_exposure_min_exposure_value); + + ADD_GROUP("Frustum", "frustum_"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "frustum_focus_distance", PROPERTY_HINT_RANGE, "0.01,4000.0,0.01,suffix:m"), "set_focus_distance", "get_focus_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "frustum_focal_length", PROPERTY_HINT_RANGE, "1.0,800.0,0.01,exp,suffix:mm"), "set_focal_length", "get_focal_length"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "frustum_near", PROPERTY_HINT_RANGE, "0.001,10,0.001,or_greater,exp,suffix:m"), "set_near", "get_near"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "frustum_far", PROPERTY_HINT_RANGE, "0.01,4000,0.01,or_greater,exp,suffix:m"), "set_far", "get_far"); + + ADD_GROUP("Exposure", "exposure"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "exposure_aperture", PROPERTY_HINT_RANGE, "0.5,64.0,0.01,exp,suffix:f-stop"), "set_aperture", "get_aperture"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "exposure_shutter_speed", PROPERTY_HINT_RANGE, "0.1,8000.0,0.001,suffix:1/s"), "set_shutter_speed", "get_shutter_speed"); + + ADD_GROUP("Auto Exposure", "auto_exposure_"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "auto_exposure_min_exposure_value", PROPERTY_HINT_RANGE, "-16.0,16.0,0.01,or_greater,suffix:EV100"), "set_auto_exposure_min_exposure_value", "get_auto_exposure_min_exposure_value"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "auto_exposure_max_exposure_value", PROPERTY_HINT_RANGE, "-16.0,16.0,0.01,or_greater,suffix:EV100"), "set_auto_exposure_max_exposure_value", "get_auto_exposure_max_exposure_value"); +}; + +CameraAttributesPhysical::CameraAttributesPhysical() { + _update_exposure(); + _update_frustum(); + set_auto_exposure_min_exposure_value(-8); + set_auto_exposure_max_exposure_value(10); // Use a wide range by default to feel more like a real camera. + notify_property_list_changed(); +} + +CameraAttributesPhysical::~CameraAttributesPhysical() { +} diff --git a/scene/resources/camera_attributes.h b/scene/resources/camera_attributes.h new file mode 100644 index 0000000000..c4c783af29 --- /dev/null +++ b/scene/resources/camera_attributes.h @@ -0,0 +1,183 @@ +/*************************************************************************/ +/* camera_attributes.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef CAMERA_ATTRIBUTES_H +#define CAMERA_ATTRIBUTES_H + +#include "core/io/resource.h" +#include "core/templates/rid.h" + +class CameraAttributes : public Resource { + GDCLASS(CameraAttributes, Resource); + +private: + RID camera_attributes; + +protected: + static void _bind_methods(); + void _validate_property(PropertyInfo &p_property) const; + + float exposure_multiplier = 1.0; + float exposure_sensitivity = 100.0; // In ISO. + void _update_exposure(); + + bool auto_exposure_enabled = false; + float auto_exposure_min = 0.01; + float auto_exposure_max = 64.0; + float auto_exposure_speed = 0.5; + float auto_exposure_scale = 0.4; + virtual void _update_auto_exposure(){}; + +public: + virtual RID get_rid() const override; + virtual float calculate_exposure_normalization() const { return 1.0; } + + void set_exposure_multiplier(float p_multiplier); + float get_exposure_multiplier() const; + void set_exposure_sensitivity(float p_sensitivity); + float get_exposure_sensitivity() const; + + void set_auto_exposure_enabled(bool p_enabled); + bool is_auto_exposure_enabled() const; + void set_auto_exposure_speed(float p_auto_exposure_speed); + float get_auto_exposure_speed() const; + void set_auto_exposure_scale(float p_auto_exposure_scale); + float get_auto_exposure_scale() const; + + CameraAttributes(); + ~CameraAttributes(); +}; + +class CameraAttributesPractical : public CameraAttributes { + GDCLASS(CameraAttributesPractical, CameraAttributes); + +private: + // DOF blur + bool dof_blur_far_enabled = false; + float dof_blur_far_distance = 10.0; + float dof_blur_far_transition = 5.0; + + bool dof_blur_near_enabled = false; + float dof_blur_near_distance = 2.0; + float dof_blur_near_transition = 1.0; + + float dof_blur_amount = 0.1; + void _update_dof_blur(); + + virtual void _update_auto_exposure() override; + +protected: + static void _bind_methods(); + void _validate_property(PropertyInfo &p_property) const; + +public: + // DOF blur + void set_dof_blur_far_enabled(bool p_enabled); + bool is_dof_blur_far_enabled() const; + void set_dof_blur_far_distance(float p_distance); + float get_dof_blur_far_distance() const; + void set_dof_blur_far_transition(float p_distance); + float get_dof_blur_far_transition() const; + + void set_dof_blur_near_enabled(bool p_enabled); + bool is_dof_blur_near_enabled() const; + void set_dof_blur_near_distance(float p_distance); + float get_dof_blur_near_distance() const; + void set_dof_blur_near_transition(float p_distance); + float get_dof_blur_near_transition() const; + void set_dof_blur_amount(float p_amount); + float get_dof_blur_amount() const; + + void set_auto_exposure_min_sensitivity(float p_min); + float get_auto_exposure_min_sensitivity() const; + void set_auto_exposure_max_sensitivity(float p_max); + float get_auto_exposure_max_sensitivity() const; + + virtual float calculate_exposure_normalization() const override; + + CameraAttributesPractical(); + ~CameraAttributesPractical(); +}; + +class CameraAttributesPhysical : public CameraAttributes { + GDCLASS(CameraAttributesPhysical, CameraAttributes); + +private: + // Exposure + float exposure_aperture = 16.0; // In f-stops; + float exposure_shutter_speed = 100.0; // In 1 / seconds; + + // Camera properties. + float frustum_focal_length = 35.0; // In millimeters. + float frustum_focus_distance = 10.0; // In Meters. + real_t frustum_near = 0.05; + real_t frustum_far = 4000.0; + real_t frustum_fov = 75.0; + void _update_frustum(); + + virtual void _update_auto_exposure() override; + +protected: + static void _bind_methods(); + void _validate_property(PropertyInfo &property) const; + +public: + void set_aperture(float p_aperture); + float get_aperture() const; + + void set_shutter_speed(float p_shutter_speed); + float get_shutter_speed() const; + + void set_focal_length(float p_focal_length); + float get_focal_length() const; + + void set_focus_distance(float p_focus_distance); + float get_focus_distance() const; + + void set_near(real_t p_near); + real_t get_near() const; + + void set_far(real_t p_far); + real_t get_far() const; + + real_t get_fov() const; + + void set_auto_exposure_min_exposure_value(float p_min); + float get_auto_exposure_min_exposure_value() const; + void set_auto_exposure_max_exposure_value(float p_max); + float get_auto_exposure_max_exposure_value() const; + + virtual float calculate_exposure_normalization() const override; + + CameraAttributesPhysical(); + ~CameraAttributesPhysical(); +}; + +#endif // CAMERA_ATTRIBUTES_H diff --git a/scene/resources/camera_effects.cpp b/scene/resources/camera_effects.cpp deleted file mode 100644 index 97617adbae..0000000000 --- a/scene/resources/camera_effects.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/*************************************************************************/ -/* camera_effects.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "camera_effects.h" - -#include "servers/rendering_server.h" - -RID CameraEffects::get_rid() const { - return camera_effects; -} - -// DOF blur - -void CameraEffects::set_dof_blur_far_enabled(bool p_enabled) { - dof_blur_far_enabled = p_enabled; - _update_dof_blur(); - notify_property_list_changed(); -} - -bool CameraEffects::is_dof_blur_far_enabled() const { - return dof_blur_far_enabled; -} - -void CameraEffects::set_dof_blur_far_distance(float p_distance) { - dof_blur_far_distance = p_distance; - _update_dof_blur(); -} - -float CameraEffects::get_dof_blur_far_distance() const { - return dof_blur_far_distance; -} - -void CameraEffects::set_dof_blur_far_transition(float p_distance) { - dof_blur_far_transition = p_distance; - _update_dof_blur(); -} - -float CameraEffects::get_dof_blur_far_transition() const { - return dof_blur_far_transition; -} - -void CameraEffects::set_dof_blur_near_enabled(bool p_enabled) { - dof_blur_near_enabled = p_enabled; - _update_dof_blur(); - notify_property_list_changed(); -} - -bool CameraEffects::is_dof_blur_near_enabled() const { - return dof_blur_near_enabled; -} - -void CameraEffects::set_dof_blur_near_distance(float p_distance) { - dof_blur_near_distance = p_distance; - _update_dof_blur(); -} - -float CameraEffects::get_dof_blur_near_distance() const { - return dof_blur_near_distance; -} - -void CameraEffects::set_dof_blur_near_transition(float p_distance) { - dof_blur_near_transition = p_distance; - _update_dof_blur(); -} - -float CameraEffects::get_dof_blur_near_transition() const { - return dof_blur_near_transition; -} - -void CameraEffects::set_dof_blur_amount(float p_amount) { - dof_blur_amount = p_amount; - _update_dof_blur(); -} - -float CameraEffects::get_dof_blur_amount() const { - return dof_blur_amount; -} - -void CameraEffects::_update_dof_blur() { - RS::get_singleton()->camera_effects_set_dof_blur( - camera_effects, - dof_blur_far_enabled, - dof_blur_far_distance, - dof_blur_far_transition, - dof_blur_near_enabled, - dof_blur_near_distance, - dof_blur_near_transition, - dof_blur_amount); -} - -// Custom exposure - -void CameraEffects::set_override_exposure_enabled(bool p_enabled) { - override_exposure_enabled = p_enabled; - _update_override_exposure(); - notify_property_list_changed(); -} - -bool CameraEffects::is_override_exposure_enabled() const { - return override_exposure_enabled; -} - -void CameraEffects::set_override_exposure(float p_exposure) { - override_exposure = p_exposure; - _update_override_exposure(); -} - -float CameraEffects::get_override_exposure() const { - return override_exposure; -} - -void CameraEffects::_update_override_exposure() { - RS::get_singleton()->camera_effects_set_custom_exposure( - camera_effects, - override_exposure_enabled, - override_exposure); -} - -// Private methods, constructor and destructor - -void CameraEffects::_validate_property(PropertyInfo &property) const { - if ((!dof_blur_far_enabled && (property.name == "dof_blur_far_distance" || property.name == "dof_blur_far_transition")) || - (!dof_blur_near_enabled && (property.name == "dof_blur_near_distance" || property.name == "dof_blur_near_transition")) || - (!override_exposure_enabled && property.name == "override_exposure")) { - property.usage = PROPERTY_USAGE_NO_EDITOR; - } -} - -void CameraEffects::_bind_methods() { - // DOF blur - - ClassDB::bind_method(D_METHOD("set_dof_blur_far_enabled", "enabled"), &CameraEffects::set_dof_blur_far_enabled); - ClassDB::bind_method(D_METHOD("is_dof_blur_far_enabled"), &CameraEffects::is_dof_blur_far_enabled); - ClassDB::bind_method(D_METHOD("set_dof_blur_far_distance", "distance"), &CameraEffects::set_dof_blur_far_distance); - ClassDB::bind_method(D_METHOD("get_dof_blur_far_distance"), &CameraEffects::get_dof_blur_far_distance); - ClassDB::bind_method(D_METHOD("set_dof_blur_far_transition", "distance"), &CameraEffects::set_dof_blur_far_transition); - ClassDB::bind_method(D_METHOD("get_dof_blur_far_transition"), &CameraEffects::get_dof_blur_far_transition); - - ClassDB::bind_method(D_METHOD("set_dof_blur_near_enabled", "enabled"), &CameraEffects::set_dof_blur_near_enabled); - ClassDB::bind_method(D_METHOD("is_dof_blur_near_enabled"), &CameraEffects::is_dof_blur_near_enabled); - ClassDB::bind_method(D_METHOD("set_dof_blur_near_distance", "distance"), &CameraEffects::set_dof_blur_near_distance); - ClassDB::bind_method(D_METHOD("get_dof_blur_near_distance"), &CameraEffects::get_dof_blur_near_distance); - ClassDB::bind_method(D_METHOD("set_dof_blur_near_transition", "distance"), &CameraEffects::set_dof_blur_near_transition); - ClassDB::bind_method(D_METHOD("get_dof_blur_near_transition"), &CameraEffects::get_dof_blur_near_transition); - - ClassDB::bind_method(D_METHOD("set_dof_blur_amount", "amount"), &CameraEffects::set_dof_blur_amount); - ClassDB::bind_method(D_METHOD("get_dof_blur_amount"), &CameraEffects::get_dof_blur_amount); - - ADD_GROUP("DOF Blur", "dof_blur_"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dof_blur_far_enabled"), "set_dof_blur_far_enabled", "is_dof_blur_far_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_far_distance", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp,suffix:m"), "set_dof_blur_far_distance", "get_dof_blur_far_distance"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_far_transition", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp"), "set_dof_blur_far_transition", "get_dof_blur_far_transition"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dof_blur_near_enabled"), "set_dof_blur_near_enabled", "is_dof_blur_near_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_near_distance", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp,suffix:m"), "set_dof_blur_near_distance", "get_dof_blur_near_distance"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_near_transition", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp"), "set_dof_blur_near_transition", "get_dof_blur_near_transition"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_amount", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_dof_blur_amount", "get_dof_blur_amount"); - - // Override exposure - - ClassDB::bind_method(D_METHOD("set_override_exposure_enabled", "enabled"), &CameraEffects::set_override_exposure_enabled); - ClassDB::bind_method(D_METHOD("is_override_exposure_enabled"), &CameraEffects::is_override_exposure_enabled); - ClassDB::bind_method(D_METHOD("set_override_exposure", "exposure"), &CameraEffects::set_override_exposure); - ClassDB::bind_method(D_METHOD("get_override_exposure"), &CameraEffects::get_override_exposure); - - ADD_GROUP("Override Exposure", "override_"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_exposure_enabled"), "set_override_exposure_enabled", "is_override_exposure_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "override_exposure", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_override_exposure", "get_override_exposure"); -} - -CameraEffects::CameraEffects() { - camera_effects = RS::get_singleton()->camera_effects_create(); - - _update_dof_blur(); - _update_override_exposure(); -} - -CameraEffects::~CameraEffects() { - RS::get_singleton()->free(camera_effects); -} diff --git a/scene/resources/camera_effects.h b/scene/resources/camera_effects.h deleted file mode 100644 index 85ae64cdf5..0000000000 --- a/scene/resources/camera_effects.h +++ /dev/null @@ -1,95 +0,0 @@ -/*************************************************************************/ -/* camera_effects.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef CAMERA_EFFECTS_H -#define CAMERA_EFFECTS_H - -#include "core/io/resource.h" -#include "core/templates/rid.h" - -class CameraEffects : public Resource { - GDCLASS(CameraEffects, Resource); - -private: - RID camera_effects; - - // DOF blur - bool dof_blur_far_enabled = false; - float dof_blur_far_distance = 10.0; - float dof_blur_far_transition = 5.0; - - bool dof_blur_near_enabled = false; - float dof_blur_near_distance = 2.0; - float dof_blur_near_transition = 1.0; - - float dof_blur_amount = 0.1; - void _update_dof_blur(); - - // Override exposure - bool override_exposure_enabled = false; - float override_exposure = 1.0; - void _update_override_exposure(); - -protected: - static void _bind_methods(); - void _validate_property(PropertyInfo &property) const override; - -public: - virtual RID get_rid() const override; - - // DOF blur - void set_dof_blur_far_enabled(bool p_enabled); - bool is_dof_blur_far_enabled() const; - void set_dof_blur_far_distance(float p_distance); - float get_dof_blur_far_distance() const; - void set_dof_blur_far_transition(float p_distance); - float get_dof_blur_far_transition() const; - - void set_dof_blur_near_enabled(bool p_enabled); - bool is_dof_blur_near_enabled() const; - void set_dof_blur_near_distance(float p_distance); - float get_dof_blur_near_distance() const; - void set_dof_blur_near_transition(float p_distance); - float get_dof_blur_near_transition() const; - - void set_dof_blur_amount(float p_amount); - float get_dof_blur_amount() const; - - // Override exposure - void set_override_exposure_enabled(bool p_enabled); - bool is_override_exposure_enabled() const; - void set_override_exposure(float p_exposure); - float get_override_exposure() const; - - CameraEffects(); - ~CameraEffects(); -}; - -#endif // CAMERA_EFFECTS_H diff --git a/scene/resources/canvas_item_material.cpp b/scene/resources/canvas_item_material.cpp index aa6cc4aded..b16059c218 100644 --- a/scene/resources/canvas_item_material.cpp +++ b/scene/resources/canvas_item_material.cpp @@ -227,9 +227,9 @@ bool CanvasItemMaterial::get_particles_anim_loop() const { return particles_anim_loop; } -void CanvasItemMaterial::_validate_property(PropertyInfo &property) const { - if (property.name.begins_with("particles_anim_") && !particles_animation) { - property.usage = PROPERTY_USAGE_NONE; +void CanvasItemMaterial::_validate_property(PropertyInfo &p_property) const { + if (p_property.name.begins_with("particles_anim_") && !particles_animation) { + p_property.usage = PROPERTY_USAGE_NONE; } } diff --git a/scene/resources/canvas_item_material.h b/scene/resources/canvas_item_material.h index 160c67d6b1..7eaf5051d4 100644 --- a/scene/resources/canvas_item_material.h +++ b/scene/resources/canvas_item_material.h @@ -117,7 +117,7 @@ private: protected: static void _bind_methods(); - void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; public: void set_blend_mode(BlendMode p_blend_mode); diff --git a/scene/resources/capsule_shape_3d.cpp b/scene/resources/capsule_shape_3d.cpp index 214004824f..b0454004a0 100644 --- a/scene/resources/capsule_shape_3d.cpp +++ b/scene/resources/capsule_shape_3d.cpp @@ -40,8 +40,8 @@ Vector<Vector3> CapsuleShape3D::get_debug_mesh_lines() const { Vector3 d(0, height * 0.5 - radius, 0); for (int i = 0; i < 360; i++) { - float ra = Math::deg2rad((float)i); - float rb = Math::deg2rad((float)i + 1); + float ra = Math::deg_to_rad((float)i); + float rb = Math::deg_to_rad((float)i + 1); Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius; Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius; diff --git a/scene/resources/capsule_shape_3d.h b/scene/resources/capsule_shape_3d.h index 4c039ab326..ad7f2e5b74 100644 --- a/scene/resources/capsule_shape_3d.h +++ b/scene/resources/capsule_shape_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef CAPSULE_SHAPE_H -#define CAPSULE_SHAPE_H +#ifndef CAPSULE_SHAPE_3D_H +#define CAPSULE_SHAPE_3D_H #include "scene/resources/shape_3d.h" @@ -55,4 +55,4 @@ public: CapsuleShape3D(); }; -#endif // CAPSULE_SHAPE_H +#endif // CAPSULE_SHAPE_3D_H diff --git a/scene/resources/concave_polygon_shape_2d.h b/scene/resources/concave_polygon_shape_2d.h index 0f49a0d80f..d350d753b0 100644 --- a/scene/resources/concave_polygon_shape_2d.h +++ b/scene/resources/concave_polygon_shape_2d.h @@ -52,4 +52,4 @@ public: ConcavePolygonShape2D(); }; -#endif +#endif // CONCAVE_POLYGON_SHAPE_2D_H diff --git a/scene/resources/concave_polygon_shape_3d.h b/scene/resources/concave_polygon_shape_3d.h index a265590edd..68feacfa25 100644 --- a/scene/resources/concave_polygon_shape_3d.h +++ b/scene/resources/concave_polygon_shape_3d.h @@ -77,4 +77,4 @@ public: ConcavePolygonShape3D(); }; -#endif // CONCAVE_POLYGON_SHAPE_H +#endif // CONCAVE_POLYGON_SHAPE_3D_H diff --git a/scene/resources/convex_polygon_shape_3d.h b/scene/resources/convex_polygon_shape_3d.h index 930edb015d..5ca4d2a713 100644 --- a/scene/resources/convex_polygon_shape_3d.h +++ b/scene/resources/convex_polygon_shape_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef CONVEX_POLYGON_SHAPE_H -#define CONVEX_POLYGON_SHAPE_H +#ifndef CONVEX_POLYGON_SHAPE_3D_H +#define CONVEX_POLYGON_SHAPE_3D_H #include "scene/resources/shape_3d.h" @@ -52,4 +52,4 @@ public: ConvexPolygonShape3D(); }; -#endif // CONVEX_POLYGON_SHAPE_H +#endif // CONVEX_POLYGON_SHAPE_3D_H diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp index da26a0261f..0ea5264935 100644 --- a/scene/resources/curve.cpp +++ b/scene/resources/curve.cpp @@ -313,7 +313,7 @@ void Curve::set_max_value(real_t p_max) { emit_signal(SNAME(SIGNAL_RANGE_CHANGED)); } -real_t Curve::interpolate(real_t p_offset) const { +real_t Curve::sample(real_t p_offset) const { if (_points.size() == 0) { return 0; } @@ -333,10 +333,10 @@ real_t Curve::interpolate(real_t p_offset) const { return _points[0].position.y; } - return interpolate_local_nocheck(i, local); + return sample_local_nocheck(i, local); } -real_t Curve::interpolate_local_nocheck(int p_index, real_t p_local_offset) const { +real_t Curve::sample_local_nocheck(int p_index, real_t p_local_offset) const { const Point a = _points[p_index]; const Point b = _points[p_index + 1]; @@ -440,7 +440,7 @@ void Curve::bake() { for (int i = 1; i < _bake_resolution - 1; ++i) { real_t x = i / static_cast<real_t>(_bake_resolution); - real_t y = interpolate(x); + real_t y = sample(x); _baked_cache.write[i] = y; } @@ -459,7 +459,7 @@ void Curve::set_bake_resolution(int p_resolution) { _baked_cache_dirty = true; } -real_t Curve::interpolate_baked(real_t p_offset) const { +real_t Curve::sample_baked(real_t p_offset) const { if (_baked_cache_dirty) { // Last-second bake if not done already const_cast<Curve *>(this)->bake(); @@ -486,7 +486,7 @@ real_t Curve::interpolate_baked(real_t p_offset) const { fi = 0; } - // Interpolate + // Sample if (i + 1 < _baked_cache.size()) { real_t t = fi - i; return Math::lerp(_baked_cache[i], _baked_cache[i + 1], t); @@ -595,8 +595,8 @@ void Curve::_bind_methods() { ClassDB::bind_method(D_METHOD("get_point_position", "index"), &Curve::get_point_position); ClassDB::bind_method(D_METHOD("set_point_value", "index", "y"), &Curve::set_point_value); ClassDB::bind_method(D_METHOD("set_point_offset", "index", "offset"), &Curve::set_point_offset); - ClassDB::bind_method(D_METHOD("interpolate", "offset"), &Curve::interpolate); - ClassDB::bind_method(D_METHOD("interpolate_baked", "offset"), &Curve::interpolate_baked); + ClassDB::bind_method(D_METHOD("sample", "offset"), &Curve::sample); + ClassDB::bind_method(D_METHOD("sample_baked", "offset"), &Curve::sample_baked); ClassDB::bind_method(D_METHOD("get_point_left_tangent", "index"), &Curve::get_point_left_tangent); ClassDB::bind_method(D_METHOD("get_point_right_tangent", "index"), &Curve::get_point_right_tangent); ClassDB::bind_method(D_METHOD("get_point_left_mode", "index"), &Curve::get_point_left_mode); @@ -720,7 +720,7 @@ void Curve2D::clear_points() { } } -Vector2 Curve2D::interpolate(int p_index, const real_t p_offset) const { +Vector2 Curve2D::sample(int p_index, const real_t p_offset) const { int pc = points.size(); ERR_FAIL_COND_V(pc == 0, Vector2()); @@ -738,14 +738,14 @@ Vector2 Curve2D::interpolate(int p_index, const real_t p_offset) const { return p0.bezier_interpolate(p1, p2, p3, p_offset); } -Vector2 Curve2D::interpolatef(real_t p_findex) const { +Vector2 Curve2D::samplef(real_t p_findex) const { if (p_findex < 0) { p_findex = 0; } else if (p_findex >= points.size()) { p_findex = points.size(); } - return interpolate((int)p_findex, Math::fmod(p_findex, (real_t)1.0)); + return sample((int)p_findex, Math::fmod(p_findex, (real_t)1.0)); } void Curve2D::mark_dirty() { @@ -763,7 +763,7 @@ void Curve2D::_bake_segment2d(RBMap<real_t, Vector2> &r_bake, real_t p_begin, re Vector2 nb = (end - mid).normalized(); real_t dp = na.dot(nb); - if (dp < Math::cos(Math::deg2rad(p_tol))) { + if (dp < Math::cos(Math::deg_to_rad(p_tol))) { r_bake[mp] = mid; } @@ -883,7 +883,7 @@ real_t Curve2D::get_baked_length() const { return baked_max_ofs; } -Vector2 Curve2D::interpolate_baked(real_t p_offset, bool p_cubic) const { +Vector2 Curve2D::sample_baked(real_t p_offset, bool p_cubic) const { if (baked_cache_dirty) { _bake(); } @@ -923,7 +923,7 @@ Vector2 Curve2D::interpolate_baked(real_t p_offset, bool p_cubic) const { real_t offset_end = baked_dist_cache[idx + 1]; real_t idx_interval = offset_end - offset_begin; - ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, Vector2(), "failed to find baked segment"); + ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, Vector2(), "Couldn't find baked segment."); real_t frac = (p_offset - offset_begin) / idx_interval; @@ -1167,7 +1167,7 @@ void Curve2D::_get_property_list(List<PropertyInfo> *p_list) const { void Curve2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_point_count"), &Curve2D::get_point_count); ClassDB::bind_method(D_METHOD("set_point_count", "count"), &Curve2D::set_point_count); - ClassDB::bind_method(D_METHOD("add_point", "position", "in", "out", "at_position"), &Curve2D::add_point, DEFVAL(Vector2()), DEFVAL(Vector2()), DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("add_point", "position", "in", "out", "index"), &Curve2D::add_point, DEFVAL(Vector2()), DEFVAL(Vector2()), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("set_point_position", "idx", "position"), &Curve2D::set_point_position); ClassDB::bind_method(D_METHOD("get_point_position", "idx"), &Curve2D::get_point_position); ClassDB::bind_method(D_METHOD("set_point_in", "idx", "position"), &Curve2D::set_point_in); @@ -1176,21 +1176,21 @@ void Curve2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_point_out", "idx"), &Curve2D::get_point_out); ClassDB::bind_method(D_METHOD("remove_point", "idx"), &Curve2D::remove_point); ClassDB::bind_method(D_METHOD("clear_points"), &Curve2D::clear_points); - ClassDB::bind_method(D_METHOD("interpolate", "idx", "t"), &Curve2D::interpolate); - ClassDB::bind_method(D_METHOD("interpolatef", "fofs"), &Curve2D::interpolatef); + ClassDB::bind_method(D_METHOD("sample", "idx", "t"), &Curve2D::sample); + ClassDB::bind_method(D_METHOD("samplef", "fofs"), &Curve2D::samplef); //ClassDB::bind_method(D_METHOD("bake","subdivs"),&Curve2D::bake,DEFVAL(10)); ClassDB::bind_method(D_METHOD("set_bake_interval", "distance"), &Curve2D::set_bake_interval); ClassDB::bind_method(D_METHOD("get_bake_interval"), &Curve2D::get_bake_interval); ClassDB::bind_method(D_METHOD("get_baked_length"), &Curve2D::get_baked_length); - ClassDB::bind_method(D_METHOD("interpolate_baked", "offset", "cubic"), &Curve2D::interpolate_baked, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("sample_baked", "offset", "cubic"), &Curve2D::sample_baked, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_baked_points"), &Curve2D::get_baked_points); ClassDB::bind_method(D_METHOD("get_closest_point", "to_point"), &Curve2D::get_closest_point); ClassDB::bind_method(D_METHOD("get_closest_offset", "to_point"), &Curve2D::get_closest_offset); ClassDB::bind_method(D_METHOD("tessellate", "max_stages", "tolerance_degrees"), &Curve2D::tessellate, DEFVAL(5), DEFVAL(4)); ClassDB::bind_method(D_METHOD("_get_data"), &Curve2D::_get_data); - ClassDB::bind_method(D_METHOD("_set_data"), &Curve2D::_set_data); + ClassDB::bind_method(D_METHOD("_set_data", "data"), &Curve2D::_set_data); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bake_interval", PROPERTY_HINT_RANGE, "0.01,512,0.01"), "set_bake_interval", "get_bake_interval"); ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); @@ -1309,7 +1309,7 @@ void Curve3D::clear_points() { } } -Vector3 Curve3D::interpolate(int p_index, real_t p_offset) const { +Vector3 Curve3D::sample(int p_index, real_t p_offset) const { int pc = points.size(); ERR_FAIL_COND_V(pc == 0, Vector3()); @@ -1327,14 +1327,14 @@ Vector3 Curve3D::interpolate(int p_index, real_t p_offset) const { return p0.bezier_interpolate(p1, p2, p3, p_offset); } -Vector3 Curve3D::interpolatef(real_t p_findex) const { +Vector3 Curve3D::samplef(real_t p_findex) const { if (p_findex < 0) { p_findex = 0; } else if (p_findex >= points.size()) { p_findex = points.size(); } - return interpolate((int)p_findex, Math::fmod(p_findex, (real_t)1.0)); + return sample((int)p_findex, Math::fmod(p_findex, (real_t)1.0)); } void Curve3D::mark_dirty() { @@ -1352,7 +1352,7 @@ void Curve3D::_bake_segment3d(RBMap<real_t, Vector3> &r_bake, real_t p_begin, re Vector3 nb = (end - mid).normalized(); real_t dp = na.dot(nb); - if (dp < Math::cos(Math::deg2rad(p_tol))) { + if (dp < Math::cos(Math::deg_to_rad(p_tol))) { r_bake[mp] = mid; } if (p_depth < p_max_depth) { @@ -1536,7 +1536,7 @@ real_t Curve3D::get_baked_length() const { return baked_max_ofs; } -Vector3 Curve3D::interpolate_baked(real_t p_offset, bool p_cubic) const { +Vector3 Curve3D::sample_baked(real_t p_offset, bool p_cubic) const { if (baked_cache_dirty) { _bake(); } @@ -1576,7 +1576,7 @@ Vector3 Curve3D::interpolate_baked(real_t p_offset, bool p_cubic) const { real_t offset_end = baked_dist_cache[idx + 1]; real_t idx_interval = offset_end - offset_begin; - ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, Vector3(), "failed to find baked segment"); + ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, Vector3(), "Couldn't find baked segment."); real_t frac = (p_offset - offset_begin) / idx_interval; @@ -1589,7 +1589,7 @@ Vector3 Curve3D::interpolate_baked(real_t p_offset, bool p_cubic) const { } } -real_t Curve3D::interpolate_baked_tilt(real_t p_offset) const { +real_t Curve3D::sample_baked_tilt(real_t p_offset) const { if (baked_cache_dirty) { _bake(); } @@ -1629,14 +1629,14 @@ real_t Curve3D::interpolate_baked_tilt(real_t p_offset) const { real_t offset_end = baked_dist_cache[idx + 1]; real_t idx_interval = offset_end - offset_begin; - ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, 0, "failed to find baked segment"); + ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, 0, "Couldn't find baked segment."); real_t frac = (p_offset - offset_begin) / idx_interval; return Math::lerp(r[idx], r[idx + 1], (real_t)frac); } -Vector3 Curve3D::interpolate_baked_up_vector(real_t p_offset, bool p_apply_tilt) const { +Vector3 Curve3D::sample_baked_up_vector(real_t p_offset, bool p_apply_tilt) const { if (baked_cache_dirty) { _bake(); } @@ -1671,7 +1671,7 @@ Vector3 Curve3D::interpolate_baked_up_vector(real_t p_offset, bool p_apply_tilt) real_t offset_end = baked_dist_cache[idx + 1]; real_t idx_interval = offset_end - offset_begin; - ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, Vector3(0, 1, 0), "failed to find baked segment"); + ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, Vector3(0, 1, 0), "Couldn't find baked segment."); real_t frac = (p_offset - offset_begin) / idx_interval; @@ -1972,7 +1972,7 @@ void Curve3D::_get_property_list(List<PropertyInfo> *p_list) const { void Curve3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_point_count"), &Curve3D::get_point_count); ClassDB::bind_method(D_METHOD("set_point_count", "count"), &Curve3D::set_point_count); - ClassDB::bind_method(D_METHOD("add_point", "position", "in", "out", "at_position"), &Curve3D::add_point, DEFVAL(Vector3()), DEFVAL(Vector3()), DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("add_point", "position", "in", "out", "index"), &Curve3D::add_point, DEFVAL(Vector3()), DEFVAL(Vector3()), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("set_point_position", "idx", "position"), &Curve3D::set_point_position); ClassDB::bind_method(D_METHOD("get_point_position", "idx"), &Curve3D::get_point_position); ClassDB::bind_method(D_METHOD("set_point_tilt", "idx", "tilt"), &Curve3D::set_point_tilt); @@ -1983,8 +1983,8 @@ void Curve3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_point_out", "idx"), &Curve3D::get_point_out); ClassDB::bind_method(D_METHOD("remove_point", "idx"), &Curve3D::remove_point); ClassDB::bind_method(D_METHOD("clear_points"), &Curve3D::clear_points); - ClassDB::bind_method(D_METHOD("interpolate", "idx", "t"), &Curve3D::interpolate); - ClassDB::bind_method(D_METHOD("interpolatef", "fofs"), &Curve3D::interpolatef); + ClassDB::bind_method(D_METHOD("sample", "idx", "t"), &Curve3D::sample); + ClassDB::bind_method(D_METHOD("samplef", "fofs"), &Curve3D::samplef); //ClassDB::bind_method(D_METHOD("bake","subdivs"),&Curve3D::bake,DEFVAL(10)); ClassDB::bind_method(D_METHOD("set_bake_interval", "distance"), &Curve3D::set_bake_interval); ClassDB::bind_method(D_METHOD("get_bake_interval"), &Curve3D::get_bake_interval); @@ -1992,8 +1992,8 @@ void Curve3D::_bind_methods() { ClassDB::bind_method(D_METHOD("is_up_vector_enabled"), &Curve3D::is_up_vector_enabled); ClassDB::bind_method(D_METHOD("get_baked_length"), &Curve3D::get_baked_length); - ClassDB::bind_method(D_METHOD("interpolate_baked", "offset", "cubic"), &Curve3D::interpolate_baked, DEFVAL(false)); - ClassDB::bind_method(D_METHOD("interpolate_baked_up_vector", "offset", "apply_tilt"), &Curve3D::interpolate_baked_up_vector, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("sample_baked", "offset", "cubic"), &Curve3D::sample_baked, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("sample_baked_up_vector", "offset", "apply_tilt"), &Curve3D::sample_baked_up_vector, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_baked_points"), &Curve3D::get_baked_points); ClassDB::bind_method(D_METHOD("get_baked_tilts"), &Curve3D::get_baked_tilts); ClassDB::bind_method(D_METHOD("get_baked_up_vectors"), &Curve3D::get_baked_up_vectors); @@ -2002,7 +2002,7 @@ void Curve3D::_bind_methods() { ClassDB::bind_method(D_METHOD("tessellate", "max_stages", "tolerance_degrees"), &Curve3D::tessellate, DEFVAL(5), DEFVAL(4)); ClassDB::bind_method(D_METHOD("_get_data"), &Curve3D::_get_data); - ClassDB::bind_method(D_METHOD("_set_data"), &Curve3D::_set_data); + ClassDB::bind_method(D_METHOD("_set_data", "data"), &Curve3D::_set_data); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bake_interval", PROPERTY_HINT_RANGE, "0.01,512,0.01"), "set_bake_interval", "get_bake_interval"); ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); diff --git a/scene/resources/curve.h b/scene/resources/curve.h index 08807b1b6e..88b6dda096 100644 --- a/scene/resources/curve.h +++ b/scene/resources/curve.h @@ -100,8 +100,8 @@ public: real_t get_max_value() const { return _max_value; } void set_max_value(real_t p_max); - real_t interpolate(real_t p_offset) const; - real_t interpolate_local_nocheck(int p_index, real_t p_local_offset) const; + real_t sample(real_t p_offset) const; + real_t sample_local_nocheck(int p_index, real_t p_local_offset) const; void clean_dupes(); @@ -123,7 +123,7 @@ public: void bake(); int get_bake_resolution() const { return _bake_resolution; } void set_bake_resolution(int p_resolution); - real_t interpolate_baked(real_t p_offset) const; + real_t sample_baked(real_t p_offset) const; void ensure_default_setup(real_t p_min, real_t p_max); @@ -208,14 +208,14 @@ public: void remove_point(int p_index); void clear_points(); - Vector2 interpolate(int p_index, real_t p_offset) const; - Vector2 interpolatef(real_t p_findex) const; + Vector2 sample(int p_index, real_t p_offset) const; + Vector2 samplef(real_t p_findex) const; void set_bake_interval(real_t p_tolerance); real_t get_bake_interval() const; real_t get_baked_length() const; - Vector2 interpolate_baked(real_t p_offset, bool p_cubic = false) const; + Vector2 sample_baked(real_t p_offset, bool p_cubic = false) const; PackedVector2Array get_baked_points() const; //useful for going through Vector2 get_closest_point(const Vector2 &p_to_point) const; real_t get_closest_offset(const Vector2 &p_to_point) const; @@ -285,8 +285,8 @@ public: void remove_point(int p_index); void clear_points(); - Vector3 interpolate(int p_index, real_t p_offset) const; - Vector3 interpolatef(real_t p_findex) const; + Vector3 sample(int p_index, real_t p_offset) const; + Vector3 samplef(real_t p_findex) const; void set_bake_interval(real_t p_tolerance); real_t get_bake_interval() const; @@ -294,9 +294,9 @@ public: bool is_up_vector_enabled() const; real_t get_baked_length() const; - Vector3 interpolate_baked(real_t p_offset, bool p_cubic = false) const; - real_t interpolate_baked_tilt(real_t p_offset) const; - Vector3 interpolate_baked_up_vector(real_t p_offset, bool p_apply_tilt = false) const; + Vector3 sample_baked(real_t p_offset, bool p_cubic = false) const; + real_t sample_baked_tilt(real_t p_offset) const; + Vector3 sample_baked_up_vector(real_t p_offset, bool p_apply_tilt = false) const; PackedVector3Array get_baked_points() const; //useful for going through Vector<real_t> get_baked_tilts() const; //useful for going through PackedVector3Array get_baked_up_vectors() const; diff --git a/scene/resources/cylinder_shape_3d.cpp b/scene/resources/cylinder_shape_3d.cpp index 345df5ffed..a5951db8ea 100644 --- a/scene/resources/cylinder_shape_3d.cpp +++ b/scene/resources/cylinder_shape_3d.cpp @@ -40,8 +40,8 @@ Vector<Vector3> CylinderShape3D::get_debug_mesh_lines() const { Vector3 d(0, height * 0.5, 0); for (int i = 0; i < 360; i++) { - float ra = Math::deg2rad((float)i); - float rb = Math::deg2rad((float)i + 1); + float ra = Math::deg_to_rad((float)i); + float rb = Math::deg_to_rad((float)i + 1); Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius; Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius; diff --git a/scene/resources/cylinder_shape_3d.h b/scene/resources/cylinder_shape_3d.h index 65427423c8..f554358044 100644 --- a/scene/resources/cylinder_shape_3d.h +++ b/scene/resources/cylinder_shape_3d.h @@ -54,4 +54,4 @@ public: CylinderShape3D(); }; -#endif // CYLINDER_SHAPE_H +#endif // CYLINDER_SHAPE_3D_H diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index 520a0a04ed..f03e3813cc 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -35,6 +35,7 @@ #include "default_theme_icons.gen.h" #include "scene/resources/font.h" #include "scene/resources/theme.h" +#include "scene/theme/theme_db.h" #include "servers/text_server.h" #include "modules/modules_enabled.gen.h" // For svg. @@ -42,6 +43,8 @@ #include "modules/svg/image_loader_svg.h" #endif +static const int default_font_size = 16; + static float scale = 1.0; static const int default_margin = 4; @@ -50,10 +53,7 @@ static const int default_corner_radius = 3; static Ref<StyleBoxFlat> make_flat_stylebox(Color p_color, float p_margin_left = default_margin, float p_margin_top = default_margin, float p_margin_right = default_margin, float p_margin_bottom = default_margin, int p_corner_radius = default_corner_radius, bool p_draw_center = true, int p_border_width = 0) { Ref<StyleBoxFlat> style(memnew(StyleBoxFlat)); style->set_bg_color(p_color); - style->set_default_margin(SIDE_LEFT, p_margin_left * scale); - style->set_default_margin(SIDE_RIGHT, p_margin_right * scale); - style->set_default_margin(SIDE_BOTTOM, p_margin_bottom * scale); - style->set_default_margin(SIDE_TOP, p_margin_top * scale); + style->set_default_margin_individual(p_margin_left * scale, p_margin_top * scale, p_margin_right * scale, p_margin_bottom * scale); style->set_corner_radius_all(p_corner_radius); style->set_anti_aliased(true); @@ -84,7 +84,7 @@ static Ref<ImageTexture> generate_icon(int p_index) { // with integer scales. const bool upsample = !Math::is_equal_approx(Math::round(scale), scale); ImageLoaderSVG img_loader; - img_loader.create_image_from_string(img, default_theme_icons_sources[p_index], scale, upsample, false); + img_loader.create_image_from_string(img, default_theme_icons_sources[p_index], scale, upsample, HashMap<Color, Color>()); #endif return ImageTexture::create_from_image(img); @@ -92,12 +92,7 @@ static Ref<ImageTexture> generate_icon(int p_index) { static Ref<StyleBox> make_empty_stylebox(float p_margin_left = -1, float p_margin_top = -1, float p_margin_right = -1, float p_margin_bottom = -1) { Ref<StyleBox> style(memnew(StyleBoxEmpty)); - - style->set_default_margin(SIDE_LEFT, p_margin_left * scale); - style->set_default_margin(SIDE_RIGHT, p_margin_right * scale); - style->set_default_margin(SIDE_BOTTOM, p_margin_bottom * scale); - style->set_default_margin(SIDE_TOP, p_margin_top * scale); - + style->set_default_margin_individual(p_margin_left * scale, p_margin_top * scale, p_margin_right * scale, p_margin_bottom * scale); return style; } @@ -138,7 +133,6 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const // Panel theme->set_stylebox("panel", "Panel", make_flat_stylebox(style_normal_color, 0, 0, 0, 0)); - theme->set_stylebox("panel_fg", "Panel", make_flat_stylebox(style_normal_color, 0, 0, 0, 0)); // Button @@ -177,6 +171,27 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_constant("h_separation", "Button", 2 * scale); + // MenuBar + theme->set_stylebox("normal", "MenuBar", button_normal); + theme->set_stylebox("hover", "MenuBar", button_hover); + theme->set_stylebox("pressed", "MenuBar", button_pressed); + theme->set_stylebox("disabled", "MenuBar", button_disabled); + theme->set_stylebox("focus", "MenuBar", focus); + + theme->set_font("font", "MenuBar", Ref<Font>()); + theme->set_font_size("font_size", "MenuBar", -1); + theme->set_constant("outline_size", "MenuBar", 0 * scale); + + theme->set_color("font_color", "MenuBar", control_font_color); + theme->set_color("font_pressed_color", "MenuBar", control_font_pressed_color); + theme->set_color("font_hover_color", "MenuBar", control_font_hover_color); + theme->set_color("font_focus_color", "MenuBar", control_font_focus_color); + theme->set_color("font_hover_pressed_color", "MenuBar", control_font_pressed_color); + theme->set_color("font_disabled_color", "MenuBar", control_font_disabled_color); + theme->set_color("font_outline_color", "MenuBar", Color(1, 1, 1)); + + theme->set_constant("h_separation", "MenuBar", 4 * scale); + // LinkButton theme->set_stylebox("focus", "LinkButton", focus); @@ -224,6 +239,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("font_color", "OptionButton", control_font_color); theme->set_color("font_pressed_color", "OptionButton", control_font_pressed_color); theme->set_color("font_hover_color", "OptionButton", control_font_hover_color); + theme->set_color("font_hover_pressed_color", "OptionButton", control_font_pressed_color); theme->set_color("font_focus_color", "OptionButton", control_font_focus_color); theme->set_color("font_disabled_color", "OptionButton", control_font_disabled_color); theme->set_color("font_outline_color", "OptionButton", Color(1, 1, 1)); @@ -231,6 +247,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_constant("h_separation", "OptionButton", 2 * scale); theme->set_constant("arrow_margin", "OptionButton", 4 * scale); theme->set_constant("outline_size", "OptionButton", 0); + theme->set_constant("modulate_arrow", "OptionButton", false); // MenuButton @@ -256,15 +273,9 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const // CheckBox Ref<StyleBox> cbx_empty = memnew(StyleBoxEmpty); - cbx_empty->set_default_margin(SIDE_LEFT, 4 * scale); - cbx_empty->set_default_margin(SIDE_RIGHT, 4 * scale); - cbx_empty->set_default_margin(SIDE_TOP, 4 * scale); - cbx_empty->set_default_margin(SIDE_BOTTOM, 4 * scale); + cbx_empty->set_default_margin_all(4 * scale); Ref<StyleBox> cbx_focus = focus; - cbx_focus->set_default_margin(SIDE_LEFT, 4 * scale); - cbx_focus->set_default_margin(SIDE_RIGHT, 4 * scale); - cbx_focus->set_default_margin(SIDE_TOP, 4 * scale); - cbx_focus->set_default_margin(SIDE_BOTTOM, 4 * scale); + cbx_focus->set_default_margin_all(4 * scale); theme->set_stylebox("normal", "CheckBox", cbx_empty); theme->set_stylebox("pressed", "CheckBox", cbx_empty); @@ -294,16 +305,13 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("font_outline_color", "CheckBox", Color(1, 1, 1)); theme->set_constant("h_separation", "CheckBox", 4 * scale); - theme->set_constant("check_v_adjust", "CheckBox", 0 * scale); + theme->set_constant("check_v_offset", "CheckBox", 0 * scale); theme->set_constant("outline_size", "CheckBox", 0); // CheckButton Ref<StyleBox> cb_empty = memnew(StyleBoxEmpty); - cb_empty->set_default_margin(SIDE_LEFT, 6 * scale); - cb_empty->set_default_margin(SIDE_RIGHT, 6 * scale); - cb_empty->set_default_margin(SIDE_TOP, 4 * scale); - cb_empty->set_default_margin(SIDE_BOTTOM, 4 * scale); + cb_empty->set_default_margin_individual(6 * scale, 4 * scale, 6 * scale, 4 * scale); theme->set_stylebox("normal", "CheckButton", cb_empty); theme->set_stylebox("pressed", "CheckButton", cb_empty); @@ -312,15 +320,15 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_stylebox("hover_pressed", "CheckButton", cb_empty); theme->set_stylebox("focus", "CheckButton", focus); - theme->set_icon("on", "CheckButton", icons["toggle_on"]); - theme->set_icon("on_disabled", "CheckButton", icons["toggle_on_disabled"]); - theme->set_icon("off", "CheckButton", icons["toggle_off"]); - theme->set_icon("off_disabled", "CheckButton", icons["toggle_off_disabled"]); + theme->set_icon("checked", "CheckButton", icons["toggle_on"]); + theme->set_icon("checked_disabled", "CheckButton", icons["toggle_on_disabled"]); + theme->set_icon("unchecked", "CheckButton", icons["toggle_off"]); + theme->set_icon("unchecked_disabled", "CheckButton", icons["toggle_off_disabled"]); - theme->set_icon("on_mirrored", "CheckButton", icons["toggle_on_mirrored"]); - theme->set_icon("on_disabled_mirrored", "CheckButton", icons["toggle_on_disabled_mirrored"]); - theme->set_icon("off_mirrored", "CheckButton", icons["toggle_off_mirrored"]); - theme->set_icon("off_disabled_mirrored", "CheckButton", icons["toggle_off_disabled_mirrored"]); + theme->set_icon("checked_mirrored", "CheckButton", icons["toggle_on_mirrored"]); + theme->set_icon("checked_disabled_mirrored", "CheckButton", icons["toggle_on_disabled_mirrored"]); + theme->set_icon("unchecked_mirrored", "CheckButton", icons["toggle_off_mirrored"]); + theme->set_icon("unchecked_disabled_mirrored", "CheckButton", icons["toggle_off_disabled_mirrored"]); theme->set_font("font", "CheckButton", Ref<Font>()); theme->set_font_size("font_size", "CheckButton", -1); @@ -334,7 +342,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("font_outline_color", "CheckButton", Color(1, 1, 1)); theme->set_constant("h_separation", "CheckButton", 4 * scale); - theme->set_constant("check_v_adjust", "CheckButton", 0 * scale); + theme->set_constant("check_v_offset", "CheckButton", 0 * scale); theme->set_constant("outline_size", "CheckButton", 0); // Label @@ -399,8 +407,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const // ProgressBar - theme->set_stylebox("bg", "ProgressBar", make_flat_stylebox(style_disabled_color, 2, 2, 2, 2, 6)); - theme->set_stylebox("fg", "ProgressBar", make_flat_stylebox(style_progress_color, 2, 2, 2, 2, 6)); + theme->set_stylebox("background", "ProgressBar", make_flat_stylebox(style_disabled_color, 2, 2, 2, 2, 6)); + theme->set_stylebox("fill", "ProgressBar", make_flat_stylebox(style_progress_color, 2, 2, 2, 2, 6)); theme->set_font("font", "ProgressBar", Ref<Font>()); theme->set_font_size("font_size", "ProgressBar", -1); @@ -564,7 +572,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const Ref<StyleBoxEmpty> empty; empty.instantiate(); - theme->set_stylebox("bg", "ScrollContainer", empty); + theme->set_stylebox("panel", "ScrollContainer", empty); // Window @@ -585,12 +593,9 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const // Dialogs - theme->set_constant("margin", "Dialogs", 8 * scale); - theme->set_constant("button_margin", "Dialogs", 32 * scale); - - // AcceptDialog - - theme->set_stylebox("panel", "AcceptDialog", make_flat_stylebox(style_popup_color, 0, 0, 0, 0)); + // AcceptDialog is currently the base dialog, so this defines styles for all extending nodes. + theme->set_stylebox("panel", "AcceptDialog", make_flat_stylebox(style_popup_color, 8 * scale, 8 * scale, 8 * scale, 8 * scale)); + theme->set_constant("buttons_separation", "AcceptDialog", 10 * scale); // File Dialog @@ -601,9 +606,9 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("toggle_hidden", "FileDialog", icons["visibility_visible"]); theme->set_icon("folder", "FileDialog", icons["folder"]); theme->set_icon("file", "FileDialog", icons["file"]); - theme->set_color("folder_icon_modulate", "FileDialog", Color(1, 1, 1)); - theme->set_color("file_icon_modulate", "FileDialog", Color(1, 1, 1)); - theme->set_color("files_disabled", "FileDialog", Color(1, 1, 1, 0.25)); + theme->set_color("folder_icon_color", "FileDialog", Color(1, 1, 1)); + theme->set_color("file_icon_color", "FileDialog", Color(1, 1, 1)); + theme->set_color("file_disabled_color", "FileDialog", Color(1, 1, 1, 0.25)); // Popup @@ -618,16 +623,10 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const Ref<StyleBoxLine> separator_horizontal = memnew(StyleBoxLine); separator_horizontal->set_thickness(Math::round(scale)); separator_horizontal->set_color(style_separator_color); - separator_horizontal->set_default_margin(SIDE_LEFT, default_margin); - separator_horizontal->set_default_margin(SIDE_TOP, 0); - separator_horizontal->set_default_margin(SIDE_RIGHT, default_margin); - separator_horizontal->set_default_margin(SIDE_BOTTOM, 0); + separator_horizontal->set_default_margin_individual(default_margin, 0, default_margin, 0); Ref<StyleBoxLine> separator_vertical = separator_horizontal->duplicate(); separator_vertical->set_vertical(true); - separator_vertical->set_default_margin(SIDE_LEFT, 0); - separator_vertical->set_default_margin(SIDE_TOP, default_margin); - separator_vertical->set_default_margin(SIDE_RIGHT, 0); - separator_vertical->set_default_margin(SIDE_BOTTOM, default_margin); + separator_vertical->set_default_margin_individual(0, default_margin, 0, default_margin); // Always display a border for PopupMenus so they can be distinguished from their background. Ref<StyleBoxFlat> style_popup_panel = make_flat_stylebox(style_popup_color); @@ -644,9 +643,13 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_stylebox("labeled_separator_right", "PopupMenu", separator_horizontal); theme->set_icon("checked", "PopupMenu", icons["checked"]); + theme->set_icon("checked_disabled", "PopupMenu", icons["checked"]); theme->set_icon("unchecked", "PopupMenu", icons["unchecked"]); + theme->set_icon("unchecked_disabled", "PopupMenu", icons["unchecked"]); theme->set_icon("radio_checked", "PopupMenu", icons["radio_checked"]); + theme->set_icon("radio_checked_disabled", "PopupMenu", icons["radio_checked"]); theme->set_icon("radio_unchecked", "PopupMenu", icons["radio_unchecked"]); + theme->set_icon("radio_unchecked_disabled", "PopupMenu", icons["radio_unchecked"]); theme->set_icon("submenu", "PopupMenu", icons["popup_menu_arrow_right"]); theme->set_icon("submenu_mirrored", "PopupMenu", icons["popup_menu_arrow_left"]); @@ -663,6 +666,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("font_outline_color", "PopupMenu", Color(1, 1, 1)); theme->set_color("font_separator_outline_color", "PopupMenu", Color(1, 1, 1)); + theme->set_constant("indent", "PopupMenu", 10 * scale); theme->set_constant("h_separation", "PopupMenu", 4 * scale); theme->set_constant("v_separation", "PopupMenu", 4 * scale); theme->set_constant("outline_size", "PopupMenu", 0); @@ -703,14 +707,15 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("resizer_color", "GraphNode", control_font_color); theme->set_constant("separation", "GraphNode", 2 * scale); theme->set_constant("title_offset", "GraphNode", 26 * scale); + theme->set_constant("title_h_offset", "GraphNode", 0); theme->set_constant("close_offset", "GraphNode", 22 * scale); theme->set_constant("close_h_offset", "GraphNode", 22 * scale); theme->set_constant("port_offset", "GraphNode", 0); // Tree - theme->set_stylebox("bg", "Tree", make_flat_stylebox(style_normal_color, 4, 4, 4, 5)); - theme->set_stylebox("bg_focus", "Tree", focus); + theme->set_stylebox("panel", "Tree", make_flat_stylebox(style_normal_color, 4, 4, 4, 5)); + theme->set_stylebox("focus", "Tree", focus); theme->set_stylebox("selected", "Tree", make_flat_stylebox(style_selected_color)); theme->set_stylebox("selected_focus", "Tree", make_flat_stylebox(style_selected_color)); theme->set_stylebox("cursor", "Tree", focus); @@ -741,7 +746,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("font_selected_color", "Tree", control_font_pressed_color); theme->set_color("font_outline_color", "Tree", Color(1, 1, 1)); theme->set_color("guide_color", "Tree", Color(0.7, 0.7, 0.7, 0.25)); - theme->set_color("drop_position_color", "Tree", Color(1, 0.3, 0.2)); + theme->set_color("drop_position_color", "Tree", Color(1, 1, 1)); theme->set_color("relationship_line_color", "Tree", Color(0.27, 0.27, 0.27)); theme->set_color("parent_hl_line_color", "Tree", Color(0.27, 0.27, 0.27)); theme->set_color("children_hl_line_color", "Tree", Color(0.27, 0.27, 0.27)); @@ -763,8 +768,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const // ItemList - theme->set_stylebox("bg", "ItemList", make_flat_stylebox(style_normal_color)); - theme->set_stylebox("bg_focus", "ItemList", focus); + theme->set_stylebox("panel", "ItemList", make_flat_stylebox(style_normal_color)); + theme->set_stylebox("focus", "ItemList", focus); theme->set_constant("h_separation", "ItemList", 4); theme->set_constant("v_separation", "ItemList", 2); theme->set_constant("icon_margin", "ItemList", 4); @@ -776,7 +781,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("font_color", "ItemList", control_font_lower_color); theme->set_color("font_selected_color", "ItemList", control_font_pressed_color); theme->set_color("font_outline_color", "ItemList", Color(1, 1, 1)); - theme->set_color("guide_color", "ItemList", Color(0, 0, 0, 0.1)); + theme->set_color("guide_color", "ItemList", Color(0.7, 0.7, 0.7, 0.25)); theme->set_stylebox("selected", "ItemList", make_flat_stylebox(style_selected_color)); theme->set_stylebox("selected_focus", "ItemList", make_flat_stylebox(style_selected_color)); theme->set_stylebox("cursor", "ItemList", focus); @@ -801,6 +806,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_stylebox("tab_unselected", "TabContainer", style_tab_unselected); theme->set_stylebox("tab_disabled", "TabContainer", style_tab_disabled); theme->set_stylebox("panel", "TabContainer", make_flat_stylebox(style_normal_color, 0, 0, 0, 0)); + theme->set_stylebox("tabbar_background", "TabContainer", make_empty_stylebox(0, 0, 0, 0)); theme->set_icon("increment", "TabContainer", icons["scroll_button_right"]); theme->set_icon("increment_highlight", "TabContainer", icons["scroll_button_right_hl"]); @@ -967,9 +973,12 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const // Containers + theme->set_icon("h_grabber", "SplitContainer", icons["hsplitter"]); + theme->set_icon("v_grabber", "SplitContainer", icons["vsplitter"]); theme->set_icon("grabber", "VSplitContainer", icons["vsplitter"]); theme->set_icon("grabber", "HSplitContainer", icons["hsplitter"]); + theme->set_constant("separation", "BoxContainer", 4 * scale); theme->set_constant("separation", "HBoxContainer", 4 * scale); theme->set_constant("separation", "VBoxContainer", 4 * scale); theme->set_constant("margin_left", "MarginContainer", 0 * scale); @@ -978,10 +987,17 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_constant("margin_bottom", "MarginContainer", 0 * scale); theme->set_constant("h_separation", "GridContainer", 4 * scale); theme->set_constant("v_separation", "GridContainer", 4 * scale); + theme->set_constant("separation", "SplitContainer", 12 * scale); theme->set_constant("separation", "HSplitContainer", 12 * scale); theme->set_constant("separation", "VSplitContainer", 12 * scale); + theme->set_constant("minimum_grab_thickness", "SplitContainer", 6 * scale); + theme->set_constant("minimum_grab_thickness", "HSplitContainer", 6 * scale); + theme->set_constant("minimum_grab_thickness", "VSplitContainer", 6 * scale); + theme->set_constant("autohide", "SplitContainer", 1 * scale); theme->set_constant("autohide", "HSplitContainer", 1 * scale); theme->set_constant("autohide", "VSplitContainer", 1 * scale); + theme->set_constant("h_separation", "FlowContainer", 4 * scale); + theme->set_constant("v_separation", "FlowContainer", 4 * scale); theme->set_constant("h_separation", "HFlowContainer", 4 * scale); theme->set_constant("v_separation", "HFlowContainer", 4 * scale); theme->set_constant("h_separation", "VFlowContainer", 4 * scale); @@ -1024,7 +1040,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const default_style = make_flat_stylebox(Color(1, 0.365, 0.365), 4, 4, 4, 4, 0, false, 2); } -void make_default_theme(float p_scale, Ref<Font> p_font, TextServer::SubpixelPositioning p_font_subpixel, TextServer::Hinting p_font_hinting, bool p_font_antialiased, bool p_font_msdf, bool p_font_generate_mipmaps) { +void make_default_theme(float p_scale, Ref<Font> p_font, TextServer::SubpixelPositioning p_font_subpixel, TextServer::Hinting p_font_hinting, TextServer::FontAntialiasing p_font_antialiasing, bool p_font_msdf, bool p_font_generate_mipmaps) { Ref<Theme> t; t.instantiate(); @@ -1048,7 +1064,7 @@ void make_default_theme(float p_scale, Ref<Font> p_font, TextServer::SubpixelPos dynamic_font->set_data_ptr(_font_OpenSans_SemiBold, _font_OpenSans_SemiBold_size); dynamic_font->set_subpixel_positioning(p_font_subpixel); dynamic_font->set_hinting(p_font_hinting); - dynamic_font->set_antialiased(p_font_antialiased); + dynamic_font->set_antialiasing(p_font_antialiasing); dynamic_font->set_multichannel_signed_distance_field(p_font_msdf); dynamic_font->set_generate_mipmaps(p_font_generate_mipmaps); @@ -1072,18 +1088,11 @@ void make_default_theme(float p_scale, Ref<Font> p_font, TextServer::SubpixelPos fill_default_theme(t, default_font, bold_font, bold_italics_font, italics_font, default_icon, default_style, default_scale); - Theme::set_default(t); - Theme::set_fallback_base_scale(default_scale); - Theme::set_fallback_icon(default_icon); - Theme::set_fallback_style(default_style); - Theme::set_fallback_font(default_font); - Theme::set_fallback_font_size(default_font_size * default_scale); -} + ThemeDB::get_singleton()->set_default_theme(t); -void clear_default_theme() { - Theme::set_project_default(nullptr); - Theme::set_default(nullptr); - Theme::set_fallback_icon(nullptr); - Theme::set_fallback_style(nullptr); - Theme::set_fallback_font(nullptr); + ThemeDB::get_singleton()->set_fallback_base_scale(default_scale); + ThemeDB::get_singleton()->set_fallback_icon(default_icon); + ThemeDB::get_singleton()->set_fallback_stylebox(default_style); + ThemeDB::get_singleton()->set_fallback_font(default_font); + ThemeDB::get_singleton()->set_fallback_font_size(default_font_size * default_scale); } diff --git a/scene/resources/default_theme/default_theme.h b/scene/resources/default_theme/default_theme.h index f777330a07..5243bcefa7 100644 --- a/scene/resources/default_theme/default_theme.h +++ b/scene/resources/default_theme/default_theme.h @@ -33,10 +33,7 @@ #include "scene/resources/theme.h" -const int default_font_size = 16; - void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const Ref<Font> &bold_font, const Ref<Font> &bold_italics_font, const Ref<Font> &italics_font, Ref<Texture2D> &default_icon, Ref<StyleBox> &default_style, float p_scale); -void make_default_theme(float p_scale, Ref<Font> p_font, TextServer::SubpixelPositioning p_font_subpixel = TextServer::SUBPIXEL_POSITIONING_AUTO, TextServer::Hinting p_font_hinting = TextServer::HINTING_LIGHT, bool p_font_antialiased = true, bool p_font_msdf = false, bool p_font_generate_mipmaps = false); -void clear_default_theme(); +void make_default_theme(float p_scale, Ref<Font> p_font, TextServer::SubpixelPositioning p_font_subpixel = TextServer::SUBPIXEL_POSITIONING_AUTO, TextServer::Hinting p_font_hinting = TextServer::HINTING_LIGHT, TextServer::FontAntialiasing p_font_antialiased = TextServer::FONT_ANTIALIASING_GRAY, bool p_font_msdf = false, bool p_font_generate_mipmaps = false); -#endif +#endif // DEFAULT_THEME_H diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp index 854bd34d6a..ebdaaaa95f 100644 --- a/scene/resources/environment.cpp +++ b/scene/resources/environment.cpp @@ -94,13 +94,30 @@ Color Environment::get_bg_color() const { return bg_color; } -void Environment::set_bg_energy(float p_energy) { - bg_energy = p_energy; - RS::get_singleton()->environment_set_bg_energy(environment, p_energy); +void Environment::set_bg_energy_multiplier(float p_multiplier) { + bg_energy_multiplier = p_multiplier; + _update_bg_energy(); } -float Environment::get_bg_energy() const { - return bg_energy; +float Environment::get_bg_energy_multiplier() const { + return bg_energy_multiplier; +} + +void Environment::set_bg_intensity(float p_exposure_value) { + bg_intensity = p_exposure_value; + _update_bg_energy(); +} + +float Environment::get_bg_intensity() const { + return bg_intensity; +} + +void Environment::_update_bg_energy() { + if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + RS::get_singleton()->environment_set_bg_energy(environment, bg_energy_multiplier, bg_intensity); + } else { + RS::get_singleton()->environment_set_bg_energy(environment, bg_energy_multiplier, 1.0); + } } void Environment::set_canvas_max_layer(int p_max_layer) { @@ -214,63 +231,12 @@ float Environment::get_tonemap_white() const { return tonemap_white; } -void Environment::set_tonemap_auto_exposure_enabled(bool p_enabled) { - tonemap_auto_exposure_enabled = p_enabled; - _update_tonemap(); - notify_property_list_changed(); -} - -bool Environment::is_tonemap_auto_exposure_enabled() const { - return tonemap_auto_exposure_enabled; -} - -void Environment::set_tonemap_auto_exposure_min(float p_auto_exposure_min) { - tonemap_auto_exposure_min = p_auto_exposure_min; - _update_tonemap(); -} - -float Environment::get_tonemap_auto_exposure_min() const { - return tonemap_auto_exposure_min; -} - -void Environment::set_tonemap_auto_exposure_max(float p_auto_exposure_max) { - tonemap_auto_exposure_max = p_auto_exposure_max; - _update_tonemap(); -} - -float Environment::get_tonemap_auto_exposure_max() const { - return tonemap_auto_exposure_max; -} - -void Environment::set_tonemap_auto_exposure_speed(float p_auto_exposure_speed) { - tonemap_auto_exposure_speed = p_auto_exposure_speed; - _update_tonemap(); -} - -float Environment::get_tonemap_auto_exposure_speed() const { - return tonemap_auto_exposure_speed; -} - -void Environment::set_tonemap_auto_exposure_grey(float p_auto_exposure_grey) { - tonemap_auto_exposure_grey = p_auto_exposure_grey; - _update_tonemap(); -} - -float Environment::get_tonemap_auto_exposure_grey() const { - return tonemap_auto_exposure_grey; -} - void Environment::_update_tonemap() { RS::get_singleton()->environment_set_tonemap( environment, RS::EnvironmentToneMapper(tone_mapper), tonemap_exposure, - tonemap_white, - tonemap_auto_exposure_enabled, - tonemap_auto_exposure_min, - tonemap_auto_exposure_max, - tonemap_auto_exposure_speed, - tonemap_auto_exposure_grey); + tonemap_white); } // SSR @@ -852,6 +818,15 @@ float Environment::get_fog_aerial_perspective() const { return fog_aerial_perspective; } +void Environment::set_fog_sky_affect(float p_sky_affect) { + fog_sky_affect = p_sky_affect; + _update_fog(); +} + +float Environment::get_fog_sky_affect() const { + return fog_sky_affect; +} + void Environment::_update_fog() { RS::get_singleton()->environment_set_fog( environment, @@ -862,13 +837,28 @@ void Environment::_update_fog() { fog_density, fog_height, fog_height_density, - fog_aerial_perspective); + fog_aerial_perspective, + fog_sky_affect); } // Volumetric Fog void Environment::_update_volumetric_fog() { - RS::get_singleton()->environment_set_volumetric_fog(environment, volumetric_fog_enabled, volumetric_fog_density, volumetric_fog_albedo, volumetric_fog_emission, volumetric_fog_emission_energy, volumetric_fog_anisotropy, volumetric_fog_length, volumetric_fog_detail_spread, volumetric_fog_gi_inject, volumetric_fog_temporal_reproject, volumetric_fog_temporal_reproject_amount, volumetric_fog_ambient_inject); + RS::get_singleton()->environment_set_volumetric_fog( + environment, + volumetric_fog_enabled, + volumetric_fog_density, + volumetric_fog_albedo, + volumetric_fog_emission, + volumetric_fog_emission_energy, + volumetric_fog_anisotropy, + volumetric_fog_length, + volumetric_fog_detail_spread, + volumetric_fog_gi_inject, + volumetric_fog_temporal_reproject, + volumetric_fog_temporal_reproject_amount, + volumetric_fog_ambient_inject, + volumetric_fog_sky_affect); } void Environment::set_volumetric_fog_enabled(bool p_enable) { @@ -946,6 +936,15 @@ float Environment::get_volumetric_fog_ambient_inject() const { return volumetric_fog_ambient_inject; } +void Environment::set_volumetric_fog_sky_affect(float p_sky_affect) { + volumetric_fog_sky_affect = p_sky_affect; + _update_volumetric_fog(); +} + +float Environment::get_volumetric_fog_sky_affect() const { + return volumetric_fog_sky_affect; +} + void Environment::set_volumetric_fog_temporal_reprojection_enabled(bool p_enable) { volumetric_fog_temporal_reproject = p_enable; _update_volumetric_fog(); @@ -1037,53 +1036,56 @@ void Environment::_update_adjustment() { // Private methods, constructor and destructor -void Environment::_validate_property(PropertyInfo &property) const { - if (property.name == "sky" || property.name == "sky_custom_fov" || property.name == "sky_rotation" || property.name == "ambient_light/sky_contribution") { +void Environment::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "sky" || p_property.name == "sky_custom_fov" || p_property.name == "sky_rotation" || p_property.name == "ambient_light_sky_contribution") { if (bg_mode != BG_SKY && ambient_source != AMBIENT_SOURCE_SKY && reflection_source != REFLECTION_SOURCE_SKY) { - property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } } - if (property.name == "fog_aerial_perspective") { + if (p_property.name == "fog_aerial_perspective") { if (bg_mode != BG_SKY) { - property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } } - if (property.name == "tonemap_white" && tone_mapper == TONE_MAPPER_LINEAR) { - property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + if (p_property.name == "tonemap_white" && tone_mapper == TONE_MAPPER_LINEAR) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } - if (property.name == "glow_intensity" && glow_blend_mode == GLOW_BLEND_MODE_MIX) { - property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + if (p_property.name == "glow_intensity" && glow_blend_mode == GLOW_BLEND_MODE_MIX) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } - if (property.name == "glow_mix" && glow_blend_mode != GLOW_BLEND_MODE_MIX) { - property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + if (p_property.name == "glow_mix" && glow_blend_mode != GLOW_BLEND_MODE_MIX) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } - if (property.name == "background_color") { + if (p_property.name == "background_color") { if (bg_mode != BG_COLOR && ambient_source != AMBIENT_SOURCE_COLOR) { - property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } } - if (property.name == "background_canvas_max_layer") { + if (p_property.name == "background_canvas_max_layer") { if (bg_mode != BG_CANVAS) { - property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } } - if (property.name == "background_camera_feed_id") { + if (p_property.name == "background_camera_feed_id") { if (bg_mode != BG_CAMERA_FEED) { - property.usage = PROPERTY_USAGE_NO_EDITOR; + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } } + if (p_property.name == "background_intensity" && !GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } + static const char *hide_prefixes[] = { "fog_", "volumetric_fog_", - "auto_exposure_", "ssr_", "ssao_", "ssil_", @@ -1095,7 +1097,6 @@ void Environment::_validate_property(PropertyInfo &property) const { }; static const char *high_end_prefixes[] = { - "auto_exposure_", "ssr_", "ssao_", nullptr @@ -1107,8 +1108,8 @@ void Environment::_validate_property(PropertyInfo &property) const { String prefix = String(*prefixes); String enabled = prefix + "enabled"; - if (property.name.begins_with(prefix) && property.name != enabled && !bool(get(enabled))) { - property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + if (p_property.name.begins_with(prefix) && p_property.name != enabled && !bool(get(enabled))) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; return; } @@ -1120,8 +1121,8 @@ void Environment::_validate_property(PropertyInfo &property) const { while (*prefixes) { String prefix = String(*prefixes); - if (property.name.begins_with(prefix)) { - property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + if (p_property.name.begins_with(prefix)) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; return; } @@ -1162,8 +1163,10 @@ void Environment::_bind_methods() { ClassDB::bind_method(D_METHOD("get_sky_rotation"), &Environment::get_sky_rotation); ClassDB::bind_method(D_METHOD("set_bg_color", "color"), &Environment::set_bg_color); ClassDB::bind_method(D_METHOD("get_bg_color"), &Environment::get_bg_color); - ClassDB::bind_method(D_METHOD("set_bg_energy", "energy"), &Environment::set_bg_energy); - ClassDB::bind_method(D_METHOD("get_bg_energy"), &Environment::get_bg_energy); + ClassDB::bind_method(D_METHOD("set_bg_energy_multiplier", "energy"), &Environment::set_bg_energy_multiplier); + ClassDB::bind_method(D_METHOD("get_bg_energy_multiplier"), &Environment::get_bg_energy_multiplier); + ClassDB::bind_method(D_METHOD("set_bg_intensity", "energy"), &Environment::set_bg_intensity); + ClassDB::bind_method(D_METHOD("get_bg_intensity"), &Environment::get_bg_intensity); ClassDB::bind_method(D_METHOD("set_canvas_max_layer", "layer"), &Environment::set_canvas_max_layer); ClassDB::bind_method(D_METHOD("get_canvas_max_layer"), &Environment::get_canvas_max_layer); ClassDB::bind_method(D_METHOD("set_camera_feed_id", "id"), &Environment::set_camera_feed_id); @@ -1172,14 +1175,16 @@ void Environment::_bind_methods() { ADD_GROUP("Background", "background_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "background_mode", PROPERTY_HINT_ENUM, "Clear Color,Custom Color,Sky,Canvas,Keep,Camera Feed"), "set_background", "get_background"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "background_color"), "set_bg_color", "get_bg_color"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "background_energy", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_bg_energy", "get_bg_energy"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "background_energy_multiplier", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_bg_energy_multiplier", "get_bg_energy_multiplier"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "background_intensity", PROPERTY_HINT_RANGE, "0,100000,0.01,suffix:nt"), "set_bg_intensity", "get_bg_intensity"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "background_canvas_max_layer", PROPERTY_HINT_RANGE, "-1000,1000,1"), "set_canvas_max_layer", "get_canvas_max_layer"); ADD_PROPERTY(PropertyInfo(Variant::INT, "background_camera_feed_id", PROPERTY_HINT_RANGE, "1,10,1"), "set_camera_feed_id", "get_camera_feed_id"); ADD_GROUP("Sky", "sky_"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "sky", PROPERTY_HINT_RESOURCE_TYPE, "Sky"), "set_sky", "get_sky"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sky_custom_fov", PROPERTY_HINT_RANGE, "0,180,0.1,degrees"), "set_sky_custom_fov", "get_sky_custom_fov"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "sky_rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_lesser,or_greater,radians"), "set_sky_rotation", "get_sky_rotation"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "sky_rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_less,or_greater,radians"), "set_sky_rotation", "get_sky_rotation"); // Ambient light @@ -1211,27 +1216,11 @@ void Environment::_bind_methods() { ClassDB::bind_method(D_METHOD("get_tonemap_exposure"), &Environment::get_tonemap_exposure); ClassDB::bind_method(D_METHOD("set_tonemap_white", "white"), &Environment::set_tonemap_white); ClassDB::bind_method(D_METHOD("get_tonemap_white"), &Environment::get_tonemap_white); - ClassDB::bind_method(D_METHOD("set_tonemap_auto_exposure_enabled", "enabled"), &Environment::set_tonemap_auto_exposure_enabled); - ClassDB::bind_method(D_METHOD("is_tonemap_auto_exposure_enabled"), &Environment::is_tonemap_auto_exposure_enabled); - ClassDB::bind_method(D_METHOD("set_tonemap_auto_exposure_max", "exposure_max"), &Environment::set_tonemap_auto_exposure_max); - ClassDB::bind_method(D_METHOD("get_tonemap_auto_exposure_max"), &Environment::get_tonemap_auto_exposure_max); - ClassDB::bind_method(D_METHOD("set_tonemap_auto_exposure_min", "exposure_min"), &Environment::set_tonemap_auto_exposure_min); - ClassDB::bind_method(D_METHOD("get_tonemap_auto_exposure_min"), &Environment::get_tonemap_auto_exposure_min); - ClassDB::bind_method(D_METHOD("set_tonemap_auto_exposure_speed", "exposure_speed"), &Environment::set_tonemap_auto_exposure_speed); - ClassDB::bind_method(D_METHOD("get_tonemap_auto_exposure_speed"), &Environment::get_tonemap_auto_exposure_speed); - ClassDB::bind_method(D_METHOD("set_tonemap_auto_exposure_grey", "exposure_grey"), &Environment::set_tonemap_auto_exposure_grey); - ClassDB::bind_method(D_METHOD("get_tonemap_auto_exposure_grey"), &Environment::get_tonemap_auto_exposure_grey); ADD_GROUP("Tonemap", "tonemap_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "tonemap_mode", PROPERTY_HINT_ENUM, "Linear,Reinhard,Filmic,ACES"), "set_tonemapper", "get_tonemapper"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "tonemap_exposure", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_tonemap_exposure", "get_tonemap_exposure"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "tonemap_white", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_tonemap_white", "get_tonemap_white"); - ADD_GROUP("Auto Exposure", "auto_exposure_"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_exposure_enabled"), "set_tonemap_auto_exposure_enabled", "is_tonemap_auto_exposure_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "auto_exposure_scale", PROPERTY_HINT_RANGE, "0.01,64,0.01"), "set_tonemap_auto_exposure_grey", "get_tonemap_auto_exposure_grey"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "auto_exposure_min_luma", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_tonemap_auto_exposure_min", "get_tonemap_auto_exposure_min"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "auto_exposure_max_luma", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_tonemap_auto_exposure_max", "get_tonemap_auto_exposure_max"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "auto_exposure_speed", PROPERTY_HINT_RANGE, "0.01,64,0.01"), "set_tonemap_auto_exposure_speed", "get_tonemap_auto_exposure_speed"); // SSR @@ -1249,8 +1238,8 @@ void Environment::_bind_methods() { ADD_GROUP("SSR", "ssr_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ssr_enabled"), "set_ssr_enabled", "is_ssr_enabled"); ADD_PROPERTY(PropertyInfo(Variant::INT, "ssr_max_steps", PROPERTY_HINT_RANGE, "1,512,1"), "set_ssr_max_steps", "get_ssr_max_steps"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssr_fade_in", PROPERTY_HINT_EXP_EASING), "set_ssr_fade_in", "get_ssr_fade_in"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssr_fade_out", PROPERTY_HINT_EXP_EASING), "set_ssr_fade_out", "get_ssr_fade_out"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssr_fade_in", PROPERTY_HINT_EXP_EASING, "positive_only"), "set_ssr_fade_in", "get_ssr_fade_in"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssr_fade_out", PROPERTY_HINT_EXP_EASING, "positive_only"), "set_ssr_fade_out", "get_ssr_fade_out"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssr_depth_tolerance", PROPERTY_HINT_RANGE, "0.01,128,0.1"), "set_ssr_depth_tolerance", "get_ssr_depth_tolerance"); // SSAO @@ -1277,7 +1266,7 @@ void Environment::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ssao_enabled"), "set_ssao_enabled", "is_ssao_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_radius", PROPERTY_HINT_RANGE, "0.01,16,0.01,or_greater"), "set_ssao_radius", "get_ssao_radius"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_intensity", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_ssao_intensity", "get_ssao_intensity"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_power", PROPERTY_HINT_EXP_EASING), "set_ssao_power", "get_ssao_power"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_power", PROPERTY_HINT_EXP_EASING, "positive_only"), "set_ssao_power", "get_ssao_power"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_detail", PROPERTY_HINT_RANGE, "0,5,0.01"), "set_ssao_detail", "get_ssao_detail"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_horizon", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_ssao_horizon", "get_ssao_horizon"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_sharpness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_ssao_sharpness", "get_ssao_sharpness"); @@ -1337,8 +1326,10 @@ void Environment::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_bounce_feedback", PROPERTY_HINT_RANGE, "0,1.99,0.01"), "set_sdfgi_bounce_feedback", "get_sdfgi_bounce_feedback"); ADD_PROPERTY(PropertyInfo(Variant::INT, "sdfgi_cascades", PROPERTY_HINT_RANGE, "1,8,1"), "set_sdfgi_cascades", "get_sdfgi_cascades"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_min_cell_size", PROPERTY_HINT_RANGE, "0.01,64,0.01"), "set_sdfgi_min_cell_size", "get_sdfgi_min_cell_size"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_cascade0_distance", PROPERTY_HINT_RANGE, "0.1,16384,0.1,or_greater"), "set_sdfgi_cascade0_distance", "get_sdfgi_cascade0_distance"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_max_distance", PROPERTY_HINT_RANGE, "0.1,16384,0.1,or_greater"), "set_sdfgi_max_distance", "get_sdfgi_max_distance"); + // Don't store the values of `sdfgi_cascade0_distance` and `sdfgi_max_distance` + // as they're derived from `sdfgi_min_cell_size`. + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_cascade0_distance", PROPERTY_HINT_RANGE, "0.1,16384,0.1,or_greater", PROPERTY_USAGE_EDITOR), "set_sdfgi_cascade0_distance", "get_sdfgi_cascade0_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_max_distance", PROPERTY_HINT_RANGE, "0.1,16384,0.1,or_greater", PROPERTY_USAGE_EDITOR), "set_sdfgi_max_distance", "get_sdfgi_max_distance"); ADD_PROPERTY(PropertyInfo(Variant::INT, "sdfgi_y_scale", PROPERTY_HINT_ENUM, "50% (Compact),75% (Balanced),100% (Sparse)"), "set_sdfgi_y_scale", "get_sdfgi_y_scale"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_energy"), "set_sdfgi_energy", "get_sdfgi_energy"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_normal_bias"), "set_sdfgi_normal_bias", "get_sdfgi_normal_bias"); @@ -1417,16 +1408,20 @@ void Environment::_bind_methods() { ClassDB::bind_method(D_METHOD("set_fog_aerial_perspective", "aerial_perspective"), &Environment::set_fog_aerial_perspective); ClassDB::bind_method(D_METHOD("get_fog_aerial_perspective"), &Environment::get_fog_aerial_perspective); + ClassDB::bind_method(D_METHOD("set_fog_sky_affect", "sky_affect"), &Environment::set_fog_sky_affect); + ClassDB::bind_method(D_METHOD("get_fog_sky_affect"), &Environment::get_fog_sky_affect); + ADD_GROUP("Fog", "fog_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fog_enabled"), "set_fog_enabled", "is_fog_enabled"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "fog_light_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_fog_light_color", "get_fog_light_color"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_light_energy", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_fog_light_energy", "get_fog_light_energy"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_sun_scatter", PROPERTY_HINT_RANGE, "0,1,0.01,or_greater"), "set_fog_sun_scatter", "get_fog_sun_scatter"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_density", PROPERTY_HINT_RANGE, "0,16,0.0001"), "set_fog_density", "get_fog_density"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_density", PROPERTY_HINT_RANGE, "0,1,0.0001,or_greater"), "set_fog_density", "get_fog_density"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_aerial_perspective", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_fog_aerial_perspective", "get_fog_aerial_perspective"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height", PROPERTY_HINT_RANGE, "-1024,1024,0.01,or_lesser,or_greater,suffix:m"), "set_fog_height", "get_fog_height"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height_density", PROPERTY_HINT_RANGE, "-16,16,0.0001,or_lesser,or_greater"), "set_fog_height_density", "get_fog_height_density"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_sky_affect", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_fog_sky_affect", "get_fog_sky_affect"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height", PROPERTY_HINT_RANGE, "-1024,1024,0.01,or_less,or_greater,suffix:m"), "set_fog_height", "get_fog_height"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height_density", PROPERTY_HINT_RANGE, "-16,16,0.0001,or_less,or_greater"), "set_fog_height_density", "get_fog_height_density"); ClassDB::bind_method(D_METHOD("set_volumetric_fog_enabled", "enabled"), &Environment::set_volumetric_fog_enabled); ClassDB::bind_method(D_METHOD("is_volumetric_fog_enabled"), &Environment::is_volumetric_fog_enabled); @@ -1448,6 +1443,8 @@ void Environment::_bind_methods() { ClassDB::bind_method(D_METHOD("get_volumetric_fog_gi_inject"), &Environment::get_volumetric_fog_gi_inject); ClassDB::bind_method(D_METHOD("set_volumetric_fog_ambient_inject", "enabled"), &Environment::set_volumetric_fog_ambient_inject); ClassDB::bind_method(D_METHOD("get_volumetric_fog_ambient_inject"), &Environment::get_volumetric_fog_ambient_inject); + ClassDB::bind_method(D_METHOD("set_volumetric_fog_sky_affect", "sky_affect"), &Environment::set_volumetric_fog_sky_affect); + ClassDB::bind_method(D_METHOD("get_volumetric_fog_sky_affect"), &Environment::get_volumetric_fog_sky_affect); ClassDB::bind_method(D_METHOD("set_volumetric_fog_temporal_reprojection_enabled", "enabled"), &Environment::set_volumetric_fog_temporal_reprojection_enabled); ClassDB::bind_method(D_METHOD("is_volumetric_fog_temporal_reprojection_enabled"), &Environment::is_volumetric_fog_temporal_reprojection_enabled); ClassDB::bind_method(D_METHOD("set_volumetric_fog_temporal_reprojection_amount", "temporal_reprojection_amount"), &Environment::set_volumetric_fog_temporal_reprojection_amount); @@ -1462,8 +1459,9 @@ void Environment::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_gi_inject", PROPERTY_HINT_RANGE, "0.0,16,0.01,exp"), "set_volumetric_fog_gi_inject", "get_volumetric_fog_gi_inject"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_anisotropy", PROPERTY_HINT_RANGE, "-0.9,0.9,0.01"), "set_volumetric_fog_anisotropy", "get_volumetric_fog_anisotropy"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_length", PROPERTY_HINT_RANGE, "0,1024,0.01,or_greater"), "set_volumetric_fog_length", "get_volumetric_fog_length"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_detail_spread", PROPERTY_HINT_EXP_EASING), "set_volumetric_fog_detail_spread", "get_volumetric_fog_detail_spread"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_detail_spread", PROPERTY_HINT_EXP_EASING, "positive_only"), "set_volumetric_fog_detail_spread", "get_volumetric_fog_detail_spread"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_ambient_inject", PROPERTY_HINT_RANGE, "0.0,16,0.01,exp"), "set_volumetric_fog_ambient_inject", "get_volumetric_fog_ambient_inject"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_sky_affect", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_volumetric_fog_sky_affect", "get_volumetric_fog_sky_affect"); ADD_SUBGROUP("Temporal Reprojection", "volumetric_fog_temporal_reprojection_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "volumetric_fog_temporal_reprojection_enabled"), "set_volumetric_fog_temporal_reprojection_enabled", "is_volumetric_fog_temporal_reprojection_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_temporal_reprojection_amount", PROPERTY_HINT_RANGE, "0.5,0.99,0.001"), "set_volumetric_fog_temporal_reprojection_amount", "get_volumetric_fog_temporal_reprojection_amount"); @@ -1547,6 +1545,7 @@ Environment::Environment() { _update_fog(); _update_adjustment(); _update_volumetric_fog(); + _update_bg_energy(); notify_property_list_changed(); } diff --git a/scene/resources/environment.h b/scene/resources/environment.h index b71fe8904a..507a0cee39 100644 --- a/scene/resources/environment.h +++ b/scene/resources/environment.h @@ -92,9 +92,11 @@ private: float bg_sky_custom_fov = 0.0; Vector3 bg_sky_rotation; Color bg_color; - float bg_energy = 1.0; int bg_canvas_max_layer = 0; int bg_camera_feed_id = 1; + float bg_energy_multiplier = 1.0; + float bg_intensity = 30000.0; // Measured in nits or candela/m^2 + void _update_bg_energy(); // Ambient light Color ambient_color; @@ -108,11 +110,6 @@ private: ToneMapper tone_mapper = TONE_MAPPER_LINEAR; float tonemap_exposure = 1.0; float tonemap_white = 1.0; - bool tonemap_auto_exposure_enabled = false; - float tonemap_auto_exposure_min = 0.05; - float tonemap_auto_exposure_max = 8.0; - float tonemap_auto_exposure_speed = 0.5; - float tonemap_auto_exposure_grey = 0.4; void _update_tonemap(); // SSR @@ -175,13 +172,14 @@ private: // Fog bool fog_enabled = false; - Color fog_light_color = Color(0.5, 0.6, 0.7); + Color fog_light_color = Color(0.518, 0.553, 0.608); float fog_light_energy = 1.0; float fog_sun_scatter = 0.0; - float fog_density = 0.001; + float fog_density = 0.01; float fog_height = 0.0; float fog_height_density = 0.0; //can be negative to invert effect float fog_aerial_perspective = 0.0; + float fog_sky_affect = 1.0; void _update_fog(); @@ -194,8 +192,9 @@ private: float volumetric_fog_anisotropy = 0.2; float volumetric_fog_length = 64.0; float volumetric_fog_detail_spread = 2.0; - float volumetric_fog_gi_inject = 0.0; - float volumetric_fog_ambient_inject = false; + float volumetric_fog_gi_inject = 1.0; + float volumetric_fog_ambient_inject = 0.0; + float volumetric_fog_sky_affect = 1.0; bool volumetric_fog_temporal_reproject = true; float volumetric_fog_temporal_reproject_amount = 0.9; void _update_volumetric_fog(); @@ -211,7 +210,7 @@ private: protected: static void _bind_methods(); - virtual void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; #ifndef DISABLE_DEPRECATED // Kept for compatibility from 3.x to 4.0. bool _set(const StringName &p_name, const Variant &p_value); @@ -231,8 +230,10 @@ public: Vector3 get_sky_rotation() const; void set_bg_color(const Color &p_color); Color get_bg_color() const; - void set_bg_energy(float p_energy); - float get_bg_energy() const; + void set_bg_energy_multiplier(float p_energy); + float get_bg_energy_multiplier() const; + void set_bg_intensity(float p_energy); + float get_bg_intensity() const; void set_canvas_max_layer(int p_max_layer); int get_canvas_max_layer() const; void set_camera_feed_id(int p_id); @@ -257,16 +258,6 @@ public: float get_tonemap_exposure() const; void set_tonemap_white(float p_white); float get_tonemap_white() const; - void set_tonemap_auto_exposure_enabled(bool p_enabled); - bool is_tonemap_auto_exposure_enabled() const; - void set_tonemap_auto_exposure_min(float p_auto_exposure_min); - float get_tonemap_auto_exposure_min() const; - void set_tonemap_auto_exposure_max(float p_auto_exposure_max); - float get_tonemap_auto_exposure_max() const; - void set_tonemap_auto_exposure_speed(float p_auto_exposure_speed); - float get_tonemap_auto_exposure_speed() const; - void set_tonemap_auto_exposure_grey(float p_auto_exposure_grey); - float get_tonemap_auto_exposure_grey() const; // SSR void set_ssr_enabled(bool p_enabled); @@ -385,6 +376,8 @@ public: float get_fog_height_density() const; void set_fog_aerial_perspective(float p_aerial_perspective); float get_fog_aerial_perspective() const; + void set_fog_sky_affect(float p_sky_affect); + float get_fog_sky_affect() const; // Volumetric Fog void set_volumetric_fog_enabled(bool p_enable); @@ -407,6 +400,8 @@ public: float get_volumetric_fog_gi_inject() const; void set_volumetric_fog_ambient_inject(float p_ambient_inject); float get_volumetric_fog_ambient_inject() const; + void set_volumetric_fog_sky_affect(float p_sky_affect); + float get_volumetric_fog_sky_affect() const; void set_volumetric_fog_temporal_reprojection_enabled(bool p_enable); bool is_volumetric_fog_temporal_reprojection_enabled() const; void set_volumetric_fog_temporal_reprojection_amount(float p_amount); diff --git a/scene/resources/fog_material.cpp b/scene/resources/fog_material.cpp index 39ade85af6..46b44d681f 100644 --- a/scene/resources/fog_material.cpp +++ b/scene/resources/fog_material.cpp @@ -122,7 +122,7 @@ void FogMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_density_texture", "density_texture"), &FogMaterial::set_density_texture); ClassDB::bind_method(D_METHOD("get_density_texture"), &FogMaterial::get_density_texture); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "density", PROPERTY_HINT_RANGE, "0.0,16.0,0.0001,or_greater,or_lesser"), "set_density", "get_density"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "density", PROPERTY_HINT_RANGE, "-8.0,8.0,0.0001,or_greater,or_less"), "set_density", "get_density"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "albedo", PROPERTY_HINT_COLOR_NO_ALPHA), "set_albedo", "get_albedo"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "emission", PROPERTY_HINT_COLOR_NO_ALPHA), "set_emission", "get_emission"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height_falloff", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_height_falloff", "get_height_falloff"); diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index 6053d27ef7..6a278f1f39 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -39,6 +39,7 @@ #include "scene/resources/text_line.h" #include "scene/resources/text_paragraph.h" #include "scene/resources/theme.h" +#include "scene/theme/theme_db.h" /*************************************************************************/ /* Font */ @@ -69,19 +70,19 @@ void Font::_bind_methods() { // Drawing string. ClassDB::bind_method(D_METHOD("set_cache_capacity", "single_line", "multi_line"), &Font::set_cache_capacity); - ClassDB::bind_method(D_METHOD("get_string_size", "text", "alignment", "width", "font_size", "flags", "direction", "orientation"), &Font::get_string_size, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); - ClassDB::bind_method(D_METHOD("get_multiline_string_size", "text", "alignment", "width", "font_size", "max_lines", "flags", "direction", "orientation"), &Font::get_multiline_string_size, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + ClassDB::bind_method(D_METHOD("get_string_size", "text", "alignment", "width", "font_size", "jst_flags", "direction", "orientation"), &Font::get_string_size, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + ClassDB::bind_method(D_METHOD("get_multiline_string_size", "text", "alignment", "width", "font_size", "max_lines", "brk_flags", "jst_flags", "direction", "orientation"), &Font::get_multiline_string_size, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); - ClassDB::bind_method(D_METHOD("draw_string", "canvas_item", "pos", "text", "alignment", "width", "font_size", "modulate", "flags", "direction", "orientation"), &Font::draw_string, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); - ClassDB::bind_method(D_METHOD("draw_multiline_string", "canvas_item", "pos", "text", "alignment", "width", "font_size", "max_lines", "modulate", "flags", "direction", "orientation"), &Font::draw_multiline_string, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + ClassDB::bind_method(D_METHOD("draw_string", "canvas_item", "pos", "text", "alignment", "width", "font_size", "modulate", "jst_flags", "direction", "orientation"), &Font::draw_string, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + ClassDB::bind_method(D_METHOD("draw_multiline_string", "canvas_item", "pos", "text", "alignment", "width", "font_size", "max_lines", "modulate", "brk_flags", "jst_flags", "direction", "orientation"), &Font::draw_multiline_string, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); - ClassDB::bind_method(D_METHOD("draw_string_outline", "canvas_item", "pos", "text", "alignment", "width", "font_size", "size", "modulate", "flags", "direction", "orientation"), &Font::draw_string_outline, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); - ClassDB::bind_method(D_METHOD("draw_multiline_string_outline", "canvas_item", "pos", "text", "alignment", "width", "font_size", "max_lines", "size", "modulate", "flags", "direction", "orientation"), &Font::draw_multiline_string_outline, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + ClassDB::bind_method(D_METHOD("draw_string_outline", "canvas_item", "pos", "text", "alignment", "width", "font_size", "size", "modulate", "jst_flags", "direction", "orientation"), &Font::draw_string_outline, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + ClassDB::bind_method(D_METHOD("draw_multiline_string_outline", "canvas_item", "pos", "text", "alignment", "width", "font_size", "max_lines", "size", "modulate", "brk_flags", "jst_flags", "direction", "orientation"), &Font::draw_multiline_string_outline, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); // Drawing char. - ClassDB::bind_method(D_METHOD("get_char_size", "char"), &Font::get_char_size); - ClassDB::bind_method(D_METHOD("draw_char", "canvas_item", "pos", "char", "modulate"), &Font::draw_char, DEFVAL(Color(1.0, 1.0, 1.0))); - ClassDB::bind_method(D_METHOD("draw_char_outline", "canvas_item", "pos", "char", "size", "modulate"), &Font::draw_char_outline, DEFVAL(-1), DEFVAL(Color(1.0, 1.0, 1.0))); + ClassDB::bind_method(D_METHOD("get_char_size", "char", "font_size"), &Font::get_char_size); + ClassDB::bind_method(D_METHOD("draw_char", "canvas_item", "pos", "char", "font_size", "modulate"), &Font::draw_char, DEFVAL(Color(1.0, 1.0, 1.0))); + ClassDB::bind_method(D_METHOD("draw_char_outline", "canvas_item", "pos", "char", "font_size", "size", "modulate"), &Font::draw_char_outline, DEFVAL(-1), DEFVAL(Color(1.0, 1.0, 1.0))); // Helper functions. ClassDB::bind_method(D_METHOD("has_char", "char"), &Font::has_char); @@ -126,16 +127,18 @@ void Font::_invalidate_rids() { } bool Font::_is_cyclic(const Ref<Font> &p_f, int p_depth) const { - ERR_FAIL_COND_V(p_depth > MAX_FALLBACK_DEPTH, false); + ERR_FAIL_COND_V(p_depth > MAX_FALLBACK_DEPTH, true); if (p_f.is_null()) { return false; } + if (p_f == this) { + return true; + } for (int i = 0; i < p_f->fallbacks.size(); i++) { const Ref<Font> &f = p_f->fallbacks[i]; - if (f == this) { + if (_is_cyclic(f, p_depth + 1)) { return true; } - return _is_cyclic(f, p_depth + 1); } return false; } @@ -146,7 +149,10 @@ void Font::reset_state() { // Fallbacks. void Font::set_fallbacks(const TypedArray<Font> &p_fallbacks) { - ERR_FAIL_COND(_is_cyclic(this, 0)); + for (int i = 0; i < p_fallbacks.size(); i++) { + const Ref<Font> &f = p_fallbacks[i]; + ERR_FAIL_COND_MSG(_is_cyclic(f, 0), "Cyclic font fallback."); + } for (int i = 0; i < fallbacks.size(); i++) { Ref<Font> f = fallbacks[i]; if (f.is_valid()) { @@ -157,7 +163,7 @@ void Font::set_fallbacks(const TypedArray<Font> &p_fallbacks) { for (int i = 0; i < fallbacks.size(); i++) { Ref<Font> f = fallbacks[i]; if (f.is_valid()) { - f->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED); + f->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED); } } _invalidate_rids(); @@ -239,7 +245,7 @@ String Font::get_font_style_name() const { return TS->font_get_style_name(_get_rid()); } -uint32_t Font::get_font_style() const { +BitField<TextServer::FontStyle> Font::get_font_style() const { return TS->font_get_style(_get_rid()); } @@ -253,15 +259,15 @@ void Font::set_cache_capacity(int p_single_line, int p_multi_line) { cache_wrap.set_capacity(p_multi_line); } -Size2 Font::get_string_size(const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, uint16_t p_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { +Size2 Font::get_string_size(const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { uint64_t hash = p_text.hash64(); hash = hash_djb2_one_64(p_font_size, hash); if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) { hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); - hash = hash_djb2_one_64(p_flags, hash); - hash = hash_djb2_one_64(p_direction, hash); - hash = hash_djb2_one_64(p_orientation, hash); + hash = hash_djb2_one_64(p_jst_flags.operator int64_t(), hash); } + hash = hash_djb2_one_64(p_direction, hash); + hash = hash_djb2_one_64(p_orientation, hash); Ref<TextLine> buffer; if (cache.has(hash)) { @@ -273,14 +279,22 @@ Size2 Font::get_string_size(const String &p_text, HorizontalAlignment p_alignmen buffer->add_string(p_text, Ref<Font>(this), p_font_size); cache.insert(hash, buffer); } + + buffer->set_width(p_width); + buffer->set_horizontal_alignment(p_alignment); + if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) { + buffer->set_flags(p_jst_flags); + } + return buffer->get_size(); } -Size2 Font::get_multiline_string_size(const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_max_lines, uint16_t p_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { +Size2 Font::get_multiline_string_size(const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_max_lines, BitField<TextServer::LineBreakFlag> p_brk_flags, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { uint64_t hash = p_text.hash64(); hash = hash_djb2_one_64(p_font_size, hash); hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); - hash = hash_djb2_one_64(p_flags, hash); + hash = hash_djb2_one_64(p_brk_flags.operator int64_t(), hash); + hash = hash_djb2_one_64(p_jst_flags.operator int64_t(), hash); hash = hash_djb2_one_64(p_direction, hash); hash = hash_djb2_one_64(p_orientation, hash); @@ -293,7 +307,8 @@ Size2 Font::get_multiline_string_size(const String &p_text, HorizontalAlignment lines_buffer->set_orientation(p_orientation); lines_buffer->add_string(p_text, Ref<Font>(this), p_font_size); lines_buffer->set_width(p_width); - lines_buffer->set_flags(p_flags); + lines_buffer->set_break_flags(p_brk_flags); + lines_buffer->set_justification_flags(p_jst_flags); cache_wrap.insert(hash, lines_buffer); } @@ -303,13 +318,15 @@ Size2 Font::get_multiline_string_size(const String &p_text, HorizontalAlignment return lines_buffer->get_size(); } -void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, const Color &p_modulate, uint16_t p_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { +void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, const Color &p_modulate, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { uint64_t hash = p_text.hash64(); hash = hash_djb2_one_64(p_font_size, hash); if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) { hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); - hash = hash_djb2_one_64(p_flags, hash); + hash = hash_djb2_one_64(p_jst_flags.operator int64_t(), hash); } + hash = hash_djb2_one_64(p_direction, hash); + hash = hash_djb2_one_64(p_orientation, hash); Ref<TextLine> buffer; if (cache.has(hash)) { @@ -331,16 +348,19 @@ void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_t buffer->set_width(p_width); buffer->set_horizontal_alignment(p_alignment); - buffer->set_flags(p_flags); + if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) { + buffer->set_flags(p_jst_flags); + } buffer->draw(p_canvas_item, ofs, p_modulate); } -void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_max_lines, const Color &p_modulate, uint16_t p_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { +void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_max_lines, const Color &p_modulate, BitField<TextServer::LineBreakFlag> p_brk_flags, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { uint64_t hash = p_text.hash64(); hash = hash_djb2_one_64(p_font_size, hash); hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); - hash = hash_djb2_one_64(p_flags, hash); + hash = hash_djb2_one_64(p_brk_flags.operator int64_t(), hash); + hash = hash_djb2_one_64(p_jst_flags.operator int64_t(), hash); hash = hash_djb2_one_64(p_direction, hash); hash = hash_djb2_one_64(p_orientation, hash); @@ -353,7 +373,8 @@ void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const S lines_buffer->set_orientation(p_orientation); lines_buffer->add_string(p_text, Ref<Font>(this), p_font_size); lines_buffer->set_width(p_width); - lines_buffer->set_flags(p_flags); + lines_buffer->set_break_flags(p_brk_flags); + lines_buffer->set_justification_flags(p_jst_flags); cache_wrap.insert(hash, lines_buffer); } @@ -370,13 +391,15 @@ void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const S lines_buffer->draw(p_canvas_item, ofs, p_modulate); } -void Font::draw_string_outline(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_size, const Color &p_modulate, uint16_t p_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { +void Font::draw_string_outline(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_size, const Color &p_modulate, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { uint64_t hash = p_text.hash64(); hash = hash_djb2_one_64(p_font_size, hash); if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) { hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); - hash = hash_djb2_one_64(p_flags, hash); + hash = hash_djb2_one_64(p_jst_flags.operator int64_t(), hash); } + hash = hash_djb2_one_64(p_direction, hash); + hash = hash_djb2_one_64(p_orientation, hash); Ref<TextLine> buffer; if (cache.has(hash)) { @@ -398,16 +421,19 @@ void Font::draw_string_outline(RID p_canvas_item, const Point2 &p_pos, const Str buffer->set_width(p_width); buffer->set_horizontal_alignment(p_alignment); - buffer->set_flags(p_flags); + if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) { + buffer->set_flags(p_jst_flags); + } buffer->draw_outline(p_canvas_item, ofs, p_size, p_modulate); } -void Font::draw_multiline_string_outline(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_max_lines, int p_size, const Color &p_modulate, uint16_t p_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { +void Font::draw_multiline_string_outline(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_max_lines, int p_size, const Color &p_modulate, BitField<TextServer::LineBreakFlag> p_brk_flags, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { uint64_t hash = p_text.hash64(); hash = hash_djb2_one_64(p_font_size, hash); hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); - hash = hash_djb2_one_64(p_flags, hash); + hash = hash_djb2_one_64(p_brk_flags.operator int64_t(), hash); + hash = hash_djb2_one_64(p_jst_flags.operator int64_t(), hash); hash = hash_djb2_one_64(p_direction, hash); hash = hash_djb2_one_64(p_orientation, hash); @@ -420,7 +446,8 @@ void Font::draw_multiline_string_outline(RID p_canvas_item, const Point2 &p_pos, lines_buffer->set_orientation(p_orientation); lines_buffer->add_string(p_text, Ref<Font>(this), p_font_size); lines_buffer->set_width(p_width); - lines_buffer->set_flags(p_flags); + lines_buffer->set_break_flags(p_brk_flags); + lines_buffer->set_justification_flags(p_jst_flags); cache_wrap.insert(hash, lines_buffer); } @@ -534,7 +561,6 @@ Font::Font() { } Font::~Font() { - reset_state(); } /*************************************************************************/ @@ -557,7 +583,7 @@ _FORCE_INLINE_ void FontFile::_ensure_rid(int p_cache_index) const { if (unlikely(!cache[p_cache_index].is_valid())) { cache.write[p_cache_index] = TS->create_font(); TS->font_set_data_ptr(cache[p_cache_index], data_ptr, data_size); - TS->font_set_antialiased(cache[p_cache_index], antialiased); + TS->font_set_antialiasing(cache[p_cache_index], antialiasing); TS->font_set_generate_mipmaps(cache[p_cache_index], mipmaps); TS->font_set_multichannel_signed_distance_field(cache[p_cache_index], msdf); TS->font_set_msdf_pixel_range(cache[p_cache_index], msdf_pixel_range); @@ -856,8 +882,8 @@ void FontFile::_bind_methods() { ClassDB::bind_method(D_METHOD("set_font_style_name", "name"), &FontFile::set_font_style_name); ClassDB::bind_method(D_METHOD("set_font_style", "style"), &FontFile::set_font_style); - ClassDB::bind_method(D_METHOD("set_antialiased", "antialiased"), &FontFile::set_antialiased); - ClassDB::bind_method(D_METHOD("is_antialiased"), &FontFile::is_antialiased); + ClassDB::bind_method(D_METHOD("set_antialiasing", "antialiasing"), &FontFile::set_antialiasing); + ClassDB::bind_method(D_METHOD("get_antialiasing"), &FontFile::get_antialiasing); ClassDB::bind_method(D_METHOD("set_generate_mipmaps", "generate_mipmaps"), &FontFile::set_generate_mipmaps); ClassDB::bind_method(D_METHOD("get_generate_mipmaps"), &FontFile::get_generate_mipmaps); @@ -977,10 +1003,10 @@ void FontFile::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_data", "get_data"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "generate_mipmaps", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_generate_mipmaps", "get_generate_mipmaps"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "antialiased", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_antialiased", "is_antialiased"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "antialiasing", PROPERTY_HINT_ENUM, "None,Grayscale,LCD sub-pixel", PROPERTY_USAGE_STORAGE), "set_antialiasing", "get_antialiasing"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "font_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_font_name", "get_font_name"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "style_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_font_style_name", "get_font_style_name"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "font_style", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_font_style", "get_font_style"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "font_style", PROPERTY_HINT_FLAGS, "Bold,Italic,Fixed Size", PROPERTY_USAGE_STORAGE), "set_font_style", "get_font_style"); ADD_PROPERTY(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One half of a pixel,One quarter of a pixel", PROPERTY_USAGE_STORAGE), "set_subpixel_positioning", "get_subpixel_positioning"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_multichannel_signed_distance_field", "is_multichannel_signed_distance_field"); ADD_PROPERTY(PropertyInfo(Variant::INT, "msdf_pixel_range", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_msdf_pixel_range", "get_msdf_pixel_range"); @@ -1248,7 +1274,7 @@ void FontFile::_get_property_list(List<PropertyInfo> *p_list) const { } for (int i = 0; i < cache.size(); i++) { String prefix = "cache/" + itos(i) + "/"; - Array sizes = get_size_cache_list(i); + TypedArray<Vector2i> sizes = get_size_cache_list(i); p_list->push_back(PropertyInfo(Variant::DICTIONARY, prefix + "variation_coordinates", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); p_list->push_back(PropertyInfo(Variant::INT, "face_index", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); p_list->push_back(PropertyInfo(Variant::FLOAT, "embolden", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); @@ -1270,7 +1296,7 @@ void FontFile::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::PACKED_INT32_ARRAY, prefix_sz + "textures/" + itos(k) + "/offsets", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); p_list->push_back(PropertyInfo(Variant::OBJECT, prefix_sz + "textures/" + itos(k) + "/image", PROPERTY_HINT_RESOURCE_TYPE, "Image", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT)); } - Array glyphs = get_glyph_list(i, sz); + PackedInt32Array glyphs = get_glyph_list(i, sz); for (int k = 0; k < glyphs.size(); k++) { const int32_t &gl = glyphs[k]; if (sz.y == 0) { @@ -1282,7 +1308,7 @@ void FontFile::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::INT, prefix_sz + "glyphs/" + itos(gl) + "/texture_idx", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); } if (sz.y == 0) { - Array kerning_map = get_kerning_list(i, sz.x); + TypedArray<Vector2i> kerning_map = get_kerning_list(i, sz.x); for (int k = 0; k < kerning_map.size(); k++) { const Vector2i &gl_pair = kerning_map[k]; p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "kerning_overrides/" + itos(gl_pair.x) + "/" + itos(gl_pair.y), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); @@ -1299,7 +1325,7 @@ void FontFile::reset_state() { data_size = 0; cache.clear(); - antialiased = true; + antialiasing = TextServer::FONT_ANTIALIASING_GRAY; mipmaps = false; msdf = false; force_autohinter = false; @@ -1318,7 +1344,7 @@ void FontFile::reset_state() { Error FontFile::load_bitmap_font(const String &p_path) { reset_state(); - antialiased = false; + antialiasing = TextServer::FONT_ANTIALIASING_NONE; mipmaps = false; msdf = false; force_autohinter = false; @@ -1332,7 +1358,7 @@ Error FontFile::load_bitmap_font(const String &p_path) { int height = 0; int ascent = 0; int outline = 0; - uint32_t st_flags = 0; + BitField<TextServer::FontStyle> st_flags = 0; String font_name; bool packed = false; @@ -1358,10 +1384,10 @@ Error FontFile::load_bitmap_font(const String &p_path) { uint8_t flags = f->get_8(); ERR_FAIL_COND_V_MSG(flags & 0x02, ERR_CANT_CREATE, RTR("Non-unicode version of BMFont is not supported.")); if (flags & (1 << 3)) { - st_flags |= TextServer::FONT_BOLD; + st_flags.set_flag(TextServer::FONT_BOLD); } if (flags & (1 << 2)) { - st_flags |= TextServer::FONT_ITALIC; + st_flags.set_flag(TextServer::FONT_ITALIC); } f->get_8(); // non-unicode charset, skip f->get_16(); // stretch_h, skip @@ -1407,7 +1433,7 @@ Error FontFile::load_bitmap_font(const String &p_path) { while (!f->eof_reached() && f->get_position() <= off + block_size) { if (c == '\0') { String base_dir = p_path.get_base_dir(); - String file = base_dir.plus_file(String::utf8(cs.ptr(), cs.length())); + String file = base_dir.path_join(String::utf8(cs.ptr(), cs.length())); if (RenderingServer::get_singleton() != nullptr) { Ref<Image> img; img.instantiate(); @@ -1588,12 +1614,12 @@ Error FontFile::load_bitmap_font(const String &p_path) { } if (keys.has("bold")) { if (keys["bold"].to_int()) { - st_flags |= TextServer::FONT_BOLD; + st_flags.set_flag(TextServer::FONT_BOLD); } } if (keys.has("italic")) { if (keys["italic"].to_int()) { - st_flags |= TextServer::FONT_ITALIC; + st_flags.set_flag(TextServer::FONT_ITALIC); } } if (keys.has("face")) { @@ -1640,7 +1666,7 @@ Error FontFile::load_bitmap_font(const String &p_path) { } if (keys.has("file")) { String base_dir = p_path.get_base_dir(); - String file = base_dir.plus_file(keys["file"]); + String file = base_dir.path_join(keys["file"]); if (RenderingServer::get_singleton() != nullptr) { Ref<Image> img; img.instantiate(); @@ -1798,11 +1824,9 @@ void FontFile::set_data_ptr(const uint8_t *p_data, size_t p_size) { data_ptr = p_data; data_size = p_size; - if (data_ptr != nullptr) { - for (int i = 0; i < cache.size(); i++) { - if (cache[i].is_valid()) { - TS->font_set_data_ptr(cache[i], data_ptr, data_size); - } + for (int i = 0; i < cache.size(); i++) { + if (cache[i].is_valid()) { + TS->font_set_data_ptr(cache[i], data_ptr, data_size); } } } @@ -1812,11 +1836,9 @@ void FontFile::set_data(const PackedByteArray &p_data) { data_ptr = data.ptr(); data_size = data.size(); - if (data_ptr != nullptr) { - for (int i = 0; i < cache.size(); i++) { - if (cache[i].is_valid()) { - TS->font_set_data_ptr(cache[i], data_ptr, data_size); - } + for (int i = 0; i < cache.size(); i++) { + if (cache[i].is_valid()) { + TS->font_set_data_ptr(cache[i], data_ptr, data_size); } } } @@ -1840,24 +1862,24 @@ void FontFile::set_font_style_name(const String &p_name) { TS->font_set_style_name(cache[0], p_name); } -void FontFile::set_font_style(uint32_t p_style) { +void FontFile::set_font_style(BitField<TextServer::FontStyle> p_style) { _ensure_rid(0); TS->font_set_style(cache[0], p_style); } -void FontFile::set_antialiased(bool p_antialiased) { - if (antialiased != p_antialiased) { - antialiased = p_antialiased; +void FontFile::set_antialiasing(TextServer::FontAntialiasing p_antialiasing) { + if (antialiasing != p_antialiasing) { + antialiasing = p_antialiasing; for (int i = 0; i < cache.size(); i++) { _ensure_rid(i); - TS->font_set_antialiased(cache[i], antialiased); + TS->font_set_antialiasing(cache[i], antialiasing); } emit_changed(); } } -bool FontFile::is_antialiased() const { - return antialiased; +TextServer::FontAntialiasing FontFile::get_antialiasing() const { + return antialiasing; } void FontFile::set_generate_mipmaps(bool p_generate_mipmaps) { @@ -2070,7 +2092,7 @@ void FontFile::remove_cache(int p_cache_index) { emit_changed(); } -Array FontFile::get_size_cache_list(int p_cache_index) const { +TypedArray<Vector2i> FontFile::get_size_cache_list(int p_cache_index) const { ERR_FAIL_COND_V(p_cache_index < 0, Array()); _ensure_rid(p_cache_index); return TS->font_get_size_cache_list(cache[p_cache_index]); @@ -2241,8 +2263,8 @@ PackedInt32Array FontFile::get_texture_offsets(int p_cache_index, const Vector2i return TS->font_get_texture_offsets(cache[p_cache_index], p_size, p_texture_index); } -Array FontFile::get_glyph_list(int p_cache_index, const Vector2i &p_size) const { - ERR_FAIL_COND_V(p_cache_index < 0, Array()); +PackedInt32Array FontFile::get_glyph_list(int p_cache_index, const Vector2i &p_size) const { + ERR_FAIL_COND_V(p_cache_index < 0, PackedInt32Array()); _ensure_rid(p_cache_index); return TS->font_get_glyph_list(cache[p_cache_index], p_size); } @@ -2319,7 +2341,7 @@ int FontFile::get_glyph_texture_idx(int p_cache_index, const Vector2i &p_size, i return TS->font_get_glyph_texture_idx(cache[p_cache_index], p_size, p_glyph); } -Array FontFile::get_kerning_list(int p_cache_index, int p_size) const { +TypedArray<Vector2i> FontFile::get_kerning_list(int p_cache_index, int p_size) const { ERR_FAIL_COND_V(p_cache_index < 0, Array()); _ensure_rid(p_cache_index); return TS->font_get_kerning_list(cache[p_cache_index], p_size); @@ -2417,11 +2439,10 @@ int32_t FontFile::get_glyph_index(int p_size, char32_t p_char, char32_t p_variat } FontFile::FontFile() { - /* NOP */ } FontFile::~FontFile() { - reset_state(); + _clear_cache(); } /*************************************************************************/ @@ -2515,7 +2536,7 @@ void FontVariation::set_base_font(const Ref<Font> &p_font) { } base_font = p_font; if (base_font.is_valid()) { - base_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED); + base_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED); } _invalidate_rids(); notify_property_list_changed(); @@ -2537,16 +2558,16 @@ Ref<Font> FontVariation::_get_base_font_or_default() const { } // Check the project-defined Theme resource. - if (Theme::get_project_default().is_valid()) { + if (ThemeDB::get_singleton()->get_project_theme().is_valid()) { List<StringName> theme_types; - Theme::get_project_default()->get_type_dependencies(get_class_name(), StringName(), &theme_types); + ThemeDB::get_singleton()->get_project_theme()->get_type_dependencies(get_class_name(), StringName(), &theme_types); for (const StringName &E : theme_types) { - if (Theme::get_project_default()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { - Ref<Font> f = Theme::get_project_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); + if (ThemeDB::get_singleton()->get_project_theme()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { + Ref<Font> f = ThemeDB::get_singleton()->get_project_theme()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); if (f.is_valid()) { theme_font = f; - theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED); + theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED); } return f; } @@ -2554,26 +2575,26 @@ Ref<Font> FontVariation::_get_base_font_or_default() const { } // Lastly, fall back on the items defined in the default Theme, if they exist. - if (Theme::get_default().is_valid()) { + if (ThemeDB::get_singleton()->get_default_theme().is_valid()) { List<StringName> theme_types; - Theme::get_default()->get_type_dependencies(get_class_name(), StringName(), &theme_types); + ThemeDB::get_singleton()->get_default_theme()->get_type_dependencies(get_class_name(), StringName(), &theme_types); for (const StringName &E : theme_types) { - if (Theme::get_default()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { - Ref<Font> f = Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); + if (ThemeDB::get_singleton()->get_default_theme()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { + Ref<Font> f = ThemeDB::get_singleton()->get_default_theme()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); if (f.is_valid()) { theme_font = f; - theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED); + theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED); } return f; } } // If they don't exist, use any type to return the default/empty value. - Ref<Font> f = Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", StringName()); + Ref<Font> f = ThemeDB::get_singleton()->get_default_theme()->get_theme_item(Theme::DATA_TYPE_FONT, "font", StringName()); if (f.is_valid()) { theme_font = f; - theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED); + theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED); } return f; } @@ -2672,5 +2693,396 @@ FontVariation::FontVariation() { } FontVariation::~FontVariation() { - reset_state(); +} + +/*************************************************************************/ +/* SystemFont */ +/*************************************************************************/ + +void SystemFont::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_antialiasing", "antialiasing"), &SystemFont::set_antialiasing); + ClassDB::bind_method(D_METHOD("get_antialiasing"), &SystemFont::get_antialiasing); + + ClassDB::bind_method(D_METHOD("set_generate_mipmaps", "generate_mipmaps"), &SystemFont::set_generate_mipmaps); + ClassDB::bind_method(D_METHOD("get_generate_mipmaps"), &SystemFont::get_generate_mipmaps); + + ClassDB::bind_method(D_METHOD("set_force_autohinter", "force_autohinter"), &SystemFont::set_force_autohinter); + ClassDB::bind_method(D_METHOD("is_force_autohinter"), &SystemFont::is_force_autohinter); + + ClassDB::bind_method(D_METHOD("set_hinting", "hinting"), &SystemFont::set_hinting); + ClassDB::bind_method(D_METHOD("get_hinting"), &SystemFont::get_hinting); + + ClassDB::bind_method(D_METHOD("set_subpixel_positioning", "subpixel_positioning"), &SystemFont::set_subpixel_positioning); + ClassDB::bind_method(D_METHOD("get_subpixel_positioning"), &SystemFont::get_subpixel_positioning); + + ClassDB::bind_method(D_METHOD("set_multichannel_signed_distance_field", "msdf"), &SystemFont::set_multichannel_signed_distance_field); + ClassDB::bind_method(D_METHOD("is_multichannel_signed_distance_field"), &SystemFont::is_multichannel_signed_distance_field); + + ClassDB::bind_method(D_METHOD("set_oversampling", "oversampling"), &SystemFont::set_oversampling); + ClassDB::bind_method(D_METHOD("get_oversampling"), &SystemFont::get_oversampling); + + ClassDB::bind_method(D_METHOD("get_font_names"), &SystemFont::get_font_names); + ClassDB::bind_method(D_METHOD("set_font_names", "names"), &SystemFont::set_font_names); + + ClassDB::bind_method(D_METHOD("set_font_style", "style"), &SystemFont::set_font_style); + + ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "font_names"), "set_font_names", "get_font_names"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "font_style", PROPERTY_HINT_FLAGS, "Bold,Italic"), "set_font_style", "get_font_style"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "antialiasing", PROPERTY_HINT_ENUM, "None,Grayscale,LCD sub-pixel", PROPERTY_USAGE_STORAGE), "set_antialiasing", "get_antialiasing"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "generate_mipmaps"), "set_generate_mipmaps", "get_generate_mipmaps"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "force_autohinter"), "set_force_autohinter", "is_force_autohinter"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal"), "set_hinting", "get_hinting"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One half of a pixel,One quarter of a pixel"), "set_subpixel_positioning", "get_subpixel_positioning"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field"), "set_multichannel_signed_distance_field", "is_multichannel_signed_distance_field"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_RANGE, "0,10,0.1"), "set_oversampling", "get_oversampling"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "Font")), "set_fallbacks", "get_fallbacks"); +} + +void SystemFont::_update_rids() const { + Ref<Font> f = _get_base_font_or_default(); + + rids.clear(); + if (fallbacks.is_empty() && f.is_valid()) { + RID rid = _get_rid(); + if (rid.is_valid()) { + rids.push_back(rid); + } + + const TypedArray<Font> &base_fallbacks = f->get_fallbacks(); + for (int i = 0; i < base_fallbacks.size(); i++) { + _update_rids_fb(base_fallbacks[i], 0); + } + } else { + _update_rids_fb(const_cast<SystemFont *>(this), 0); + } + dirty_rids = false; +} + +void SystemFont::_update_base_font() { + if (base_font.is_valid()) { + base_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids)); + base_font.unref(); + } + + face_indeces.clear(); + ftr_weight = 0; + ftr_italic = 0; + for (const String &E : names) { + if (E.is_empty()) { + continue; + } + + String path = OS::get_singleton()->get_system_font_path(E, style & TextServer::FONT_BOLD, style & TextServer::FONT_ITALIC); + if (path.is_empty()) { + continue; + } + Ref<FontFile> file; + file.instantiate(); + Error err = file->load_dynamic_font(path); + if (err != OK) { + continue; + } + + // If it's a font collection check all faces to match requested style. + for (int i = 0; i < file->get_face_count(); i++) { + file->set_face_index(0, i); + if (((file->get_font_style() & TextServer::FONT_BOLD) == (style & TextServer::FONT_BOLD)) && ((file->get_font_style() & TextServer::FONT_ITALIC) == (style & TextServer::FONT_ITALIC))) { + face_indeces.push_back(i); + } + } + if (face_indeces.is_empty()) { + face_indeces.push_back(0); + } + file->set_face_index(0, face_indeces[0]); + + // If it's a variable font, apply weight and italic coordinates to match requested style. + Dictionary ftr = file->get_supported_variation_list(); + if ((style & TextServer::FONT_BOLD) && ftr.has(TS->name_to_tag("weight"))) { + ftr_weight = 700; + } + if ((style & TextServer::FONT_ITALIC) && ftr.has(TS->name_to_tag("italic"))) { + ftr_italic = 1; + } + + // Apply font rendering settings. + file->set_antialiasing(antialiasing); + file->set_generate_mipmaps(mipmaps); + file->set_force_autohinter(force_autohinter); + file->set_hinting(hinting); + file->set_subpixel_positioning(subpixel_positioning); + file->set_multichannel_signed_distance_field(msdf); + file->set_oversampling(oversampling); + + base_font = file; + + break; + } + + if (base_font.is_valid()) { + base_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED); + } + + _invalidate_rids(); + notify_property_list_changed(); +} + +void SystemFont::reset_state() { + if (base_font.is_valid()) { + base_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids)); + base_font.unref(); + } + + if (theme_font.is_valid()) { + theme_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids)); + theme_font.unref(); + } + + names.clear(); + face_indeces.clear(); + ftr_weight = 0; + ftr_italic = 0; + style = 0; + antialiasing = TextServer::FONT_ANTIALIASING_GRAY; + mipmaps = false; + force_autohinter = false; + hinting = TextServer::HINTING_LIGHT; + subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_DISABLED; + oversampling = 0.f; + msdf = false; + + Font::reset_state(); +} + +Ref<Font> SystemFont::_get_base_font_or_default() const { + if (theme_font.is_valid()) { + theme_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids)); + theme_font.unref(); + } + + if (base_font.is_valid()) { + return base_font; + } + + // Check the project-defined Theme resource. + if (ThemeDB::get_singleton()->get_project_theme().is_valid()) { + List<StringName> theme_types; + ThemeDB::get_singleton()->get_project_theme()->get_type_dependencies(get_class_name(), StringName(), &theme_types); + + for (const StringName &E : theme_types) { + if (ThemeDB::get_singleton()->get_project_theme()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { + Ref<Font> f = ThemeDB::get_singleton()->get_project_theme()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); + if (f.is_valid()) { + theme_font = f; + theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED); + } + return f; + } + } + } + + // Lastly, fall back on the items defined in the default Theme, if they exist. + if (ThemeDB::get_singleton()->get_default_theme().is_valid()) { + List<StringName> theme_types; + ThemeDB::get_singleton()->get_default_theme()->get_type_dependencies(get_class_name(), StringName(), &theme_types); + + for (const StringName &E : theme_types) { + if (ThemeDB::get_singleton()->get_default_theme()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { + Ref<Font> f = ThemeDB::get_singleton()->get_default_theme()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); + if (f.is_valid()) { + theme_font = f; + theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED); + } + return f; + } + } + + // If they don't exist, use any type to return the default/empty value. + Ref<Font> f = ThemeDB::get_singleton()->get_default_theme()->get_theme_item(Theme::DATA_TYPE_FONT, "font", StringName()); + if (f.is_valid()) { + theme_font = f; + theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED); + } + return f; + } + + return Ref<Font>(); +} + +void SystemFont::set_antialiasing(TextServer::FontAntialiasing p_antialiasing) { + if (antialiasing != p_antialiasing) { + antialiasing = p_antialiasing; + if (base_font.is_valid()) { + base_font->set_antialiasing(antialiasing); + } + emit_changed(); + } +} + +TextServer::FontAntialiasing SystemFont::get_antialiasing() const { + return antialiasing; +} + +void SystemFont::set_generate_mipmaps(bool p_generate_mipmaps) { + if (mipmaps != p_generate_mipmaps) { + mipmaps = p_generate_mipmaps; + if (base_font.is_valid()) { + base_font->set_generate_mipmaps(mipmaps); + } + emit_changed(); + } +} + +bool SystemFont::get_generate_mipmaps() const { + return mipmaps; +} + +void SystemFont::set_force_autohinter(bool p_force_autohinter) { + if (force_autohinter != p_force_autohinter) { + force_autohinter = p_force_autohinter; + if (base_font.is_valid()) { + base_font->set_force_autohinter(force_autohinter); + } + emit_changed(); + } +} + +bool SystemFont::is_force_autohinter() const { + return force_autohinter; +} + +void SystemFont::set_hinting(TextServer::Hinting p_hinting) { + if (hinting != p_hinting) { + hinting = p_hinting; + if (base_font.is_valid()) { + base_font->set_hinting(hinting); + } + emit_changed(); + } +} + +TextServer::Hinting SystemFont::get_hinting() const { + return hinting; +} + +void SystemFont::set_subpixel_positioning(TextServer::SubpixelPositioning p_subpixel) { + if (subpixel_positioning != p_subpixel) { + subpixel_positioning = p_subpixel; + if (base_font.is_valid()) { + base_font->set_subpixel_positioning(subpixel_positioning); + } + emit_changed(); + } +} + +TextServer::SubpixelPositioning SystemFont::get_subpixel_positioning() const { + return subpixel_positioning; +} + +void SystemFont::set_multichannel_signed_distance_field(bool p_msdf) { + if (msdf != p_msdf) { + msdf = p_msdf; + if (base_font.is_valid()) { + base_font->set_multichannel_signed_distance_field(msdf); + } + emit_changed(); + } +} + +bool SystemFont::is_multichannel_signed_distance_field() const { + return msdf; +} + +void SystemFont::set_oversampling(real_t p_oversampling) { + if (oversampling != p_oversampling) { + oversampling = p_oversampling; + if (base_font.is_valid()) { + base_font->set_oversampling(oversampling); + } + emit_changed(); + } +} + +real_t SystemFont::get_oversampling() const { + return oversampling; +} + +void SystemFont::set_font_names(const PackedStringArray &p_names) { + if (names != p_names) { + names = p_names; + _update_base_font(); + } +} + +PackedStringArray SystemFont::get_font_names() const { + return names; +} + +void SystemFont::set_font_style(BitField<TextServer::FontStyle> p_style) { + if (style != p_style) { + style = p_style; + _update_base_font(); + } +} + +BitField<TextServer::FontStyle> SystemFont::get_font_style() const { + return style; +} + +int SystemFont::get_spacing(TextServer::SpacingType p_spacing) const { + if (base_font.is_valid()) { + return base_font->get_spacing(p_spacing); + } else { + return 0; + } +} + +RID SystemFont::find_variation(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform) const { + Ref<Font> f = _get_base_font_or_default(); + if (f.is_valid()) { + Dictionary var = p_variation_coordinates; + if (ftr_weight > 0 && !var.has(TS->name_to_tag("weight"))) { + var[TS->name_to_tag("weight")] = ftr_weight; + } + if (ftr_italic > 0 && !var.has(TS->name_to_tag("italic"))) { + var[TS->name_to_tag("italic")] = ftr_italic; + } + + if (!face_indeces.is_empty()) { + int face_index = CLAMP(p_face_index, 0, face_indeces.size() - 1); + return f->find_variation(var, face_indeces[face_index], p_strength, p_transform); + } else { + return f->find_variation(var, 0, p_strength, p_transform); + } + } + return RID(); +} + +RID SystemFont::_get_rid() const { + Ref<Font> f = _get_base_font_or_default(); + if (f.is_valid()) { + if (!face_indeces.is_empty()) { + Dictionary var; + if (ftr_weight > 0) { + var[TS->name_to_tag("weight")] = ftr_weight; + } + if (ftr_italic > 0) { + var[TS->name_to_tag("italic")] = ftr_italic; + } + return f->find_variation(var, face_indeces[0]); + } else { + return f->_get_rid(); + } + } + return RID(); +} + +int64_t SystemFont::get_face_count() const { + return face_indeces.size(); +} + +SystemFont::SystemFont() { + /* NOP */ +} + +SystemFont::~SystemFont() { } diff --git a/scene/resources/font.h b/scene/resources/font.h index 40b223b0f5..5cf596b41d 100644 --- a/scene/resources/font.h +++ b/scene/resources/font.h @@ -41,7 +41,7 @@ class TextLine; class TextParagraph; /*************************************************************************/ -/* Font */ +/* Font */ /*************************************************************************/ class Font : public Resource { @@ -91,7 +91,7 @@ public: virtual String get_font_name() const; virtual String get_font_style_name() const; - virtual uint32_t get_font_style() const; + virtual BitField<TextServer::FontStyle> get_font_style() const; virtual int get_spacing(TextServer::SpacingType p_spacing) const { return 0; }; virtual Dictionary get_opentype_features() const; @@ -99,14 +99,14 @@ public: // Drawing string. virtual void set_cache_capacity(int p_single_line, int p_multi_line); - virtual Size2 get_string_size(const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = DEFAULT_FONT_SIZE, uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const; - virtual Size2 get_multiline_string_size(const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = DEFAULT_FONT_SIZE, int p_max_lines = -1, uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const; + virtual Size2 get_string_size(const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = DEFAULT_FONT_SIZE, BitField<TextServer::JustificationFlag> p_jst_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const; + virtual Size2 get_multiline_string_size(const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = DEFAULT_FONT_SIZE, int p_max_lines = -1, BitField<TextServer::LineBreakFlag> p_brk_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND, BitField<TextServer::JustificationFlag> p_jst_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const; - virtual void draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1.0, 1.0, 1.0), uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const; - virtual void draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = DEFAULT_FONT_SIZE, int p_max_lines = -1, const Color &p_modulate = Color(1.0, 1.0, 1.0), uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const; + virtual void draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1.0, 1.0, 1.0), BitField<TextServer::JustificationFlag> p_jst_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const; + virtual void draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = DEFAULT_FONT_SIZE, int p_max_lines = -1, const Color &p_modulate = Color(1.0, 1.0, 1.0), BitField<TextServer::LineBreakFlag> p_brk_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND, BitField<TextServer::JustificationFlag> p_jst_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const; - virtual void draw_string_outline(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = DEFAULT_FONT_SIZE, int p_size = 1, const Color &p_modulate = Color(1.0, 1.0, 1.0), uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const; - virtual void draw_multiline_string_outline(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = DEFAULT_FONT_SIZE, int p_max_lines = -1, int p_size = 1, const Color &p_modulate = Color(1.0, 1.0, 1.0), uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const; + virtual void draw_string_outline(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = DEFAULT_FONT_SIZE, int p_size = 1, const Color &p_modulate = Color(1.0, 1.0, 1.0), BitField<TextServer::JustificationFlag> p_jst_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const; + virtual void draw_multiline_string_outline(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = DEFAULT_FONT_SIZE, int p_max_lines = -1, int p_size = 1, const Color &p_modulate = Color(1.0, 1.0, 1.0), BitField<TextServer::LineBreakFlag> p_brk_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND, BitField<TextServer::JustificationFlag> p_jst_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const; // Drawing char. virtual Size2 get_char_size(char32_t p_char, int p_font_size = DEFAULT_FONT_SIZE) const; @@ -141,7 +141,7 @@ class FontFile : public Font { size_t data_size = 0; PackedByteArray data; - bool antialiased = true; + TextServer::FontAntialiasing antialiasing = TextServer::FONT_ANTIALIASING_GRAY; bool mipmaps = false; bool msdf = false; int msdf_pixel_range = 16; @@ -190,10 +190,10 @@ public: // Common properties. virtual void set_font_name(const String &p_name); virtual void set_font_style_name(const String &p_name); - virtual void set_font_style(uint32_t p_style); + virtual void set_font_style(BitField<TextServer::FontStyle> p_style); - virtual void set_antialiased(bool p_antialiased); - virtual bool is_antialiased() const; + virtual void set_antialiasing(TextServer::FontAntialiasing p_antialiasing); + virtual TextServer::FontAntialiasing get_antialiasing() const; virtual void set_generate_mipmaps(bool p_generate_mipmaps); virtual bool get_generate_mipmaps() const; @@ -230,7 +230,7 @@ public: virtual void clear_cache(); virtual void remove_cache(int p_cache_index); - virtual Array get_size_cache_list(int p_cache_index) const; + virtual TypedArray<Vector2i> get_size_cache_list(int p_cache_index) const; virtual void clear_size_cache(int p_cache_index); virtual void remove_size_cache(int p_cache_index, const Vector2i &p_size); @@ -271,7 +271,7 @@ public: virtual void set_texture_offsets(int p_cache_index, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset); virtual PackedInt32Array get_texture_offsets(int p_cache_index, const Vector2i &p_size, int p_texture_index) const; - virtual Array get_glyph_list(int p_cache_index, const Vector2i &p_size) const; + virtual PackedInt32Array get_glyph_list(int p_cache_index, const Vector2i &p_size) const; virtual void clear_glyphs(int p_cache_index, const Vector2i &p_size); virtual void remove_glyph(int p_cache_index, const Vector2i &p_size, int32_t p_glyph); @@ -290,7 +290,7 @@ public: virtual void set_glyph_texture_idx(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx); virtual int get_glyph_texture_idx(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const; - virtual Array get_kerning_list(int p_cache_index, int p_size) const; + virtual TypedArray<Vector2i> get_kerning_list(int p_cache_index, int p_size) const; virtual void clear_kerning_map(int p_cache_index, int p_size); virtual void remove_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair); @@ -381,4 +381,78 @@ public: ~FontVariation(); }; -#endif /* FONT_H */ +/*************************************************************************/ +/* SystemFont */ +/*************************************************************************/ + +class SystemFont : public Font { + GDCLASS(SystemFont, Font); + + PackedStringArray names; + BitField<TextServer::FontStyle> style = 0; + + mutable Ref<Font> theme_font; + + Ref<FontFile> base_font; + Vector<int> face_indeces; + int ftr_weight = 0; + int ftr_italic = 0; + + TextServer::FontAntialiasing antialiasing = TextServer::FONT_ANTIALIASING_GRAY; + bool mipmaps = false; + bool force_autohinter = false; + TextServer::Hinting hinting = TextServer::HINTING_LIGHT; + TextServer::SubpixelPositioning subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO; + real_t oversampling = 0.f; + bool msdf = false; + +protected: + static void _bind_methods(); + + virtual void _update_base_font(); + virtual void _update_rids() const override; + + virtual void reset_state() override; + +public: + virtual Ref<Font> _get_base_font_or_default() const; + + virtual void set_antialiasing(TextServer::FontAntialiasing p_antialiasing); + virtual TextServer::FontAntialiasing get_antialiasing() const; + + virtual void set_generate_mipmaps(bool p_generate_mipmaps); + virtual bool get_generate_mipmaps() const; + + virtual void set_force_autohinter(bool p_force_autohinter); + virtual bool is_force_autohinter() const; + + virtual void set_hinting(TextServer::Hinting p_hinting); + virtual TextServer::Hinting get_hinting() const; + + virtual void set_subpixel_positioning(TextServer::SubpixelPositioning p_subpixel); + virtual TextServer::SubpixelPositioning get_subpixel_positioning() const; + + virtual void set_oversampling(real_t p_oversampling); + virtual real_t get_oversampling() const; + + virtual void set_multichannel_signed_distance_field(bool p_msdf); + virtual bool is_multichannel_signed_distance_field() const; + + virtual void set_font_names(const PackedStringArray &p_names); + virtual PackedStringArray get_font_names() const; + + virtual void set_font_style(BitField<TextServer::FontStyle> p_style); + virtual BitField<TextServer::FontStyle> get_font_style() const override; + + virtual int get_spacing(TextServer::SpacingType p_spacing) const override; + + virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D()) const override; + virtual RID _get_rid() const override; + + int64_t get_face_count() const override; + + SystemFont(); + ~SystemFont(); +}; + +#endif // FONT_H diff --git a/scene/resources/gradient.cpp b/scene/resources/gradient.cpp index a9c44dc6bf..f04eb75d86 100644 --- a/scene/resources/gradient.cpp +++ b/scene/resources/gradient.cpp @@ -56,7 +56,7 @@ void Gradient::_bind_methods() { ClassDB::bind_method(D_METHOD("set_color", "point", "color"), &Gradient::set_color); ClassDB::bind_method(D_METHOD("get_color", "point"), &Gradient::get_color); - ClassDB::bind_method(D_METHOD("interpolate", "offset"), &Gradient::get_color_at_offset); + ClassDB::bind_method(D_METHOD("sample", "offset"), &Gradient::get_color_at_offset); ClassDB::bind_method(D_METHOD("get_point_count"), &Gradient::get_points_count); diff --git a/scene/resources/gradient.h b/scene/resources/gradient.h index 2b04ead0af..2b91331ab0 100644 --- a/scene/resources/gradient.h +++ b/scene/resources/gradient.h @@ -122,7 +122,7 @@ public: } } - // Return interpolated value. + // Return sampled value. if (points[middle].offset > p_offset) { middle--; } @@ -157,10 +157,10 @@ public: const Point &pointP3 = points[p3]; float x = (p_offset - pointFirst.offset) / (pointSecond.offset - pointFirst.offset); - float r = Math::cubic_interpolate(pointP0.color.r, pointFirst.color.r, pointSecond.color.r, pointP3.color.r, x); - float g = Math::cubic_interpolate(pointP0.color.g, pointFirst.color.g, pointSecond.color.g, pointP3.color.g, x); - float b = Math::cubic_interpolate(pointP0.color.b, pointFirst.color.b, pointSecond.color.b, pointP3.color.b, x); - float a = Math::cubic_interpolate(pointP0.color.a, pointFirst.color.a, pointSecond.color.a, pointP3.color.a, x); + float r = Math::cubic_interpolate(pointFirst.color.r, pointSecond.color.r, pointP0.color.r, pointP3.color.r, x); + float g = Math::cubic_interpolate(pointFirst.color.g, pointSecond.color.g, pointP0.color.g, pointP3.color.g, x); + float b = Math::cubic_interpolate(pointFirst.color.b, pointSecond.color.b, pointP0.color.b, pointP3.color.b, x); + float a = Math::cubic_interpolate(pointFirst.color.a, pointSecond.color.a, pointP0.color.a, pointP3.color.a, x); return Color(r, g, b, a); } break; diff --git a/scene/resources/height_map_shape_3d.h b/scene/resources/height_map_shape_3d.h index 79d1b15674..633238030b 100644 --- a/scene/resources/height_map_shape_3d.h +++ b/scene/resources/height_map_shape_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef HEIGHT_MAP_SHAPE_H -#define HEIGHT_MAP_SHAPE_H +#ifndef HEIGHT_MAP_SHAPE_3D_H +#define HEIGHT_MAP_SHAPE_3D_H #include "scene/resources/shape_3d.h" @@ -60,4 +60,4 @@ public: HeightMapShape3D(); }; -#endif /* !HEIGHT_MAP_SHAPE_H */ +#endif // HEIGHT_MAP_SHAPE_3D_H diff --git a/scene/resources/immediate_mesh.cpp b/scene/resources/immediate_mesh.cpp index 044477e744..90cc3ea5f4 100644 --- a/scene/resources/immediate_mesh.cpp +++ b/scene/resources/immediate_mesh.cpp @@ -340,8 +340,8 @@ Array ImmediateMesh::surface_get_arrays(int p_surface) const { ERR_FAIL_INDEX_V(p_surface, int(surfaces.size()), Array()); return RS::get_singleton()->mesh_surface_get_arrays(mesh, p_surface); } -Array ImmediateMesh::surface_get_blend_shape_arrays(int p_surface) const { - return Array(); +TypedArray<Array> ImmediateMesh::surface_get_blend_shape_arrays(int p_surface) const { + return TypedArray<Array>(); } Dictionary ImmediateMesh::surface_get_lods(int p_surface) const { return Dictionary(); diff --git a/scene/resources/immediate_mesh.h b/scene/resources/immediate_mesh.h index e5f627ae8e..0dad62f555 100644 --- a/scene/resources/immediate_mesh.h +++ b/scene/resources/immediate_mesh.h @@ -97,7 +97,7 @@ public: virtual int surface_get_array_len(int p_idx) const override; virtual int surface_get_array_index_len(int p_idx) const override; virtual Array surface_get_arrays(int p_surface) const override; - virtual Array surface_get_blend_shape_arrays(int p_surface) const override; + virtual TypedArray<Array> surface_get_blend_shape_arrays(int p_surface) const override; virtual Dictionary surface_get_lods(int p_surface) const override; virtual uint32_t surface_get_format(int p_idx) const override; virtual PrimitiveType surface_get_primitive_type(int p_idx) const override; @@ -115,4 +115,4 @@ public: ~ImmediateMesh(); }; -#endif // IMMEDIATEMESH_H +#endif // IMMEDIATE_MESH_H diff --git a/scene/resources/importer_mesh.cpp b/scene/resources/importer_mesh.cpp index 293fdd6f05..de3d502102 100644 --- a/scene/resources/importer_mesh.cpp +++ b/scene/resources/importer_mesh.cpp @@ -154,7 +154,7 @@ Mesh::BlendShapeMode ImporterMesh::get_blend_shape_mode() const { return blend_shape_mode; } -void ImporterMesh::add_surface(Mesh::PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes, const Dictionary &p_lods, const Ref<Material> &p_material, const String &p_name, const uint32_t p_flags) { +void ImporterMesh::add_surface(Mesh::PrimitiveType p_primitive, const Array &p_arrays, const TypedArray<Array> &p_blend_shapes, const Dictionary &p_lods, const Ref<Material> &p_material, const String &p_name, const uint32_t p_flags) { ERR_FAIL_COND(p_blend_shapes.size() != blend_shapes.size()); ERR_FAIL_COND(p_arrays.size() != Mesh::ARRAY_MAX); Surface s; @@ -254,7 +254,20 @@ void ImporterMesh::set_surface_material(int p_surface, const Ref<Material> &p_ma mesh.unref(); } -void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_split_angle) { +#define VERTEX_SKIN_FUNC(bone_count, vert_idx, read_array, write_array, transform_array, bone_array, weight_array) \ + Vector3 transformed_vert = Vector3(); \ + for (unsigned int weight_idx = 0; weight_idx < bone_count; weight_idx++) { \ + int bone_idx = bone_array[vert_idx * bone_count + weight_idx]; \ + float w = weight_array[vert_idx * bone_count + weight_idx]; \ + if (w < FLT_EPSILON) { \ + continue; \ + } \ + ERR_FAIL_INDEX(bone_idx, static_cast<int>(transform_array.size())); \ + transformed_vert += transform_array[bone_idx].xform(read_array[vert_idx]) * w; \ + } \ + write_array[vert_idx] = transformed_vert; + +void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_split_angle, Array p_bone_transform_array) { if (!SurfaceTool::simplify_scale_func) { return; } @@ -265,6 +278,12 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli return; } + LocalVector<Transform3D> bone_transform_vector; + for (int i = 0; i < p_bone_transform_array.size(); i++) { + ERR_FAIL_COND(p_bone_transform_array[i].get_type() != Variant::TRANSFORM3D); + bone_transform_vector.push_back(p_bone_transform_array[i]); + } + for (int i = 0; i < surfaces.size(); i++) { if (surfaces[i].primitive != Mesh::PRIMITIVE_TRIANGLES) { continue; @@ -276,6 +295,8 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli Vector<Vector3> normals = surfaces[i].arrays[RS::ARRAY_NORMAL]; Vector<Vector2> uvs = surfaces[i].arrays[RS::ARRAY_TEX_UV]; Vector<Vector2> uv2s = surfaces[i].arrays[RS::ARRAY_TEX_UV2]; + Vector<int> bones = surfaces[i].arrays[RS::ARRAY_BONES]; + Vector<float> weights = surfaces[i].arrays[RS::ARRAY_WEIGHTS]; unsigned int index_count = indices.size(); unsigned int vertex_count = vertices.size(); @@ -301,9 +322,25 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli } } - float normal_merge_threshold = Math::cos(Math::deg2rad(p_normal_merge_angle)); - float normal_pre_split_threshold = Math::cos(Math::deg2rad(MIN(180.0f, p_normal_split_angle * 2.0f))); - float normal_split_threshold = Math::cos(Math::deg2rad(p_normal_split_angle)); + if (bones.size() > 0 && weights.size() && bone_transform_vector.size() > 0) { + Vector3 *vertices_ptrw = vertices.ptrw(); + + // Apply bone transforms to regular surface. + unsigned int bone_weight_length = surfaces[i].flags & Mesh::ARRAY_FLAG_USE_8_BONE_WEIGHTS ? 8 : 4; + + const int *bo = bones.ptr(); + const float *we = weights.ptr(); + + for (unsigned int j = 0; j < vertex_count; j++) { + VERTEX_SKIN_FUNC(bone_weight_length, j, vertices_ptr, vertices_ptrw, bone_transform_vector, bo, we) + } + + vertices_ptr = vertices.ptr(); + } + + float normal_merge_threshold = Math::cos(Math::deg_to_rad(p_normal_merge_angle)); + float normal_pre_split_threshold = Math::cos(Math::deg_to_rad(MIN(180.0f, p_normal_split_angle * 2.0f))); + float normal_split_threshold = Math::cos(Math::deg_to_rad(p_normal_split_angle)); const Vector3 *normals_ptr = normals.ptr(); HashMap<Vector3, LocalVector<Pair<int, int>>> unique_vertices; @@ -1230,7 +1267,7 @@ void ImporterMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_blend_shape_mode", "mode"), &ImporterMesh::set_blend_shape_mode); ClassDB::bind_method(D_METHOD("get_blend_shape_mode"), &ImporterMesh::get_blend_shape_mode); - ClassDB::bind_method(D_METHOD("add_surface", "primitive", "arrays", "blend_shapes", "lods", "material", "name", "flags"), &ImporterMesh::add_surface, DEFVAL(Array()), DEFVAL(Dictionary()), DEFVAL(Ref<Material>()), DEFVAL(String()), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("add_surface", "primitive", "arrays", "blend_shapes", "lods", "material", "name", "flags"), &ImporterMesh::add_surface, DEFVAL(TypedArray<Array>()), DEFVAL(Dictionary()), DEFVAL(Ref<Material>()), DEFVAL(String()), DEFVAL(0)); ClassDB::bind_method(D_METHOD("get_surface_count"), &ImporterMesh::get_surface_count); ClassDB::bind_method(D_METHOD("get_surface_primitive_type", "surface_idx"), &ImporterMesh::get_surface_primitive_type); @@ -1246,7 +1283,7 @@ void ImporterMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_surface_name", "surface_idx", "name"), &ImporterMesh::set_surface_name); ClassDB::bind_method(D_METHOD("set_surface_material", "surface_idx", "material"), &ImporterMesh::set_surface_material); - ClassDB::bind_method(D_METHOD("generate_lods", "normal_merge_angle", "normal_split_angle"), &ImporterMesh::generate_lods); + ClassDB::bind_method(D_METHOD("generate_lods", "normal_merge_angle", "normal_split_angle", "bone_transform_array"), &ImporterMesh::generate_lods); ClassDB::bind_method(D_METHOD("get_mesh", "base_mesh"), &ImporterMesh::get_mesh, DEFVAL(Ref<ArrayMesh>())); ClassDB::bind_method(D_METHOD("clear"), &ImporterMesh::clear); diff --git a/scene/resources/importer_mesh.h b/scene/resources/importer_mesh.h index 8f77597a58..088a77edd1 100644 --- a/scene/resources/importer_mesh.h +++ b/scene/resources/importer_mesh.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SCENE_IMPORTER_MESH_H -#define SCENE_IMPORTER_MESH_H +#ifndef IMPORTER_MESH_H +#define IMPORTER_MESH_H #include "core/io/resource.h" #include "core/templates/local_vector.h" @@ -93,7 +93,7 @@ public: int get_blend_shape_count() const; String get_blend_shape_name(int p_blend_shape) const; - void add_surface(Mesh::PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes = Array(), const Dictionary &p_lods = Dictionary(), const Ref<Material> &p_material = Ref<Material>(), const String &p_name = String(), const uint32_t p_flags = 0); + void add_surface(Mesh::PrimitiveType p_primitive, const Array &p_arrays, const TypedArray<Array> &p_blend_shapes = Array(), const Dictionary &p_lods = Dictionary(), const Ref<Material> &p_material = Ref<Material>(), const String &p_name = String(), const uint32_t p_flags = 0); int get_surface_count() const; void set_blend_shape_mode(Mesh::BlendShapeMode p_blend_shape_mode); @@ -112,7 +112,7 @@ public: void set_surface_material(int p_surface, const Ref<Material> &p_material); - void generate_lods(float p_normal_merge_angle, float p_normal_split_angle); + void generate_lods(float p_normal_merge_angle, float p_normal_split_angle, Array p_skin_pose_transform_array); void create_shadow_mesh(); Ref<ImporterMesh> get_shadow_mesh() const; @@ -130,4 +130,5 @@ public: Ref<ArrayMesh> get_mesh(const Ref<ArrayMesh> &p_base = Ref<ArrayMesh>()); void clear(); }; -#endif // SCENE_IMPORTER_MESH_H + +#endif // IMPORTER_MESH_H diff --git a/scene/resources/label_settings.cpp b/scene/resources/label_settings.cpp new file mode 100644 index 0000000000..ef380a68f9 --- /dev/null +++ b/scene/resources/label_settings.cpp @@ -0,0 +1,187 @@ +/*************************************************************************/ +/* label_settings.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "label_settings.h" + +#include "core/core_string_names.h" + +void LabelSettings::_font_changed() { + emit_changed(); +} + +void LabelSettings::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_line_spacing", "spacing"), &LabelSettings::set_line_spacing); + ClassDB::bind_method(D_METHOD("get_line_spacing"), &LabelSettings::get_line_spacing); + + ClassDB::bind_method(D_METHOD("set_font", "font"), &LabelSettings::set_font); + ClassDB::bind_method(D_METHOD("get_font"), &LabelSettings::get_font); + + ClassDB::bind_method(D_METHOD("set_font_size", "size"), &LabelSettings::set_font_size); + ClassDB::bind_method(D_METHOD("get_font_size"), &LabelSettings::get_font_size); + + ClassDB::bind_method(D_METHOD("set_font_color", "color"), &LabelSettings::set_font_color); + ClassDB::bind_method(D_METHOD("get_font_color"), &LabelSettings::get_font_color); + + ClassDB::bind_method(D_METHOD("set_outline_size", "size"), &LabelSettings::set_outline_size); + ClassDB::bind_method(D_METHOD("get_outline_size"), &LabelSettings::get_outline_size); + + ClassDB::bind_method(D_METHOD("set_outline_color", "color"), &LabelSettings::set_outline_color); + ClassDB::bind_method(D_METHOD("get_outline_color"), &LabelSettings::get_outline_color); + + ClassDB::bind_method(D_METHOD("set_shadow_size", "size"), &LabelSettings::set_shadow_size); + ClassDB::bind_method(D_METHOD("get_shadow_size"), &LabelSettings::get_shadow_size); + + ClassDB::bind_method(D_METHOD("set_shadow_color", "color"), &LabelSettings::set_shadow_color); + ClassDB::bind_method(D_METHOD("get_shadow_color"), &LabelSettings::get_shadow_color); + + ClassDB::bind_method(D_METHOD("set_shadow_offset", "offset"), &LabelSettings::set_shadow_offset); + ClassDB::bind_method(D_METHOD("get_shadow_offset"), &LabelSettings::get_shadow_offset); + + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "line_spacing", PROPERTY_HINT_NONE, "suffix:px"), "set_line_spacing", "get_line_spacing"); + + ADD_GROUP("Font", "font"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_font", "get_font"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "font_size", PROPERTY_HINT_RANGE, "1,1024,1,or_greater,suffix:px"), "set_font_size", "get_font_size"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "font_color"), "set_font_color", "get_font_color"); + + ADD_GROUP("Outline", "outline"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "outline_size", PROPERTY_HINT_RANGE, "0,127,1,or_greater,suffix:px"), "set_outline_size", "get_outline_size"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "outline_color"), "set_outline_color", "get_outline_color"); + + ADD_GROUP("Shadow", "shadow"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "shadow_size", PROPERTY_HINT_RANGE, "0,127,1,or_greater,suffix:px"), "set_shadow_size", "get_shadow_size"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "shadow_color"), "set_shadow_color", "get_shadow_color"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "shadow_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_shadow_offset", "get_shadow_offset"); +} + +void LabelSettings::set_line_spacing(real_t p_spacing) { + if (line_spacing != p_spacing) { + line_spacing = p_spacing; + emit_changed(); + } +} + +real_t LabelSettings::get_line_spacing() const { + return line_spacing; +} + +void LabelSettings::set_font(const Ref<Font> &p_font) { + if (font != p_font) { + if (font.is_valid()) { + font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &LabelSettings::_font_changed)); + } + font = p_font; + if (font.is_valid()) { + font->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &LabelSettings::_font_changed), CONNECT_REFERENCE_COUNTED); + } + emit_changed(); + } +} + +Ref<Font> LabelSettings::get_font() const { + return font; +} + +void LabelSettings::set_font_size(int p_size) { + if (font_size != p_size) { + font_size = p_size; + emit_changed(); + } +} + +int LabelSettings::get_font_size() const { + return font_size; +} + +void LabelSettings::set_font_color(const Color &p_color) { + if (font_color != p_color) { + font_color = p_color; + emit_changed(); + } +} + +Color LabelSettings::get_font_color() const { + return font_color; +} + +void LabelSettings::set_outline_size(int p_size) { + if (outline_size != p_size) { + outline_size = p_size; + emit_changed(); + } +} + +int LabelSettings::get_outline_size() const { + return outline_size; +} + +void LabelSettings::set_outline_color(const Color &p_color) { + if (outline_color != p_color) { + outline_color = p_color; + emit_changed(); + } +} + +Color LabelSettings::get_outline_color() const { + return outline_color; +} + +void LabelSettings::set_shadow_size(int p_size) { + if (shadow_size != p_size) { + shadow_size = p_size; + emit_changed(); + } +} + +int LabelSettings::get_shadow_size() const { + return shadow_size; +} + +void LabelSettings::set_shadow_color(const Color &p_color) { + if (shadow_color != p_color) { + shadow_color = p_color; + emit_changed(); + } +} + +Color LabelSettings::get_shadow_color() const { + return shadow_color; +} + +void LabelSettings::set_shadow_offset(const Vector2 &p_offset) { + if (shadow_offset != p_offset) { + shadow_offset = p_offset; + emit_changed(); + } +} + +Vector2 LabelSettings::get_shadow_offset() const { + return shadow_offset; +} diff --git a/scene/multiplayer/multiplayer_synchronizer.h b/scene/resources/label_settings.h index f61ef459da..062d499d50 100644 --- a/scene/multiplayer/multiplayer_synchronizer.h +++ b/scene/resources/label_settings.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* multiplayer_synchronizer.h */ +/* label_settings.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,45 +28,62 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef MULTIPLAYER_SYNCHRONIZER_H -#define MULTIPLAYER_SYNCHRONIZER_H +#ifndef LABEL_SETTINGS_H +#define LABEL_SETTINGS_H -#include "scene/main/node.h" +#include "core/io/resource.h" +#include "font.h" -#include "scene/resources/scene_replication_config.h" +/*************************************************************************/ + +class LabelSettings : public Resource { + GDCLASS(LabelSettings, Resource); + + real_t line_spacing = 3; + + Ref<Font> font; + int font_size = Font::DEFAULT_FONT_SIZE; + Color font_color = Color(1, 1, 1); -class MultiplayerSynchronizer : public Node { - GDCLASS(MultiplayerSynchronizer, Node); + int outline_size = 0; + Color outline_color = Color(1, 1, 1); -private: - Ref<SceneReplicationConfig> replication_config; - NodePath root_path = NodePath(".."); // Start with parent, like with AnimationPlayer. - uint64_t interval_msec = 0; + int shadow_size = 1; + Color shadow_color = Color(0, 0, 0, 0); + Vector2 shadow_offset = Vector2(1, 1); - static Object *_get_prop_target(Object *p_obj, const NodePath &p_prop); - void _start(); - void _stop(); + void _font_changed(); protected: static void _bind_methods(); - void _notification(int p_what); public: - static Error get_state(const List<NodePath> &p_properties, Object *p_obj, Vector<Variant> &r_variant, Vector<const Variant *> &r_variant_ptrs); - static Error set_state(const List<NodePath> &p_properties, Object *p_obj, const Vector<Variant> &p_state); + void set_line_spacing(real_t p_spacing); + real_t get_line_spacing() const; + + void set_font(const Ref<Font> &p_font); + Ref<Font> get_font() const; + + void set_font_size(int p_size); + int get_font_size() const; + + void set_font_color(const Color &p_color); + Color get_font_color() const; + + void set_outline_size(int p_size); + int get_outline_size() const; - void set_replication_interval(double p_interval); - double get_replication_interval() const; - uint64_t get_replication_interval_msec() const; + void set_outline_color(const Color &p_color); + Color get_outline_color() const; - void set_replication_config(Ref<SceneReplicationConfig> p_config); - Ref<SceneReplicationConfig> get_replication_config(); + void set_shadow_size(int p_size); + int get_shadow_size() const; - void set_root_path(const NodePath &p_path); - NodePath get_root_path() const; - virtual void set_multiplayer_authority(int p_peer_id, bool p_recursive = true) override; + void set_shadow_color(const Color &p_color); + Color get_shadow_color() const; - MultiplayerSynchronizer() {} + void set_shadow_offset(const Vector2 &p_offset); + Vector2 get_shadow_offset() const; }; -#endif // MULTIPLAYER_SYNCHRONIZER_H +#endif // LABEL_SETTINGS_H diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index b7a3b677f5..838927e34f 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -31,6 +31,8 @@ #include "material.h" #include "core/config/engine.h" +#include "core/config/project_settings.h" +#include "core/error/error_macros.h" #include "core/version.h" #include "scene/main/scene_tree.h" #include "scene/scene_string_names.h" @@ -71,12 +73,12 @@ RID Material::get_rid() const { return material; } -void Material::_validate_property(PropertyInfo &property) const { - if (!_can_do_next_pass() && property.name == "next_pass") { - property.usage = PROPERTY_USAGE_NONE; +void Material::_validate_property(PropertyInfo &p_property) const { + if (!_can_do_next_pass() && p_property.name == "next_pass") { + p_property.usage = PROPERTY_USAGE_NONE; } - if (!_can_use_render_priority() && property.name == "render_priority") { - property.usage = PROPERTY_USAGE_NONE; + if (!_can_use_render_priority() && p_property.name == "render_priority") { + p_property.usage = PROPERTY_USAGE_NONE; } } @@ -154,18 +156,9 @@ Material::~Material() { bool ShaderMaterial::_set(const StringName &p_name, const Variant &p_value) { if (shader.is_valid()) { - StringName pr = shader->remap_param(p_name); - if (!pr) { - String n = p_name; - if (n.find("param/") == 0) { //backwards compatibility - pr = n.substr(6, n.length()); - } - if (n.find("shader_param/") == 0) { //backwards compatibility - pr = n.replace_first("shader_param/", ""); - } - } + StringName pr = shader->remap_parameter(p_name); if (pr) { - set_shader_param(pr, p_value); + set_shader_parameter(pr, p_value); return true; } } @@ -175,24 +168,9 @@ bool ShaderMaterial::_set(const StringName &p_name, const Variant &p_value) { bool ShaderMaterial::_get(const StringName &p_name, Variant &r_ret) const { if (shader.is_valid()) { - StringName pr = shader->remap_param(p_name); - if (!pr) { - String n = p_name; - if (n.find("param/") == 0) { //backwards compatibility - pr = n.substr(6, n.length()); - } - if (n.find("shader_param/") == 0) { //backwards compatibility - pr = n.replace_first("shader_param/", ""); - } - } - + StringName pr = shader->remap_parameter(p_name); if (pr) { - HashMap<StringName, Variant>::ConstIterator E = param_cache.find(pr); - if (E) { - r_ret = E->value; - } else { - r_ret = Variant(); - } + r_ret = get_shader_parameter(pr); return true; } } @@ -202,15 +180,111 @@ bool ShaderMaterial::_get(const StringName &p_name, Variant &r_ret) const { void ShaderMaterial::_get_property_list(List<PropertyInfo> *p_list) const { if (!shader.is_null()) { - shader->get_param_list(p_list); + List<PropertyInfo> list; + shader->get_shader_uniform_list(&list, true); + + HashMap<String, HashMap<String, List<PropertyInfo>>> groups; + { + HashMap<String, List<PropertyInfo>> none_subgroup; + none_subgroup.insert("<None>", List<PropertyInfo>()); + groups.insert("<None>", none_subgroup); + } + + String last_group = "<None>"; + String last_subgroup = "<None>"; + + bool is_none_group_undefined = true; + bool is_none_group = true; + + for (List<PropertyInfo>::Element *E = list.front(); E; E = E->next()) { + if (E->get().usage == PROPERTY_USAGE_GROUP) { + if (!E->get().name.is_empty()) { + Vector<String> vgroup = E->get().name.split("::"); + last_group = vgroup[0]; + if (vgroup.size() > 1) { + last_subgroup = vgroup[1]; + } else { + last_subgroup = "<None>"; + } + is_none_group = false; + + if (!groups.has(last_group)) { + PropertyInfo info; + info.usage = PROPERTY_USAGE_GROUP; + info.name = last_group.capitalize(); + info.hint_string = "shader_parameter/"; + + List<PropertyInfo> none_subgroup; + none_subgroup.push_back(info); + + HashMap<String, List<PropertyInfo>> subgroup_map; + subgroup_map.insert("<None>", none_subgroup); + + groups.insert(last_group, subgroup_map); + } + + if (!groups[last_group].has(last_subgroup)) { + PropertyInfo info; + info.usage = PROPERTY_USAGE_SUBGROUP; + info.name = last_subgroup.capitalize(); + info.hint_string = "shader_parameter/"; + + List<PropertyInfo> subgroup; + subgroup.push_back(info); + + groups[last_group].insert(last_subgroup, subgroup); + } + } else { + last_group = "<None>"; + last_subgroup = "<None>"; + is_none_group = true; + } + continue; // Pass group. + } + + if (is_none_group_undefined && is_none_group) { + is_none_group_undefined = false; + + PropertyInfo info; + info.usage = PROPERTY_USAGE_GROUP; + info.name = "Shader Parameters"; + info.hint_string = "shader_parameter/"; + groups["<None>"]["<None>"].push_back(info); + } + + PropertyInfo info = E->get(); + info.name = "shader_parameter/" + info.name; + groups[last_group][last_subgroup].push_back(info); + } + + List<String> group_names; + for (HashMap<String, HashMap<String, List<PropertyInfo>>>::Iterator group = groups.begin(); group; ++group) { + group_names.push_back(group->key); + } + group_names.sort(); + + for (const String &group_name : group_names) { + List<String> subgroup_names; + HashMap<String, List<PropertyInfo>> &subgroups = groups[group_name]; + for (HashMap<String, List<PropertyInfo>>::Iterator subgroup = subgroups.begin(); subgroup; ++subgroup) { + subgroup_names.push_back(subgroup->key); + } + subgroup_names.sort(); + for (const String &subgroup_name : subgroup_names) { + List<PropertyInfo> &prop_infos = subgroups[subgroup_name]; + for (List<PropertyInfo>::Element *item = prop_infos.front(); item; item = item->next()) { + p_list->push_back(item->get()); + } + } + } } } -bool ShaderMaterial::property_can_revert(const String &p_name) { +bool ShaderMaterial::_property_can_revert(const StringName &p_name) const { if (shader.is_valid()) { - StringName pr = shader->remap_param(p_name); + StringName pr = shader->remap_parameter(p_name); if (pr) { - Variant default_value = RenderingServer::get_singleton()->shader_get_param_default(shader->get_rid(), pr); + Variant default_value = RenderingServer::get_singleton()->shader_get_parameter_default(shader->get_rid(), pr); Variant current_value; _get(p_name, current_value); return default_value.get_type() != Variant::NIL && default_value != current_value; @@ -219,15 +293,15 @@ bool ShaderMaterial::property_can_revert(const String &p_name) { return false; } -Variant ShaderMaterial::property_get_revert(const String &p_name) { - Variant r_ret; +bool ShaderMaterial::_property_get_revert(const StringName &p_name, Variant &r_property) const { if (shader.is_valid()) { - StringName pr = shader->remap_param(p_name); + StringName pr = shader->remap_parameter(p_name); if (pr) { - r_ret = RenderingServer::get_singleton()->shader_get_param_default(shader->get_rid(), pr); + r_property = RenderingServer::get_singleton()->shader_get_parameter_default(shader->get_rid(), pr); + return true; } } - return r_ret; + return false; } void ShaderMaterial::set_shader(const Ref<Shader> &p_shader) { @@ -258,7 +332,7 @@ Ref<Shader> ShaderMaterial::get_shader() const { return shader; } -void ShaderMaterial::set_shader_param(const StringName &p_param, const Variant &p_value) { +void ShaderMaterial::set_shader_parameter(const StringName &p_param, const Variant &p_value) { if (p_value.get_type() == Variant::NIL) { param_cache.erase(p_param); RS::get_singleton()->material_set_param(_get_material(), p_param, Variant()); @@ -278,7 +352,7 @@ void ShaderMaterial::set_shader_param(const StringName &p_param, const Variant & } } -Variant ShaderMaterial::get_shader_param(const StringName &p_param) const { +Variant ShaderMaterial::get_shader_parameter(const StringName &p_param) const { if (param_cache.has(p_param)) { return param_cache[p_param]; } else { @@ -293,22 +367,20 @@ void ShaderMaterial::_shader_changed() { void ShaderMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_shader", "shader"), &ShaderMaterial::set_shader); ClassDB::bind_method(D_METHOD("get_shader"), &ShaderMaterial::get_shader); - ClassDB::bind_method(D_METHOD("set_shader_param", "param", "value"), &ShaderMaterial::set_shader_param); - ClassDB::bind_method(D_METHOD("get_shader_param", "param"), &ShaderMaterial::get_shader_param); - ClassDB::bind_method(D_METHOD("property_can_revert", "name"), &ShaderMaterial::property_can_revert); - ClassDB::bind_method(D_METHOD("property_get_revert", "name"), &ShaderMaterial::property_get_revert); + ClassDB::bind_method(D_METHOD("set_shader_parameter", "param", "value"), &ShaderMaterial::set_shader_parameter); + ClassDB::bind_method(D_METHOD("get_shader_parameter", "param"), &ShaderMaterial::get_shader_parameter); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shader", PROPERTY_HINT_RESOURCE_TYPE, "Shader"), "set_shader", "get_shader"); } void ShaderMaterial::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { String f = p_function.operator String(); - if ((f == "get_shader_param" || f == "set_shader_param") && p_idx == 0) { + if ((f == "get_shader_parameter" || f == "set_shader_parameter") && p_idx == 0) { if (shader.is_valid()) { List<PropertyInfo> pl; - shader->get_param_list(&pl); + shader->get_shader_uniform_list(&pl); for (const PropertyInfo &E : pl) { - r_options->push_back(E.name.replace_first("shader_param/", "").quote()); + r_options->push_back(E.name.replace_first("shader_parameter/", "").quote()); } } } @@ -831,6 +903,7 @@ void BaseMaterial3D::_update_shader() { if (flags[FLAG_BILLBOARD_KEEP_SCALE]) { code += " MODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(length(MODEL_MATRIX[0].xyz), 0.0, 0.0, 0.0), vec4(0.0, length(MODEL_MATRIX[1].xyz), 0.0, 0.0), vec4(0.0, 0.0, length(MODEL_MATRIX[2].xyz), 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n"; } + code += " MODELVIEW_NORMAL_MATRIX = mat3(MODELVIEW_MATRIX);\n"; } break; case BILLBOARD_FIXED_Y: { code += " MODELVIEW_MATRIX = VIEW_MATRIX * mat4(vec4(normalize(cross(vec3(0.0, 1.0, 0.0), INV_VIEW_MATRIX[2].xyz)), 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(normalize(cross(INV_VIEW_MATRIX[0].xyz, vec3(0.0, 1.0, 0.0))), 0.0), MODEL_MATRIX[3]);\n"; @@ -838,6 +911,7 @@ void BaseMaterial3D::_update_shader() { if (flags[FLAG_BILLBOARD_KEEP_SCALE]) { code += " MODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(length(MODEL_MATRIX[0].xyz), 0.0, 0.0, 0.0),vec4(0.0, length(MODEL_MATRIX[1].xyz), 0.0, 0.0), vec4(0.0, 0.0, length(MODEL_MATRIX[2].xyz), 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n"; } + code += " MODELVIEW_NORMAL_MATRIX = mat3(MODELVIEW_MATRIX);\n"; } break; case BILLBOARD_PARTICLES: { //make billboard @@ -846,6 +920,8 @@ void BaseMaterial3D::_update_shader() { code += " mat_world = mat_world * mat4(vec4(cos(INSTANCE_CUSTOM.x), -sin(INSTANCE_CUSTOM.x), 0.0, 0.0), vec4(sin(INSTANCE_CUSTOM.x), cos(INSTANCE_CUSTOM.x), 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n"; //set modelview code += " MODELVIEW_MATRIX = VIEW_MATRIX * mat_world;\n"; + //set modelview normal + code += " MODELVIEW_NORMAL_MATRIX = mat3(MODELVIEW_MATRIX);\n"; //handle animation code += " float h_frames = float(particles_anim_h_frames);\n"; @@ -856,7 +932,7 @@ void BaseMaterial3D::_update_shader() { code += " particle_frame = clamp(particle_frame, 0.0, particle_total_frames - 1.0);\n"; code += " } else {\n"; code += " particle_frame = mod(particle_frame, particle_total_frames);\n"; - code += " }"; + code += " }\n"; code += " UV /= vec2(h_frames, v_frames);\n"; code += " UV += vec2(mod(particle_frame, h_frames) / h_frames, floor((particle_frame + 0.5) / h_frames) / v_frames);\n"; } break; @@ -976,7 +1052,8 @@ void BaseMaterial3D::_update_shader() { code += " float num_layers = mix(float(heightmap_max_layers),float(heightmap_min_layers), abs(dot(vec3(0.0, 0.0, 1.0), view_dir)));\n"; code += " float layer_depth = 1.0 / num_layers;\n"; code += " float current_layer_depth = 0.0;\n"; - code += " vec2 P = view_dir.xy * heightmap_scale;\n"; + // Multiply the heightmap scale by 0.01 to improve heightmap scale usability. + code += " vec2 P = view_dir.xy * heightmap_scale * 0.01;\n"; code += " vec2 delta = P / num_layers;\n"; code += " vec2 ofs = base_uv;\n"; if (flags[FLAG_INVERT_HEIGHTMAP]) { @@ -1012,7 +1089,8 @@ void BaseMaterial3D::_update_shader() { } // Use offset limiting to improve the appearance of non-deep parallax. // This reduces the impression of depth, but avoids visible warping in the distance. - code += " vec2 ofs = base_uv - view_dir.xy * depth * heightmap_scale;\n"; + // Multiply the heightmap scale by 0.01 to improve heightmap scale usability. + code += " vec2 ofs = base_uv - view_dir.xy * depth * heightmap_scale * 0.01;\n"; } code += " base_uv=ofs;\n"; @@ -1185,38 +1263,21 @@ void BaseMaterial3D::_update_shader() { if ((distance_fade == DISTANCE_FADE_OBJECT_DITHER || distance_fade == DISTANCE_FADE_PIXEL_DITHER)) { if (!RenderingServer::get_singleton()->is_low_end()) { code += " {\n"; + if (distance_fade == DISTANCE_FADE_OBJECT_DITHER) { code += " float fade_distance = abs((VIEW_MATRIX * MODEL_MATRIX[3]).z);\n"; } else { - code += " float fade_distance=-VERTEX.z;\n"; + code += " float fade_distance = -VERTEX.z;\n"; } + // Use interleaved gradient noise, which is fast but still looks good. + code += " const vec3 magic = vec3(0.06711056f, 0.00583715f, 52.9829189f);"; + code += " float fade = clamp(smoothstep(distance_fade_min, distance_fade_max, fade_distance), 0.0, 1.0);\n"; + // Use a hard cap to prevent a few stray pixels from remaining when past the fade-out distance. + code += " if (fade < 0.001 || fade < fract(magic.z * fract(dot(FRAGCOORD.xy, magic.xy)))) {\n"; + code += " discard;\n"; + code += " }\n"; - code += " float fade=clamp(smoothstep(distance_fade_min,distance_fade_max,fade_distance),0.0,1.0);\n"; - code += " int x = int(FRAGCOORD.x) % 4;\n"; - code += " int y = int(FRAGCOORD.y) % 4;\n"; - code += " int index = x + y * 4;\n"; - code += " float limit = 0.0;\n\n"; - code += " if (x < 8) {\n"; - code += " if (index == 0) limit = 0.0625;\n"; - code += " if (index == 1) limit = 0.5625;\n"; - code += " if (index == 2) limit = 0.1875;\n"; - code += " if (index == 3) limit = 0.6875;\n"; - code += " if (index == 4) limit = 0.8125;\n"; - code += " if (index == 5) limit = 0.3125;\n"; - code += " if (index == 6) limit = 0.9375;\n"; - code += " if (index == 7) limit = 0.4375;\n"; - code += " if (index == 8) limit = 0.25;\n"; - code += " if (index == 9) limit = 0.75;\n"; - code += " if (index == 10) limit = 0.125;\n"; - code += " if (index == 11) limit = 0.625;\n"; - code += " if (index == 12) limit = 1.0;\n"; - code += " if (index == 13) limit = 0.5;\n"; - code += " if (index == 14) limit = 0.875;\n"; - code += " if (index == 15) limit = 0.375;\n"; - code += " }\n\n"; - code += " if (fade < limit)\n"; - code += " discard;\n"; code += " }\n\n"; } @@ -1430,13 +1491,27 @@ Color BaseMaterial3D::get_emission() const { return emission; } -void BaseMaterial3D::set_emission_energy(float p_emission_energy) { - emission_energy = p_emission_energy; - RS::get_singleton()->material_set_param(_get_material(), shader_names->emission_energy, p_emission_energy); +void BaseMaterial3D::set_emission_energy_multiplier(float p_emission_energy_multiplier) { + emission_energy_multiplier = p_emission_energy_multiplier; + if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + RS::get_singleton()->material_set_param(_get_material(), shader_names->emission_energy, p_emission_energy_multiplier * emission_intensity); + } else { + RS::get_singleton()->material_set_param(_get_material(), shader_names->emission_energy, p_emission_energy_multiplier); + } +} + +float BaseMaterial3D::get_emission_energy_multiplier() const { + return emission_energy_multiplier; +} + +void BaseMaterial3D::set_emission_intensity(float p_emission_intensity) { + ERR_FAIL_COND_EDMSG(!GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units"), "Cannot set material emission intensity when Physical Light Units disabled."); + emission_intensity = p_emission_intensity; + RS::get_singleton()->material_set_param(_get_material(), shader_names->emission_energy, emission_energy_multiplier * emission_intensity); } -float BaseMaterial3D::get_emission_energy() const { - return emission_energy; +float BaseMaterial3D::get_emission_intensity() const { + return emission_intensity; } void BaseMaterial3D::set_normal_scale(float p_normal_scale) { @@ -1702,12 +1777,21 @@ void BaseMaterial3D::set_flag(Flags p_flag, bool p_enabled) { } flags[p_flag] = p_enabled; - if (p_flag == FLAG_USE_SHADOW_TO_OPACITY || p_flag == FLAG_USE_TEXTURE_REPEAT || p_flag == FLAG_SUBSURFACE_MODE_SKIN || p_flag == FLAG_USE_POINT_SIZE) { + + if ( + p_flag == FLAG_USE_SHADOW_TO_OPACITY || + p_flag == FLAG_USE_TEXTURE_REPEAT || + p_flag == FLAG_SUBSURFACE_MODE_SKIN || + p_flag == FLAG_USE_POINT_SIZE || + p_flag == FLAG_UV1_USE_TRIPLANAR || + p_flag == FLAG_UV2_USE_TRIPLANAR) { notify_property_list_changed(); } + if (p_flag == FLAG_PARTICLE_TRAILS_MODE) { update_configuration_warning(); } + _queue_shader_change(); } @@ -1784,53 +1868,65 @@ void BaseMaterial3D::_validate_high_end(const String &text, PropertyInfo &proper } } -void BaseMaterial3D::_validate_property(PropertyInfo &property) const { - _validate_feature("normal", FEATURE_NORMAL_MAPPING, property); - _validate_feature("emission", FEATURE_EMISSION, property); - _validate_feature("rim", FEATURE_RIM, property); - _validate_feature("clearcoat", FEATURE_CLEARCOAT, property); - _validate_feature("anisotropy", FEATURE_ANISOTROPY, property); - _validate_feature("ao", FEATURE_AMBIENT_OCCLUSION, property); - _validate_feature("heightmap", FEATURE_HEIGHT_MAPPING, property); - _validate_feature("subsurf_scatter", FEATURE_SUBSURFACE_SCATTERING, property); - _validate_feature("backlight", FEATURE_BACKLIGHT, property); - _validate_feature("refraction", FEATURE_REFRACTION, property); - _validate_feature("detail", FEATURE_DETAIL, property); +void BaseMaterial3D::_validate_property(PropertyInfo &p_property) const { + _validate_feature("normal", FEATURE_NORMAL_MAPPING, p_property); + _validate_feature("emission", FEATURE_EMISSION, p_property); + _validate_feature("rim", FEATURE_RIM, p_property); + _validate_feature("clearcoat", FEATURE_CLEARCOAT, p_property); + _validate_feature("anisotropy", FEATURE_ANISOTROPY, p_property); + _validate_feature("ao", FEATURE_AMBIENT_OCCLUSION, p_property); + _validate_feature("heightmap", FEATURE_HEIGHT_MAPPING, p_property); + _validate_feature("subsurf_scatter", FEATURE_SUBSURFACE_SCATTERING, p_property); + _validate_feature("backlight", FEATURE_BACKLIGHT, p_property); + _validate_feature("refraction", FEATURE_REFRACTION, p_property); + _validate_feature("detail", FEATURE_DETAIL, p_property); - _validate_high_end("refraction", property); - _validate_high_end("subsurf_scatter", property); - _validate_high_end("heightmap", property); + _validate_high_end("refraction", p_property); + _validate_high_end("subsurf_scatter", p_property); + _validate_high_end("heightmap", p_property); - if (property.name.begins_with("particles_anim_") && billboard_mode != BILLBOARD_PARTICLES) { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name == "emission_intensity" && !GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + p_property.usage = PROPERTY_USAGE_NONE; } - if (property.name == "billboard_keep_scale" && billboard_mode == BILLBOARD_DISABLED) { - property.usage = PROPERTY_USAGE_NO_EDITOR; + if (p_property.name.begins_with("particles_anim_") && billboard_mode != BILLBOARD_PARTICLES) { + p_property.usage = PROPERTY_USAGE_NONE; } - if (property.name == "grow_amount" && !grow_enabled) { - property.usage = PROPERTY_USAGE_NO_EDITOR; + if (p_property.name == "billboard_keep_scale" && billboard_mode == BILLBOARD_DISABLED) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } - if (property.name == "point_size" && !flags[FLAG_USE_POINT_SIZE]) { - property.usage = PROPERTY_USAGE_NO_EDITOR; + if (p_property.name == "grow_amount" && !grow_enabled) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } - if (property.name == "proximity_fade_distance" && !proximity_fade_enabled) { - property.usage = PROPERTY_USAGE_NO_EDITOR; + if (p_property.name == "point_size" && !flags[FLAG_USE_POINT_SIZE]) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } - if (property.name == "msdf_pixel_range" && !flags[FLAG_ALBEDO_TEXTURE_MSDF]) { - property.usage = PROPERTY_USAGE_NO_EDITOR; + if (p_property.name == "proximity_fade_distance" && !proximity_fade_enabled) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } - if (property.name == "msdf_outline_size" && !flags[FLAG_ALBEDO_TEXTURE_MSDF]) { - property.usage = PROPERTY_USAGE_NO_EDITOR; + if (p_property.name == "msdf_pixel_range" && !flags[FLAG_ALBEDO_TEXTURE_MSDF]) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } - if ((property.name == "distance_fade_max_distance" || property.name == "distance_fade_min_distance") && distance_fade == DISTANCE_FADE_DISABLED) { - property.usage = PROPERTY_USAGE_NO_EDITOR; + if (p_property.name == "msdf_outline_size" && !flags[FLAG_ALBEDO_TEXTURE_MSDF]) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } + + if ((p_property.name == "distance_fade_max_distance" || p_property.name == "distance_fade_min_distance") && distance_fade == DISTANCE_FADE_DISABLED) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } + + if ((p_property.name == "uv1_triplanar_sharpness" || p_property.name == "uv1_world_triplanar") && !flags[FLAG_UV1_USE_TRIPLANAR]) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } + + if ((p_property.name == "uv2_triplanar_sharpness" || p_property.name == "uv2_world_triplanar") && !flags[FLAG_UV2_USE_TRIPLANAR]) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } // you can only enable anti-aliasing (in materials) on alpha scissor and alpha hash @@ -1839,96 +1935,96 @@ void BaseMaterial3D::_validate_property(PropertyInfo &property) const { const bool alpha_aa_enabled = (alpha_antialiasing_mode != ALPHA_ANTIALIASING_OFF) && can_select_aa; // alpha scissor slider isn't needed when alpha antialiasing is enabled - if (property.name == "alpha_scissor_threshold" && transparency != TRANSPARENCY_ALPHA_SCISSOR) { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name == "alpha_scissor_threshold" && transparency != TRANSPARENCY_ALPHA_SCISSOR) { + p_property.usage = PROPERTY_USAGE_NONE; } // alpha hash scale slider is only needed if transparency is alpha hash - if (property.name == "alpha_hash_scale" && transparency != TRANSPARENCY_ALPHA_HASH) { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name == "alpha_hash_scale" && transparency != TRANSPARENCY_ALPHA_HASH) { + p_property.usage = PROPERTY_USAGE_NONE; } - if (property.name == "alpha_antialiasing_mode" && !can_select_aa) { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name == "alpha_antialiasing_mode" && !can_select_aa) { + p_property.usage = PROPERTY_USAGE_NONE; } // we can't choose an antialiasing mode if alpha isn't possible - if (property.name == "alpha_antialiasing_edge" && !alpha_aa_enabled) { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name == "alpha_antialiasing_edge" && !alpha_aa_enabled) { + p_property.usage = PROPERTY_USAGE_NONE; } - if (property.name == "blend_mode" && alpha_aa_enabled) { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name == "blend_mode" && alpha_aa_enabled) { + p_property.usage = PROPERTY_USAGE_NONE; } - if ((property.name == "heightmap_min_layers" || property.name == "heightmap_max_layers") && !deep_parallax) { - property.usage = PROPERTY_USAGE_NONE; + if ((p_property.name == "heightmap_min_layers" || p_property.name == "heightmap_max_layers") && !deep_parallax) { + p_property.usage = PROPERTY_USAGE_NONE; } - if (flags[FLAG_SUBSURFACE_MODE_SKIN] && (property.name == "subsurf_scatter_transmittance_color" || property.name == "subsurf_scatter_transmittance_texture")) { - property.usage = PROPERTY_USAGE_NONE; + if (flags[FLAG_SUBSURFACE_MODE_SKIN] && (p_property.name == "subsurf_scatter_transmittance_color" || p_property.name == "subsurf_scatter_transmittance_texture")) { + p_property.usage = PROPERTY_USAGE_NONE; } if (orm) { - if (property.name == "shading_mode") { + if (p_property.name == "shading_mode") { // Vertex not supported in ORM mode, since no individual roughness. - property.hint_string = "Unshaded,Per-Pixel"; + p_property.hint_string = "Unshaded,Per-Pixel"; } - if (property.name.begins_with("roughness") || property.name.begins_with("metallic") || property.name.begins_with("ao_texture")) { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name.begins_with("roughness") || p_property.name.begins_with("metallic") || p_property.name.begins_with("ao_texture")) { + p_property.usage = PROPERTY_USAGE_NONE; } } else { - if (property.name == "orm_texture") { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name == "orm_texture") { + p_property.usage = PROPERTY_USAGE_NONE; } } if (shading_mode != SHADING_MODE_PER_PIXEL) { if (shading_mode != SHADING_MODE_PER_VERTEX) { //these may still work per vertex - if (property.name.begins_with("ao")) { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name.begins_with("ao")) { + p_property.usage = PROPERTY_USAGE_NONE; } - if (property.name.begins_with("emission")) { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name.begins_with("emission")) { + p_property.usage = PROPERTY_USAGE_NONE; } - if (property.name.begins_with("metallic")) { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name.begins_with("metallic")) { + p_property.usage = PROPERTY_USAGE_NONE; } - if (property.name.begins_with("rim")) { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name.begins_with("rim")) { + p_property.usage = PROPERTY_USAGE_NONE; } - if (property.name.begins_with("roughness")) { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name.begins_with("roughness")) { + p_property.usage = PROPERTY_USAGE_NONE; } - if (property.name.begins_with("subsurf_scatter")) { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name.begins_with("subsurf_scatter")) { + p_property.usage = PROPERTY_USAGE_NONE; } } //these definitely only need per pixel - if (property.name.begins_with("anisotropy")) { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name.begins_with("anisotropy")) { + p_property.usage = PROPERTY_USAGE_NONE; } - if (property.name.begins_with("clearcoat")) { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name.begins_with("clearcoat")) { + p_property.usage = PROPERTY_USAGE_NONE; } - if (property.name.begins_with("normal")) { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name.begins_with("normal")) { + p_property.usage = PROPERTY_USAGE_NONE; } - if (property.name.begins_with("backlight")) { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name.begins_with("backlight")) { + p_property.usage = PROPERTY_USAGE_NONE; } - if (property.name.begins_with("transmittance")) { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name.begins_with("transmittance")) { + p_property.usage = PROPERTY_USAGE_NONE; } } } @@ -1961,8 +2057,9 @@ Vector3 BaseMaterial3D::get_uv1_offset() const { } void BaseMaterial3D::set_uv1_triplanar_blend_sharpness(float p_sharpness) { - uv1_triplanar_sharpness = p_sharpness; - RS::get_singleton()->material_set_param(_get_material(), shader_names->uv1_blend_sharpness, p_sharpness); + // Negative values or values higher than 150 can result in NaNs, leading to broken rendering. + uv1_triplanar_sharpness = CLAMP(p_sharpness, 0.0, 150.0); + RS::get_singleton()->material_set_param(_get_material(), shader_names->uv1_blend_sharpness, uv1_triplanar_sharpness); } float BaseMaterial3D::get_uv1_triplanar_blend_sharpness() const { @@ -1988,8 +2085,9 @@ Vector3 BaseMaterial3D::get_uv2_offset() const { } void BaseMaterial3D::set_uv2_triplanar_blend_sharpness(float p_sharpness) { - uv2_triplanar_sharpness = p_sharpness; - RS::get_singleton()->material_set_param(_get_material(), shader_names->uv2_blend_sharpness, p_sharpness); + // Negative values or values higher than 150 can result in NaNs, leading to broken rendering. + uv2_triplanar_sharpness = CLAMP(p_sharpness, 0.0, 150.0); + RS::get_singleton()->material_set_param(_get_material(), shader_names->uv2_blend_sharpness, uv2_triplanar_sharpness); } float BaseMaterial3D::get_uv2_triplanar_blend_sharpness() const { @@ -2370,8 +2468,11 @@ void BaseMaterial3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_emission", "emission"), &BaseMaterial3D::set_emission); ClassDB::bind_method(D_METHOD("get_emission"), &BaseMaterial3D::get_emission); - ClassDB::bind_method(D_METHOD("set_emission_energy", "emission_energy"), &BaseMaterial3D::set_emission_energy); - ClassDB::bind_method(D_METHOD("get_emission_energy"), &BaseMaterial3D::get_emission_energy); + ClassDB::bind_method(D_METHOD("set_emission_energy_multiplier", "emission_energy_multiplier"), &BaseMaterial3D::set_emission_energy_multiplier); + ClassDB::bind_method(D_METHOD("get_emission_energy_multiplier"), &BaseMaterial3D::get_emission_energy_multiplier); + + ClassDB::bind_method(D_METHOD("set_emission_intensity", "emission_energy_multiplier"), &BaseMaterial3D::set_emission_intensity); + ClassDB::bind_method(D_METHOD("get_emission_intensity"), &BaseMaterial3D::get_emission_intensity); ClassDB::bind_method(D_METHOD("set_normal_scale", "normal_scale"), &BaseMaterial3D::set_normal_scale); ClassDB::bind_method(D_METHOD("get_normal_scale"), &BaseMaterial3D::get_normal_scale); @@ -2546,7 +2647,7 @@ void BaseMaterial3D::_bind_methods() { ADD_GROUP("Transparency", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "transparency", PROPERTY_HINT_ENUM, "Disabled,Alpha,Alpha Scissor,Alpha Hash,Depth Pre-Pass"), "set_transparency", "get_transparency"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_scissor_threshold", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_alpha_scissor_threshold", "get_alpha_scissor_threshold"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_scissor_threshold", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_alpha_scissor_threshold", "get_alpha_scissor_threshold"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_hash_scale", PROPERTY_HINT_RANGE, "0,2,0.01"), "set_alpha_hash_scale", "get_alpha_hash_scale"); ADD_PROPERTY(PropertyInfo(Variant::INT, "alpha_antialiasing_mode", PROPERTY_HINT_ENUM, "Disabled,Alpha Edge Blend,Alpha Edge Clip"), "set_alpha_antialiasing", "get_alpha_antialiasing"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_antialiasing_edge", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_alpha_antialiasing_edge", "get_alpha_antialiasing_edge"); @@ -2588,12 +2689,14 @@ void BaseMaterial3D::_bind_methods() { ADD_GROUP("Emission", "emission_"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "emission_enabled"), "set_feature", "get_feature", FEATURE_EMISSION); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "emission", PROPERTY_HINT_COLOR_NO_ALPHA), "set_emission", "get_emission"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_energy", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_emission_energy", "get_emission_energy"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_energy_multiplier", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_emission_energy_multiplier", "get_emission_energy_multiplier"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_intensity", PROPERTY_HINT_RANGE, "0,100000.0,0.01,or_greater,suffix:nt"), "set_emission_intensity", "get_emission_intensity"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_operator", PROPERTY_HINT_ENUM, "Add,Multiply"), "set_emission_operator", "get_emission_operator"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "emission_on_uv2"), "set_flag", "get_flag", FLAG_EMISSION_ON_UV2); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "emission_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture", TEXTURE_EMISSION); - ADD_GROUP("NormalMap", "normal_"); + ADD_GROUP("Normal Map", "normal_"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "normal_enabled"), "set_feature", "get_feature", FEATURE_NORMAL_MAPPING); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "normal_scale", PROPERTY_HINT_RANGE, "-16,16,0.01"), "set_normal_scale", "get_normal_scale"); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "normal_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture", TEXTURE_NORMAL); @@ -2633,7 +2736,7 @@ void BaseMaterial3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "heightmap_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture", TEXTURE_HEIGHTMAP); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "heightmap_flip_texture"), "set_flag", "get_flag", FLAG_INVERT_HEIGHTMAP); - ADD_GROUP("Subsurf Scatter", "subsurf_scatter_"); + ADD_GROUP("Subsurface Scattering", "subsurf_scatter_"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "subsurf_scatter_enabled"), "set_feature", "get_feature", FEATURE_SUBSURFACE_SCATTERING); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "subsurf_scatter_strength", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_subsurface_scattering_strength", "get_subsurface_scattering_strength"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "subsurf_scatter_skin_mode"), "set_flag", "get_flag", FLAG_SUBSURFACE_MODE_SKIN); @@ -2850,14 +2953,14 @@ BaseMaterial3D::BaseMaterial3D(bool p_orm) : set_roughness(1.0); set_metallic(0.0); set_emission(Color(0, 0, 0)); - set_emission_energy(1.0); + set_emission_energy_multiplier(1.0); set_normal_scale(1); set_rim(1.0); set_rim_tint(0.5); set_clearcoat(1); set_clearcoat_roughness(0.5); set_anisotropy(0); - set_heightmap_scale(0.05); + set_heightmap_scale(5.0); set_subsurface_scattering_strength(0); set_backlight(Color(0, 0, 0)); set_transmittance_color(Color(1, 1, 1, 1)); @@ -2878,7 +2981,9 @@ BaseMaterial3D::BaseMaterial3D(bool p_orm) : set_transparency(TRANSPARENCY_DISABLED); set_alpha_antialiasing(ALPHA_ANTIALIASING_OFF); - set_alpha_scissor_threshold(0.05); + // Alpha scissor threshold of 0.5 matches the glTF specification and Label3D default. + // <https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#_material_alphacutoff> + set_alpha_scissor_threshold(0.5); set_alpha_hash_scale(1.0); set_alpha_antialiasing_edge(0.3); @@ -3001,6 +3106,8 @@ bool StandardMaterial3D::_set(const StringName &p_name, const Variant &p_value) { "depth_flip_binormal", "heightmap_flip_binormal" }, { "depth_texture", "heightmap_texture" }, + { "emission_energy", "emission_energy_multiplier" }, + { nullptr, nullptr }, }; @@ -3016,8 +3123,6 @@ bool StandardMaterial3D::_set(const StringName &p_name, const Variant &p_value) WARN_PRINT("Godot 3.x SpatialMaterial remapped parameter not found: " + String(p_name)); return true; } - - return false; } #endif // DISABLE_DEPRECATED diff --git a/scene/resources/material.h b/scene/resources/material.h index b845fd68c8..dd9589c577 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -54,7 +54,7 @@ protected: virtual bool _can_do_next_pass() const; virtual bool _can_use_render_priority() const; - void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; GDVIRTUAL0RC(RID, _get_shader_rid) GDVIRTUAL0RC(Shader::Mode, _get_shader_mode) @@ -88,8 +88,8 @@ protected: bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; - bool property_can_revert(const String &p_name); - Variant property_get_revert(const String &p_name); + bool _property_can_revert(const StringName &p_name) const; + bool _property_get_revert(const StringName &p_name, Variant &r_property) const; static void _bind_methods(); @@ -104,8 +104,8 @@ public: void set_shader(const Ref<Shader> &p_shader); Ref<Shader> get_shader() const; - void set_shader_param(const StringName &p_param, const Variant &p_value); - Variant get_shader_param(const StringName &p_param) const; + void set_shader_parameter(const StringName &p_param, const Variant &p_value); + Variant get_shader_parameter(const StringName &p_param) const; virtual Shader::Mode get_shader_mode() const override; @@ -456,7 +456,8 @@ private: float metallic = 0.0f; float roughness = 0.0f; Color emission; - float emission_energy = 0.0f; + float emission_energy_multiplier = 1.0f; + float emission_intensity = 1000.0f; // In nits, equivalent to indoor lighting. float normal_scale = 0.0f; float rim = 0.0f; float rim_tint = 0.0f; @@ -542,7 +543,7 @@ private: protected: static void _bind_methods(); - void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; virtual bool _can_do_next_pass() const override { return true; } virtual bool _can_use_render_priority() const override { return true; } @@ -562,8 +563,11 @@ public: void set_emission(const Color &p_emission); Color get_emission() const; - void set_emission_energy(float p_emission_energy); - float get_emission_energy() const; + void set_emission_energy_multiplier(float p_emission_energy_multiplier); + float get_emission_energy_multiplier() const; + + void set_emission_intensity(float p_emission_intensity); + float get_emission_intensity() const; void set_normal_scale(float p_normal_scale); float get_normal_scale() const; @@ -809,4 +813,4 @@ public: ////////////////////// -#endif +#endif // MATERIAL_H diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index 3e7b0a2808..b42e65c8df 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -32,11 +32,10 @@ #include "core/math/convex_hull.h" #include "core/templates/pair.h" +#include "scene/resources/surface_tool.h" + #include "scene/resources/concave_polygon_shape_3d.h" #include "scene/resources/convex_polygon_shape_3d.h" -#include "surface_tool.h" - -#include <stdlib.h> Mesh::ConvexDecompositionFunc Mesh::convex_decomposition_function = nullptr; @@ -72,13 +71,13 @@ Array Mesh::surface_get_arrays(int p_surface) const { return Array(); } -Array Mesh::surface_get_blend_shape_arrays(int p_surface) const { - Array ret; +TypedArray<Array> Mesh::surface_get_blend_shape_arrays(int p_surface) const { + TypedArray<Array> ret; if (GDVIRTUAL_REQUIRED_CALL(_surface_get_blend_shape_arrays, p_surface, ret)) { return ret; } - return Array(); + return TypedArray<Array>(); } Dictionary Mesh::surface_get_lods(int p_surface) const { @@ -201,7 +200,9 @@ Ref<TriangleMesh> Mesh::generate_triangle_mesh() const { continue; } int len = (surface_get_format(i) & ARRAY_FORMAT_INDEX) ? surface_get_array_index_len(i) : surface_get_array_len(i); - if ((primitive == PRIMITIVE_TRIANGLES && (len == 0 || (len % 3) != 0)) || (primitive == PRIMITIVE_TRIANGLE_STRIP && len < 3)) { + if ((primitive == PRIMITIVE_TRIANGLES && (len == 0 || (len % 3) != 0)) || + (primitive == PRIMITIVE_TRIANGLE_STRIP && len < 3) || + (surface_get_format(i) & ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY)) { // Error was already shown, just skip (including zero). continue; } @@ -211,6 +212,7 @@ Ref<TriangleMesh> Mesh::generate_triangle_mesh() const { int vc = surface_get_array_len(i); Vector<Vector3> vertices = a[ARRAY_VERTEX]; + ERR_FAIL_COND_V(vertices.is_empty(), Ref<TriangleMesh>()); const Vector3 *vr = vertices.ptr(); int32_t from_index = widx / 3; @@ -260,6 +262,64 @@ Ref<TriangleMesh> Mesh::generate_triangle_mesh() const { return triangle_mesh; } +Ref<TriangleMesh> Mesh::generate_surface_triangle_mesh(int p_surface) const { + ERR_FAIL_INDEX_V(p_surface, get_surface_count(), Ref<TriangleMesh>()); + + if (surface_triangle_meshes.size() != get_surface_count()) { + surface_triangle_meshes.resize(get_surface_count()); + } + + if (surface_triangle_meshes[p_surface].is_valid()) { + return surface_triangle_meshes[p_surface]; + } + + int facecount = 0; + + if (surface_get_primitive_type(p_surface) != PRIMITIVE_TRIANGLES) { + return Ref<TriangleMesh>(); + } + + if (surface_get_format(p_surface) & ARRAY_FORMAT_INDEX) { + facecount += surface_get_array_index_len(p_surface); + } else { + facecount += surface_get_array_len(p_surface); + } + + Vector<Vector3> faces; + faces.resize(facecount); + Vector3 *facesw = faces.ptrw(); + + Array a = surface_get_arrays(p_surface); + ERR_FAIL_COND_V(a.is_empty(), Ref<TriangleMesh>()); + + int vc = surface_get_array_len(p_surface); + Vector<Vector3> vertices = a[ARRAY_VERTEX]; + const Vector3 *vr = vertices.ptr(); + int widx = 0; + + if (surface_get_format(p_surface) & ARRAY_FORMAT_INDEX) { + int ic = surface_get_array_index_len(p_surface); + Vector<int> indices = a[ARRAY_INDEX]; + const int *ir = indices.ptr(); + + for (int j = 0; j < ic; j++) { + int index = ir[j]; + facesw[widx++] = vr[index]; + } + + } else { + for (int j = 0; j < vc; j++) { + facesw[widx++] = vr[j]; + } + } + + Ref<TriangleMesh> triangle_mesh = Ref<TriangleMesh>(memnew(TriangleMesh)); + triangle_mesh->create(faces); + surface_triangle_meshes.set(p_surface, triangle_mesh); + + return triangle_mesh; +} + void Mesh::generate_debug_mesh_lines(Vector<Vector3> &r_lines) { if (debug_lines.size() > 0) { r_lines = debug_lines; @@ -320,6 +380,14 @@ Vector<Face3> Mesh::get_faces() const { return Vector<Face3>(); } +Vector<Face3> Mesh::get_surface_faces(int p_surface) const { + Ref<TriangleMesh> tm = generate_surface_triangle_mesh(p_surface); + if (tm.is_valid()) { + return tm->get_faces(); + } + return Vector<Face3>(); +} + Ref<Shape3D> Mesh::create_convex_shape(bool p_clean, bool p_simplify) const { if (p_simplify) { ConvexDecompositionSettings settings; @@ -797,27 +865,6 @@ static Mesh::PrimitiveType _old_primitives[7] = { }; #endif // DISABLE_DEPRECATED -// Convert Octahedron-mapped normalized vector back to Cartesian -// Assumes normalized format (elements of v within range [-1, 1]) -Vector3 _oct_to_norm(const Vector2 v) { - Vector3 res(v.x, v.y, 1 - (Math::absf(v.x) + Math::absf(v.y))); - float t = MAX(-res.z, 0.0f); - res.x += t * -SIGN(res.x); - res.y += t * -SIGN(res.y); - return res.normalized(); -} - -// Convert Octahedron-mapped normalized tangent vector back to Cartesian -// out_sign provides the direction for the original cartesian tangent -// Assumes normalized format (elements of v within range [-1, 1]) -Vector3 _oct_to_tangent(const Vector2 v, float *out_sign) { - Vector2 v_decompressed = v; - v_decompressed.y = Math::absf(v_decompressed.y) * 2 - 1; - Vector3 res = _oct_to_norm(v_decompressed); - *out_sign = SIGN(v[1]); - return res; -} - void _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_old_format, uint32_t p_new_format, uint32_t p_elements, Vector<uint8_t> &vertex_data, Vector<uint8_t> &attribute_data, Vector<uint8_t> &skin_data) { uint32_t dst_vertex_stride; uint32_t dst_attribute_stride; @@ -888,127 +935,93 @@ void _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_old_forma if ((p_old_format & OLD_ARRAY_COMPRESS_NORMAL) && (p_old_format & OLD_ARRAY_FORMAT_TANGENT) && (p_old_format & OLD_ARRAY_COMPRESS_TANGENT)) { for (uint32_t i = 0; i < p_elements; i++) { const int8_t *src = (const int8_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; - uint32_t *dst = (uint32_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_NORMAL]]; - const Vector2 src_vec(src[0] / 127.0f, src[1] / 127.0f); - - const Vector3 res = _oct_to_norm(src_vec) * Vector3(0.5, 0.5, 0.5) + Vector3(0.5, 0.5, 0.5); - *dst = 0; - *dst |= CLAMP(int(res.x * 1023.0f), 0, 1023); - *dst |= CLAMP(int(res.y * 1023.0f), 0, 1023) << 10; - *dst |= CLAMP(int(res.z * 1023.0f), 0, 1023) << 20; + int16_t *dst = (int16_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_NORMAL]]; + + dst[0] = (int16_t)CLAMP(src[0] / 127.0f * 32767, -32768, 32767); + dst[1] = (int16_t)CLAMP(src[1] / 127.0f * 32767, -32768, 32767); } - src_offset += sizeof(int8_t) * 2; + src_offset += sizeof(int16_t) * 2; } else { for (uint32_t i = 0; i < p_elements; i++) { const int16_t *src = (const int16_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; - uint32_t *dst = (uint32_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_NORMAL]]; - const Vector2 src_vec(src[0] / 32767.0f, src[1] / 32767.0f); - - const Vector3 res = _oct_to_norm(src_vec) * Vector3(0.5, 0.5, 0.5) + Vector3(0.5, 0.5, 0.5); - *dst = 0; - *dst |= CLAMP(int(res.x * 1023.0f), 0, 1023); - *dst |= CLAMP(int(res.y * 1023.0f), 0, 1023) << 10; - *dst |= CLAMP(int(res.z * 1023.0f), 0, 1023) << 20; + int16_t *dst = (int16_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_NORMAL]]; + + dst[0] = src[0]; + dst[1] = src[1]; } src_offset += sizeof(int16_t) * 2; } } else { // No Octahedral compression if (p_old_format & OLD_ARRAY_COMPRESS_NORMAL) { - const float multiplier = 1.f / 127.f * 1023.0f; - for (uint32_t i = 0; i < p_elements; i++) { const int8_t *src = (const int8_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; - uint32_t *dst = (uint32_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_NORMAL]]; + const Vector3 original_normal(src[0], src[1], src[2]); + Vector2 res = original_normal.octahedron_encode(); - *dst = 0; - *dst |= CLAMP(int(src[0] * multiplier), 0, 1023); - *dst |= CLAMP(int(src[1] * multiplier), 0, 1023) << 10; - *dst |= CLAMP(int(src[2] * multiplier), 0, 1023) << 20; + uint16_t *dst = (uint16_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_NORMAL]]; + dst[0] = (uint16_t)CLAMP(res.x * 65535, 0, 65535); + dst[1] = (uint16_t)CLAMP(res.y * 65535, 0, 65535); } - src_offset += sizeof(uint32_t); + src_offset += sizeof(uint16_t) * 2; } else { for (uint32_t i = 0; i < p_elements; i++) { const float *src = (const float *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; - uint32_t *dst = (uint32_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_NORMAL]]; + const Vector3 original_normal(src[0], src[1], src[2]); + Vector2 res = original_normal.octahedron_encode(); - *dst = 0; - *dst |= CLAMP(int(src[0] * 1023.0), 0, 1023); - *dst |= CLAMP(int(src[1] * 1023.0), 0, 1023) << 10; - *dst |= CLAMP(int(src[2] * 1023.0), 0, 1023) << 20; + uint16_t *dst = (uint16_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_NORMAL]]; + dst[0] = (uint16_t)CLAMP(res.x * 65535, 0, 65535); + dst[1] = (uint16_t)CLAMP(res.y * 65535, 0, 65535); } - src_offset += sizeof(float) * 3; + src_offset += sizeof(uint16_t) * 2; } } } break; case OLD_ARRAY_TANGENT: { if (p_old_format & OLD_ARRAY_FLAG_USE_OCTAHEDRAL_COMPRESSION) { - if (p_old_format & OLD_ARRAY_COMPRESS_TANGENT) { // int8 + if (p_old_format & OLD_ARRAY_COMPRESS_TANGENT) { // int8 SNORM -> uint16 UNORM for (uint32_t i = 0; i < p_elements; i++) { const int8_t *src = (const int8_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; - uint32_t *dst = (uint32_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_TANGENT]]; - const Vector2 src_vec(src[0] / 127.0f, src[1] / 127.0f); - float out_sign; - const Vector3 res = _oct_to_tangent(src_vec, &out_sign) * Vector3(0.5, 0.5, 0.5) + Vector3(0.5, 0.5, 0.5); - - *dst = 0; - *dst |= CLAMP(int(res.x * 1023.0), 0, 1023); - *dst |= CLAMP(int(res.y * 1023.0), 0, 1023) << 10; - *dst |= CLAMP(int(res.z * 1023.0), 0, 1023) << 20; - if (out_sign > 0) { - *dst |= 3 << 30; - } + uint16_t *dst = (uint16_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_TANGENT]]; + + dst[0] = (uint16_t)CLAMP((src[0] / 127.0f * .5f + .5f) * 65535, 0, 65535); + dst[1] = (uint16_t)CLAMP((src[1] / 127.0f * .5f + .5f) * 65535, 0, 65535); } - src_offset += sizeof(int8_t) * 2; - } else { // int16 + src_offset += sizeof(uint16_t) * 2; + } else { // int16 SNORM -> uint16 UNORM for (uint32_t i = 0; i < p_elements; i++) { const int16_t *src = (const int16_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; - uint32_t *dst = (uint32_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_TANGENT]]; - const Vector2 src_vec(src[0] / 32767.0f, src[1] / 32767.0f); - float out_sign; - Vector3 res = _oct_to_tangent(src_vec, &out_sign) * Vector3(0.5, 0.5, 0.5) + Vector3(0.5, 0.5, 0.5); - - *dst = 0; - *dst |= CLAMP(int(res.x * 1023.0), 0, 1023); - *dst |= CLAMP(int(res.y * 1023.0), 0, 1023) << 10; - *dst |= CLAMP(int(res.z * 1023.0), 0, 1023) << 20; - if (out_sign > 0) { - *dst |= 3 << 30; - } + uint16_t *dst = (uint16_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_TANGENT]]; + + dst[0] = (uint16_t)CLAMP((src[0] / 32767.0f * .5f + .5f) * 65535, 0, 65535); + dst[1] = (uint16_t)CLAMP((src[1] / 32767.0f * .5f + .5f) * 65535, 0, 65535); } - src_offset += sizeof(int16_t) * 2; + src_offset += sizeof(uint16_t) * 2; } } else { // No Octahedral compression if (p_old_format & OLD_ARRAY_COMPRESS_TANGENT) { - const float multiplier = 1.f / 127.f * 1023.0f; - for (uint32_t i = 0; i < p_elements; i++) { const int8_t *src = (const int8_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; - uint32_t *dst = (uint32_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_TANGENT]]; - - *dst = 0; - *dst |= CLAMP(int(src[0] * multiplier), 0, 1023); - *dst |= CLAMP(int(src[1] * multiplier), 0, 1023) << 10; - *dst |= CLAMP(int(src[2] * multiplier), 0, 1023) << 20; - if (src[3] > 0) { - *dst |= 3 << 30; - } + const Vector3 original_tangent(src[0], src[1], src[2]); + Vector2 res = original_tangent.octahedron_tangent_encode(src[3]); + + uint16_t *dst = (uint16_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_NORMAL]]; + dst[0] = (uint16_t)CLAMP(res.x * 65535, 0, 65535); + dst[1] = (uint16_t)CLAMP(res.y * 65535, 0, 65535); } - src_offset += sizeof(uint32_t); + src_offset += sizeof(uint16_t) * 2; } else { for (uint32_t i = 0; i < p_elements; i++) { const float *src = (const float *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; - uint32_t *dst = (uint32_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_TANGENT]]; - - *dst = 0; - *dst |= CLAMP(int(src[0] * 1023.0), 0, 1023); - *dst |= CLAMP(int(src[1] * 1023.0), 0, 1023) << 10; - *dst |= CLAMP(int(src[2] * 1023.0), 0, 1023) << 20; - if (src[3] > 0) { - *dst |= 3 << 30; - } + const Vector3 original_tangent(src[0], src[1], src[2]); + Vector2 res = original_tangent.octahedron_tangent_encode(src[3]); + + uint16_t *dst = (uint16_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_NORMAL]]; + dst[0] = (uint16_t)CLAMP(res.x * 65535, 0, 65535); + dst[1] = (uint16_t)CLAMP(res.y * 65535, 0, 65535); } - src_offset += sizeof(float) * 4; + src_offset += sizeof(uint16_t) * 2; } } } break; @@ -1601,7 +1614,7 @@ void ArrayMesh::add_surface(uint32_t p_format, PrimitiveType p_primitive, const emit_changed(); } -void ArrayMesh::add_surface_from_arrays(PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes, const Dictionary &p_lods, uint32_t p_flags) { +void ArrayMesh::add_surface_from_arrays(PrimitiveType p_primitive, const Array &p_arrays, const TypedArray<Array> &p_blend_shapes, const Dictionary &p_lods, uint32_t p_flags) { ERR_FAIL_COND(p_arrays.size() != ARRAY_MAX); RS::SurfaceData surface; @@ -1627,8 +1640,8 @@ Array ArrayMesh::surface_get_arrays(int p_surface) const { return RenderingServer::get_singleton()->mesh_surface_get_arrays(mesh, p_surface); } -Array ArrayMesh::surface_get_blend_shape_arrays(int p_surface) const { - ERR_FAIL_INDEX_V(p_surface, surfaces.size(), Array()); +TypedArray<Array> ArrayMesh::surface_get_blend_shape_arrays(int p_surface) const { + ERR_FAIL_INDEX_V(p_surface, surfaces.size(), TypedArray<Array>()); return RenderingServer::get_singleton()->mesh_surface_get_blend_shape_arrays(mesh, p_surface); } diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h index b166d12ead..5ed4164117 100644 --- a/scene/resources/mesh.h +++ b/scene/resources/mesh.h @@ -42,6 +42,7 @@ class Mesh : public Resource { GDCLASS(Mesh, Resource); mutable Ref<TriangleMesh> triangle_mesh; //cached + mutable Vector<Ref<TriangleMesh>> surface_triangle_meshes; //cached mutable Vector<Vector3> debug_lines; Size2i lightmap_size_hint; @@ -62,7 +63,7 @@ protected: GDVIRTUAL1RC(int, _surface_get_array_len, int) GDVIRTUAL1RC(int, _surface_get_array_index_len, int) GDVIRTUAL1RC(Array, _surface_get_arrays, int) - GDVIRTUAL1RC(Array, _surface_get_blend_shape_arrays, int) + GDVIRTUAL1RC(TypedArray<Array>, _surface_get_blend_shape_arrays, int) GDVIRTUAL1RC(Dictionary, _surface_get_lods, int) GDVIRTUAL1RC(uint32_t, _surface_get_format, int) GDVIRTUAL1RC(uint32_t, _surface_get_primitive_type, int) @@ -143,13 +144,14 @@ public: ARRAY_FLAG_USE_DYNAMIC_UPDATE = RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE, ARRAY_FLAG_USE_8_BONE_WEIGHTS = RS::ARRAY_FLAG_USE_8_BONE_WEIGHTS, + ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY = RS::ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY, }; virtual int get_surface_count() const; virtual int surface_get_array_len(int p_idx) const; virtual int surface_get_array_index_len(int p_idx) const; virtual Array surface_get_arrays(int p_surface) const; - virtual Array surface_get_blend_shape_arrays(int p_surface) const; + virtual TypedArray<Array> surface_get_blend_shape_arrays(int p_surface) const; virtual Dictionary surface_get_lods(int p_surface) const; virtual uint32_t surface_get_format(int p_idx) const; virtual PrimitiveType surface_get_primitive_type(int p_idx) const; @@ -161,13 +163,12 @@ public: virtual AABB get_aabb() const; Vector<Face3> get_faces() const; + Vector<Face3> get_surface_faces(int p_surface) const; Ref<TriangleMesh> generate_triangle_mesh() const; + Ref<TriangleMesh> generate_surface_triangle_mesh(int p_surface) const; void generate_debug_mesh_lines(Vector<Vector3> &r_lines); void generate_debug_mesh_indices(Vector<Vector3> &r_points); - Ref<Shape3D> create_trimesh_shape() const; - Ref<Shape3D> create_convex_shape(bool p_clean = true, bool p_simplify = false) const; - Ref<Mesh> create_outline(float p_margin) const; void set_lightmap_size_hint(const Size2i &p_size); @@ -210,6 +211,8 @@ public: static ConvexDecompositionFunc convex_decomposition_function; Vector<Ref<Shape3D>> convex_decompose(const ConvexDecompositionSettings &p_settings) const; + Ref<Shape3D> create_convex_shape(bool p_clean = true, bool p_simplify = false) const; + Ref<Shape3D> create_trimesh_shape() const; virtual int get_builtin_bind_pose_count() const; virtual Transform3D get_builtin_bind_pose(int p_index) const; @@ -262,12 +265,12 @@ protected: static void _bind_methods(); public: - void add_surface_from_arrays(PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes = Array(), const Dictionary &p_lods = Dictionary(), uint32_t p_flags = 0); + void add_surface_from_arrays(PrimitiveType p_primitive, const Array &p_arrays, const TypedArray<Array> &p_blend_shapes = TypedArray<Array>(), const Dictionary &p_lods = Dictionary(), uint32_t p_flags = 0); void add_surface(uint32_t p_format, PrimitiveType p_primitive, const Vector<uint8_t> &p_array, const Vector<uint8_t> &p_attribute_array, const Vector<uint8_t> &p_skin_array, int p_vertex_count, const Vector<uint8_t> &p_index_array, int p_index_count, const AABB &p_aabb, const Vector<uint8_t> &p_blend_shape_data = Vector<uint8_t>(), const Vector<AABB> &p_bone_aabbs = Vector<AABB>(), const Vector<RS::SurfaceData::LOD> &p_lods = Vector<RS::SurfaceData::LOD>()); Array surface_get_arrays(int p_surface) const override; - Array surface_get_blend_shape_arrays(int p_surface) const override; + TypedArray<Array> surface_get_blend_shape_arrays(int p_surface) const override; Dictionary surface_get_lods(int p_surface) const override; void add_blend_shape(const StringName &p_name); @@ -342,7 +345,7 @@ public: virtual int surface_get_array_len(int p_idx) const override { return 0; } virtual int surface_get_array_index_len(int p_idx) const override { return 0; } virtual Array surface_get_arrays(int p_surface) const override { return Array(); } - virtual Array surface_get_blend_shape_arrays(int p_surface) const override { return Array(); } + virtual TypedArray<Array> surface_get_blend_shape_arrays(int p_surface) const override { return TypedArray<Array>(); } virtual Dictionary surface_get_lods(int p_surface) const override { return Dictionary(); } virtual uint32_t surface_get_format(int p_idx) const override { return 0; } virtual PrimitiveType surface_get_primitive_type(int p_idx) const override { return PRIMITIVE_TRIANGLES; } @@ -362,4 +365,4 @@ public: ~PlaceholderMesh(); }; -#endif +#endif // MESH_H diff --git a/scene/resources/mesh_library.cpp b/scene/resources/mesh_library.cpp index c8bfb73b2d..2d3f9d9afc 100644 --- a/scene/resources/mesh_library.cpp +++ b/scene/resources/mesh_library.cpp @@ -107,7 +107,7 @@ void MeshLibrary::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::ARRAY, name + PNAME("shapes"))); p_list->push_back(PropertyInfo(Variant::OBJECT, name + PNAME("navmesh"), PROPERTY_HINT_RESOURCE_TYPE, "NavigationMesh")); p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, name + PNAME("navmesh_transform"), PROPERTY_HINT_NONE, "suffix:m")); - p_list->push_back(PropertyInfo(Variant::OBJECT, name + PNAME("preview"), PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_HELPER)); + p_list->push_back(PropertyInfo(Variant::OBJECT, name + PNAME("preview"), PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_DEFAULT)); } } diff --git a/scene/resources/mesh_library.h b/scene/resources/mesh_library.h index 4105bd6960..79acb41c4e 100644 --- a/scene/resources/mesh_library.h +++ b/scene/resources/mesh_library.h @@ -33,8 +33,8 @@ #include "core/io/resource.h" #include "core/templates/rb_map.h" -#include "mesh.h" #include "scene/3d/navigation_region_3d.h" +#include "scene/resources/mesh.h" #include "shape_3d.h" class MeshLibrary : public Resource { diff --git a/scene/resources/multimesh.cpp b/scene/resources/multimesh.cpp index e5fc61ade5..ff4a7a4560 100644 --- a/scene/resources/multimesh.cpp +++ b/scene/resources/multimesh.cpp @@ -340,13 +340,13 @@ void MultiMesh::_bind_methods() { #ifndef DISABLE_DEPRECATED // Kept for compatibility from 3.x to 4.0. - ClassDB::bind_method(D_METHOD("_set_transform_array"), &MultiMesh::_set_transform_array); + ClassDB::bind_method(D_METHOD("_set_transform_array", "array"), &MultiMesh::_set_transform_array); ClassDB::bind_method(D_METHOD("_get_transform_array"), &MultiMesh::_get_transform_array); - ClassDB::bind_method(D_METHOD("_set_transform_2d_array"), &MultiMesh::_set_transform_2d_array); + ClassDB::bind_method(D_METHOD("_set_transform_2d_array", "array"), &MultiMesh::_set_transform_2d_array); ClassDB::bind_method(D_METHOD("_get_transform_2d_array"), &MultiMesh::_get_transform_2d_array); - ClassDB::bind_method(D_METHOD("_set_color_array"), &MultiMesh::_set_color_array); + ClassDB::bind_method(D_METHOD("_set_color_array", "array"), &MultiMesh::_set_color_array); ClassDB::bind_method(D_METHOD("_get_color_array"), &MultiMesh::_get_color_array); - ClassDB::bind_method(D_METHOD("_set_custom_data_array"), &MultiMesh::_set_custom_data_array); + ClassDB::bind_method(D_METHOD("_set_custom_data_array", "array"), &MultiMesh::_set_custom_data_array); ClassDB::bind_method(D_METHOD("_get_custom_data_array"), &MultiMesh::_get_custom_data_array); ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "transform_array", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "_set_transform_array", "_get_transform_array"); diff --git a/scene/resources/multimesh.h b/scene/resources/multimesh.h index 30ada5365f..0f8cc76173 100644 --- a/scene/resources/multimesh.h +++ b/scene/resources/multimesh.h @@ -113,4 +113,4 @@ public: VARIANT_ENUM_CAST(MultiMesh::TransformFormat); -#endif // MULTI_MESH_H +#endif // MULTIMESH_H diff --git a/scene/resources/navigation_mesh.cpp b/scene/resources/navigation_mesh.cpp index a808ead66b..90ea879012 100644 --- a/scene/resources/navigation_mesh.cpp +++ b/scene/resources/navigation_mesh.cpp @@ -30,6 +30,10 @@ #include "navigation_mesh.h" +#ifdef DEBUG_ENABLED +#include "servers/navigation_server_3d.h" +#endif // DEBUG_ENABLED + void NavigationMesh::create_from_mesh(const Ref<Mesh> &p_mesh) { ERR_FAIL_COND(p_mesh.is_null()); @@ -337,89 +341,98 @@ void NavigationMesh::clear_polygons() { polygons.clear(); } -Ref<Mesh> NavigationMesh::get_debug_mesh() { +#ifdef DEBUG_ENABLED +Ref<ArrayMesh> NavigationMesh::get_debug_mesh() { if (debug_mesh.is_valid()) { + // Blocks further updates for now, code below is intended for dynamic updates e.g. when settings change. return debug_mesh; } - Vector<Vector3> vertices = get_vertices(); - const Vector3 *vr = vertices.ptr(); - List<Face3> faces; - for (int i = 0; i < get_polygon_count(); i++) { - Vector<int> p = get_polygon(i); - - for (int j = 2; j < p.size(); j++) { - Face3 f; - f.vertex[0] = vr[p[0]]; - f.vertex[1] = vr[p[j - 1]]; - f.vertex[2] = vr[p[j]]; + if (!debug_mesh.is_valid()) { + debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + } else { + debug_mesh->clear_surfaces(); + } - faces.push_back(f); - } + if (vertices.size() == 0) { + return debug_mesh; } - HashMap<_EdgeKey, bool, _EdgeKey> edge_map; - Vector<Vector3> tmeshfaces; - tmeshfaces.resize(faces.size() * 3); - - { - Vector3 *tw = tmeshfaces.ptrw(); - int tidx = 0; - - for (const Face3 &f : faces) { - for (int j = 0; j < 3; j++) { - tw[tidx++] = f.vertex[j]; - _EdgeKey ek; - ek.from = f.vertex[j].snapped(Vector3(CMP_EPSILON, CMP_EPSILON, CMP_EPSILON)); - ek.to = f.vertex[(j + 1) % 3].snapped(Vector3(CMP_EPSILON, CMP_EPSILON, CMP_EPSILON)); - if (ek.from < ek.to) { - SWAP(ek.from, ek.to); - } - - HashMap<_EdgeKey, bool, _EdgeKey>::Iterator F = edge_map.find(ek); - - if (F) { - F->value = false; - - } else { - edge_map[ek] = true; - } - } - } + int polygon_count = get_polygon_count(); + + if (polygon_count < 1) { + // no face, no play + return debug_mesh; } - List<Vector3> lines; - for (const KeyValue<_EdgeKey, bool> &E : edge_map) { - if (E.value) { - lines.push_back(E.key.from); - lines.push_back(E.key.to); - } + // build geometry face surface + Vector<Vector3> face_vertex_array; + face_vertex_array.resize(polygon_count * 3); + + for (int i = 0; i < polygon_count; i++) { + Vector<int> polygon = get_polygon(i); + + face_vertex_array.push_back(vertices[polygon[0]]); + face_vertex_array.push_back(vertices[polygon[1]]); + face_vertex_array.push_back(vertices[polygon[2]]); } - Vector<Vector3> varr; - varr.resize(lines.size()); - { - Vector3 *w = varr.ptrw(); - int idx = 0; - for (const Vector3 &E : lines) { - w[idx++] = E; + Array face_mesh_array; + face_mesh_array.resize(Mesh::ARRAY_MAX); + face_mesh_array[Mesh::ARRAY_VERTEX] = face_vertex_array; + + // if enabled add vertex colors to colorize each face individually + bool enabled_geometry_face_random_color = NavigationServer3D::get_singleton()->get_debug_navigation_enable_geometry_face_random_color(); + if (enabled_geometry_face_random_color) { + Color debug_navigation_geometry_face_color = NavigationServer3D::get_singleton()->get_debug_navigation_geometry_face_color(); + Color polygon_color = debug_navigation_geometry_face_color; + + Vector<Color> face_color_array; + face_color_array.resize(polygon_count * 3); + + for (int i = 0; i < polygon_count; i++) { + polygon_color = debug_navigation_geometry_face_color * (Color(Math::randf(), Math::randf(), Math::randf())); + + face_color_array.push_back(polygon_color); + face_color_array.push_back(polygon_color); + face_color_array.push_back(polygon_color); } + face_mesh_array[Mesh::ARRAY_COLOR] = face_color_array; } - debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, face_mesh_array); + Ref<StandardMaterial3D> debug_geometry_face_material = NavigationServer3D::get_singleton_mut()->get_debug_navigation_geometry_face_material(); + debug_mesh->surface_set_material(0, debug_geometry_face_material); - if (!lines.size()) { - return debug_mesh; - } + // if enabled build geometry edge line surface + bool enabled_edge_lines = NavigationServer3D::get_singleton()->get_debug_navigation_enable_edge_lines(); + + if (enabled_edge_lines) { + Vector<Vector3> line_vertex_array; + line_vertex_array.resize(polygon_count * 6); - Array arr; - arr.resize(Mesh::ARRAY_MAX); - arr[Mesh::ARRAY_VERTEX] = varr; + for (int i = 0; i < polygon_count; i++) { + Vector<int> polygon = get_polygon(i); - debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, arr); + line_vertex_array.push_back(vertices[polygon[0]]); + line_vertex_array.push_back(vertices[polygon[1]]); + line_vertex_array.push_back(vertices[polygon[1]]); + line_vertex_array.push_back(vertices[polygon[2]]); + line_vertex_array.push_back(vertices[polygon[2]]); + line_vertex_array.push_back(vertices[polygon[0]]); + } + + Array line_mesh_array; + line_mesh_array.resize(Mesh::ARRAY_MAX); + line_mesh_array[Mesh::ARRAY_VERTEX] = line_vertex_array; + debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, line_mesh_array); + Ref<StandardMaterial3D> debug_geometry_edge_material = NavigationServer3D::get_singleton_mut()->get_debug_navigation_geometry_edge_material(); + debug_mesh->surface_set_material(1, debug_geometry_edge_material); + } return debug_mesh; } +#endif // DEBUG_ENABLED void NavigationMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_sample_partition_type", "sample_partition_type"), &NavigationMesh::set_sample_partition_type); @@ -513,8 +526,10 @@ void NavigationMesh::_bind_methods() { ADD_GROUP("Geometry", "geometry_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "geometry_parsed_geometry_type", PROPERTY_HINT_ENUM, "Mesh Instances,Static Colliders,Both"), "set_parsed_geometry_type", "get_parsed_geometry_type"); ADD_PROPERTY(PropertyInfo(Variant::INT, "geometry_collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask"); + ADD_PROPERTY_DEFAULT("geometry_collision_mask", 0xFFFFFFFF); ADD_PROPERTY(PropertyInfo(Variant::INT, "geometry_source_geometry_mode", PROPERTY_HINT_ENUM, "NavMesh Children, Group With Children, Group Explicit"), "set_source_geometry_mode", "get_source_geometry_mode"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "geometry_source_group_name"), "set_source_group_name", "get_source_group_name"); + ADD_PROPERTY_DEFAULT("geometry_source_group_name", StringName("navmesh")); ADD_GROUP("Cells", "cell_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "cell_size", PROPERTY_HINT_RANGE, "0.01,500.0,0.01,or_greater,suffix:m"), "set_cell_size", "get_cell_size"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "cell_height", PROPERTY_HINT_RANGE, "0.01,500.0,0.01,or_greater,suffix:m"), "set_cell_height", "get_cell_height"); @@ -557,17 +572,17 @@ void NavigationMesh::_bind_methods() { BIND_ENUM_CONSTANT(SOURCE_GEOMETRY_MAX); } -void NavigationMesh::_validate_property(PropertyInfo &property) const { - if (property.name == "geometry/collision_mask") { +void NavigationMesh::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "geometry_collision_mask") { if (parsed_geometry_type == PARSED_GEOMETRY_MESH_INSTANCES) { - property.usage = PROPERTY_USAGE_NONE; + p_property.usage = PROPERTY_USAGE_NONE; return; } } - if (property.name == "geometry/source_group_name") { + if (p_property.name == "geometry_source_group_name") { if (source_geometry_mode == SOURCE_GEOMETRY_NAVMESH_CHILDREN) { - property.usage = PROPERTY_USAGE_NONE; + p_property.usage = PROPERTY_USAGE_NONE; return; } } diff --git a/scene/resources/navigation_mesh.h b/scene/resources/navigation_mesh.h index 40b275c792..5ddbd75dcb 100644 --- a/scene/resources/navigation_mesh.h +++ b/scene/resources/navigation_mesh.h @@ -33,8 +33,6 @@ #include "scene/resources/mesh.h" -class Mesh; - class NavigationMesh : public Resource { GDCLASS(NavigationMesh, Resource); @@ -60,7 +58,7 @@ class NavigationMesh : public Resource { protected: static void _bind_methods(); - virtual void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; #ifndef DISABLE_DEPRECATED bool _set(const StringName &p_name, const Variant &p_value); @@ -204,7 +202,9 @@ public: Vector<int> get_polygon(int p_idx); void clear_polygons(); - Ref<Mesh> get_debug_mesh(); +#ifdef DEBUG_ENABLED + Ref<ArrayMesh> get_debug_mesh(); +#endif // DEBUG_ENABLED NavigationMesh(); }; diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index 2c58aa83a9..e0bedad595 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -279,25 +279,36 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { Ref<Resource> res = value; if (res.is_valid()) { if (res->is_local_to_scene()) { - HashMap<Ref<Resource>, Ref<Resource>>::Iterator E = resources_local_to_scene.find(res); - - if (E) { - value = E->value; + // In a situation where a local-to-scene resource is used in a child node of a non-editable instance, + // we need to avoid the parent scene from overriding the resource potentially also used in the root + // of the instantiated scene. That would to the instance having two different instances of the resource. + // Since at this point it's too late to propagate the resource instance in the parent scene to all the relevant + // nodes in the instance (and that would require very complex bookkepping), what we do instead is + // tampering the resource object already there with the values from the node in the parent scene and + // then tell this node to reference that resource. + if (n.instance >= 0) { + Ref<Resource> node_res = node->get(snames[nprops[j].name]); + if (node_res.is_valid()) { + node_res->copy_from(res); + node_res->configure_for_local_scene(node, resources_local_to_scene); + value = node_res; + } } else { + HashMap<Ref<Resource>, Ref<Resource>>::Iterator E = resources_local_to_scene.find(res); Node *base = i == 0 ? node : ret_nodes[0]; - - if (p_edit_state == GEN_EDIT_STATE_MAIN || p_edit_state == GEN_EDIT_STATE_MAIN_INHERITED) { - //for the main scene, use the resource as is - res->configure_for_local_scene(base, resources_local_to_scene); - resources_local_to_scene[res] = res; - + if (E) { + value = E->value; } else { - //for instances, a copy must be made - Node *base2 = i == 0 ? node : ret_nodes[0]; - Ref<Resource> local_dupe = res->duplicate_for_local_scene(base2, resources_local_to_scene); - resources_local_to_scene[res] = local_dupe; - res = local_dupe; - value = local_dupe; + if (p_edit_state == GEN_EDIT_STATE_MAIN) { + //for the main scene, use the resource as is + res->configure_for_local_scene(base, resources_local_to_scene); + resources_local_to_scene[res] = res; + } else { + //for instances, a copy must be made + Ref<Resource> local_dupe = res->duplicate_for_local_scene(base, resources_local_to_scene); + resources_local_to_scene[res] = local_dupe; + value = local_dupe; + } } } //must make a copy, because this res is local to scene @@ -398,7 +409,9 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } for (KeyValue<Ref<Resource>, Ref<Resource>> &E : resources_local_to_scene) { - E.value->setup_local_to_scene(); + if (E.value->get_local_scene() == ret_nodes[0]) { + E.value->setup_local_to_scene(); + } } //do connections @@ -434,10 +447,10 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { for (int j = 0; j < binds.size(); j++) { argptrs[j] = &binds[j]; } - callable = callable.bind(argptrs, binds.size()); + callable = callable.bindp(argptrs, binds.size()); } - cfrom->connect(snames[c.signal], callable, varray(), CONNECT_PERSIST | c.flags); + cfrom->connect(snames[c.signal], callable, CONNECT_PERSIST | c.flags); } //Node *s = ret_nodes[0]; @@ -892,9 +905,7 @@ Error SceneState::_parse_connections(Node *p_owner, Node *p_node, HashMap<String cd.signal = _nm_get_string(c.signal.get_name(), name_map); cd.flags = c.flags; cd.unbinds = unbinds; - for (int i = 0; i < c.binds.size(); i++) { // TODO: This could be removed now. - cd.binds.push_back(_vm_get_variant(c.binds[i], variant_map)); - } + for (int i = 0; i < binds.size(); i++) { cd.binds.push_back(_vm_get_variant(binds[i], variant_map)); } @@ -1744,7 +1755,7 @@ Node *PackedScene::instantiate(GenEditState p_edit_state) const { s->set_scene_file_path(get_path()); } - s->notification(Node::NOTIFICATION_INSTANCED); + s->notification(Node::NOTIFICATION_SCENE_INSTANTIATED); return s; } @@ -1781,7 +1792,7 @@ void PackedScene::_bind_methods() { ClassDB::bind_method(D_METHOD("pack", "path"), &PackedScene::pack); ClassDB::bind_method(D_METHOD("instantiate", "edit_state"), &PackedScene::instantiate, DEFVAL(GEN_EDIT_STATE_DISABLED)); ClassDB::bind_method(D_METHOD("can_instantiate"), &PackedScene::can_instantiate); - ClassDB::bind_method(D_METHOD("_set_bundled_scene"), &PackedScene::_set_bundled_scene); + ClassDB::bind_method(D_METHOD("_set_bundled_scene", "scene"), &PackedScene::_set_bundled_scene); ClassDB::bind_method(D_METHOD("_get_bundled_scene"), &PackedScene::_get_bundled_scene); ClassDB::bind_method(D_METHOD("get_state"), &PackedScene::get_state); diff --git a/scene/resources/packed_scene.h b/scene/resources/packed_scene.h index 5f8001c871..8e1a1d29b6 100644 --- a/scene/resources/packed_scene.h +++ b/scene/resources/packed_scene.h @@ -249,4 +249,4 @@ public: VARIANT_ENUM_CAST(PackedScene::GenEditState) -#endif // SCENE_PRELOADER_H +#endif // PACKED_SCENE_H diff --git a/scene/resources/particles_material.cpp b/scene/resources/particle_process_material.cpp index 7a49b9b515..e51c786786 100644 --- a/scene/resources/particles_material.cpp +++ b/scene/resources/particle_process_material.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* particles_material.cpp */ +/* particle_process_material.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,17 +28,17 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "particles_material.h" +#include "particle_process_material.h" #include "core/version.h" -Mutex ParticlesMaterial::material_mutex; -SelfList<ParticlesMaterial>::List *ParticlesMaterial::dirty_materials = nullptr; -HashMap<ParticlesMaterial::MaterialKey, ParticlesMaterial::ShaderData, ParticlesMaterial::MaterialKey> ParticlesMaterial::shader_map; -ParticlesMaterial::ShaderNames *ParticlesMaterial::shader_names = nullptr; +Mutex ParticleProcessMaterial::material_mutex; +SelfList<ParticleProcessMaterial>::List *ParticleProcessMaterial::dirty_materials = nullptr; +HashMap<ParticleProcessMaterial::MaterialKey, ParticleProcessMaterial::ShaderData, ParticleProcessMaterial::MaterialKey> ParticleProcessMaterial::shader_map; +ParticleProcessMaterial::ShaderNames *ParticleProcessMaterial::shader_names = nullptr; -void ParticlesMaterial::init_shaders() { - dirty_materials = memnew(SelfList<ParticlesMaterial>::List); +void ParticleProcessMaterial::init_shaders() { + dirty_materials = memnew(SelfList<ParticleProcessMaterial>::List); shader_names = memnew(ShaderNames); @@ -98,6 +98,17 @@ void ParticlesMaterial::init_shaders() { shader_names->emission_ring_radius = "emission_ring_radius"; shader_names->emission_ring_inner_radius = "emission_ring_inner_radius"; + shader_names->turbulence_enabled = "turbulence_enabled"; + shader_names->turbulence_noise_strength = "turbulence_noise_strength"; + shader_names->turbulence_noise_scale = "turbulence_noise_scale"; + shader_names->turbulence_noise_speed = "turbulence_noise_speed"; + shader_names->turbulence_noise_speed_random = "turbulence_noise_speed_random"; + shader_names->turbulence_influence_over_life = "turbulence_influence_over_life"; + shader_names->turbulence_influence_min = "turbulence_influence_min"; + shader_names->turbulence_influence_max = "turbulence_influence_max"; + shader_names->turbulence_initial_displacement_min = "turbulence_initial_displacement_min"; + shader_names->turbulence_initial_displacement_max = "turbulence_initial_displacement_max"; + shader_names->gravity = "gravity"; shader_names->lifetime_randomness = "lifetime_randomness"; @@ -110,14 +121,14 @@ void ParticlesMaterial::init_shaders() { shader_names->collision_bounce = "collision_bounce"; } -void ParticlesMaterial::finish_shaders() { +void ParticleProcessMaterial::finish_shaders() { memdelete(dirty_materials); dirty_materials = nullptr; memdelete(shader_names); } -void ParticlesMaterial::_update_shader() { +void ParticleProcessMaterial::_update_shader() { dirty_materials->remove(&element); MaterialKey mk = _compute_key(); @@ -141,11 +152,10 @@ void ParticlesMaterial::_update_shader() { shader_map[mk].users++; return; } - //must create a shader! // Add a comment to describe the shader origin (useful when converting to ShaderMaterial). - String code = "// NOTE: Shader automatically converted from " VERSION_NAME " " VERSION_FULL_CONFIG "'s ParticlesMaterial.\n\n"; + String code = "// NOTE: Shader automatically converted from " VERSION_NAME " " VERSION_FULL_CONFIG "'s ParticleProcessMaterial.\n\n"; code += "shader_type particles;\n"; @@ -277,11 +287,82 @@ void ParticlesMaterial::_update_shader() { code += "uniform sampler2D anim_offset_texture : repeat_disable;\n"; } - if (collision_enabled) { + if (collision_mode == COLLISION_RIGID) { code += "uniform float collision_friction;\n"; code += "uniform float collision_bounce;\n"; } + if (turbulence_enabled) { + code += "uniform float turbulence_noise_strength;\n"; + code += "uniform float turbulence_noise_scale;\n"; + code += "uniform float turbulence_influence_min;\n"; + code += "uniform float turbulence_influence_max;\n"; + code += "uniform float turbulence_initial_displacement_min;\n"; + code += "uniform float turbulence_initial_displacement_max;\n"; + code += "uniform float turbulence_noise_speed_random;\n"; + code += "uniform vec3 turbulence_noise_speed = vec3(1.0, 1.0, 1.0);\n"; + if (tex_parameters[PARAM_TURB_INFLUENCE_OVER_LIFE].is_valid()) { + code += "uniform sampler2D turbulence_influence_over_life;\n"; + } + if (turbulence_color_ramp.is_valid()) { + code += "uniform sampler2D turbulence_color_ramp;\n"; + } + code += "\n"; + + //functions for 3D noise / turbulence + code += "\n\n"; + code += "// 3D Noise with friendly permission by Inigo Quilez\n"; + code += "vec3 hash_noise( vec3 p ) {\n"; + code += " p *= mat3(vec3(127.1, 311.7, -53.7), vec3(269.5, 183.3, 77.1), vec3(-301.7, 27.3, 215.3));\n"; + code += " return 2.0 * fract(fract(p)*4375.55) -1.;\n"; + code += "}\n"; + code += "\n"; + code += "float noise( vec3 p) {\n"; + code += " vec3 i = floor(p);;\n"; + code += " vec3 f = fract(p);\n "; + code += " vec3 u = f * f * (3.0 - 2.0 * f);\n"; + code += "\n"; + code += " return 2.0*mix( mix( mix( dot( hash_noise( i + vec3(0.0,0.0,0.0) ), f - vec3(0.0,0.0,0.0) ), dot( hash_noise( i + vec3(1.0,0.0,0.0) ), f - vec3(1.0,0.0,0.0) ), u.x),\n"; + code += " mix( dot( hash_noise( i + vec3(0.0,1.0,0.0) ), f - vec3(0.0,1.0,0.0) ), dot( hash_noise( i + vec3(1.0,1.0,0.0) ), f - vec3(1.0,1.0,0.0) ), u.x), u.y),\n"; + code += " mix( mix( dot( hash_noise( i + vec3(0.0,0.0,1.0) ), f - vec3(0.0,0.0,1.0) ), dot( hash_noise( i + vec3(1.0,0.0,1.0) ), f - vec3(1.0,0.0,1.0) ), u.x),\n"; + code += " mix( dot( hash_noise( i + vec3(0.0,1.0,1.0) ), f - vec3(0.0,1.0,1.0) ), dot( hash_noise( i + vec3(1.0,1.0,1.0) ), f - vec3(1.0,1.0,1.0) ), u.x), u.y), u.z);\n"; + code += "}\n\n"; + code += "// Curl 3D and noise_3d function with friendly permission by Isaac Cohen\n"; + code += "vec3 noise_3d(vec3 p) {\n"; + code += " float s = noise(p);\n"; + code += " float s1 = noise(vec3(p.y - 19.1, p.z + 33.4, p.x + 47.2));\n"; + code += " float s2 = noise(vec3(p.z + 74.2, p.x - 124.5, p.y + 99.4));\n"; + code += " vec3 c = vec3(s, s1, s2);\n"; + code += " return c;\n"; + code += "}\n\n"; + code += "vec3 curl_3d(vec3 p, float c) {\n"; + code += " float epsilon = 0.001 + c;\n"; + code += " vec3 dx = vec3(epsilon, 0.0, 0.0);\n"; + code += " vec3 dy = vec3(0.0, epsilon, 0.0);\n"; + code += " vec3 dz = vec3(0.0, 0.0, epsilon);\n"; + code += " vec3 x0 = noise_3d(p - dx).xyz;\n"; + code += " vec3 x1 = noise_3d(p + dx).xyz;\n"; + code += " vec3 y0 = noise_3d(p - dy).xyz;\n"; + code += " vec3 y1 = noise_3d(p + dy).xyz;\n"; + code += " vec3 z0 = noise_3d(p - dz).xyz;\n"; + code += " vec3 z1 = noise_3d(p + dz).xyz;\n"; + code += " float x = y1.z - y0.z - z1.y + z0.y;\n"; + code += " float y = z1.x - z0.x - x1.z + x0.z;\n"; + code += " float z = x1.y - x0.y - y1.x + y0.x;\n"; + code += " float divisor = 1.0 / (2.0 * epsilon);\n"; + code += " return vec3(normalize(vec3(x, y, z) * divisor));\n"; + code += "}\n"; + code += "vec3 get_noise_direction(vec3 pos, vec3 emission_pos, vec3 time_noise) {\n"; + code += " float adj_contrast = max((turbulence_noise_strength - 1.0), 0.0) * 70.0;\n"; + code += " vec3 noise_time = (vec3(TIME) * turbulence_noise_speed) + time_noise;\n"; + code += " vec3 noise_pos = (pos * turbulence_noise_scale) - emission_pos;\n"; + code += " vec3 diff = pos - emission_pos;\n"; + code += " vec3 noise_direction = curl_3d(noise_pos + noise_time - diff, adj_contrast);\n"; + code += " noise_direction = mix(0.9 * noise_direction, noise_direction, turbulence_noise_strength - 9.0);\n"; + code += " return noise_direction;\n"; + code += "}\n"; + } + //need a random function code += "\n\n"; code += "float rand_from_seed(inout uint seed) {\n"; @@ -463,8 +544,18 @@ void ParticlesMaterial::_update_shader() { break; } } - code += " if (RESTART_VELOCITY) VELOCITY = (EMISSION_TRANSFORM * vec4(VELOCITY, 0.0)).xyz;\n"; + // Apply noise/turbulence: initial displacement. + if (turbulence_enabled) { + if (get_turbulence_noise_speed_random() >= 0.0) { + code += " vec3 time_noise = noise_3d( vec3(TIME) * turbulence_noise_speed_random ) * -turbulence_noise_speed;\n"; + } else { + code += " const vec3 time_noise = vec3(0.0);\n"; + } + code += " vec3 noise_direction = get_noise_direction(TRANSFORM[3].xyz, EMISSION_TRANSFORM[3].xyz, time_noise);\n"; + code += " float turb_init_displacement = mix(turbulence_initial_displacement_min, turbulence_initial_displacement_max, rand_from_seed(alt_seed));"; + code += " TRANSFORM[3].xyz += noise_direction * turb_init_displacement;\n"; + } code += " TRANSFORM = EMISSION_TRANSFORM * TRANSFORM;\n"; if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { code += " VELOCITY.z = 0.0;\n"; @@ -483,7 +574,6 @@ void ParticlesMaterial::_update_shader() { if (color_initial_ramp.is_valid()) { code += " float color_initial_rand = rand_from_seed(alt_seed);\n"; } - code += " float pi = 3.14159;\n"; code += " float degree_to_rad = pi / 180.0;\n"; code += "\n"; @@ -570,7 +660,7 @@ void ParticlesMaterial::_update_shader() { code += " vec3 diff = pos - org;\n"; code += " force += length(diff) > 0.0 ? normalize(diff) * tex_radial_accel * mix(radial_accel_min, radial_accel_max, rand_from_seed(alt_seed)) : vec3(0.0);\n"; code += " // apply tangential acceleration;\n"; - code += " float tangent_accel_val = tex_tangent_accel * mix(tangent_accel_min, tangent_accel_max, rand_from_seed(alt_seed))\n;"; + code += " float tangent_accel_val = tex_tangent_accel * mix(tangent_accel_min, tangent_accel_max, rand_from_seed(alt_seed));\n"; if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { code += " force += length(diff.yx) > 0.0 ? vec3(normalize(diff.yx * vec2(-1.0, 1.0)), 0.0) * tangent_accel_val : vec3(0.0);\n"; @@ -584,6 +674,43 @@ void ParticlesMaterial::_update_shader() { code += " // apply attractor forces\n"; code += " VELOCITY += force * DELTA;\n"; + + if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { + code += " VELOCITY = normalize(VELOCITY) * tex_linear_velocity;\n"; + } + + // Apply noise/turbulence. + if (turbulence_enabled) { + code += " // apply turbulence\n"; + if (tex_parameters[PARAM_TURB_INFLUENCE_OVER_LIFE].is_valid()) { + code += " float turbulence_influence = textureLod(turbulence_influence_over_life, vec2(tv, 0.0), 0.0).r;\n"; + } else { + code += " const float turbulence_influence = 1.0;\n"; + } + code += " \n"; + if (get_turbulence_noise_speed_random() >= 0.0) { + code += " vec3 time_noise = noise_3d( vec3(TIME) * turbulence_noise_speed_random ) * -turbulence_noise_speed;\n"; + } else { + code += " const vec3 time_noise = vec3(0.0);\n"; + } + code += " vec3 noise_direction = get_noise_direction(TRANSFORM[3].xyz, EMISSION_TRANSFORM[3].xyz, time_noise);\n"; + // If collision happened, turbulence is no longer applied. + // We don't need this check when the collision mode is "hide on contact", + // as the particle will be hidden anyway. + String extra_tab = ""; + if (collision_mode != COLLISION_RIGID) { + code += " if (!COLLIDED) {\n"; + extra_tab = " "; + } + code += extra_tab + " \n"; + code += extra_tab + " float vel_mag = length(VELOCITY);\n"; + code += extra_tab + " float vel_infl = clamp(mix(turbulence_influence_min, turbulence_influence_max, rand_from_seed(alt_seed)) * turbulence_influence, 0.0, 1.0);\n"; + code += extra_tab + " VELOCITY = mix(VELOCITY, normalize(noise_direction) * vel_mag * (1.0 + (1.0 - vel_infl) * 0.2), vel_infl);\n"; + if (collision_mode != COLLISION_RIGID) { + code += " }"; + } + } + code += " \n"; code += " // orbit velocity\n"; if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { code += " float orbit_amount = tex_orbit_velocity * mix(orbit_velocity_min, orbit_velocity_max, rand_from_seed(alt_seed));\n"; @@ -595,9 +722,6 @@ void ParticlesMaterial::_update_shader() { code += " }\n"; } - if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { - code += " VELOCITY = normalize(VELOCITY) * tex_linear_velocity;\n"; - } code += " float dmp = mix(damping_min, damping_max, rand_from_seed(alt_seed));\n"; code += " if (dmp * tex_damping > 0.0) {\n"; code += " float v = length(VELOCITY);\n"; @@ -701,24 +825,46 @@ void ParticlesMaterial::_update_shader() { code += " TRANSFORM[3] = origin;\n"; } } - //scale by scale + + if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { + code += " TRANSFORM[3].z = 0.0;\n"; + } + + if (collision_mode == COLLISION_RIGID) { + code += " if (COLLIDED) {\n"; + code += " if (length(VELOCITY) > 3.0) {\n"; + code += " TRANSFORM[3].xyz += COLLISION_NORMAL * COLLISION_DEPTH;\n"; + code += " VELOCITY -= COLLISION_NORMAL * dot(COLLISION_NORMAL, VELOCITY) * (1.0 + collision_bounce);\n"; + code += " VELOCITY = mix(VELOCITY,vec3(0.0),clamp(collision_friction, 0.0, 1.0));\n"; + code += " } else {\n"; + code += " VELOCITY = vec3(0.0);\n"; + // If turbulence is enabled, set the noise direction to up so the turbulence color is "neutral" + if (turbulence_enabled) { + code += " noise_direction = vec3(1.0, 0.0, 0.0);\n"; + } + code += " }\n"; + code += " }\n"; + } + + // scale by scale code += " float base_scale = mix(scale_min, scale_max, scale_rand);\n"; code += " base_scale = sign(base_scale) * max(abs(base_scale), 0.001);\n"; - code += " TRANSFORM[0].xyz *= base_scale * sign(tex_scale.r) * max(abs(tex_scale.r), 0.001);\n"; code += " TRANSFORM[1].xyz *= base_scale * sign(tex_scale.g) * max(abs(tex_scale.g), 0.001);\n"; code += " TRANSFORM[2].xyz *= base_scale * sign(tex_scale.b) * max(abs(tex_scale.b), 0.001);\n"; - if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { - code += " VELOCITY.z = 0.0;\n"; - code += " TRANSFORM[3].z = 0.0;\n"; - } - if (collision_enabled) { + + if (collision_mode == COLLISION_RIGID) { code += " if (COLLIDED) {\n"; code += " TRANSFORM[3].xyz+=COLLISION_NORMAL * COLLISION_DEPTH;\n"; code += " VELOCITY -= COLLISION_NORMAL * dot(COLLISION_NORMAL, VELOCITY) * (1.0 + collision_bounce);\n"; code += " VELOCITY = mix(VELOCITY,vec3(0.0),collision_friction * DELTA * 100.0);\n"; code += " }\n"; + } else if (collision_mode == COLLISION_HIDE_ON_CONTACT) { + code += " if (COLLIDED) {\n"; + code += " ACTIVE = false;\n"; + code += " }\n"; } + if (sub_emitter_mode != SUB_EMITTER_DISABLED) { code += " int emit_count = 0;\n"; switch (sub_emitter_mode) { @@ -762,7 +908,7 @@ void ParticlesMaterial::_update_shader() { RS::get_singleton()->material_set_shader(_get_material(), shader_data.shader); } -void ParticlesMaterial::flush_changes() { +void ParticleProcessMaterial::flush_changes() { MutexLock lock(material_mutex); while (dirty_materials->first()) { @@ -770,7 +916,7 @@ void ParticlesMaterial::flush_changes() { } } -void ParticlesMaterial::_queue_shader_change() { +void ParticleProcessMaterial::_queue_shader_change() { MutexLock lock(material_mutex); if (is_initialized && !element.in_list()) { @@ -778,40 +924,40 @@ void ParticlesMaterial::_queue_shader_change() { } } -bool ParticlesMaterial::_is_shader_dirty() const { +bool ParticleProcessMaterial::_is_shader_dirty() const { MutexLock lock(material_mutex); return element.in_list(); } -void ParticlesMaterial::set_direction(Vector3 p_direction) { +void ParticleProcessMaterial::set_direction(Vector3 p_direction) { direction = p_direction; RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->direction, direction); } -Vector3 ParticlesMaterial::get_direction() const { +Vector3 ParticleProcessMaterial::get_direction() const { return direction; } -void ParticlesMaterial::set_spread(float p_spread) { +void ParticleProcessMaterial::set_spread(float p_spread) { spread = p_spread; RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->spread, p_spread); } -float ParticlesMaterial::get_spread() const { +float ParticleProcessMaterial::get_spread() const { return spread; } -void ParticlesMaterial::set_flatness(float p_flatness) { +void ParticleProcessMaterial::set_flatness(float p_flatness) { flatness = p_flatness; RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->flatness, p_flatness); } -float ParticlesMaterial::get_flatness() const { +float ParticleProcessMaterial::get_flatness() const { return flatness; } -void ParticlesMaterial::set_param_min(Parameter p_param, float p_value) { +void ParticleProcessMaterial::set_param_min(Parameter p_param, float p_value) { ERR_FAIL_INDEX(p_param, PARAM_MAX); params_min[p_param] = p_value; @@ -856,18 +1002,27 @@ void ParticlesMaterial::set_param_min(Parameter p_param, float p_value) { case PARAM_ANIM_OFFSET: { RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset_min, p_value); } break; + case PARAM_TURB_VEL_INFLUENCE: { + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->turbulence_influence_min, p_value); + } break; + case PARAM_TURB_INIT_DISPLACEMENT: { + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->turbulence_initial_displacement_min, p_value); + } break; + case PARAM_TURB_INFLUENCE_OVER_LIFE: { + // Can't happen, but silences warning + } break; case PARAM_MAX: break; // Can't happen, but silences warning } } -float ParticlesMaterial::get_param_min(Parameter p_param) const { +float ParticleProcessMaterial::get_param_min(Parameter p_param) const { ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0); return params_min[p_param]; } -void ParticlesMaterial::set_param_max(Parameter p_param, float p_value) { +void ParticleProcessMaterial::set_param_max(Parameter p_param, float p_value) { ERR_FAIL_INDEX(p_param, PARAM_MAX); params_max[p_param] = p_value; @@ -912,12 +1067,21 @@ void ParticlesMaterial::set_param_max(Parameter p_param, float p_value) { case PARAM_ANIM_OFFSET: { RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset_max, p_value); } break; + case PARAM_TURB_VEL_INFLUENCE: { + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->turbulence_influence_max, p_value); + } break; + case PARAM_TURB_INIT_DISPLACEMENT: { + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->turbulence_initial_displacement_max, p_value); + } break; + case PARAM_TURB_INFLUENCE_OVER_LIFE: { + // Can't happen, but silences warning + } break; case PARAM_MAX: break; // Can't happen, but silences warning } } -float ParticlesMaterial::get_param_max(Parameter p_param) const { +float ParticleProcessMaterial::get_param_max(Parameter p_param) const { ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0); return params_max[p_param]; @@ -932,7 +1096,7 @@ static void _adjust_curve_range(const Ref<Texture2D> &p_texture, float p_min, fl curve_tex->ensure_default_setup(p_min, p_max); } -void ParticlesMaterial::set_param_texture(Parameter p_param, const Ref<Texture2D> &p_texture) { +void ParticleProcessMaterial::set_param_texture(Parameter p_param, const Ref<Texture2D> &p_texture) { ERR_FAIL_INDEX(p_param, PARAM_MAX); tex_parameters[p_param] = p_texture; @@ -986,6 +1150,16 @@ void ParticlesMaterial::set_param_texture(Parameter p_param, const Ref<Texture2D case PARAM_ANIM_OFFSET: { RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset_texture, tex_rid); } break; + case PARAM_TURB_INFLUENCE_OVER_LIFE: { + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->turbulence_influence_over_life, tex_rid); + _adjust_curve_range(p_texture, 0, 1); + } break; + case PARAM_TURB_VEL_INFLUENCE: { + // Can't happen, but silences warning + } break; + case PARAM_TURB_INIT_DISPLACEMENT: { + // Can't happen, but silences warning + } break; case PARAM_MAX: break; // Can't happen, but silences warning } @@ -993,22 +1167,22 @@ void ParticlesMaterial::set_param_texture(Parameter p_param, const Ref<Texture2D _queue_shader_change(); } -Ref<Texture2D> ParticlesMaterial::get_param_texture(Parameter p_param) const { +Ref<Texture2D> ParticleProcessMaterial::get_param_texture(Parameter p_param) const { ERR_FAIL_INDEX_V(p_param, PARAM_MAX, Ref<Texture2D>()); return tex_parameters[p_param]; } -void ParticlesMaterial::set_color(const Color &p_color) { +void ParticleProcessMaterial::set_color(const Color &p_color) { RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->color, p_color); color = p_color; } -Color ParticlesMaterial::get_color() const { +Color ParticleProcessMaterial::get_color() const { return color; } -void ParticlesMaterial::set_color_ramp(const Ref<Texture2D> &p_texture) { +void ParticleProcessMaterial::set_color_ramp(const Ref<Texture2D> &p_texture) { color_ramp = p_texture; RID tex_rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->color_ramp, tex_rid); @@ -1016,11 +1190,11 @@ void ParticlesMaterial::set_color_ramp(const Ref<Texture2D> &p_texture) { notify_property_list_changed(); } -Ref<Texture2D> ParticlesMaterial::get_color_ramp() const { +Ref<Texture2D> ParticleProcessMaterial::get_color_ramp() const { return color_ramp; } -void ParticlesMaterial::set_color_initial_ramp(const Ref<Texture2D> &p_texture) { +void ParticleProcessMaterial::set_color_initial_ramp(const Ref<Texture2D> &p_texture) { color_initial_ramp = p_texture; RID tex_rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->color_initial_ramp, tex_rid); @@ -1028,11 +1202,11 @@ void ParticlesMaterial::set_color_initial_ramp(const Ref<Texture2D> &p_texture) notify_property_list_changed(); } -Ref<Texture2D> ParticlesMaterial::get_color_initial_ramp() const { +Ref<Texture2D> ParticleProcessMaterial::get_color_initial_ramp() const { return color_initial_ramp; } -void ParticlesMaterial::set_particle_flag(ParticleFlags p_particle_flag, bool p_enable) { +void ParticleProcessMaterial::set_particle_flag(ParticleFlags p_particle_flag, bool p_enable) { ERR_FAIL_INDEX(p_particle_flag, PARTICLE_FLAG_MAX); particle_flags[p_particle_flag] = p_enable; _queue_shader_change(); @@ -1041,117 +1215,165 @@ void ParticlesMaterial::set_particle_flag(ParticleFlags p_particle_flag, bool p_ } } -bool ParticlesMaterial::get_particle_flag(ParticleFlags p_particle_flag) const { +bool ParticleProcessMaterial::get_particle_flag(ParticleFlags p_particle_flag) const { ERR_FAIL_INDEX_V(p_particle_flag, PARTICLE_FLAG_MAX, false); return particle_flags[p_particle_flag]; } -void ParticlesMaterial::set_emission_shape(EmissionShape p_shape) { +void ParticleProcessMaterial::set_emission_shape(EmissionShape p_shape) { ERR_FAIL_INDEX(p_shape, EMISSION_SHAPE_MAX); emission_shape = p_shape; notify_property_list_changed(); _queue_shader_change(); } -void ParticlesMaterial::set_emission_sphere_radius(real_t p_radius) { +void ParticleProcessMaterial::set_emission_sphere_radius(real_t p_radius) { emission_sphere_radius = p_radius; RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_sphere_radius, p_radius); } -void ParticlesMaterial::set_emission_box_extents(Vector3 p_extents) { +void ParticleProcessMaterial::set_emission_box_extents(Vector3 p_extents) { emission_box_extents = p_extents; RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_box_extents, p_extents); } -void ParticlesMaterial::set_emission_point_texture(const Ref<Texture2D> &p_points) { +void ParticleProcessMaterial::set_emission_point_texture(const Ref<Texture2D> &p_points) { emission_point_texture = p_points; RID tex_rid = p_points.is_valid() ? p_points->get_rid() : RID(); RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_points, tex_rid); } -void ParticlesMaterial::set_emission_normal_texture(const Ref<Texture2D> &p_normals) { +void ParticleProcessMaterial::set_emission_normal_texture(const Ref<Texture2D> &p_normals) { emission_normal_texture = p_normals; RID tex_rid = p_normals.is_valid() ? p_normals->get_rid() : RID(); RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_normal, tex_rid); } -void ParticlesMaterial::set_emission_color_texture(const Ref<Texture2D> &p_colors) { +void ParticleProcessMaterial::set_emission_color_texture(const Ref<Texture2D> &p_colors) { emission_color_texture = p_colors; RID tex_rid = p_colors.is_valid() ? p_colors->get_rid() : RID(); RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_color, tex_rid); _queue_shader_change(); } -void ParticlesMaterial::set_emission_point_count(int p_count) { +void ParticleProcessMaterial::set_emission_point_count(int p_count) { emission_point_count = p_count; RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_point_count, p_count); } -void ParticlesMaterial::set_emission_ring_axis(Vector3 p_axis) { +void ParticleProcessMaterial::set_emission_ring_axis(Vector3 p_axis) { emission_ring_axis = p_axis; RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_ring_axis, p_axis); } -void ParticlesMaterial::set_emission_ring_height(real_t p_height) { +void ParticleProcessMaterial::set_emission_ring_height(real_t p_height) { emission_ring_height = p_height; RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_ring_height, p_height); } -void ParticlesMaterial::set_emission_ring_radius(real_t p_radius) { +void ParticleProcessMaterial::set_emission_ring_radius(real_t p_radius) { emission_ring_radius = p_radius; RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_ring_radius, p_radius); } -void ParticlesMaterial::set_emission_ring_inner_radius(real_t p_radius) { +void ParticleProcessMaterial::set_emission_ring_inner_radius(real_t p_radius) { emission_ring_inner_radius = p_radius; RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_ring_inner_radius, p_radius); } -ParticlesMaterial::EmissionShape ParticlesMaterial::get_emission_shape() const { +ParticleProcessMaterial::EmissionShape ParticleProcessMaterial::get_emission_shape() const { return emission_shape; } -real_t ParticlesMaterial::get_emission_sphere_radius() const { +real_t ParticleProcessMaterial::get_emission_sphere_radius() const { return emission_sphere_radius; } -Vector3 ParticlesMaterial::get_emission_box_extents() const { +Vector3 ParticleProcessMaterial::get_emission_box_extents() const { return emission_box_extents; } -Ref<Texture2D> ParticlesMaterial::get_emission_point_texture() const { +Ref<Texture2D> ParticleProcessMaterial::get_emission_point_texture() const { return emission_point_texture; } -Ref<Texture2D> ParticlesMaterial::get_emission_normal_texture() const { +Ref<Texture2D> ParticleProcessMaterial::get_emission_normal_texture() const { return emission_normal_texture; } -Ref<Texture2D> ParticlesMaterial::get_emission_color_texture() const { +Ref<Texture2D> ParticleProcessMaterial::get_emission_color_texture() const { return emission_color_texture; } -int ParticlesMaterial::get_emission_point_count() const { +int ParticleProcessMaterial::get_emission_point_count() const { return emission_point_count; } -Vector3 ParticlesMaterial::get_emission_ring_axis() const { +Vector3 ParticleProcessMaterial::get_emission_ring_axis() const { return emission_ring_axis; } -real_t ParticlesMaterial::get_emission_ring_height() const { +real_t ParticleProcessMaterial::get_emission_ring_height() const { return emission_ring_height; } -real_t ParticlesMaterial::get_emission_ring_radius() const { +real_t ParticleProcessMaterial::get_emission_ring_radius() const { return emission_ring_radius; } -real_t ParticlesMaterial::get_emission_ring_inner_radius() const { +real_t ParticleProcessMaterial::get_emission_ring_inner_radius() const { return emission_ring_inner_radius; } -void ParticlesMaterial::set_gravity(const Vector3 &p_gravity) { +void ParticleProcessMaterial::set_turbulence_enabled(const bool p_turbulence_enabled) { + turbulence_enabled = p_turbulence_enabled; + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->turbulence_enabled, turbulence_enabled); + _queue_shader_change(); + notify_property_list_changed(); +} + +bool ParticleProcessMaterial::get_turbulence_enabled() const { + return turbulence_enabled; +} + +void ParticleProcessMaterial::set_turbulence_noise_strength(float p_turbulence_noise_strength) { + turbulence_noise_strength = p_turbulence_noise_strength; + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->turbulence_noise_strength, p_turbulence_noise_strength); +} + +float ParticleProcessMaterial::get_turbulence_noise_strength() const { + return turbulence_noise_strength; +} + +void ParticleProcessMaterial::set_turbulence_noise_scale(float p_turbulence_noise_scale) { + turbulence_noise_scale = p_turbulence_noise_scale; + float shader_turbulence_noise_scale = (pow(p_turbulence_noise_scale, 0.25) * 5.6234 / 10.0) * 4.0 - 3.0; + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->turbulence_noise_scale, shader_turbulence_noise_scale); +} + +float ParticleProcessMaterial::get_turbulence_noise_scale() const { + return turbulence_noise_scale; +} + +void ParticleProcessMaterial::set_turbulence_noise_speed_random(float p_turbulence_noise_speed_random) { + turbulence_noise_speed_random = p_turbulence_noise_speed_random; + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->turbulence_noise_speed_random, p_turbulence_noise_speed_random); +} + +float ParticleProcessMaterial::get_turbulence_noise_speed_random() const { + return turbulence_noise_speed_random; +} + +void ParticleProcessMaterial::set_turbulence_noise_speed(const Vector3 &p_turbulence_noise_speed) { + turbulence_noise_speed = p_turbulence_noise_speed; + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->turbulence_noise_speed, turbulence_noise_speed); +} + +Vector3 ParticleProcessMaterial::get_turbulence_noise_speed() const { + return turbulence_noise_speed; +} + +void ParticleProcessMaterial::set_gravity(const Vector3 &p_gravity) { gravity = p_gravity; Vector3 gset = gravity; if (gset == Vector3()) { @@ -1160,243 +1382,281 @@ void ParticlesMaterial::set_gravity(const Vector3 &p_gravity) { RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->gravity, gset); } -Vector3 ParticlesMaterial::get_gravity() const { +Vector3 ParticleProcessMaterial::get_gravity() const { return gravity; } -void ParticlesMaterial::set_lifetime_randomness(double p_lifetime) { +void ParticleProcessMaterial::set_lifetime_randomness(double p_lifetime) { lifetime_randomness = p_lifetime; RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->lifetime_randomness, lifetime_randomness); } -double ParticlesMaterial::get_lifetime_randomness() const { +double ParticleProcessMaterial::get_lifetime_randomness() const { return lifetime_randomness; } -RID ParticlesMaterial::get_shader_rid() const { +RID ParticleProcessMaterial::get_shader_rid() const { ERR_FAIL_COND_V(!shader_map.has(current_key), RID()); return shader_map[current_key].shader; } -void ParticlesMaterial::_validate_property(PropertyInfo &property) const { - if (property.name == "emission_sphere_radius" && (emission_shape != EMISSION_SHAPE_SPHERE && emission_shape != EMISSION_SHAPE_SPHERE_SURFACE)) { - property.usage = PROPERTY_USAGE_NONE; +void ParticleProcessMaterial::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "emission_sphere_radius" && (emission_shape != EMISSION_SHAPE_SPHERE && emission_shape != EMISSION_SHAPE_SPHERE_SURFACE)) { + p_property.usage = PROPERTY_USAGE_NONE; + } + + if (p_property.name == "emission_box_extents" && emission_shape != EMISSION_SHAPE_BOX) { + p_property.usage = PROPERTY_USAGE_NONE; + } + + if ((p_property.name == "emission_point_texture" || p_property.name == "emission_color_texture") && (emission_shape != EMISSION_SHAPE_POINTS && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS)) { + p_property.usage = PROPERTY_USAGE_NONE; } - if (property.name == "emission_box_extents" && emission_shape != EMISSION_SHAPE_BOX) { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name == "emission_normal_texture" && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) { + p_property.usage = PROPERTY_USAGE_NONE; } - if ((property.name == "emission_point_texture" || property.name == "emission_color_texture") && (emission_shape != EMISSION_SHAPE_POINTS && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS)) { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name == "emission_point_count" && (emission_shape != EMISSION_SHAPE_POINTS && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS)) { + p_property.usage = PROPERTY_USAGE_NONE; } - if (property.name == "emission_normal_texture" && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name.begins_with("emission_ring_") && emission_shape != EMISSION_SHAPE_RING) { + p_property.usage = PROPERTY_USAGE_NONE; } - if (property.name == "emission_point_count" && (emission_shape != EMISSION_SHAPE_POINTS && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS)) { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name == "sub_emitter_frequency" && sub_emitter_mode != SUB_EMITTER_CONSTANT) { + p_property.usage = PROPERTY_USAGE_NONE; } - if (property.name.begins_with("emission_ring_") && emission_shape != EMISSION_SHAPE_RING) { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name == "sub_emitter_amount_at_end" && sub_emitter_mode != SUB_EMITTER_AT_END) { + p_property.usage = PROPERTY_USAGE_NONE; } - if (property.name == "sub_emitter_frequency" && sub_emitter_mode != SUB_EMITTER_CONSTANT) { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name.begins_with("orbit_") && !particle_flags[PARTICLE_FLAG_DISABLE_Z]) { + p_property.usage = PROPERTY_USAGE_NONE; + } + + if (!turbulence_enabled) { + if (p_property.name == "turbulence_noise_strength" || + p_property.name == "turbulence_noise_scale" || + p_property.name == "turbulence_noise_speed" || + p_property.name == "turbulence_noise_speed_random" || + p_property.name == "turbulence_influence_over_life" || + p_property.name == "turbulence_influence_min" || + p_property.name == "turbulence_influence_max" || + p_property.name == "turbulence_initial_displacement_min" || + p_property.name == "turbulence_initial_displacement_max") { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } } - if (property.name == "sub_emitter_amount_at_end" && sub_emitter_mode != SUB_EMITTER_AT_END) { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name == "collision_friction" && collision_mode != COLLISION_RIGID) { + p_property.usage = PROPERTY_USAGE_NONE; } - if (property.name.begins_with("orbit_") && !particle_flags[PARTICLE_FLAG_DISABLE_Z]) { - property.usage = PROPERTY_USAGE_NONE; + if (p_property.name == "collision_bounce" && collision_mode != COLLISION_RIGID) { + p_property.usage = PROPERTY_USAGE_NONE; } } -void ParticlesMaterial::set_sub_emitter_mode(SubEmitterMode p_sub_emitter_mode) { +void ParticleProcessMaterial::set_sub_emitter_mode(SubEmitterMode p_sub_emitter_mode) { sub_emitter_mode = p_sub_emitter_mode; _queue_shader_change(); notify_property_list_changed(); } -ParticlesMaterial::SubEmitterMode ParticlesMaterial::get_sub_emitter_mode() const { +ParticleProcessMaterial::SubEmitterMode ParticleProcessMaterial::get_sub_emitter_mode() const { return sub_emitter_mode; } -void ParticlesMaterial::set_sub_emitter_frequency(double p_frequency) { +void ParticleProcessMaterial::set_sub_emitter_frequency(double p_frequency) { sub_emitter_frequency = p_frequency; RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->sub_emitter_frequency, 1.0 / p_frequency); //pass delta instead of frequency, since its easier to compute } -double ParticlesMaterial::get_sub_emitter_frequency() const { +double ParticleProcessMaterial::get_sub_emitter_frequency() const { return sub_emitter_frequency; } -void ParticlesMaterial::set_sub_emitter_amount_at_end(int p_amount) { +void ParticleProcessMaterial::set_sub_emitter_amount_at_end(int p_amount) { sub_emitter_amount_at_end = p_amount; RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->sub_emitter_amount_at_end, p_amount); } -int ParticlesMaterial::get_sub_emitter_amount_at_end() const { +int ParticleProcessMaterial::get_sub_emitter_amount_at_end() const { return sub_emitter_amount_at_end; } -void ParticlesMaterial::set_sub_emitter_keep_velocity(bool p_enable) { +void ParticleProcessMaterial::set_sub_emitter_keep_velocity(bool p_enable) { sub_emitter_keep_velocity = p_enable; RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->sub_emitter_keep_velocity, p_enable); } -bool ParticlesMaterial::get_sub_emitter_keep_velocity() const { +bool ParticleProcessMaterial::get_sub_emitter_keep_velocity() const { return sub_emitter_keep_velocity; } -void ParticlesMaterial::set_attractor_interaction_enabled(bool p_enable) { +void ParticleProcessMaterial::set_attractor_interaction_enabled(bool p_enable) { attractor_interaction_enabled = p_enable; _queue_shader_change(); } -bool ParticlesMaterial::is_attractor_interaction_enabled() const { +bool ParticleProcessMaterial::is_attractor_interaction_enabled() const { return attractor_interaction_enabled; } -void ParticlesMaterial::set_collision_enabled(bool p_enabled) { - collision_enabled = p_enabled; +void ParticleProcessMaterial::set_collision_mode(CollisionMode p_collision_mode) { + collision_mode = p_collision_mode; _queue_shader_change(); + notify_property_list_changed(); } -bool ParticlesMaterial::is_collision_enabled() const { - return collision_enabled; +ParticleProcessMaterial::CollisionMode ParticleProcessMaterial::get_collision_mode() const { + return collision_mode; } -void ParticlesMaterial::set_collision_use_scale(bool p_scale) { +void ParticleProcessMaterial::set_collision_use_scale(bool p_scale) { collision_scale = p_scale; _queue_shader_change(); } -bool ParticlesMaterial::is_collision_using_scale() const { +bool ParticleProcessMaterial::is_collision_using_scale() const { return collision_scale; } -void ParticlesMaterial::set_collision_friction(float p_friction) { +void ParticleProcessMaterial::set_collision_friction(float p_friction) { collision_friction = p_friction; RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->collision_friction, p_friction); } -float ParticlesMaterial::get_collision_friction() const { +float ParticleProcessMaterial::get_collision_friction() const { return collision_friction; } -void ParticlesMaterial::set_collision_bounce(float p_bounce) { +void ParticleProcessMaterial::set_collision_bounce(float p_bounce) { collision_bounce = p_bounce; RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->collision_bounce, p_bounce); } -float ParticlesMaterial::get_collision_bounce() const { +float ParticleProcessMaterial::get_collision_bounce() const { return collision_bounce; } -Shader::Mode ParticlesMaterial::get_shader_mode() const { +Shader::Mode ParticleProcessMaterial::get_shader_mode() const { return Shader::MODE_PARTICLES; } -void ParticlesMaterial::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_direction", "degrees"), &ParticlesMaterial::set_direction); - ClassDB::bind_method(D_METHOD("get_direction"), &ParticlesMaterial::get_direction); +void ParticleProcessMaterial::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_direction", "degrees"), &ParticleProcessMaterial::set_direction); + ClassDB::bind_method(D_METHOD("get_direction"), &ParticleProcessMaterial::get_direction); + + ClassDB::bind_method(D_METHOD("set_spread", "degrees"), &ParticleProcessMaterial::set_spread); + ClassDB::bind_method(D_METHOD("get_spread"), &ParticleProcessMaterial::get_spread); + + ClassDB::bind_method(D_METHOD("set_flatness", "amount"), &ParticleProcessMaterial::set_flatness); + ClassDB::bind_method(D_METHOD("get_flatness"), &ParticleProcessMaterial::get_flatness); + + ClassDB::bind_method(D_METHOD("set_param_min", "param", "value"), &ParticleProcessMaterial::set_param_min); + ClassDB::bind_method(D_METHOD("get_param_min", "param"), &ParticleProcessMaterial::get_param_min); + + ClassDB::bind_method(D_METHOD("set_param_max", "param", "value"), &ParticleProcessMaterial::set_param_max); + ClassDB::bind_method(D_METHOD("get_param_max", "param"), &ParticleProcessMaterial::get_param_max); + + ClassDB::bind_method(D_METHOD("set_param_texture", "param", "texture"), &ParticleProcessMaterial::set_param_texture); + ClassDB::bind_method(D_METHOD("get_param_texture", "param"), &ParticleProcessMaterial::get_param_texture); - ClassDB::bind_method(D_METHOD("set_spread", "degrees"), &ParticlesMaterial::set_spread); - ClassDB::bind_method(D_METHOD("get_spread"), &ParticlesMaterial::get_spread); + ClassDB::bind_method(D_METHOD("set_color", "color"), &ParticleProcessMaterial::set_color); + ClassDB::bind_method(D_METHOD("get_color"), &ParticleProcessMaterial::get_color); - ClassDB::bind_method(D_METHOD("set_flatness", "amount"), &ParticlesMaterial::set_flatness); - ClassDB::bind_method(D_METHOD("get_flatness"), &ParticlesMaterial::get_flatness); + ClassDB::bind_method(D_METHOD("set_color_ramp", "ramp"), &ParticleProcessMaterial::set_color_ramp); + ClassDB::bind_method(D_METHOD("get_color_ramp"), &ParticleProcessMaterial::get_color_ramp); - ClassDB::bind_method(D_METHOD("set_param_min", "param", "value"), &ParticlesMaterial::set_param_min); - ClassDB::bind_method(D_METHOD("get_param_min", "param"), &ParticlesMaterial::get_param_min); + ClassDB::bind_method(D_METHOD("set_color_initial_ramp", "ramp"), &ParticleProcessMaterial::set_color_initial_ramp); + ClassDB::bind_method(D_METHOD("get_color_initial_ramp"), &ParticleProcessMaterial::get_color_initial_ramp); - ClassDB::bind_method(D_METHOD("set_param_max", "param", "value"), &ParticlesMaterial::set_param_max); - ClassDB::bind_method(D_METHOD("get_param_max", "param"), &ParticlesMaterial::get_param_max); + ClassDB::bind_method(D_METHOD("set_particle_flag", "particle_flag", "enable"), &ParticleProcessMaterial::set_particle_flag); + ClassDB::bind_method(D_METHOD("get_particle_flag", "particle_flag"), &ParticleProcessMaterial::get_particle_flag); - ClassDB::bind_method(D_METHOD("set_param_texture", "param", "texture"), &ParticlesMaterial::set_param_texture); - ClassDB::bind_method(D_METHOD("get_param_texture", "param"), &ParticlesMaterial::get_param_texture); + ClassDB::bind_method(D_METHOD("set_emission_shape", "shape"), &ParticleProcessMaterial::set_emission_shape); + ClassDB::bind_method(D_METHOD("get_emission_shape"), &ParticleProcessMaterial::get_emission_shape); - ClassDB::bind_method(D_METHOD("set_color", "color"), &ParticlesMaterial::set_color); - ClassDB::bind_method(D_METHOD("get_color"), &ParticlesMaterial::get_color); + ClassDB::bind_method(D_METHOD("set_emission_sphere_radius", "radius"), &ParticleProcessMaterial::set_emission_sphere_radius); + ClassDB::bind_method(D_METHOD("get_emission_sphere_radius"), &ParticleProcessMaterial::get_emission_sphere_radius); - ClassDB::bind_method(D_METHOD("set_color_ramp", "ramp"), &ParticlesMaterial::set_color_ramp); - ClassDB::bind_method(D_METHOD("get_color_ramp"), &ParticlesMaterial::get_color_ramp); + ClassDB::bind_method(D_METHOD("set_emission_box_extents", "extents"), &ParticleProcessMaterial::set_emission_box_extents); + ClassDB::bind_method(D_METHOD("get_emission_box_extents"), &ParticleProcessMaterial::get_emission_box_extents); - ClassDB::bind_method(D_METHOD("set_color_initial_ramp", "ramp"), &ParticlesMaterial::set_color_initial_ramp); - ClassDB::bind_method(D_METHOD("get_color_initial_ramp"), &ParticlesMaterial::get_color_initial_ramp); + ClassDB::bind_method(D_METHOD("set_emission_point_texture", "texture"), &ParticleProcessMaterial::set_emission_point_texture); + ClassDB::bind_method(D_METHOD("get_emission_point_texture"), &ParticleProcessMaterial::get_emission_point_texture); - ClassDB::bind_method(D_METHOD("set_particle_flag", "particle_flag", "enable"), &ParticlesMaterial::set_particle_flag); - ClassDB::bind_method(D_METHOD("get_particle_flag", "particle_flag"), &ParticlesMaterial::get_particle_flag); + ClassDB::bind_method(D_METHOD("set_emission_normal_texture", "texture"), &ParticleProcessMaterial::set_emission_normal_texture); + ClassDB::bind_method(D_METHOD("get_emission_normal_texture"), &ParticleProcessMaterial::get_emission_normal_texture); - ClassDB::bind_method(D_METHOD("set_emission_shape", "shape"), &ParticlesMaterial::set_emission_shape); - ClassDB::bind_method(D_METHOD("get_emission_shape"), &ParticlesMaterial::get_emission_shape); + ClassDB::bind_method(D_METHOD("set_emission_color_texture", "texture"), &ParticleProcessMaterial::set_emission_color_texture); + ClassDB::bind_method(D_METHOD("get_emission_color_texture"), &ParticleProcessMaterial::get_emission_color_texture); - ClassDB::bind_method(D_METHOD("set_emission_sphere_radius", "radius"), &ParticlesMaterial::set_emission_sphere_radius); - ClassDB::bind_method(D_METHOD("get_emission_sphere_radius"), &ParticlesMaterial::get_emission_sphere_radius); + ClassDB::bind_method(D_METHOD("set_emission_point_count", "point_count"), &ParticleProcessMaterial::set_emission_point_count); + ClassDB::bind_method(D_METHOD("get_emission_point_count"), &ParticleProcessMaterial::get_emission_point_count); - ClassDB::bind_method(D_METHOD("set_emission_box_extents", "extents"), &ParticlesMaterial::set_emission_box_extents); - ClassDB::bind_method(D_METHOD("get_emission_box_extents"), &ParticlesMaterial::get_emission_box_extents); + ClassDB::bind_method(D_METHOD("set_emission_ring_axis", "axis"), &ParticleProcessMaterial::set_emission_ring_axis); + ClassDB::bind_method(D_METHOD("get_emission_ring_axis"), &ParticleProcessMaterial::get_emission_ring_axis); - ClassDB::bind_method(D_METHOD("set_emission_point_texture", "texture"), &ParticlesMaterial::set_emission_point_texture); - ClassDB::bind_method(D_METHOD("get_emission_point_texture"), &ParticlesMaterial::get_emission_point_texture); + ClassDB::bind_method(D_METHOD("set_emission_ring_height", "height"), &ParticleProcessMaterial::set_emission_ring_height); + ClassDB::bind_method(D_METHOD("get_emission_ring_height"), &ParticleProcessMaterial::get_emission_ring_height); - ClassDB::bind_method(D_METHOD("set_emission_normal_texture", "texture"), &ParticlesMaterial::set_emission_normal_texture); - ClassDB::bind_method(D_METHOD("get_emission_normal_texture"), &ParticlesMaterial::get_emission_normal_texture); + ClassDB::bind_method(D_METHOD("set_emission_ring_radius", "radius"), &ParticleProcessMaterial::set_emission_ring_radius); + ClassDB::bind_method(D_METHOD("get_emission_ring_radius"), &ParticleProcessMaterial::get_emission_ring_radius); - ClassDB::bind_method(D_METHOD("set_emission_color_texture", "texture"), &ParticlesMaterial::set_emission_color_texture); - ClassDB::bind_method(D_METHOD("get_emission_color_texture"), &ParticlesMaterial::get_emission_color_texture); + ClassDB::bind_method(D_METHOD("set_emission_ring_inner_radius", "inner_radius"), &ParticleProcessMaterial::set_emission_ring_inner_radius); + ClassDB::bind_method(D_METHOD("get_emission_ring_inner_radius"), &ParticleProcessMaterial::get_emission_ring_inner_radius); - ClassDB::bind_method(D_METHOD("set_emission_point_count", "point_count"), &ParticlesMaterial::set_emission_point_count); - ClassDB::bind_method(D_METHOD("get_emission_point_count"), &ParticlesMaterial::get_emission_point_count); + ClassDB::bind_method(D_METHOD("get_turbulence_enabled"), &ParticleProcessMaterial::get_turbulence_enabled); + ClassDB::bind_method(D_METHOD("set_turbulence_enabled", "turbulence_enabled"), &ParticleProcessMaterial::set_turbulence_enabled); - ClassDB::bind_method(D_METHOD("set_emission_ring_axis", "axis"), &ParticlesMaterial::set_emission_ring_axis); - ClassDB::bind_method(D_METHOD("get_emission_ring_axis"), &ParticlesMaterial::get_emission_ring_axis); + ClassDB::bind_method(D_METHOD("get_turbulence_noise_strength"), &ParticleProcessMaterial::get_turbulence_noise_strength); + ClassDB::bind_method(D_METHOD("set_turbulence_noise_strength", "turbulence_noise_strength"), &ParticleProcessMaterial::set_turbulence_noise_strength); - ClassDB::bind_method(D_METHOD("set_emission_ring_height", "height"), &ParticlesMaterial::set_emission_ring_height); - ClassDB::bind_method(D_METHOD("get_emission_ring_height"), &ParticlesMaterial::get_emission_ring_height); + ClassDB::bind_method(D_METHOD("get_turbulence_noise_scale"), &ParticleProcessMaterial::get_turbulence_noise_scale); + ClassDB::bind_method(D_METHOD("set_turbulence_noise_scale", "turbulence_noise_scale"), &ParticleProcessMaterial::set_turbulence_noise_scale); - ClassDB::bind_method(D_METHOD("set_emission_ring_radius", "radius"), &ParticlesMaterial::set_emission_ring_radius); - ClassDB::bind_method(D_METHOD("get_emission_ring_radius"), &ParticlesMaterial::get_emission_ring_radius); + ClassDB::bind_method(D_METHOD("get_turbulence_noise_speed_random"), &ParticleProcessMaterial::get_turbulence_noise_speed_random); + ClassDB::bind_method(D_METHOD("set_turbulence_noise_speed_random", "turbulence_noise_speed_random"), &ParticleProcessMaterial::set_turbulence_noise_speed_random); - ClassDB::bind_method(D_METHOD("set_emission_ring_inner_radius", "inner_radius"), &ParticlesMaterial::set_emission_ring_inner_radius); - ClassDB::bind_method(D_METHOD("get_emission_ring_inner_radius"), &ParticlesMaterial::get_emission_ring_inner_radius); + ClassDB::bind_method(D_METHOD("get_turbulence_noise_speed"), &ParticleProcessMaterial::get_turbulence_noise_speed); + ClassDB::bind_method(D_METHOD("set_turbulence_noise_speed", "turbulence_noise_speed"), &ParticleProcessMaterial::set_turbulence_noise_speed); - ClassDB::bind_method(D_METHOD("get_gravity"), &ParticlesMaterial::get_gravity); - ClassDB::bind_method(D_METHOD("set_gravity", "accel_vec"), &ParticlesMaterial::set_gravity); + ClassDB::bind_method(D_METHOD("get_gravity"), &ParticleProcessMaterial::get_gravity); + ClassDB::bind_method(D_METHOD("set_gravity", "accel_vec"), &ParticleProcessMaterial::set_gravity); - ClassDB::bind_method(D_METHOD("set_lifetime_randomness", "randomness"), &ParticlesMaterial::set_lifetime_randomness); - ClassDB::bind_method(D_METHOD("get_lifetime_randomness"), &ParticlesMaterial::get_lifetime_randomness); + ClassDB::bind_method(D_METHOD("set_lifetime_randomness", "randomness"), &ParticleProcessMaterial::set_lifetime_randomness); + ClassDB::bind_method(D_METHOD("get_lifetime_randomness"), &ParticleProcessMaterial::get_lifetime_randomness); - ClassDB::bind_method(D_METHOD("get_sub_emitter_mode"), &ParticlesMaterial::get_sub_emitter_mode); - ClassDB::bind_method(D_METHOD("set_sub_emitter_mode", "mode"), &ParticlesMaterial::set_sub_emitter_mode); + ClassDB::bind_method(D_METHOD("get_sub_emitter_mode"), &ParticleProcessMaterial::get_sub_emitter_mode); + ClassDB::bind_method(D_METHOD("set_sub_emitter_mode", "mode"), &ParticleProcessMaterial::set_sub_emitter_mode); - ClassDB::bind_method(D_METHOD("get_sub_emitter_frequency"), &ParticlesMaterial::get_sub_emitter_frequency); - ClassDB::bind_method(D_METHOD("set_sub_emitter_frequency", "hz"), &ParticlesMaterial::set_sub_emitter_frequency); + ClassDB::bind_method(D_METHOD("get_sub_emitter_frequency"), &ParticleProcessMaterial::get_sub_emitter_frequency); + ClassDB::bind_method(D_METHOD("set_sub_emitter_frequency", "hz"), &ParticleProcessMaterial::set_sub_emitter_frequency); - ClassDB::bind_method(D_METHOD("get_sub_emitter_amount_at_end"), &ParticlesMaterial::get_sub_emitter_amount_at_end); - ClassDB::bind_method(D_METHOD("set_sub_emitter_amount_at_end", "amount"), &ParticlesMaterial::set_sub_emitter_amount_at_end); + ClassDB::bind_method(D_METHOD("get_sub_emitter_amount_at_end"), &ParticleProcessMaterial::get_sub_emitter_amount_at_end); + ClassDB::bind_method(D_METHOD("set_sub_emitter_amount_at_end", "amount"), &ParticleProcessMaterial::set_sub_emitter_amount_at_end); - ClassDB::bind_method(D_METHOD("get_sub_emitter_keep_velocity"), &ParticlesMaterial::get_sub_emitter_keep_velocity); - ClassDB::bind_method(D_METHOD("set_sub_emitter_keep_velocity", "enable"), &ParticlesMaterial::set_sub_emitter_keep_velocity); + ClassDB::bind_method(D_METHOD("get_sub_emitter_keep_velocity"), &ParticleProcessMaterial::get_sub_emitter_keep_velocity); + ClassDB::bind_method(D_METHOD("set_sub_emitter_keep_velocity", "enable"), &ParticleProcessMaterial::set_sub_emitter_keep_velocity); - ClassDB::bind_method(D_METHOD("set_attractor_interaction_enabled", "enabled"), &ParticlesMaterial::set_attractor_interaction_enabled); - ClassDB::bind_method(D_METHOD("is_attractor_interaction_enabled"), &ParticlesMaterial::is_attractor_interaction_enabled); + ClassDB::bind_method(D_METHOD("set_attractor_interaction_enabled", "enabled"), &ParticleProcessMaterial::set_attractor_interaction_enabled); + ClassDB::bind_method(D_METHOD("is_attractor_interaction_enabled"), &ParticleProcessMaterial::is_attractor_interaction_enabled); - ClassDB::bind_method(D_METHOD("set_collision_enabled", "enabled"), &ParticlesMaterial::set_collision_enabled); - ClassDB::bind_method(D_METHOD("is_collision_enabled"), &ParticlesMaterial::is_collision_enabled); + ClassDB::bind_method(D_METHOD("set_collision_mode", "mode"), &ParticleProcessMaterial::set_collision_mode); + ClassDB::bind_method(D_METHOD("get_collision_mode"), &ParticleProcessMaterial::get_collision_mode); - ClassDB::bind_method(D_METHOD("set_collision_use_scale", "radius"), &ParticlesMaterial::set_collision_use_scale); - ClassDB::bind_method(D_METHOD("is_collision_using_scale"), &ParticlesMaterial::is_collision_using_scale); + ClassDB::bind_method(D_METHOD("set_collision_use_scale", "radius"), &ParticleProcessMaterial::set_collision_use_scale); + ClassDB::bind_method(D_METHOD("is_collision_using_scale"), &ParticleProcessMaterial::is_collision_using_scale); - ClassDB::bind_method(D_METHOD("set_collision_friction", "friction"), &ParticlesMaterial::set_collision_friction); - ClassDB::bind_method(D_METHOD("get_collision_friction"), &ParticlesMaterial::get_collision_friction); + ClassDB::bind_method(D_METHOD("set_collision_friction", "friction"), &ParticleProcessMaterial::set_collision_friction); + ClassDB::bind_method(D_METHOD("get_collision_friction"), &ParticleProcessMaterial::get_collision_friction); - ClassDB::bind_method(D_METHOD("set_collision_bounce", "bounce"), &ParticlesMaterial::set_collision_bounce); - ClassDB::bind_method(D_METHOD("get_collision_bounce"), &ParticlesMaterial::get_collision_bounce); + ClassDB::bind_method(D_METHOD("set_collision_bounce", "bounce"), &ParticleProcessMaterial::set_collision_bounce); + ClassDB::bind_method(D_METHOD("get_collision_bounce"), &ParticleProcessMaterial::get_collision_bounce); ADD_GROUP("Time", ""); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime_randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_lifetime_randomness", "get_lifetime_randomness"); @@ -1413,7 +1673,7 @@ void ParticlesMaterial::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_height"), "set_emission_ring_height", "get_emission_ring_height"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_radius"), "set_emission_ring_radius", "get_emission_ring_radius"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_inner_radius"), "set_emission_ring_inner_radius", "get_emission_ring_inner_radius"); - ADD_GROUP("ParticleFlags", "particle_flag_"); + ADD_GROUP("Particle Flags", "particle_flag_"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "particle_flag_align_y"), "set_particle_flag", "get_particle_flag", PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "particle_flag_rotate_y"), "set_particle_flag", "get_particle_flag", PARTICLE_FLAG_ROTATE_Y); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "particle_flag_disable_z"), "set_particle_flag", "get_particle_flag", PARTICLE_FLAG_DISABLE_Z); @@ -1424,35 +1684,35 @@ void ParticlesMaterial::_bind_methods() { ADD_GROUP("Gravity", ""); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity"), "set_gravity", "get_gravity"); ADD_GROUP("Initial Velocity", "initial_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "initial_velocity_min", PROPERTY_HINT_RANGE, "0,1000,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_INITIAL_LINEAR_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "initial_velocity_max", PROPERTY_HINT_RANGE, "0,1000,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_INITIAL_LINEAR_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "initial_velocity_min", PROPERTY_HINT_RANGE, "0,1000,0.01,or_less,or_greater"), "set_param_min", "get_param_min", PARAM_INITIAL_LINEAR_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "initial_velocity_max", PROPERTY_HINT_RANGE, "0,1000,0.01,or_less,or_greater"), "set_param_max", "get_param_max", PARAM_INITIAL_LINEAR_VELOCITY); ADD_GROUP("Angular Velocity", "angular_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_velocity_min", PROPERTY_HINT_RANGE, "-720,720,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_ANGULAR_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_velocity_max", PROPERTY_HINT_RANGE, "-720,720,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_ANGULAR_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_velocity_min", PROPERTY_HINT_RANGE, "-720,720,0.01,or_less,or_greater"), "set_param_min", "get_param_min", PARAM_ANGULAR_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_velocity_max", PROPERTY_HINT_RANGE, "-720,720,0.01,or_less,or_greater"), "set_param_max", "get_param_max", PARAM_ANGULAR_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angular_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANGULAR_VELOCITY); ADD_GROUP("Orbit Velocity", "orbit_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "orbit_velocity_min", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_ORBIT_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "orbit_velocity_max", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_ORBIT_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "orbit_velocity_min", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_less,or_greater"), "set_param_min", "get_param_min", PARAM_ORBIT_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "orbit_velocity_max", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_less,or_greater"), "set_param_max", "get_param_max", PARAM_ORBIT_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "orbit_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ORBIT_VELOCITY); ADD_GROUP("Linear Accel", "linear_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_accel_min", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_LINEAR_ACCEL); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_accel_max", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_LINEAR_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_accel_min", PROPERTY_HINT_RANGE, "-100,100,0.01,or_less,or_greater"), "set_param_min", "get_param_min", PARAM_LINEAR_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_accel_max", PROPERTY_HINT_RANGE, "-100,100,0.01,or_less,or_greater"), "set_param_max", "get_param_max", PARAM_LINEAR_ACCEL); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "linear_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_LINEAR_ACCEL); ADD_GROUP("Radial Accel", "radial_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "radial_accel_min", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_RADIAL_ACCEL); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "radial_accel_max", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_RADIAL_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "radial_accel_min", PROPERTY_HINT_RANGE, "-100,100,0.01,or_less,or_greater"), "set_param_min", "get_param_min", PARAM_RADIAL_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "radial_accel_max", PROPERTY_HINT_RANGE, "-100,100,0.01,or_less,or_greater"), "set_param_max", "get_param_max", PARAM_RADIAL_ACCEL); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "radial_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_RADIAL_ACCEL); ADD_GROUP("Tangential Accel", "tangential_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "tangential_accel_min", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_TANGENTIAL_ACCEL); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "tangential_accel_max", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_TANGENTIAL_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "tangential_accel_min", PROPERTY_HINT_RANGE, "-100,100,0.01,or_less,or_greater"), "set_param_min", "get_param_min", PARAM_TANGENTIAL_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "tangential_accel_max", PROPERTY_HINT_RANGE, "-100,100,0.01,or_less,or_greater"), "set_param_max", "get_param_max", PARAM_TANGENTIAL_ACCEL); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "tangential_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_TANGENTIAL_ACCEL); ADD_GROUP("Damping", ""); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "damping_min", PROPERTY_HINT_RANGE, "0,100,0.01,or_greater"), "set_param_min", "get_param_min", PARAM_DAMPING); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "damping_max", PROPERTY_HINT_RANGE, "0,100,0.01,or_greater"), "set_param_max", "get_param_max", PARAM_DAMPING); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "damping_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_DAMPING); ADD_GROUP("Angle", ""); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle_min", PROPERTY_HINT_RANGE, "-720,720,0.1,or_lesser,or_greater,degrees"), "set_param_min", "get_param_min", PARAM_ANGLE); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle_max", PROPERTY_HINT_RANGE, "-720,720,0.1,or_lesser,or_greater,degrees"), "set_param_max", "get_param_max", PARAM_ANGLE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle_min", PROPERTY_HINT_RANGE, "-720,720,0.1,or_less,or_greater,degrees"), "set_param_min", "get_param_min", PARAM_ANGLE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle_max", PROPERTY_HINT_RANGE, "-720,720,0.1,or_less,or_greater,degrees"), "set_param_max", "get_param_max", PARAM_ANGLE); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angle_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANGLE); ADD_GROUP("Scale", ""); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "scale_min", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param_min", "get_param_min", PARAM_SCALE); @@ -1467,12 +1727,25 @@ void ParticlesMaterial::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "hue_variation_min", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_param_min", "get_param_min", PARAM_HUE_VARIATION); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "hue_variation_max", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_param_max", "get_param_max", PARAM_HUE_VARIATION); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "hue_variation_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_HUE_VARIATION); + + ADD_GROUP("Turbulence", "turbulence_"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "turbulence_enabled"), "set_turbulence_enabled", "get_turbulence_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "turbulence_noise_strength", PROPERTY_HINT_RANGE, "0,20,0.01"), "set_turbulence_noise_strength", "get_turbulence_noise_strength"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "turbulence_noise_scale", PROPERTY_HINT_RANGE, "0,10,0.01"), "set_turbulence_noise_scale", "get_turbulence_noise_scale"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "turbulence_noise_speed"), "set_turbulence_noise_speed", "get_turbulence_noise_speed"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "turbulence_noise_speed_random", PROPERTY_HINT_RANGE, "0,10,0.01"), "set_turbulence_noise_speed_random", "get_turbulence_noise_speed_random"); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "turbulence_influence_min", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_min", "get_param_min", PARAM_TURB_VEL_INFLUENCE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "turbulence_influence_max", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_max", "get_param_max", PARAM_TURB_VEL_INFLUENCE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "turbulence_initial_displacement_min", PROPERTY_HINT_RANGE, "-100,100,0.1"), "set_param_min", "get_param_min", PARAM_TURB_INIT_DISPLACEMENT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "turbulence_initial_displacement_max", PROPERTY_HINT_RANGE, "-100,100,0.1"), "set_param_max", "get_param_max", PARAM_TURB_INIT_DISPLACEMENT); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "turbulence_influence_over_life", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_TURB_INFLUENCE_OVER_LIFE); + ADD_GROUP("Animation", "anim_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_min", PROPERTY_HINT_RANGE, "0,16,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_ANIM_SPEED); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_max", PROPERTY_HINT_RANGE, "0,16,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_ANIM_SPEED); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_min", PROPERTY_HINT_RANGE, "0,16,0.01,or_less,or_greater"), "set_param_min", "get_param_min", PARAM_ANIM_SPEED); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_max", PROPERTY_HINT_RANGE, "0,16,0.01,or_less,or_greater"), "set_param_max", "get_param_max", PARAM_ANIM_SPEED); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_speed_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANIM_SPEED); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_min", PROPERTY_HINT_RANGE, "0,16,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_ANIM_OFFSET); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_max", PROPERTY_HINT_RANGE, "0,16,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_ANIM_OFFSET); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_min", PROPERTY_HINT_RANGE, "0,16,0.01,or_less,or_greater"), "set_param_min", "get_param_min", PARAM_ANIM_OFFSET); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_max", PROPERTY_HINT_RANGE, "0,16,0.01,or_less,or_greater"), "set_param_max", "get_param_max", PARAM_ANIM_OFFSET); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_offset_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANIM_OFFSET); ADD_GROUP("Sub Emitter", "sub_emitter_"); @@ -1484,7 +1757,7 @@ void ParticlesMaterial::_bind_methods() { ADD_GROUP("Attractor Interaction", "attractor_interaction_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "attractor_interaction_enabled"), "set_attractor_interaction_enabled", "is_attractor_interaction_enabled"); ADD_GROUP("Collision", "collision_"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collision_enabled"), "set_collision_enabled", "is_collision_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mode", PROPERTY_HINT_ENUM, "Disabled,Rigid,Hide On Contact"), "set_collision_mode", "get_collision_mode"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_collision_friction", "get_collision_friction"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_collision_bounce", "get_collision_bounce"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collision_use_scale"), "set_collision_use_scale", "is_collision_using_scale"); @@ -1517,14 +1790,23 @@ void ParticlesMaterial::_bind_methods() { BIND_ENUM_CONSTANT(EMISSION_SHAPE_RING); BIND_ENUM_CONSTANT(EMISSION_SHAPE_MAX); + BIND_ENUM_CONSTANT(PARAM_TURB_VEL_INFLUENCE); + BIND_ENUM_CONSTANT(PARAM_TURB_INIT_DISPLACEMENT); + BIND_ENUM_CONSTANT(PARAM_TURB_INFLUENCE_OVER_LIFE); + BIND_ENUM_CONSTANT(SUB_EMITTER_DISABLED); BIND_ENUM_CONSTANT(SUB_EMITTER_CONSTANT); BIND_ENUM_CONSTANT(SUB_EMITTER_AT_END); BIND_ENUM_CONSTANT(SUB_EMITTER_AT_COLLISION); BIND_ENUM_CONSTANT(SUB_EMITTER_MAX); + + BIND_ENUM_CONSTANT(COLLISION_DISABLED); + BIND_ENUM_CONSTANT(COLLISION_RIGID); + BIND_ENUM_CONSTANT(COLLISION_HIDE_ON_CONTACT); + BIND_ENUM_CONSTANT(COLLISION_MAX); } -ParticlesMaterial::ParticlesMaterial() : +ParticleProcessMaterial::ParticleProcessMaterial() : element(this) { set_direction(Vector3(1, 0, 0)); set_spread(45); @@ -1560,6 +1842,17 @@ ParticlesMaterial::ParticlesMaterial() : set_emission_ring_height(1); set_emission_ring_radius(1); set_emission_ring_inner_radius(0); + + set_turbulence_enabled(false); + set_turbulence_noise_speed(Vector3(0.5, 0.5, 0.5)); + set_turbulence_noise_strength(1); + set_turbulence_noise_scale(9); + set_turbulence_noise_speed_random(0); + set_param_min(PARAM_TURB_VEL_INFLUENCE, 0.1); + set_param_max(PARAM_TURB_VEL_INFLUENCE, 0.1); + set_param_min(PARAM_TURB_INIT_DISPLACEMENT, 0.0); + set_param_max(PARAM_TURB_INIT_DISPLACEMENT, 0.0); + set_gravity(Vector3(0, -9.8, 0)); set_lifetime_randomness(0); @@ -1569,7 +1862,7 @@ ParticlesMaterial::ParticlesMaterial() : set_sub_emitter_keep_velocity(false); set_attractor_interaction_enabled(true); - set_collision_enabled(false); + set_collision_mode(COLLISION_DISABLED); set_collision_bounce(0.0); set_collision_friction(0.0); set_collision_use_scale(false); @@ -1586,7 +1879,7 @@ ParticlesMaterial::ParticlesMaterial() : _queue_shader_change(); } -ParticlesMaterial::~ParticlesMaterial() { +ParticleProcessMaterial::~ParticleProcessMaterial() { MutexLock lock(material_mutex); if (shader_map.has(current_key)) { diff --git a/scene/resources/particles_material.h b/scene/resources/particle_process_material.h index af45593f38..9430e5797d 100644 --- a/scene/resources/particles_material.h +++ b/scene/resources/particle_process_material.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* particles_material.h */ +/* particle_process_material.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,12 +28,12 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#ifndef PARTICLE_PROCESS_MATERIAL_H +#define PARTICLE_PROCESS_MATERIAL_H + #include "core/templates/rid.h" #include "scene/resources/material.h" -#ifndef PARTICLES_MATERIAL_H -#define PARTICLES_MATERIAL_H - /* TODO: -Path following @@ -41,8 +41,8 @@ -Proper trails */ -class ParticlesMaterial : public Material { - GDCLASS(ParticlesMaterial, Material); +class ParticleProcessMaterial : public Material { + GDCLASS(ParticleProcessMaterial, Material); public: enum Parameter { @@ -58,6 +58,9 @@ public: PARAM_HUE_VARIATION, PARAM_ANIM_SPEED, PARAM_ANIM_OFFSET, + PARAM_TURB_INFLUENCE_OVER_LIFE, + PARAM_TURB_VEL_INFLUENCE, + PARAM_TURB_INIT_DISPLACEMENT, PARAM_MAX }; @@ -90,6 +93,14 @@ public: SUB_EMITTER_MAX }; + // When extending, make sure not to overflow the size of the MaterialKey below. + enum CollisionMode { + COLLISION_DISABLED, + COLLISION_RIGID, + COLLISION_HIDE_ON_CONTACT, + COLLISION_MAX + }; + private: union MaterialKey { // The bit size of the struct must be kept below or equal to 32 bits. @@ -103,11 +114,12 @@ private: uint32_t has_emission_color : 1; uint32_t sub_emitter : 2; uint32_t attractor_enabled : 1; - uint32_t collision_enabled : 1; + uint32_t collision_mode : 2; uint32_t collision_scale : 1; + uint32_t turbulence_enabled : 1; }; - uint32_t key = 0; + uint64_t key = 0; static uint32_t hash(const MaterialKey &p_key) { return hash_murmur3_one_32(p_key.key); @@ -149,15 +161,16 @@ private: mk.emission_shape = emission_shape; mk.has_emission_color = emission_shape >= EMISSION_SHAPE_POINTS && emission_color_texture.is_valid(); mk.sub_emitter = sub_emitter_mode; - mk.collision_enabled = collision_enabled; + mk.collision_mode = collision_mode; mk.attractor_enabled = attractor_interaction_enabled; mk.collision_scale = collision_scale; + mk.turbulence_enabled = turbulence_enabled; return mk; } static Mutex material_mutex; - static SelfList<ParticlesMaterial>::List *dirty_materials; + static SelfList<ParticleProcessMaterial>::List *dirty_materials; struct ShaderNames { StringName direction; @@ -216,6 +229,17 @@ private: StringName emission_ring_radius; StringName emission_ring_inner_radius; + StringName turbulence_enabled; + StringName turbulence_noise_strength; + StringName turbulence_noise_scale; + StringName turbulence_noise_speed; + StringName turbulence_noise_speed_random; + StringName turbulence_influence_over_life; + StringName turbulence_influence_min; + StringName turbulence_influence_max; + StringName turbulence_initial_displacement_min; + StringName turbulence_initial_displacement_max; + StringName gravity; StringName lifetime_randomness; @@ -230,7 +254,7 @@ private: static ShaderNames *shader_names; - SelfList<ParticlesMaterial> element; + SelfList<ParticleProcessMaterial> element; void _update_shader(); _FORCE_INLINE_ void _queue_shader_change(); @@ -243,6 +267,7 @@ private: float params_min[PARAM_MAX]; float params_max[PARAM_MAX]; + float params[PARAM_MAX]; Ref<Texture2D> tex_parameters[PARAM_MAX]; Color color; @@ -265,6 +290,13 @@ private: bool anim_loop = false; + bool turbulence_enabled; + Vector3 turbulence_noise_speed; + Ref<Texture2D> turbulence_color_ramp; + float turbulence_noise_strength = 0.0f; + float turbulence_noise_scale = 0.0f; + float turbulence_noise_speed_random = 0.0f; + Vector3 gravity; double lifetime_randomness = 0.0; @@ -276,14 +308,14 @@ private: //do not save emission points here bool attractor_interaction_enabled = false; - bool collision_enabled = false; + CollisionMode collision_mode; bool collision_scale = false; float collision_friction = 0.0f; float collision_bounce = 0.0f; protected: static void _bind_methods(); - virtual void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; public: void set_direction(Vector3 p_direction); @@ -340,6 +372,18 @@ public: real_t get_emission_ring_inner_radius() const; int get_emission_point_count() const; + void set_turbulence_enabled(bool p_turbulence_enabled); + void set_turbulence_noise_strength(float p_turbulence_noise_strength); + void set_turbulence_noise_scale(float p_turbulence_noise_scale); + void set_turbulence_noise_speed_random(float p_turbulence_noise_speed_random); + void set_turbulence_noise_speed(const Vector3 &p_turbulence_noise_speed); + + bool get_turbulence_enabled() const; + float get_turbulence_noise_strength() const; + float get_turbulence_noise_scale() const; + float get_turbulence_noise_speed_random() const; + Vector3 get_turbulence_noise_speed() const; + void set_gravity(const Vector3 &p_gravity); Vector3 get_gravity() const; @@ -349,8 +393,8 @@ public: void set_attractor_interaction_enabled(bool p_enable); bool is_attractor_interaction_enabled() const; - void set_collision_enabled(bool p_enabled); - bool is_collision_enabled() const; + void set_collision_mode(CollisionMode p_collision_mode); + CollisionMode get_collision_mode() const; void set_collision_use_scale(bool p_scale); bool is_collision_using_scale() const; @@ -381,13 +425,14 @@ public: virtual Shader::Mode get_shader_mode() const override; - ParticlesMaterial(); - ~ParticlesMaterial(); + ParticleProcessMaterial(); + ~ParticleProcessMaterial(); }; -VARIANT_ENUM_CAST(ParticlesMaterial::Parameter) -VARIANT_ENUM_CAST(ParticlesMaterial::ParticleFlags) -VARIANT_ENUM_CAST(ParticlesMaterial::EmissionShape) -VARIANT_ENUM_CAST(ParticlesMaterial::SubEmitterMode) +VARIANT_ENUM_CAST(ParticleProcessMaterial::Parameter) +VARIANT_ENUM_CAST(ParticleProcessMaterial::ParticleFlags) +VARIANT_ENUM_CAST(ParticleProcessMaterial::EmissionShape) +VARIANT_ENUM_CAST(ParticleProcessMaterial::SubEmitterMode) +VARIANT_ENUM_CAST(ParticleProcessMaterial::CollisionMode) -#endif // PARTICLES_MATERIAL_H +#endif // PARTICLE_PROCESS_MATERIAL_H diff --git a/scene/resources/polygon_path_finder.cpp b/scene/resources/polygon_path_finder.cpp index 29135e30c9..5e18671c11 100644 --- a/scene/resources/polygon_path_finder.cpp +++ b/scene/resources/polygon_path_finder.cpp @@ -553,7 +553,7 @@ void PolygonPathFinder::_bind_methods() { ClassDB::bind_method(D_METHOD("get_point_penalty", "idx"), &PolygonPathFinder::get_point_penalty); ClassDB::bind_method(D_METHOD("get_bounds"), &PolygonPathFinder::get_bounds); - ClassDB::bind_method(D_METHOD("_set_data"), &PolygonPathFinder::_set_data); + ClassDB::bind_method(D_METHOD("_set_data", "data"), &PolygonPathFinder::_set_data); ClassDB::bind_method(D_METHOD("_get_data"), &PolygonPathFinder::_get_data); ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp index 68441afb1c..c017c90370 100644 --- a/scene/resources/primitive_meshes.cpp +++ b/scene/resources/primitive_meshes.cpp @@ -32,6 +32,7 @@ #include "core/core_string_names.h" #include "scene/resources/theme.h" +#include "scene/theme/theme_db.h" #include "servers/rendering_server.h" #include "thirdparty/misc/clipper.hpp" #include "thirdparty/misc/polypartition.h" @@ -152,8 +153,8 @@ Dictionary PrimitiveMesh::surface_get_lods(int p_surface) const { return Dictionary(); //not really supported } -Array PrimitiveMesh::surface_get_blend_shape_arrays(int p_surface) const { - return Array(); //not really supported +TypedArray<Array> PrimitiveMesh::surface_get_blend_shape_arrays(int p_surface) const { + return TypedArray<Array>(); //not really supported } uint32_t PrimitiveMesh::surface_get_format(int p_idx) const { @@ -990,6 +991,13 @@ void PlaneMesh::_create_mesh_array(Array &p_arr) const { Size2 start_pos = size * -0.5; + Vector3 normal = Vector3(0.0, 1.0, 0.0); + if (orientation == FACE_X) { + normal = Vector3(1.0, 0.0, 0.0); + } else if (orientation == FACE_Z) { + normal = Vector3(0.0, 0.0, 1.0); + } + Vector<Vector3> points; Vector<Vector3> normals; Vector<float> tangents; @@ -1015,8 +1023,14 @@ void PlaneMesh::_create_mesh_array(Array &p_arr) const { u /= (subdivide_w + 1.0); v /= (subdivide_d + 1.0); - points.push_back(Vector3(-x, 0.0, -z) + center_offset); - normals.push_back(Vector3(0.0, 1.0, 0.0)); + if (orientation == FACE_X) { + points.push_back(Vector3(0.0, z, x) + center_offset); + } else if (orientation == FACE_Y) { + points.push_back(Vector3(-x, 0.0, -z) + center_offset); + } else if (orientation == FACE_Z) { + points.push_back(Vector3(-x, z, 0.0) + center_offset); + } + normals.push_back(normal); ADD_TANGENT(1.0, 0.0, 0.0, 1.0); uvs.push_back(Vector2(1.0 - u, 1.0 - v)); /* 1.0 - uv to match orientation with Quad */ point++; @@ -1053,13 +1067,22 @@ void PlaneMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("get_subdivide_width"), &PlaneMesh::get_subdivide_width); ClassDB::bind_method(D_METHOD("set_subdivide_depth", "subdivide"), &PlaneMesh::set_subdivide_depth); ClassDB::bind_method(D_METHOD("get_subdivide_depth"), &PlaneMesh::get_subdivide_depth); + ClassDB::bind_method(D_METHOD("set_center_offset", "offset"), &PlaneMesh::set_center_offset); ClassDB::bind_method(D_METHOD("get_center_offset"), &PlaneMesh::get_center_offset); + ClassDB::bind_method(D_METHOD("set_orientation", "orientation"), &PlaneMesh::set_orientation); + ClassDB::bind_method(D_METHOD("get_orientation"), &PlaneMesh::get_orientation); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size", PROPERTY_HINT_NONE, "suffix:m"), "set_size", "get_size"); ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_width", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_width", "get_subdivide_width"); ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_depth", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_depth", "get_subdivide_depth"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "center_offset", PROPERTY_HINT_NONE, "suffix:m"), "set_center_offset", "get_center_offset"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "orientation", PROPERTY_HINT_ENUM, "Face X, Face Y, Face Z"), "set_orientation", "get_orientation"); + + BIND_ENUM_CONSTANT(FACE_X) + BIND_ENUM_CONSTANT(FACE_Y) + BIND_ENUM_CONSTANT(FACE_Z) } void PlaneMesh::set_size(const Size2 &p_size) { @@ -1098,6 +1121,15 @@ Vector3 PlaneMesh::get_center_offset() const { return center_offset; } +void PlaneMesh::set_orientation(const Orientation p_orientation) { + orientation = p_orientation; + _request_update(); +} + +PlaneMesh::Orientation PlaneMesh::get_orientation() const { + return orientation; +} + PlaneMesh::PlaneMesh() {} /** @@ -1381,98 +1413,6 @@ int PrismMesh::get_subdivide_depth() const { PrismMesh::PrismMesh() {} /** - QuadMesh -*/ - -void QuadMesh::_create_mesh_array(Array &p_arr) const { - Vector<Vector3> faces; - Vector<Vector3> normals; - Vector<float> tangents; - Vector<Vector2> uvs; - - faces.resize(6); - normals.resize(6); - tangents.resize(6 * 4); - uvs.resize(6); - - Vector2 _size = Vector2(size.x / 2.0f, size.y / 2.0f); - - Vector3 quad_faces[4] = { - Vector3(-_size.x, -_size.y, 0) + center_offset, - Vector3(-_size.x, _size.y, 0) + center_offset, - Vector3(_size.x, _size.y, 0) + center_offset, - Vector3(_size.x, -_size.y, 0) + center_offset, - }; - - static const int indices[6] = { - 0, 1, 2, - 0, 2, 3 - }; - - for (int i = 0; i < 6; i++) { - int j = indices[i]; - faces.set(i, quad_faces[j]); - normals.set(i, Vector3(0, 0, 1)); - tangents.set(i * 4 + 0, 1.0); - tangents.set(i * 4 + 1, 0.0); - tangents.set(i * 4 + 2, 0.0); - tangents.set(i * 4 + 3, 1.0); - - static const Vector2 quad_uv[4] = { - Vector2(0, 1), - Vector2(0, 0), - Vector2(1, 0), - Vector2(1, 1), - }; - - uvs.set(i, quad_uv[j]); - } - - p_arr[RS::ARRAY_VERTEX] = faces; - p_arr[RS::ARRAY_NORMAL] = normals; - p_arr[RS::ARRAY_TANGENT] = tangents; - p_arr[RS::ARRAY_TEX_UV] = uvs; -} - -void QuadMesh::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_size", "size"), &QuadMesh::set_size); - ClassDB::bind_method(D_METHOD("get_size"), &QuadMesh::get_size); - ClassDB::bind_method(D_METHOD("set_center_offset", "center_offset"), &QuadMesh::set_center_offset); - ClassDB::bind_method(D_METHOD("get_center_offset"), &QuadMesh::get_center_offset); - - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size", PROPERTY_HINT_NONE, "suffix:m"), "set_size", "get_size"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "center_offset", PROPERTY_HINT_NONE, "suffix:m"), "set_center_offset", "get_center_offset"); -} - -uint32_t QuadMesh::surface_get_format(int p_idx) const { - ERR_FAIL_INDEX_V(p_idx, 1, 0); - - return RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_NORMAL | RS::ARRAY_FORMAT_TANGENT | RS::ARRAY_FORMAT_TEX_UV; -} - -QuadMesh::QuadMesh() { - primitive_type = PRIMITIVE_TRIANGLES; -} - -void QuadMesh::set_size(const Size2 &p_size) { - size = p_size; - _request_update(); -} - -Size2 QuadMesh::get_size() const { - return size; -} - -void QuadMesh::set_center_offset(Vector3 p_center_offset) { - center_offset = p_center_offset; - _request_update(); -} - -Vector3 QuadMesh::get_center_offset() const { - return center_offset; -} - -/** SphereMesh */ @@ -1622,6 +1562,134 @@ bool SphereMesh::get_is_hemisphere() const { SphereMesh::SphereMesh() {} /** + TorusMesh +*/ + +void TorusMesh::_create_mesh_array(Array &p_arr) const { + // set our bounding box + + Vector<Vector3> points; + Vector<Vector3> normals; + Vector<float> tangents; + Vector<Vector2> uvs; + Vector<int> indices; + +#define ADD_TANGENT(m_x, m_y, m_z, m_d) \ + tangents.push_back(m_x); \ + tangents.push_back(m_y); \ + tangents.push_back(m_z); \ + tangents.push_back(m_d); + + ERR_FAIL_COND_MSG(inner_radius == outer_radius, "Inner radius and outer radius cannot be the same."); + + float min_radius = inner_radius; + float max_radius = outer_radius; + + if (min_radius > max_radius) { + SWAP(min_radius, max_radius); + } + + float radius = (max_radius - min_radius) * 0.5; + + for (int i = 0; i <= rings; i++) { + int prevrow = (i - 1) * (ring_segments + 1); + int thisrow = i * (ring_segments + 1); + float inci = float(i) / rings; + float angi = inci * Math_TAU; + + Vector2 normali = Vector2(-Math::sin(angi), -Math::cos(angi)); + + for (int j = 0; j <= ring_segments; j++) { + float incj = float(j) / ring_segments; + float angj = incj * Math_TAU; + + Vector2 normalj = Vector2(-Math::cos(angj), Math::sin(angj)); + Vector2 normalk = normalj * radius + Vector2(min_radius + radius, 0); + + points.push_back(Vector3(normali.x * normalk.x, normalk.y, normali.y * normalk.x)); + normals.push_back(Vector3(normali.x * normalj.x, normalj.y, normali.y * normalj.x)); + ADD_TANGENT(-Math::cos(angi), 0.0, Math::sin(angi), 1.0); + uvs.push_back(Vector2(inci, incj)); + + if (i > 0 && j > 0) { + indices.push_back(thisrow + j - 1); + indices.push_back(prevrow + j); + indices.push_back(prevrow + j - 1); + + indices.push_back(thisrow + j - 1); + indices.push_back(thisrow + j); + indices.push_back(prevrow + j); + } + } + } + + p_arr[RS::ARRAY_VERTEX] = points; + p_arr[RS::ARRAY_NORMAL] = normals; + p_arr[RS::ARRAY_TANGENT] = tangents; + p_arr[RS::ARRAY_TEX_UV] = uvs; + p_arr[RS::ARRAY_INDEX] = indices; +} + +void TorusMesh::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_inner_radius", "radius"), &TorusMesh::set_inner_radius); + ClassDB::bind_method(D_METHOD("get_inner_radius"), &TorusMesh::get_inner_radius); + + ClassDB::bind_method(D_METHOD("set_outer_radius", "radius"), &TorusMesh::set_outer_radius); + ClassDB::bind_method(D_METHOD("get_outer_radius"), &TorusMesh::get_outer_radius); + + ClassDB::bind_method(D_METHOD("set_rings", "rings"), &TorusMesh::set_rings); + ClassDB::bind_method(D_METHOD("get_rings"), &TorusMesh::get_rings); + + ClassDB::bind_method(D_METHOD("set_ring_segments", "rings"), &TorusMesh::set_ring_segments); + ClassDB::bind_method(D_METHOD("get_ring_segments"), &TorusMesh::get_ring_segments); + + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "inner_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp"), "set_inner_radius", "get_inner_radius"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "outer_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp"), "set_outer_radius", "get_outer_radius"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "3,128,1"), "set_rings", "get_rings"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "ring_segments", PROPERTY_HINT_RANGE, "3,64,1"), "set_ring_segments", "get_ring_segments"); +} + +void TorusMesh::set_inner_radius(const float p_inner_radius) { + inner_radius = p_inner_radius; + _request_update(); +} + +float TorusMesh::get_inner_radius() const { + return inner_radius; +} + +void TorusMesh::set_outer_radius(const float p_outer_radius) { + outer_radius = p_outer_radius; + _request_update(); +} + +float TorusMesh::get_outer_radius() const { + return outer_radius; +} + +void TorusMesh::set_rings(const int p_rings) { + ERR_FAIL_COND(p_rings < 3); + rings = p_rings; + _request_update(); +} + +int TorusMesh::get_rings() const { + return rings; +} + +void TorusMesh::set_ring_segments(const int p_ring_segments) { + ERR_FAIL_COND(p_ring_segments < 3); + ring_segments = p_ring_segments; + _request_update(); +} + +int TorusMesh::get_ring_segments() const { + return ring_segments; +} + +TorusMesh::TorusMesh() {} + +/** PointMesh */ @@ -1754,7 +1822,7 @@ void TubeTrailMesh::_create_mesh_array(Array &p_arr) const { float r = radius; if (curve.is_valid() && curve->get_point_count() > 0) { - r *= curve->interpolate_baked(v); + r *= curve->sample_baked(v); } float x = sin(u * Math_TAU); float z = cos(u * Math_TAU); @@ -1795,7 +1863,7 @@ void TubeTrailMesh::_create_mesh_array(Array &p_arr) const { // add top float scale_pos = 1.0; if (curve.is_valid() && curve->get_point_count() > 0) { - scale_pos = curve->interpolate_baked(0); + scale_pos = curve->sample_baked(0); } if (scale_pos > CMP_EPSILON) { @@ -1857,7 +1925,7 @@ void TubeTrailMesh::_create_mesh_array(Array &p_arr) const { float scale_neg = 1.0; if (curve.is_valid() && curve->get_point_count() > 0) { - scale_neg = curve->interpolate_baked(1.0); + scale_neg = curve->sample_baked(1.0); } // add bottom @@ -2070,7 +2138,7 @@ void RibbonTrailMesh::_create_mesh_array(Array &p_arr) const { float s = size; if (curve.is_valid() && curve->get_point_count() > 0) { - s *= curve->interpolate_baked(v); + s *= curve->sample_baked(v); } points.push_back(Vector3(-s * 0.5, y, 0)); @@ -2391,9 +2459,7 @@ void TextMesh::_create_mesh_array(Array &p_arr) const { dirty_text = false; dirty_font = false; - if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) { - TS->shaped_text_fit_to_width(text_rid, width, TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA); - } + dirty_lines = true; } else if (dirty_font) { int spans = TS->shaped_get_span_count(text_rid); for (int i = 0; i < spans; i++) { @@ -2404,81 +2470,138 @@ void TextMesh::_create_mesh_array(Array &p_arr) const { } dirty_font = false; + dirty_lines = true; + } + + if (dirty_lines) { + for (int i = 0; i < lines_rid.size(); i++) { + TS->free_rid(lines_rid[i]); + } + lines_rid.clear(); + + BitField<TextServer::LineBreakFlag> autowrap_flags = TextServer::BREAK_MANDATORY; + switch (autowrap_mode) { + case TextServer::AUTOWRAP_WORD_SMART: + autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_ADAPTIVE | TextServer::BREAK_MANDATORY; + break; + case TextServer::AUTOWRAP_WORD: + autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_MANDATORY; + break; + case TextServer::AUTOWRAP_ARBITRARY: + autowrap_flags = TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_MANDATORY; + break; + case TextServer::AUTOWRAP_OFF: + break; + } + PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(text_rid, width, 0, autowrap_flags); + + float max_line_w = 0.0; + for (int i = 0; i < line_breaks.size(); i = i + 2) { + RID line = TS->shaped_text_substr(text_rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]); + max_line_w = MAX(max_line_w, TS->shaped_text_get_width(line)); + lines_rid.push_back(line); + } + if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) { - TS->shaped_text_fit_to_width(text_rid, width, TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA); + for (int i = 0; i < lines_rid.size() - 1; i++) { + TS->shaped_text_fit_to_width(lines_rid[i], (width > 0) ? width : max_line_w, TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA); + } } + dirty_lines = false; + } + + float total_h = 0.0; + for (int i = 0; i < lines_rid.size(); i++) { + total_h += (TS->shaped_text_get_size(lines_rid[i]).y + line_spacing) * pixel_size; } - Vector2 offset; - const Glyph *glyphs = TS->shaped_text_get_glyphs(text_rid); - int gl_size = TS->shaped_text_get_glyph_count(text_rid); - float line_width = TS->shaped_text_get_width(text_rid) * pixel_size; - - switch (horizontal_alignment) { - case HORIZONTAL_ALIGNMENT_LEFT: - offset.x = 0.0; - break; - case HORIZONTAL_ALIGNMENT_FILL: - case HORIZONTAL_ALIGNMENT_CENTER: { - offset.x = -line_width / 2.0; + float vbegin = 0.0; + switch (vertical_alignment) { + case VERTICAL_ALIGNMENT_FILL: + case VERTICAL_ALIGNMENT_TOP: { + // Nothing. } break; - case HORIZONTAL_ALIGNMENT_RIGHT: { - offset.x = -line_width; + case VERTICAL_ALIGNMENT_CENTER: { + vbegin = (total_h - line_spacing * pixel_size) / 2.0; + } break; + case VERTICAL_ALIGNMENT_BOTTOM: { + vbegin = (total_h - line_spacing * pixel_size); } break; } - bool has_depth = !Math::is_zero_approx(depth); - - // Generate glyph data, precalculate size of the arrays and mesh bounds for UV. - int64_t p_size = 0; - int64_t i_size = 0; + Vector<Vector3> vertices; + Vector<Vector3> normals; + Vector<float> tangents; + Vector<Vector2> uvs; + Vector<int32_t> indices; Vector2 min_p = Vector2(INFINITY, INFINITY); Vector2 max_p = Vector2(-INFINITY, -INFINITY); - Vector2 offset_pre = offset; - for (int i = 0; i < gl_size; i++) { - if (glyphs[i].index == 0) { - offset.x += glyphs[i].advance * pixel_size * glyphs[i].repeat; - continue; + int32_t p_size = 0; + int32_t i_size = 0; + + Vector2 offset = Vector2(0, vbegin + lbl_offset.y * pixel_size); + for (int i = 0; i < lines_rid.size(); i++) { + const Glyph *glyphs = TS->shaped_text_get_glyphs(lines_rid[i]); + int gl_size = TS->shaped_text_get_glyph_count(lines_rid[i]); + float line_width = TS->shaped_text_get_width(lines_rid[i]) * pixel_size; + + switch (horizontal_alignment) { + case HORIZONTAL_ALIGNMENT_LEFT: + offset.x = 0.0; + break; + case HORIZONTAL_ALIGNMENT_FILL: + case HORIZONTAL_ALIGNMENT_CENTER: { + offset.x = -line_width / 2.0; + } break; + case HORIZONTAL_ALIGNMENT_RIGHT: { + offset.x = -line_width; + } break; } - if (glyphs[i].font_rid != RID()) { - GlyphMeshKey key = GlyphMeshKey(glyphs[i].font_rid.get_id(), glyphs[i].index); - _generate_glyph_mesh_data(key, glyphs[i]); - GlyphMeshData &gl_data = cache[key]; - - p_size += glyphs[i].repeat * gl_data.triangles.size() * ((has_depth) ? 2 : 1); - i_size += glyphs[i].repeat * gl_data.triangles.size() * ((has_depth) ? 2 : 1); - - if (has_depth) { - for (int j = 0; j < gl_data.contours.size(); j++) { - p_size += glyphs[i].repeat * gl_data.contours[j].size() * 4; - i_size += glyphs[i].repeat * gl_data.contours[j].size() * 6; - } - } + offset.x += lbl_offset.x * pixel_size; + offset.y -= TS->shaped_text_get_ascent(lines_rid[i]) * pixel_size; - for (int j = 0; j < glyphs[i].repeat; j++) { - min_p.x = MIN(gl_data.min_p.x + offset_pre.x, min_p.x); - min_p.y = MIN(gl_data.min_p.y + offset_pre.y, min_p.y); - max_p.x = MAX(gl_data.max_p.x + offset_pre.x, max_p.x); - max_p.y = MAX(gl_data.max_p.y + offset_pre.y, max_p.y); + bool has_depth = !Math::is_zero_approx(depth); - offset_pre.x += glyphs[i].advance * pixel_size; + for (int j = 0; j < gl_size; j++) { + if (glyphs[j].index == 0) { + offset.x += glyphs[j].advance * pixel_size * glyphs[j].repeat; + continue; } - } else { - p_size += glyphs[i].repeat * 4; - i_size += glyphs[i].repeat * 6; + if (glyphs[j].font_rid != RID()) { + GlyphMeshKey key = GlyphMeshKey(glyphs[j].font_rid.get_id(), glyphs[j].index); + _generate_glyph_mesh_data(key, glyphs[j]); + GlyphMeshData &gl_data = cache[key]; + + p_size += glyphs[j].repeat * gl_data.triangles.size() * ((has_depth) ? 2 : 1); + i_size += glyphs[j].repeat * gl_data.triangles.size() * ((has_depth) ? 2 : 1); - offset_pre.x += glyphs[i].advance * pixel_size * glyphs[i].repeat; + if (has_depth) { + for (int k = 0; k < gl_data.contours.size(); k++) { + p_size += glyphs[j].repeat * gl_data.contours[k].size() * 4; + i_size += glyphs[j].repeat * gl_data.contours[k].size() * 6; + } + } + + for (int r = 0; r < glyphs[j].repeat; r++) { + min_p.x = MIN(gl_data.min_p.x + offset.x, min_p.x); + min_p.y = MIN(gl_data.min_p.y - offset.y, min_p.y); + max_p.x = MAX(gl_data.max_p.x + offset.x, max_p.x); + max_p.y = MAX(gl_data.max_p.y - offset.y, max_p.y); + + offset.x += glyphs[j].advance * pixel_size; + } + } else { + p_size += glyphs[j].repeat * 4; + i_size += glyphs[j].repeat * 6; + + offset.x += glyphs[j].advance * pixel_size * glyphs[j].repeat; + } } + offset.y -= (TS->shaped_text_get_descent(lines_rid[i]) + line_spacing) * pixel_size; } - Vector<Vector3> vertices; - Vector<Vector3> normals; - Vector<float> tangents; - Vector<Vector2> uvs; - Vector<int32_t> indices; - vertices.resize(p_size); normals.resize(p_size); uvs.resize(p_size); @@ -2494,149 +2617,176 @@ void TextMesh::_create_mesh_array(Array &p_arr) const { // Generate mesh. int32_t p_idx = 0; int32_t i_idx = 0; - for (int i = 0; i < gl_size; i++) { - if (glyphs[i].index == 0) { - offset.x += glyphs[i].advance * pixel_size * glyphs[i].repeat; - continue; + + offset = Vector2(0, vbegin + lbl_offset.y * pixel_size); + for (int i = 0; i < lines_rid.size(); i++) { + const Glyph *glyphs = TS->shaped_text_get_glyphs(lines_rid[i]); + int gl_size = TS->shaped_text_get_glyph_count(lines_rid[i]); + float line_width = TS->shaped_text_get_width(lines_rid[i]) * pixel_size; + + switch (horizontal_alignment) { + case HORIZONTAL_ALIGNMENT_LEFT: + offset.x = 0.0; + break; + case HORIZONTAL_ALIGNMENT_FILL: + case HORIZONTAL_ALIGNMENT_CENTER: { + offset.x = -line_width / 2.0; + } break; + case HORIZONTAL_ALIGNMENT_RIGHT: { + offset.x = -line_width; + } break; } - if (glyphs[i].font_rid != RID()) { - GlyphMeshKey key = GlyphMeshKey(glyphs[i].font_rid.get_id(), glyphs[i].index); - _generate_glyph_mesh_data(key, glyphs[i]); - const GlyphMeshData &gl_data = cache[key]; - - int64_t ts = gl_data.triangles.size(); - const Vector2 *ts_ptr = gl_data.triangles.ptr(); - - for (int j = 0; j < glyphs[i].repeat; j++) { - for (int k = 0; k < ts; k += 3) { - // Add front face. - for (int l = 0; l < 3; l++) { - Vector3 point = Vector3(ts_ptr[k + l].x + offset.x, -ts_ptr[k + l].y + offset.y, depth / 2.0); - vertices_ptr[p_idx] = point; - normals_ptr[p_idx] = Vector3(0.0, 0.0, 1.0); - if (has_depth) { - uvs_ptr[p_idx] = Vector2(Math::range_lerp(point.x, min_p.x, max_p.x, real_t(0.0), real_t(1.0)), Math::range_lerp(point.y, -min_p.y, -max_p.y, real_t(0.0), real_t(0.4))); - } else { - uvs_ptr[p_idx] = Vector2(Math::range_lerp(point.x, min_p.x, max_p.x, real_t(0.0), real_t(1.0)), Math::range_lerp(point.y, -min_p.y, -max_p.y, real_t(0.0), real_t(1.0))); - } - tangents_ptr[p_idx * 4 + 0] = 1.0; - tangents_ptr[p_idx * 4 + 1] = 0.0; - tangents_ptr[p_idx * 4 + 2] = 0.0; - tangents_ptr[p_idx * 4 + 3] = 1.0; - indices_ptr[i_idx++] = p_idx; - p_idx++; - } - if (has_depth) { - // Add back face. - for (int l = 2; l >= 0; l--) { - Vector3 point = Vector3(ts_ptr[k + l].x + offset.x, -ts_ptr[k + l].y + offset.y, -depth / 2.0); + offset.x += lbl_offset.x * pixel_size; + offset.y -= TS->shaped_text_get_ascent(lines_rid[i]) * pixel_size; + + bool has_depth = !Math::is_zero_approx(depth); + + // Generate glyph data, precalculate size of the arrays and mesh bounds for UV. + for (int j = 0; j < gl_size; j++) { + if (glyphs[j].index == 0) { + offset.x += glyphs[j].advance * pixel_size * glyphs[j].repeat; + continue; + } + if (glyphs[j].font_rid != RID()) { + GlyphMeshKey key = GlyphMeshKey(glyphs[j].font_rid.get_id(), glyphs[j].index); + _generate_glyph_mesh_data(key, glyphs[j]); + const GlyphMeshData &gl_data = cache[key]; + + int64_t ts = gl_data.triangles.size(); + const Vector2 *ts_ptr = gl_data.triangles.ptr(); + + for (int r = 0; r < glyphs[j].repeat; r++) { + for (int k = 0; k < ts; k += 3) { + // Add front face. + for (int l = 0; l < 3; l++) { + Vector3 point = Vector3(ts_ptr[k + l].x + offset.x, -ts_ptr[k + l].y + offset.y, depth / 2.0); vertices_ptr[p_idx] = point; - normals_ptr[p_idx] = Vector3(0.0, 0.0, -1.0); - uvs_ptr[p_idx] = Vector2(Math::range_lerp(point.x, min_p.x, max_p.x, real_t(0.0), real_t(1.0)), Math::range_lerp(point.y, -min_p.y, -max_p.y, real_t(0.4), real_t(0.8))); - tangents_ptr[p_idx * 4 + 0] = -1.0; + normals_ptr[p_idx] = Vector3(0.0, 0.0, 1.0); + if (has_depth) { + uvs_ptr[p_idx] = Vector2(Math::remap(point.x, min_p.x, max_p.x, real_t(0.0), real_t(1.0)), Math::remap(point.y, -max_p.y, -min_p.y, real_t(0.4), real_t(0.0))); + } else { + uvs_ptr[p_idx] = Vector2(Math::remap(point.x, min_p.x, max_p.x, real_t(0.0), real_t(1.0)), Math::remap(point.y, -max_p.y, -min_p.y, real_t(1.0), real_t(0.0))); + } + tangents_ptr[p_idx * 4 + 0] = 1.0; tangents_ptr[p_idx * 4 + 1] = 0.0; tangents_ptr[p_idx * 4 + 2] = 0.0; tangents_ptr[p_idx * 4 + 3] = 1.0; indices_ptr[i_idx++] = p_idx; p_idx++; } - } - } - // Add sides. - if (has_depth) { - for (int k = 0; k < gl_data.contours.size(); k++) { - int64_t ps = gl_data.contours[k].size(); - const ContourPoint *ps_ptr = gl_data.contours[k].ptr(); - const ContourInfo &ps_info = gl_data.contours_info[k]; - real_t length = 0.0; - for (int l = 0; l < ps; l++) { - int prev = (l == 0) ? (ps - 1) : (l - 1); - int next = (l + 1 == ps) ? 0 : (l + 1); - Vector2 d1; - Vector2 d2 = (ps_ptr[next].point - ps_ptr[l].point).normalized(); - if (ps_ptr[l].sharp) { - d1 = d2; - } else { - d1 = (ps_ptr[l].point - ps_ptr[prev].point).normalized(); + if (has_depth) { + // Add back face. + for (int l = 2; l >= 0; l--) { + Vector3 point = Vector3(ts_ptr[k + l].x + offset.x, -ts_ptr[k + l].y + offset.y, -depth / 2.0); + vertices_ptr[p_idx] = point; + normals_ptr[p_idx] = Vector3(0.0, 0.0, -1.0); + uvs_ptr[p_idx] = Vector2(Math::remap(point.x, min_p.x, max_p.x, real_t(0.0), real_t(1.0)), Math::remap(point.y, -max_p.y, -min_p.y, real_t(0.8), real_t(0.4))); + tangents_ptr[p_idx * 4 + 0] = -1.0; + tangents_ptr[p_idx * 4 + 1] = 0.0; + tangents_ptr[p_idx * 4 + 2] = 0.0; + tangents_ptr[p_idx * 4 + 3] = 1.0; + indices_ptr[i_idx++] = p_idx; + p_idx++; } - real_t seg_len = (ps_ptr[next].point - ps_ptr[l].point).length(); - - Vector3 quad_faces[4] = { - Vector3(ps_ptr[l].point.x + offset.x, -ps_ptr[l].point.y + offset.y, -depth / 2.0), - Vector3(ps_ptr[next].point.x + offset.x, -ps_ptr[next].point.y + offset.y, -depth / 2.0), - Vector3(ps_ptr[l].point.x + offset.x, -ps_ptr[l].point.y + offset.y, depth / 2.0), - Vector3(ps_ptr[next].point.x + offset.x, -ps_ptr[next].point.y + offset.y, depth / 2.0), - }; - for (int m = 0; m < 4; m++) { - const Vector2 &d = ((m % 2) == 0) ? d1 : d2; - real_t u_pos = ((m % 2) == 0) ? length : length + seg_len; - vertices_ptr[p_idx + m] = quad_faces[m]; - normals_ptr[p_idx + m] = Vector3(d.y, d.x, 0.0); - if (m < 2) { - uvs_ptr[p_idx + m] = Vector2(Math::range_lerp(u_pos, 0, ps_info.length, real_t(0.0), real_t(1.0)), (ps_info.ccw) ? 0.8 : 0.9); + } + } + // Add sides. + if (has_depth) { + for (int k = 0; k < gl_data.contours.size(); k++) { + int64_t ps = gl_data.contours[k].size(); + const ContourPoint *ps_ptr = gl_data.contours[k].ptr(); + const ContourInfo &ps_info = gl_data.contours_info[k]; + real_t length = 0.0; + for (int l = 0; l < ps; l++) { + int prev = (l == 0) ? (ps - 1) : (l - 1); + int next = (l + 1 == ps) ? 0 : (l + 1); + Vector2 d1; + Vector2 d2 = (ps_ptr[next].point - ps_ptr[l].point).normalized(); + if (ps_ptr[l].sharp) { + d1 = d2; } else { - uvs_ptr[p_idx + m] = Vector2(Math::range_lerp(u_pos, 0, ps_info.length, real_t(0.0), real_t(1.0)), (ps_info.ccw) ? 0.9 : 1.0); + d1 = (ps_ptr[l].point - ps_ptr[prev].point).normalized(); + } + real_t seg_len = (ps_ptr[next].point - ps_ptr[l].point).length(); + + Vector3 quad_faces[4] = { + Vector3(ps_ptr[l].point.x + offset.x, -ps_ptr[l].point.y + offset.y, -depth / 2.0), + Vector3(ps_ptr[next].point.x + offset.x, -ps_ptr[next].point.y + offset.y, -depth / 2.0), + Vector3(ps_ptr[l].point.x + offset.x, -ps_ptr[l].point.y + offset.y, depth / 2.0), + Vector3(ps_ptr[next].point.x + offset.x, -ps_ptr[next].point.y + offset.y, depth / 2.0), + }; + for (int m = 0; m < 4; m++) { + const Vector2 &d = ((m % 2) == 0) ? d1 : d2; + real_t u_pos = ((m % 2) == 0) ? length : length + seg_len; + vertices_ptr[p_idx + m] = quad_faces[m]; + normals_ptr[p_idx + m] = Vector3(d.y, d.x, 0.0); + if (m < 2) { + uvs_ptr[p_idx + m] = Vector2(Math::remap(u_pos, 0, ps_info.length, real_t(0.0), real_t(1.0)), (ps_info.ccw) ? 0.8 : 0.9); + } else { + uvs_ptr[p_idx + m] = Vector2(Math::remap(u_pos, 0, ps_info.length, real_t(0.0), real_t(1.0)), (ps_info.ccw) ? 0.9 : 1.0); + } + tangents_ptr[(p_idx + m) * 4 + 0] = d.x; + tangents_ptr[(p_idx + m) * 4 + 1] = -d.y; + tangents_ptr[(p_idx + m) * 4 + 2] = 0.0; + tangents_ptr[(p_idx + m) * 4 + 3] = 1.0; } - tangents_ptr[(p_idx + m) * 4 + 0] = d.x; - tangents_ptr[(p_idx + m) * 4 + 1] = -d.y; - tangents_ptr[(p_idx + m) * 4 + 2] = 0.0; - tangents_ptr[(p_idx + m) * 4 + 3] = 1.0; - } - indices_ptr[i_idx++] = p_idx; - indices_ptr[i_idx++] = p_idx + 1; - indices_ptr[i_idx++] = p_idx + 2; + indices_ptr[i_idx++] = p_idx; + indices_ptr[i_idx++] = p_idx + 1; + indices_ptr[i_idx++] = p_idx + 2; - indices_ptr[i_idx++] = p_idx + 1; - indices_ptr[i_idx++] = p_idx + 3; - indices_ptr[i_idx++] = p_idx + 2; + indices_ptr[i_idx++] = p_idx + 1; + indices_ptr[i_idx++] = p_idx + 3; + indices_ptr[i_idx++] = p_idx + 2; - length += seg_len; - p_idx += 4; + length += seg_len; + p_idx += 4; + } } } + offset.x += glyphs[j].advance * pixel_size; } - offset.x += glyphs[i].advance * pixel_size; - } - } else { - // Add fallback quad for missing glyphs. - for (int j = 0; j < glyphs[i].repeat; j++) { - Size2 sz = TS->get_hex_code_box_size(glyphs[i].font_size, glyphs[i].index) * pixel_size; - Vector3 quad_faces[4] = { - Vector3(offset.x, offset.y, 0.0), - Vector3(offset.x, sz.y + offset.y, 0.0), - Vector3(sz.x + offset.x, sz.y + offset.y, 0.0), - Vector3(sz.x + offset.x, offset.y, 0.0), - }; - for (int k = 0; k < 4; k++) { - vertices_ptr[p_idx + k] = quad_faces[k]; - normals_ptr[p_idx + k] = Vector3(0.0, 0.0, 1.0); - if (has_depth) { - uvs_ptr[p_idx + k] = Vector2(Math::range_lerp(quad_faces[k].x, min_p.x, max_p.x, real_t(0.0), real_t(1.0)), Math::range_lerp(quad_faces[k].y, -min_p.y, -max_p.y, real_t(0.0), real_t(0.4))); - } else { - uvs_ptr[p_idx + k] = Vector2(Math::range_lerp(quad_faces[k].x, min_p.x, max_p.x, real_t(0.0), real_t(1.0)), Math::range_lerp(quad_faces[k].y, -min_p.y, -max_p.y, real_t(0.0), real_t(1.0))); + } else { + // Add fallback quad for missing glyphs. + for (int r = 0; r < glyphs[j].repeat; r++) { + Size2 sz = TS->get_hex_code_box_size(glyphs[j].font_size, glyphs[j].index) * pixel_size; + Vector3 quad_faces[4] = { + Vector3(offset.x, offset.y, 0.0), + Vector3(offset.x, sz.y + offset.y, 0.0), + Vector3(sz.x + offset.x, sz.y + offset.y, 0.0), + Vector3(sz.x + offset.x, offset.y, 0.0), + }; + for (int k = 0; k < 4; k++) { + vertices_ptr[p_idx + k] = quad_faces[k]; + normals_ptr[p_idx + k] = Vector3(0.0, 0.0, 1.0); + if (has_depth) { + uvs_ptr[p_idx + k] = Vector2(Math::remap(quad_faces[k].x, min_p.x, max_p.x, real_t(0.0), real_t(1.0)), Math::remap(quad_faces[k].y, -max_p.y, -min_p.y, real_t(0.4), real_t(0.0))); + } else { + uvs_ptr[p_idx + k] = Vector2(Math::remap(quad_faces[k].x, min_p.x, max_p.x, real_t(0.0), real_t(1.0)), Math::remap(quad_faces[k].y, -max_p.y, -min_p.y, real_t(1.0), real_t(0.0))); + } + tangents_ptr[(p_idx + k) * 4 + 0] = 1.0; + tangents_ptr[(p_idx + k) * 4 + 1] = 0.0; + tangents_ptr[(p_idx + k) * 4 + 2] = 0.0; + tangents_ptr[(p_idx + k) * 4 + 3] = 1.0; } - tangents_ptr[(p_idx + k) * 4 + 0] = 1.0; - tangents_ptr[(p_idx + k) * 4 + 1] = 0.0; - tangents_ptr[(p_idx + k) * 4 + 2] = 0.0; - tangents_ptr[(p_idx + k) * 4 + 3] = 1.0; - } - indices_ptr[i_idx++] = p_idx; - indices_ptr[i_idx++] = p_idx + 1; - indices_ptr[i_idx++] = p_idx + 2; + indices_ptr[i_idx++] = p_idx; + indices_ptr[i_idx++] = p_idx + 1; + indices_ptr[i_idx++] = p_idx + 2; - indices_ptr[i_idx++] = p_idx + 0; - indices_ptr[i_idx++] = p_idx + 2; - indices_ptr[i_idx++] = p_idx + 3; - p_idx += 4; + indices_ptr[i_idx++] = p_idx + 0; + indices_ptr[i_idx++] = p_idx + 2; + indices_ptr[i_idx++] = p_idx + 3; + p_idx += 4; - offset.x += glyphs[i].advance * pixel_size; + offset.x += glyphs[j].advance * pixel_size; + } } } + offset.y -= (TS->shaped_text_get_descent(lines_rid[i]) + line_spacing) * pixel_size; } - if (p_size == 0) { + if (indices.is_empty()) { // If empty, add single triangle to suppress errors. vertices.push_back(Vector3()); normals.push_back(Vector3()); @@ -2661,6 +2811,9 @@ void TextMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_horizontal_alignment", "alignment"), &TextMesh::set_horizontal_alignment); ClassDB::bind_method(D_METHOD("get_horizontal_alignment"), &TextMesh::get_horizontal_alignment); + ClassDB::bind_method(D_METHOD("set_vertical_alignment", "alignment"), &TextMesh::set_vertical_alignment); + ClassDB::bind_method(D_METHOD("get_vertical_alignment"), &TextMesh::get_vertical_alignment); + ClassDB::bind_method(D_METHOD("set_text", "text"), &TextMesh::set_text); ClassDB::bind_method(D_METHOD("get_text"), &TextMesh::get_text); @@ -2670,6 +2823,12 @@ void TextMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_font_size", "font_size"), &TextMesh::set_font_size); ClassDB::bind_method(D_METHOD("get_font_size"), &TextMesh::get_font_size); + ClassDB::bind_method(D_METHOD("set_line_spacing", "line_spacing"), &TextMesh::set_line_spacing); + ClassDB::bind_method(D_METHOD("get_line_spacing"), &TextMesh::get_line_spacing); + + ClassDB::bind_method(D_METHOD("set_autowrap_mode", "autowrap_mode"), &TextMesh::set_autowrap_mode); + ClassDB::bind_method(D_METHOD("get_autowrap_mode"), &TextMesh::get_autowrap_mode); + ClassDB::bind_method(D_METHOD("set_depth", "depth"), &TextMesh::set_depth); ClassDB::bind_method(D_METHOD("get_depth"), &TextMesh::get_depth); @@ -2679,6 +2838,9 @@ void TextMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_pixel_size", "pixel_size"), &TextMesh::set_pixel_size); ClassDB::bind_method(D_METHOD("get_pixel_size"), &TextMesh::get_pixel_size); + ClassDB::bind_method(D_METHOD("set_offset", "offset"), &TextMesh::set_offset); + ClassDB::bind_method(D_METHOD("get_offset"), &TextMesh::get_offset); + ClassDB::bind_method(D_METHOD("set_curve_step", "curve_step"), &TextMesh::set_curve_step); ClassDB::bind_method(D_METHOD("get_curve_step"), &TextMesh::get_curve_step); @@ -2701,17 +2863,21 @@ void TextMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("_request_update"), &TextMesh::_request_update); ADD_GROUP("Text", ""); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "text"), "set_text", "get_text"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT, ""), "set_text", "get_text"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_font", "get_font"); ADD_PROPERTY(PropertyInfo(Variant::INT, "font_size", PROPERTY_HINT_RANGE, "1,256,1,or_greater,suffix:px"), "set_font_size", "get_font_size"); ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "vertical_alignment", PROPERTY_HINT_ENUM, "Top,Center,Bottom"), "set_vertical_alignment", "get_vertical_alignment"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uppercase"), "set_uppercase", "is_uppercase"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "line_spacing", PROPERTY_HINT_NONE, "suffix:px"), "set_line_spacing", "get_line_spacing"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Off,Arbitrary,Word,Word (Smart)"), "set_autowrap_mode", "get_autowrap_mode"); ADD_GROUP("Mesh", ""); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pixel_size", PROPERTY_HINT_RANGE, "0.0001,128,0.0001,suffix:m"), "set_pixel_size", "get_pixel_size"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "curve_step", PROPERTY_HINT_RANGE, "0.1,10,0.1,suffix:px"), "set_curve_step", "get_curve_step"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth", PROPERTY_HINT_RANGE, "0.0,100.0,0.001,or_greater,suffix:m"), "set_depth", "get_depth"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "width", PROPERTY_HINT_NONE, "suffix:m"), "set_width", "get_width"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset", PROPERTY_HINT_NONE, "suffix:px"), "set_offset", "get_offset"); ADD_GROUP("BiDi", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left"), "set_text_direction", "get_text_direction"); @@ -2740,6 +2906,11 @@ TextMesh::TextMesh() { } TextMesh::~TextMesh() { + for (int i = 0; i < lines_rid.size(); i++) { + TS->free_rid(lines_rid[i]); + } + lines_rid.clear(); + TS->free_rid(text_rid); } @@ -2747,7 +2918,7 @@ void TextMesh::set_horizontal_alignment(HorizontalAlignment p_alignment) { ERR_FAIL_INDEX((int)p_alignment, 4); if (horizontal_alignment != p_alignment) { if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL || p_alignment == HORIZONTAL_ALIGNMENT_FILL) { - dirty_text = true; + dirty_lines = true; } horizontal_alignment = p_alignment; _request_update(); @@ -2758,6 +2929,18 @@ HorizontalAlignment TextMesh::get_horizontal_alignment() const { return horizontal_alignment; } +void TextMesh::set_vertical_alignment(VerticalAlignment p_alignment) { + ERR_FAIL_INDEX((int)p_alignment, 4); + if (vertical_alignment != p_alignment) { + vertical_alignment = p_alignment; + _request_update(); + } +} + +VerticalAlignment TextMesh::get_vertical_alignment() const { + return vertical_alignment; +} + void TextMesh::set_text(const String &p_string) { if (text != p_string) { text = p_string; @@ -2802,13 +2985,13 @@ Ref<Font> TextMesh::_get_font_or_default() const { } // Check the project-defined Theme resource. - if (Theme::get_project_default().is_valid()) { + if (ThemeDB::get_singleton()->get_project_theme().is_valid()) { List<StringName> theme_types; - Theme::get_project_default()->get_type_dependencies(get_class_name(), StringName(), &theme_types); + ThemeDB::get_singleton()->get_project_theme()->get_type_dependencies(get_class_name(), StringName(), &theme_types); for (const StringName &E : theme_types) { - if (Theme::get_project_default()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { - return Theme::get_project_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); + if (ThemeDB::get_singleton()->get_project_theme()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { + return ThemeDB::get_singleton()->get_project_theme()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); } } } @@ -2816,17 +2999,17 @@ Ref<Font> TextMesh::_get_font_or_default() const { // Lastly, fall back on the items defined in the default Theme, if they exist. { List<StringName> theme_types; - Theme::get_default()->get_type_dependencies(get_class_name(), StringName(), &theme_types); + ThemeDB::get_singleton()->get_default_theme()->get_type_dependencies(get_class_name(), StringName(), &theme_types); for (const StringName &E : theme_types) { - if (Theme::get_default()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { - return Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); + if (ThemeDB::get_singleton()->get_default_theme()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { + return ThemeDB::get_singleton()->get_default_theme()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); } } } // If they don't exist, use any type to return the default/empty value. - return Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", StringName()); + return ThemeDB::get_singleton()->get_default_theme()->get_theme_item(Theme::DATA_TYPE_FONT, "font", StringName()); } void TextMesh::set_font_size(int p_size) { @@ -2842,6 +3025,29 @@ int TextMesh::get_font_size() const { return font_size; } +void TextMesh::set_line_spacing(float p_line_spacing) { + if (line_spacing != p_line_spacing) { + line_spacing = p_line_spacing; + _request_update(); + } +} + +float TextMesh::get_line_spacing() const { + return line_spacing; +} + +void TextMesh::set_autowrap_mode(TextServer::AutowrapMode p_mode) { + if (autowrap_mode != p_mode) { + autowrap_mode = p_mode; + dirty_lines = true; + _request_update(); + } +} + +TextServer::AutowrapMode TextMesh::get_autowrap_mode() const { + return autowrap_mode; +} + void TextMesh::set_depth(real_t p_depth) { if (depth != p_depth) { depth = MAX(p_depth, 0.0); @@ -2856,9 +3062,7 @@ real_t TextMesh::get_depth() const { void TextMesh::set_width(real_t p_width) { if (width != p_width) { width = p_width; - if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) { - dirty_text = true; - } + dirty_lines = true; _request_update(); } } @@ -2879,6 +3083,17 @@ real_t TextMesh::get_pixel_size() const { return pixel_size; } +void TextMesh::set_offset(const Point2 &p_offset) { + if (lbl_offset != p_offset) { + lbl_offset = p_offset; + _request_update(); + } +} + +Point2 TextMesh::get_offset() const { + return lbl_offset; +} + void TextMesh::set_curve_step(real_t p_step) { if (curve_step != p_step) { curve_step = CLAMP(p_step, 0.1, 10.0); diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h index cb93211756..ee61f0ac55 100644 --- a/scene/resources/primitive_meshes.h +++ b/scene/resources/primitive_meshes.h @@ -75,7 +75,7 @@ public: virtual int surface_get_array_len(int p_idx) const override; virtual int surface_get_array_index_len(int p_idx) const override; virtual Array surface_get_arrays(int p_surface) const override; - virtual Array surface_get_blend_shape_arrays(int p_surface) const override; + virtual TypedArray<Array> surface_get_blend_shape_arrays(int p_surface) const override; virtual Dictionary surface_get_lods(int p_surface) const override; virtual uint32_t surface_get_format(int p_idx) const override; virtual Mesh::PrimitiveType surface_get_primitive_type(int p_idx) const override; @@ -217,17 +217,25 @@ public: CylinderMesh(); }; -/** - Similar to quadmesh but with tessellation support +/* + A flat rectangle, can be used as quad or heightmap. */ class PlaneMesh : public PrimitiveMesh { GDCLASS(PlaneMesh, PrimitiveMesh); +public: + enum Orientation { + FACE_X, + FACE_Y, + FACE_Z, + }; + private: Size2 size = Size2(2.0, 2.0); int subdivide_w = 0; int subdivide_d = 0; Vector3 center_offset; + Orientation orientation = FACE_Y; protected: static void _bind_methods(); @@ -246,9 +254,27 @@ public: void set_center_offset(const Vector3 p_offset); Vector3 get_center_offset() const; + void set_orientation(const Orientation p_orientation); + Orientation get_orientation() const; + PlaneMesh(); }; +VARIANT_ENUM_CAST(PlaneMesh::Orientation) + +/* + A flat rectangle, inherits from PlaneMesh but defaults to facing the Z-plane. +*/ +class QuadMesh : public PlaneMesh { + GDCLASS(QuadMesh, PlaneMesh); + +public: + QuadMesh() { + set_orientation(FACE_Z); + set_size(Size2(1, 1)); + } +}; + /** A prism shapen, handy for ramps, triangles, etc. */ @@ -286,33 +312,6 @@ public: }; /** - Our original quadmesh... -*/ - -class QuadMesh : public PrimitiveMesh { - GDCLASS(QuadMesh, PrimitiveMesh); - -private: - Size2 size = Size2(1.0, 1.0); - Vector3 center_offset; - -protected: - static void _bind_methods(); - virtual void _create_mesh_array(Array &p_arr) const override; - -public: - virtual uint32_t surface_get_format(int p_idx) const override; - - QuadMesh(); - - void set_size(const Size2 &p_size); - Size2 get_size() const; - - void set_center_offset(const Vector3 p_offset); - Vector3 get_center_offset() const; -}; - -/** A sphere.. */ class SphereMesh : public PrimitiveMesh { @@ -351,6 +350,38 @@ public: }; /** + Big donut +*/ +class TorusMesh : public PrimitiveMesh { + GDCLASS(TorusMesh, PrimitiveMesh); + +private: + float inner_radius = 0.5; + float outer_radius = 1.0; + int rings = 64; + int ring_segments = 32; + +protected: + static void _bind_methods(); + virtual void _create_mesh_array(Array &p_arr) const override; + +public: + void set_inner_radius(const float p_inner_radius); + float get_inner_radius() const; + + void set_outer_radius(const float p_outer_radius); + float get_outer_radius() const; + + void set_rings(const int p_rings); + int get_rings() const; + + void set_ring_segments(const int p_ring_segments); + int get_ring_segments() const; + + TorusMesh(); +}; + +/** A single point for use in particle systems */ @@ -516,14 +547,21 @@ private: mutable HashMap<GlyphMeshKey, GlyphMeshData, GlyphMeshKeyHasher> cache; RID text_rid; + mutable Vector<RID> lines_rid; + String text; String xl_text; int font_size = 16; Ref<Font> font_override; + + TextServer::AutowrapMode autowrap_mode = TextServer::AUTOWRAP_OFF; float width = 500.0; + float line_spacing = 0.f; + Point2 lbl_offset; HorizontalAlignment horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER; + VerticalAlignment vertical_alignment = VERTICAL_ALIGNMENT_CENTER; bool uppercase = false; String language; TextServer::Direction text_direction = TextServer::DIRECTION_AUTO; @@ -534,6 +572,7 @@ private: real_t pixel_size = 0.01; real_t curve_step = 0.5; + mutable bool dirty_lines = true; mutable bool dirty_text = true; mutable bool dirty_font = true; mutable bool dirty_cache = true; @@ -556,6 +595,9 @@ public: void set_horizontal_alignment(HorizontalAlignment p_alignment); HorizontalAlignment get_horizontal_alignment() const; + void set_vertical_alignment(VerticalAlignment p_alignment); + VerticalAlignment get_vertical_alignment() const; + void set_text(const String &p_string); String get_text() const; @@ -566,6 +608,12 @@ public: void set_font_size(int p_size); int get_font_size() const; + void set_line_spacing(float p_size); + float get_line_spacing() const; + + void set_autowrap_mode(TextServer::AutowrapMode p_mode); + TextServer::AutowrapMode get_autowrap_mode() const; + void set_text_direction(TextServer::Direction p_text_direction); TextServer::Direction get_text_direction() const; @@ -592,7 +640,11 @@ public: void set_pixel_size(real_t p_amount); real_t get_pixel_size() const; + + void set_offset(const Point2 &p_offset); + Point2 get_offset() const; }; VARIANT_ENUM_CAST(RibbonTrailMesh::Shape) -#endif + +#endif // PRIMITIVE_MESHES_H diff --git a/scene/resources/rectangle_shape_2d.cpp b/scene/resources/rectangle_shape_2d.cpp index a64b262cb4..6ebf96db8e 100644 --- a/scene/resources/rectangle_shape_2d.cpp +++ b/scene/resources/rectangle_shape_2d.cpp @@ -41,7 +41,7 @@ void RectangleShape2D::_update_shape() { bool RectangleShape2D::_set(const StringName &p_name, const Variant &p_value) { if (p_name == "extents") { // Compatibility with Godot 3.x. // Convert to `size`, twice as big. - set_size((Vector2)p_value * 2); + set_size((Size2)p_value * 2); return true; } return false; @@ -57,13 +57,13 @@ bool RectangleShape2D::_get(const StringName &p_name, Variant &r_property) const } #endif // DISABLE_DEPRECATED -void RectangleShape2D::set_size(const Vector2 &p_size) { +void RectangleShape2D::set_size(const Size2 &p_size) { ERR_FAIL_COND_MSG(p_size.x < 0 || p_size.y < 0, "RectangleShape2D size cannot be negative."); size = p_size; _update_shape(); } -Vector2 RectangleShape2D::get_size() const { +Size2 RectangleShape2D::get_size() const { return size; } @@ -106,6 +106,6 @@ void RectangleShape2D::_bind_methods() { RectangleShape2D::RectangleShape2D() : Shape2D(PhysicsServer2D::get_singleton()->rectangle_shape_create()) { - size = Vector2(20, 20); + size = Size2(20, 20); _update_shape(); } diff --git a/scene/resources/rectangle_shape_2d.h b/scene/resources/rectangle_shape_2d.h index fa85aef428..9cc7b999be 100644 --- a/scene/resources/rectangle_shape_2d.h +++ b/scene/resources/rectangle_shape_2d.h @@ -36,7 +36,7 @@ class RectangleShape2D : public Shape2D { GDCLASS(RectangleShape2D, Shape2D); - Vector2 size; + Size2 size; void _update_shape(); protected: @@ -47,8 +47,8 @@ protected: #endif // DISABLE_DEPRECATED public: - void set_size(const Vector2 &p_size); - Vector2 get_size() const; + void set_size(const Size2 &p_size); + Size2 get_size() const; virtual void draw(const RID &p_to_rid, const Color &p_color) override; virtual Rect2 get_rect() const override; diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index 66afb001fb..0d798d2e27 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -65,14 +65,18 @@ Error ResourceLoaderText::_parse_sub_resource_dummy(DummyReadData *p_data, Varia return ERR_PARSE_ERROR; } - String unique_id = token.value; + if (p_data->no_placeholders) { + r_res.unref(); + } else { + String unique_id = token.value; - if (!p_data->resource_map.has(unique_id)) { - r_err_str = "Found unique_id reference before mapping, sub-resources stored out of order in resource file"; - return ERR_PARSE_ERROR; - } + if (!p_data->resource_map.has(unique_id)) { + r_err_str = "Found unique_id reference before mapping, sub-resources stored out of order in resource file"; + return ERR_PARSE_ERROR; + } - r_res = p_data->resource_map[unique_id]; + r_res = p_data->resource_map[unique_id]; + } VariantParser::get_token(p_stream, token, line, r_err_str); if (token.type != VariantParser::TK_PARENTHESIS_CLOSE) { @@ -91,11 +95,15 @@ Error ResourceLoaderText::_parse_ext_resource_dummy(DummyReadData *p_data, Varia return ERR_PARSE_ERROR; } - String id = token.value; + if (p_data->no_placeholders) { + r_res.unref(); + } else { + String id = token.value; - ERR_FAIL_COND_V(!p_data->rev_external_resources.has(id), ERR_PARSE_ERROR); + ERR_FAIL_COND_V(!p_data->rev_external_resources.has(id), ERR_PARSE_ERROR); - r_res = p_data->rev_external_resources[id]; + r_res = p_data->rev_external_resources[id]; + } VariantParser::get_token(p_stream, token, line, r_err_str); if (token.type != VariantParser::TK_PARENTHESIS_CLOSE) { @@ -443,7 +451,7 @@ Error ResourceLoaderText::load() { if (!path.contains("://") && path.is_relative_path()) { // path is relative to file being loaded, so convert to a resource path - path = ProjectSettings::get_singleton()->localize_path(local_path.get_base_dir().plus_file(path)); + path = ProjectSettings::get_singleton()->localize_path(local_path.get_base_dir().path_join(path)); } if (remaps.has(path)) { @@ -853,7 +861,7 @@ void ResourceLoaderText::get_dependencies(Ref<FileAccess> p_f, List<String> *p_d if (!using_uid && !path.contains("://") && path.is_relative_path()) { // path is relative to file being loaded, so convert to a resource path - path = ProjectSettings::get_singleton()->localize_path(local_path.get_base_dir().plus_file(path)); + path = ProjectSettings::get_singleton()->localize_path(local_path.get_base_dir().path_join(path)); } if (p_add_types) { @@ -930,7 +938,7 @@ Error ResourceLoaderText::rename_dependencies(Ref<FileAccess> p_f, const String } bool relative = false; if (!path.begins_with("res://")) { - path = base_path.plus_file(path).simplify_path(); + path = base_path.path_join(path).simplify_path(); relative = true; } @@ -1066,7 +1074,7 @@ static void bs_save_unicode_string(Ref<FileAccess> p_f, const String &p_string, p_f->store_buffer((const uint8_t *)utf8.get_data(), utf8.length() + 1); } -Error ResourceLoaderText::save_as_binary(Ref<FileAccess> p_f, const String &p_path) { +Error ResourceLoaderText::save_as_binary(const String &p_path) { if (error) { return error; } @@ -1271,7 +1279,7 @@ Error ResourceLoaderText::save_as_binary(Ref<FileAccess> p_f, const String &p_pa } if (next_tag.name == "node") { - //this is a node, must save one more! + // This is a node, must save one more! if (!is_scene) { error_text += "found the 'node' tag on a resource file!"; @@ -1346,6 +1354,126 @@ Error ResourceLoaderText::save_as_binary(Ref<FileAccess> p_f, const String &p_pa return OK; } +Error ResourceLoaderText::get_classes_used(HashSet<StringName> *r_classes) { + if (error) { + return error; + } + + ignore_resource_parsing = true; + + DummyReadData dummy_read; + dummy_read.no_placeholders = true; + VariantParser::ResourceParser rp; + rp.ext_func = _parse_ext_resource_dummys; + rp.sub_func = _parse_sub_resource_dummys; + rp.userdata = &dummy_read; + + while (next_tag.name == "ext_resource") { + error = VariantParser::parse_tag(&stream, lines, error_text, next_tag, &rp); + + if (error) { + _printerr(); + return error; + } + } + + while (next_tag.name == "sub_resource" || next_tag.name == "resource") { + if (next_tag.name == "sub_resource") { + if (!next_tag.fields.has("type")) { + error = ERR_FILE_CORRUPT; + error_text = "Missing 'type' in external resource tag"; + _printerr(); + return error; + } + + r_classes->insert(next_tag.fields["type"]); + + } else { + r_classes->insert(next_tag.fields["res_type"]); + } + + while (true) { + String assign; + Variant value; + + error = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, &rp); + + if (error) { + if (error == ERR_FILE_EOF) { + return OK; + } + + _printerr(); + return error; + } + + if (!assign.is_empty()) { + continue; + } else if (!next_tag.name.is_empty()) { + error = OK; + break; + } else { + error = ERR_FILE_CORRUPT; + error_text = "Premature end of file while parsing [sub_resource]"; + _printerr(); + return error; + } + } + } + + while (next_tag.name == "node") { + // This is a node, must save one more! + + if (!is_scene) { + error_text += "found the 'node' tag on a resource file!"; + _printerr(); + error = ERR_FILE_CORRUPT; + return error; + } + + if (!next_tag.fields.has("type")) { + error = ERR_FILE_CORRUPT; + error_text = "Missing 'type' in external resource tag"; + _printerr(); + return error; + } + + r_classes->insert(next_tag.fields["type"]); + + while (true) { + String assign; + Variant value; + + error = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, &rp); + + if (error) { + if (error == ERR_FILE_MISSING_DEPENDENCIES) { + // Resource loading error, just skip it. + } else if (error != ERR_FILE_EOF) { + _printerr(); + return error; + } else { + return OK; + } + } + + if (!assign.is_empty()) { + continue; + } else if (!next_tag.name.is_empty()) { + error = OK; + break; + } else { + error = ERR_FILE_CORRUPT; + error_text = "Premature end of file while parsing [sub_resource]"; + _printerr(); + return error; + } + } + } + + return OK; +} + String ResourceLoaderText::recognize(Ref<FileAccess> p_f) { error = OK; @@ -1473,6 +1601,26 @@ bool ResourceFormatLoaderText::handles_type(const String &p_type) const { return true; } +void ResourceFormatLoaderText::get_classes_used(const String &p_path, HashSet<StringName> *r_classes) { + String ext = p_path.get_extension().to_lower(); + if (ext == "tscn") { + r_classes->insert("PackedScene"); + } + + // ...for anything else must test... + + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); + if (f.is_null()) { + return; // Could not read. + } + + ResourceLoaderText loader; + loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path); + loader.res_path = loader.local_path; + loader.open(f); + loader.get_classes_used(r_classes); +} + String ResourceFormatLoaderText::get_resource_type(const String &p_path) const { String ext = p_path.get_extension().to_lower(); if (ext == "tscn") { @@ -1561,7 +1709,7 @@ Error ResourceFormatLoaderText::convert_file_to_binary(const String &p_src_path, loader.local_path = ProjectSettings::get_singleton()->localize_path(path); loader.res_path = loader.local_path; loader.open(f); - return loader.save_as_binary(f, p_dst_path); + return loader.save_as_binary(p_dst_path); } /*****************************************************************************************************/ @@ -2063,7 +2211,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const Ref<Reso return OK; } -Error ResourceFormatSaverText::save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags) { +Error ResourceFormatSaverText::save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags) { if (p_path.ends_with(".tscn") && !Ref<PackedScene>(p_resource).is_valid()) { return ERR_FILE_UNRECOGNIZED; } diff --git a/scene/resources/resource_format_text.h b/scene/resources/resource_format_text.h index 5c6a937bf2..9154bcf795 100644 --- a/scene/resources/resource_format_text.h +++ b/scene/resources/resource_format_text.h @@ -90,6 +90,7 @@ class ResourceLoaderText { }; struct DummyReadData { + bool no_placeholders = false; HashMap<Ref<Resource>, int> external_resources; HashMap<String, Ref<Resource>> rev_external_resources; HashMap<Ref<Resource>, int> resource_index_map; @@ -125,8 +126,9 @@ public: ResourceUID::ID get_uid(Ref<FileAccess> p_f); void get_dependencies(Ref<FileAccess> p_f, List<String> *p_dependencies, bool p_add_types); Error rename_dependencies(Ref<FileAccess> p_f, const String &p_path, const HashMap<String, String> &p_map); + Error get_classes_used(HashSet<StringName> *r_classes); - Error save_as_binary(Ref<FileAccess> p_f, const String &p_path); + Error save_as_binary(const String &p_path); ResourceLoaderText(); }; @@ -137,6 +139,8 @@ public: virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const; virtual void get_recognized_extensions(List<String> *p_extensions) const; virtual bool handles_type(const String &p_type) const; + virtual void get_classes_used(const String &p_path, HashSet<StringName> *r_classes); + virtual String get_resource_type(const String &p_path) const; virtual ResourceUID::ID get_resource_uid(const String &p_path) const; virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false); @@ -190,7 +194,7 @@ public: class ResourceFormatSaverText : public ResourceFormatSaver { public: static ResourceFormatSaverText *singleton; - virtual Error save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags = 0); + virtual Error save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags = 0); virtual bool recognize(const Ref<Resource> &p_resource) const; virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const; diff --git a/scene/resources/scene_replication_config.cpp b/scene/resources/scene_replication_config.cpp deleted file mode 100644 index 6789f9f7d5..0000000000 --- a/scene/resources/scene_replication_config.cpp +++ /dev/null @@ -1,205 +0,0 @@ -/*************************************************************************/ -/* scene_replication_config.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "scene_replication_config.h" - -#include "core/multiplayer/multiplayer_api.h" -#include "scene/main/node.h" - -bool SceneReplicationConfig::_set(const StringName &p_name, const Variant &p_value) { - String name = p_name; - - if (name.begins_with("properties/")) { - int idx = name.get_slicec('/', 1).to_int(); - String what = name.get_slicec('/', 2); - - if (properties.size() == idx && what == "path") { - ERR_FAIL_COND_V(p_value.get_type() != Variant::NODE_PATH, false); - NodePath path = p_value; - ERR_FAIL_COND_V(path.is_empty() || path.get_subname_count() == 0, false); - add_property(path); - return true; - } - ERR_FAIL_COND_V(p_value.get_type() != Variant::BOOL, false); - ERR_FAIL_INDEX_V(idx, properties.size(), false); - ReplicationProperty &prop = properties[idx]; - if (what == "sync") { - prop.sync = p_value; - if (prop.sync) { - sync_props.push_back(prop.name); - } else { - sync_props.erase(prop.name); - } - return true; - } else if (what == "spawn") { - prop.spawn = p_value; - if (prop.spawn) { - spawn_props.push_back(prop.name); - } else { - spawn_props.erase(prop.name); - } - return true; - } - } - return false; -} - -bool SceneReplicationConfig::_get(const StringName &p_name, Variant &r_ret) const { - String name = p_name; - - if (name.begins_with("properties/")) { - int idx = name.get_slicec('/', 1).to_int(); - String what = name.get_slicec('/', 2); - ERR_FAIL_INDEX_V(idx, properties.size(), false); - const ReplicationProperty &prop = properties[idx]; - if (what == "path") { - r_ret = prop.name; - return true; - } else if (what == "sync") { - r_ret = prop.sync; - return true; - } else if (what == "spawn") { - r_ret = prop.spawn; - return true; - } - } - return false; -} - -void SceneReplicationConfig::_get_property_list(List<PropertyInfo> *p_list) const { - for (int i = 0; i < properties.size(); i++) { - p_list->push_back(PropertyInfo(Variant::STRING, "properties/" + itos(i) + "/path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL)); - p_list->push_back(PropertyInfo(Variant::STRING, "properties/" + itos(i) + "/spawn", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL)); - p_list->push_back(PropertyInfo(Variant::STRING, "properties/" + itos(i) + "/sync", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL)); - } -} - -TypedArray<NodePath> SceneReplicationConfig::get_properties() const { - TypedArray<NodePath> paths; - for (const ReplicationProperty &prop : properties) { - paths.push_back(prop.name); - } - return paths; -} - -void SceneReplicationConfig::add_property(const NodePath &p_path, int p_index) { - ERR_FAIL_COND(properties.find(p_path)); - - if (p_index < 0 || p_index == properties.size()) { - properties.push_back(ReplicationProperty(p_path)); - return; - } - - ERR_FAIL_INDEX(p_index, properties.size()); - - List<ReplicationProperty>::Element *I = properties.front(); - int c = 0; - while (c < p_index) { - I = I->next(); - c++; - } - properties.insert_before(I, ReplicationProperty(p_path)); -} - -void SceneReplicationConfig::remove_property(const NodePath &p_path) { - properties.erase(p_path); -} - -bool SceneReplicationConfig::has_property(const NodePath &p_path) const { - for (int i = 0; i < properties.size(); i++) { - if (properties[i].name == p_path) { - return true; - } - } - return false; -} - -int SceneReplicationConfig::property_get_index(const NodePath &p_path) const { - for (int i = 0; i < properties.size(); i++) { - if (properties[i].name == p_path) { - return i; - } - } - ERR_FAIL_V(-1); -} - -bool SceneReplicationConfig::property_get_spawn(const NodePath &p_path) { - List<ReplicationProperty>::Element *E = properties.find(p_path); - ERR_FAIL_COND_V(!E, false); - return E->get().spawn; -} - -void SceneReplicationConfig::property_set_spawn(const NodePath &p_path, bool p_enabled) { - List<ReplicationProperty>::Element *E = properties.find(p_path); - ERR_FAIL_COND(!E); - if (E->get().spawn == p_enabled) { - return; - } - E->get().spawn = p_enabled; - spawn_props.clear(); - for (const ReplicationProperty &prop : properties) { - if (prop.spawn) { - spawn_props.push_back(p_path); - } - } -} - -bool SceneReplicationConfig::property_get_sync(const NodePath &p_path) { - List<ReplicationProperty>::Element *E = properties.find(p_path); - ERR_FAIL_COND_V(!E, false); - return E->get().sync; -} - -void SceneReplicationConfig::property_set_sync(const NodePath &p_path, bool p_enabled) { - List<ReplicationProperty>::Element *E = properties.find(p_path); - ERR_FAIL_COND(!E); - if (E->get().sync == p_enabled) { - return; - } - E->get().sync = p_enabled; - sync_props.clear(); - for (const ReplicationProperty &prop : properties) { - if (prop.sync) { - sync_props.push_back(p_path); - } - } -} - -void SceneReplicationConfig::_bind_methods() { - ClassDB::bind_method(D_METHOD("get_properties"), &SceneReplicationConfig::get_properties); - ClassDB::bind_method(D_METHOD("add_property", "path", "index"), &SceneReplicationConfig::add_property, DEFVAL(-1)); - ClassDB::bind_method(D_METHOD("has_property", "path"), &SceneReplicationConfig::has_property); - ClassDB::bind_method(D_METHOD("remove_property", "path"), &SceneReplicationConfig::remove_property); - ClassDB::bind_method(D_METHOD("property_get_index", "path"), &SceneReplicationConfig::property_get_index); - ClassDB::bind_method(D_METHOD("property_get_spawn", "path"), &SceneReplicationConfig::property_get_spawn); - ClassDB::bind_method(D_METHOD("property_set_spawn", "path", "enabled"), &SceneReplicationConfig::property_set_spawn); - ClassDB::bind_method(D_METHOD("property_get_sync", "path"), &SceneReplicationConfig::property_get_sync); - ClassDB::bind_method(D_METHOD("property_set_sync", "path", "enabled"), &SceneReplicationConfig::property_set_sync); -} diff --git a/scene/resources/separation_ray_shape_2d.cpp b/scene/resources/separation_ray_shape_2d.cpp index 0d6aee47d8..fea41061fd 100644 --- a/scene/resources/separation_ray_shape_2d.cpp +++ b/scene/resources/separation_ray_shape_2d.cpp @@ -57,7 +57,7 @@ void SeparationRayShape2D::draw(const RID &p_to_rid, const Color &p_color) { Transform2D xf; xf.rotate(target_position.angle()); - xf.translate(Vector2(no_line ? 0 : target_position.length() - arrow_size, 0)); + xf.translate_local(Vector2(no_line ? 0 : target_position.length() - arrow_size, 0)); Vector<Vector2> pts = { xf.xform(Vector2(arrow_size, 0)), diff --git a/scene/resources/separation_ray_shape_3d.h b/scene/resources/separation_ray_shape_3d.h index 0e750a48e6..ae08ff7887 100644 --- a/scene/resources/separation_ray_shape_3d.h +++ b/scene/resources/separation_ray_shape_3d.h @@ -28,8 +28,9 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SEPARATION_RAY_SHAPE_H -#define SEPARATION_RAY_SHAPE_H +#ifndef SEPARATION_RAY_SHAPE_3D_H +#define SEPARATION_RAY_SHAPE_3D_H + #include "scene/resources/shape_3d.h" class SeparationRayShape3D : public Shape3D { @@ -53,4 +54,5 @@ public: SeparationRayShape3D(); }; -#endif // SEPARATION_RAY_SHAPE_H + +#endif // SEPARATION_RAY_SHAPE_3D_H diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp index d49157b1b8..4d566178a5 100644 --- a/scene/resources/shader.cpp +++ b/scene/resources/shader.cpp @@ -33,6 +33,7 @@ #include "core/io/file_access.h" #include "scene/scene_string_names.h" #include "servers/rendering/shader_language.h" +#include "servers/rendering/shader_preprocessor.h" #include "servers/rendering_server.h" #include "texture.h" @@ -40,7 +41,23 @@ Shader::Mode Shader::get_mode() const { return mode; } +void Shader::_dependency_changed() { + RenderingServer::get_singleton()->shader_set_code(shader, RenderingServer::get_singleton()->shader_get_code(shader)); + params_cache_dirty = true; + + emit_changed(); +} + +void Shader::set_path(const String &p_path, bool p_take_over) { + Resource::set_path(p_path, p_take_over); + RS::get_singleton()->shader_set_path_hint(shader, p_path); +} + void Shader::set_code(const String &p_code) { + for (Ref<ShaderInclude> E : include_dependencies) { + E->disconnect(SNAME("changed"), callable_mp(this, &Shader::_dependency_changed)); + } + String type = ShaderLanguage::get_shader_type(p_code); if (type == "canvas_item") { @@ -55,7 +72,27 @@ void Shader::set_code(const String &p_code) { mode = MODE_SPATIAL; } - RenderingServer::get_singleton()->shader_set_code(shader, p_code); + code = p_code; + String pp_code = p_code; + + HashSet<Ref<ShaderInclude>> new_include_dependencies; + + { + // Preprocessor must run here and not in the server because: + // 1) Need to keep track of include dependencies at resource level + // 2) Server does not do interaction with Resource filetypes, this is a scene level feature. + ShaderPreprocessor preprocessor; + preprocessor.preprocess(p_code, "", pp_code, nullptr, nullptr, nullptr, &new_include_dependencies); + } + + // This ensures previous include resources are not freed and then re-loaded during parse (which would make compiling slower) + include_dependencies = new_include_dependencies; + + for (Ref<ShaderInclude> E : include_dependencies) { + E->connect(SNAME("changed"), callable_mp(this, &Shader::_dependency_changed)); + } + + RenderingServer::get_singleton()->shader_set_code(shader, pp_code); params_cache_dirty = true; emit_changed(); @@ -63,24 +100,28 @@ void Shader::set_code(const String &p_code) { String Shader::get_code() const { _update_shader(); - return RenderingServer::get_singleton()->shader_get_code(shader); + return code; } -void Shader::get_param_list(List<PropertyInfo> *p_params) const { +void Shader::get_shader_uniform_list(List<PropertyInfo> *p_params, bool p_get_groups) const { _update_shader(); List<PropertyInfo> local; - RenderingServer::get_singleton()->shader_get_param_list(shader, &local); + RenderingServer::get_singleton()->get_shader_parameter_list(shader, &local); params_cache.clear(); params_cache_dirty = false; for (PropertyInfo &pi : local) { - if (default_textures.has(pi.name)) { //do not show default textures + bool is_group = pi.usage == PROPERTY_USAGE_GROUP || pi.usage == PROPERTY_USAGE_SUBGROUP; + if (!p_get_groups && is_group) { continue; } - String original_name = pi.name; - pi.name = "shader_param/" + pi.name; - params_cache[pi.name] = original_name; + if (!is_group) { + if (default_textures.has(pi.name)) { //do not show default textures + continue; + } + params_cache[pi.name] = pi.name; + } if (p_params) { //small little hack if (pi.type == Variant::RID) { @@ -97,35 +138,35 @@ RID Shader::get_rid() const { return shader; } -void Shader::set_default_texture_param(const StringName &p_param, const Ref<Texture2D> &p_texture, int p_index) { +void Shader::set_default_texture_parameter(const StringName &p_name, const Ref<Texture2D> &p_texture, int p_index) { if (p_texture.is_valid()) { - if (!default_textures.has(p_param)) { - default_textures[p_param] = HashMap<int, Ref<Texture2D>>(); + if (!default_textures.has(p_name)) { + default_textures[p_name] = HashMap<int, Ref<Texture2D>>(); } - default_textures[p_param][p_index] = p_texture; - RS::get_singleton()->shader_set_default_texture_param(shader, p_param, p_texture->get_rid(), p_index); + default_textures[p_name][p_index] = p_texture; + RS::get_singleton()->shader_set_default_texture_parameter(shader, p_name, p_texture->get_rid(), p_index); } else { - if (default_textures.has(p_param) && default_textures[p_param].has(p_index)) { - default_textures[p_param].erase(p_index); + if (default_textures.has(p_name) && default_textures[p_name].has(p_index)) { + default_textures[p_name].erase(p_index); - if (default_textures[p_param].is_empty()) { - default_textures.erase(p_param); + if (default_textures[p_name].is_empty()) { + default_textures.erase(p_name); } } - RS::get_singleton()->shader_set_default_texture_param(shader, p_param, RID(), p_index); + RS::get_singleton()->shader_set_default_texture_parameter(shader, p_name, RID(), p_index); } emit_changed(); } -Ref<Texture2D> Shader::get_default_texture_param(const StringName &p_param, int p_index) const { - if (default_textures.has(p_param) && default_textures[p_param].has(p_index)) { - return default_textures[p_param][p_index]; +Ref<Texture2D> Shader::get_default_texture_parameter(const StringName &p_name, int p_index) const { + if (default_textures.has(p_name) && default_textures[p_name].has(p_index)) { + return default_textures[p_name][p_index]; } return Ref<Texture2D>(); } -void Shader::get_default_texture_param_list(List<StringName> *r_textures) const { +void Shader::get_default_texture_parameter_list(List<StringName> *r_textures) const { for (const KeyValue<StringName, HashMap<int, Ref<Texture2D>>> &E : default_textures) { r_textures->push_back(E.key); } @@ -135,8 +176,8 @@ bool Shader::is_text_shader() const { return true; } -bool Shader::has_param(const StringName &p_param) const { - return params_cache.has("shader_param/" + p_param); +bool Shader::has_parameter(const StringName &p_name) const { + return params_cache.has("shader_parameter/" + p_name); } void Shader::_update_shader() const { @@ -148,10 +189,10 @@ void Shader::_bind_methods() { ClassDB::bind_method(D_METHOD("set_code", "code"), &Shader::set_code); ClassDB::bind_method(D_METHOD("get_code"), &Shader::get_code); - ClassDB::bind_method(D_METHOD("set_default_texture_param", "param", "texture", "index"), &Shader::set_default_texture_param, DEFVAL(0)); - ClassDB::bind_method(D_METHOD("get_default_texture_param", "param", "index"), &Shader::get_default_texture_param, DEFVAL(0)); + ClassDB::bind_method(D_METHOD("set_default_texture_parameter", "name", "texture", "index"), &Shader::set_default_texture_parameter, DEFVAL(0)); + ClassDB::bind_method(D_METHOD("get_default_texture_parameter", "name", "index"), &Shader::get_default_texture_parameter, DEFVAL(0)); - ClassDB::bind_method(D_METHOD("has_param", "name"), &Shader::has_param); + ClassDB::bind_method(D_METHOD("has_parameter", "name"), &Shader::has_parameter); ADD_PROPERTY(PropertyInfo(Variant::STRING, "code", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_code", "get_code"); @@ -210,7 +251,7 @@ String ResourceFormatLoaderShader::get_resource_type(const String &p_path) const return ""; } -Error ResourceFormatSaverShader::save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags) { +Error ResourceFormatSaverShader::save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags) { Ref<Shader> shader = p_resource; ERR_FAIL_COND_V(shader.is_null(), ERR_INVALID_PARAMETER); diff --git a/scene/resources/shader.h b/scene/resources/shader.h index 11c9f60ce8..57be142a95 100644 --- a/scene/resources/shader.h +++ b/scene/resources/shader.h @@ -35,6 +35,7 @@ #include "core/io/resource_loader.h" #include "core/io/resource_saver.h" #include "scene/resources/texture.h" +#include "shader_include.h" class Shader : public Resource { GDCLASS(Shader, Resource); @@ -53,6 +54,8 @@ public: private: RID shader; Mode mode = MODE_SPATIAL; + HashSet<Ref<ShaderInclude>> include_dependencies; + String code; // hack the name of performance // shaders keep a list of ShaderMaterial -> RenderingServer name translations, to make @@ -61,6 +64,7 @@ private: mutable HashMap<StringName, StringName> params_cache; //map a shader param to a material param.. HashMap<StringName, HashMap<int, Ref<Texture2D>>> default_textures; + void _dependency_changed(); virtual void _update_shader() const; //used for visual shader protected: static void _bind_methods(); @@ -69,27 +73,58 @@ public: //void set_mode(Mode p_mode); virtual Mode get_mode() const; + virtual void set_path(const String &p_path, bool p_take_over = false) override; + void set_code(const String &p_code); String get_code() const; - void get_param_list(List<PropertyInfo> *p_params) const; - bool has_param(const StringName &p_param) const; + void get_shader_uniform_list(List<PropertyInfo> *p_params, bool p_get_groups = false) const; + bool has_parameter(const StringName &p_name) const; - void set_default_texture_param(const StringName &p_param, const Ref<Texture2D> &p_texture, int p_index = 0); - Ref<Texture2D> get_default_texture_param(const StringName &p_param, int p_index = 0) const; - void get_default_texture_param_list(List<StringName> *r_textures) const; + void set_default_texture_parameter(const StringName &p_name, const Ref<Texture2D> &p_texture, int p_index = 0); + Ref<Texture2D> get_default_texture_parameter(const StringName &p_name, int p_index = 0) const; + void get_default_texture_parameter_list(List<StringName> *r_textures) const; virtual bool is_text_shader() const; - _FORCE_INLINE_ StringName remap_param(const StringName &p_param) const { + // Finds the shader parameter name for the given property name, which should start with "shader_parameter/". + _FORCE_INLINE_ StringName remap_parameter(const StringName &p_property) const { if (params_cache_dirty) { - get_param_list(nullptr); + get_shader_uniform_list(nullptr); } - const HashMap<StringName, StringName>::Iterator E = params_cache.find(p_param); - if (E) { - return E->value; + String n = p_property; + + // Backwards compatibility with old shader parameter names. + // Note: The if statements are important to make sure we are only replacing text exactly at index 0. + if (n.find("param/") == 0) { + n = n.replace_first("param/", "shader_parameter/"); + } + if (n.find("shader_param/") == 0) { + n = n.replace_first("shader_param/", "shader_parameter/"); } + if (n.find("shader_uniform/") == 0) { + n = n.replace_first("shader_uniform/", "shader_parameter/"); + } + + { + // Additional backwards compatibility for projects between #62972 and #64092 (about a month of v4.0 development). + // These projects did not have any prefix for shader uniforms due to a bug. + // This code should be removed during beta or rc of 4.0. + const HashMap<StringName, StringName>::Iterator E = params_cache.find(n); + if (E) { + return E->value; + } + } + + if (n.begins_with("shader_parameter/")) { + n = n.replace_first("shader_parameter/", ""); + const HashMap<StringName, StringName>::Iterator E = params_cache.find(n); + if (E) { + return E->value; + } + } + return StringName(); } @@ -111,7 +146,7 @@ public: class ResourceFormatSaverShader : public ResourceFormatSaver { public: - virtual Error save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags = 0); + virtual Error save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags = 0); virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const; virtual bool recognize(const Ref<Resource> &p_resource) const; }; diff --git a/scene/resources/shader_include.cpp b/scene/resources/shader_include.cpp new file mode 100644 index 0000000000..fe628dd323 --- /dev/null +++ b/scene/resources/shader_include.cpp @@ -0,0 +1,144 @@ +/*************************************************************************/ +/* shader_include.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "shader_include.h" +#include "servers/rendering/shader_language.h" +#include "servers/rendering/shader_preprocessor.h" + +void ShaderInclude::_dependency_changed() { + emit_changed(); +} + +void ShaderInclude::set_code(const String &p_code) { + HashSet<Ref<ShaderInclude>> new_dependencies; + code = p_code; + + for (Ref<ShaderInclude> E : dependencies) { + E->disconnect(SNAME("changed"), callable_mp(this, &ShaderInclude::_dependency_changed)); + } + + { + String pp_code; + ShaderPreprocessor preprocessor; + preprocessor.preprocess(p_code, "", pp_code, nullptr, nullptr, nullptr, &new_dependencies); + } + + // This ensures previous include resources are not freed and then re-loaded during parse (which would make compiling slower) + dependencies = new_dependencies; + + for (Ref<ShaderInclude> E : dependencies) { + E->connect(SNAME("changed"), callable_mp(this, &ShaderInclude::_dependency_changed)); + } + + emit_changed(); +} + +String ShaderInclude::get_code() const { + return code; +} + +void ShaderInclude::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_code", "code"), &ShaderInclude::set_code); + ClassDB::bind_method(D_METHOD("get_code"), &ShaderInclude::get_code); + + ADD_PROPERTY(PropertyInfo(Variant::STRING, "code", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_code", "get_code"); +} + +// ResourceFormatLoaderShaderInclude + +Ref<Resource> ResourceFormatLoaderShaderInclude::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) { + if (r_error) { + *r_error = ERR_FILE_CANT_OPEN; + } + + Ref<ShaderInclude> shader_inc; + shader_inc.instantiate(); + + Vector<uint8_t> buffer = FileAccess::get_file_as_array(p_path); + + String str; + str.parse_utf8((const char *)buffer.ptr(), buffer.size()); + + shader_inc->set_code(str); + + if (r_error) { + *r_error = OK; + } + + return shader_inc; +} + +void ResourceFormatLoaderShaderInclude::get_recognized_extensions(List<String> *p_extensions) const { + p_extensions->push_back("gdshaderinc"); +} + +bool ResourceFormatLoaderShaderInclude::handles_type(const String &p_type) const { + return (p_type == "ShaderInclude"); +} + +String ResourceFormatLoaderShaderInclude::get_resource_type(const String &p_path) const { + String extension = p_path.get_extension().to_lower(); + if (extension == "gdshaderinc") { + return "ShaderInclude"; + } + return ""; +} + +// ResourceFormatSaverShaderInclude + +Error ResourceFormatSaverShaderInclude::save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags) { + Ref<ShaderInclude> shader_inc = p_resource; + ERR_FAIL_COND_V(shader_inc.is_null(), ERR_INVALID_PARAMETER); + + String source = shader_inc->get_code(); + + Error error; + Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE, &error); + + ERR_FAIL_COND_V_MSG(error, error, "Cannot save shader include '" + p_path + "'."); + + file->store_string(source); + if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) { + return ERR_CANT_CREATE; + } + + return OK; +} + +void ResourceFormatSaverShaderInclude::get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const { + const ShaderInclude *shader_inc = Object::cast_to<ShaderInclude>(*p_resource); + if (shader_inc != nullptr) { + p_extensions->push_back("gdshaderinc"); + } +} + +bool ResourceFormatSaverShaderInclude::recognize(const Ref<Resource> &p_resource) const { + return p_resource->get_class_name() == "ShaderInclude"; //only shader, not inherited +} diff --git a/scene/resources/shader_include.h b/scene/resources/shader_include.h new file mode 100644 index 0000000000..b0865e3a61 --- /dev/null +++ b/scene/resources/shader_include.h @@ -0,0 +1,71 @@ +/*************************************************************************/ +/* shader_include.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SHADER_INCLUDE_H +#define SHADER_INCLUDE_H + +#include "core/io/resource.h" +#include "core/io/resource_loader.h" +#include "core/io/resource_saver.h" +#include "core/templates/hash_set.h" + +class ShaderInclude : public Resource { + GDCLASS(ShaderInclude, Resource); + OBJ_SAVE_TYPE(ShaderInclude); + +private: + String code; + HashSet<Ref<ShaderInclude>> dependencies; + void _dependency_changed(); + +protected: + static void _bind_methods(); + +public: + void set_code(const String &p_text); + String get_code() const; +}; + +class ResourceFormatLoaderShaderInclude : public ResourceFormatLoader { +public: + virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); + virtual void get_recognized_extensions(List<String> *p_extensions) const; + virtual bool handles_type(const String &p_type) const; + virtual String get_resource_type(const String &p_path) const; +}; + +class ResourceFormatSaverShaderInclude : public ResourceFormatSaver { +public: + virtual Error save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags = 0); + virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const; + virtual bool recognize(const Ref<Resource> &p_resource) const; +}; + +#endif // SHADER_INCLUDE_H diff --git a/scene/resources/shape_2d.cpp b/scene/resources/shape_2d.cpp index 16ef45829f..fe43f345d4 100644 --- a/scene/resources/shape_2d.cpp +++ b/scene/resources/shape_2d.cpp @@ -59,39 +59,39 @@ bool Shape2D::collide(const Transform2D &p_local_xform, const Ref<Shape2D> &p_sh return PhysicsServer2D::get_singleton()->shape_collide(get_rid(), p_local_xform, Vector2(), p_shape->get_rid(), p_shape_xform, Vector2(), nullptr, 0, r); } -Array Shape2D::collide_with_motion_and_get_contacts(const Transform2D &p_local_xform, const Vector2 &p_local_motion, const Ref<Shape2D> &p_shape, const Transform2D &p_shape_xform, const Vector2 &p_shape_motion) { - ERR_FAIL_COND_V(p_shape.is_null(), Array()); +PackedVector2Array Shape2D::collide_with_motion_and_get_contacts(const Transform2D &p_local_xform, const Vector2 &p_local_motion, const Ref<Shape2D> &p_shape, const Transform2D &p_shape_xform, const Vector2 &p_shape_motion) { + ERR_FAIL_COND_V(p_shape.is_null(), PackedVector2Array()); const int max_contacts = 16; Vector2 result[max_contacts * 2]; int contacts = 0; if (!PhysicsServer2D::get_singleton()->shape_collide(get_rid(), p_local_xform, p_local_motion, p_shape->get_rid(), p_shape_xform, p_shape_motion, result, max_contacts, contacts)) { - return Array(); + return PackedVector2Array(); } - Array results; + PackedVector2Array results; results.resize(contacts * 2); for (int i = 0; i < contacts * 2; i++) { - results[i] = result[i]; + results.write[i] = result[i]; } return results; } -Array Shape2D::collide_and_get_contacts(const Transform2D &p_local_xform, const Ref<Shape2D> &p_shape, const Transform2D &p_shape_xform) { - ERR_FAIL_COND_V(p_shape.is_null(), Array()); +PackedVector2Array Shape2D::collide_and_get_contacts(const Transform2D &p_local_xform, const Ref<Shape2D> &p_shape, const Transform2D &p_shape_xform) { + ERR_FAIL_COND_V(p_shape.is_null(), PackedVector2Array()); const int max_contacts = 16; Vector2 result[max_contacts * 2]; int contacts = 0; if (!PhysicsServer2D::get_singleton()->shape_collide(get_rid(), p_local_xform, Vector2(), p_shape->get_rid(), p_shape_xform, Vector2(), result, max_contacts, contacts)) { - return Array(); + return PackedVector2Array(); } - Array results; + PackedVector2Array results; results.resize(contacts * 2); for (int i = 0; i < contacts * 2; i++) { - results[i] = result[i]; + results.write[i] = result[i]; } return results; diff --git a/scene/resources/shape_2d.h b/scene/resources/shape_2d.h index e9dc10eeae..a15aecee93 100644 --- a/scene/resources/shape_2d.h +++ b/scene/resources/shape_2d.h @@ -53,8 +53,8 @@ public: bool collide_with_motion(const Transform2D &p_local_xform, const Vector2 &p_local_motion, const Ref<Shape2D> &p_shape, const Transform2D &p_shape_xform, const Vector2 &p_shape_motion); bool collide(const Transform2D &p_local_xform, const Ref<Shape2D> &p_shape, const Transform2D &p_shape_xform); - Array collide_with_motion_and_get_contacts(const Transform2D &p_local_xform, const Vector2 &p_local_motion, const Ref<Shape2D> &p_shape, const Transform2D &p_shape_xform, const Vector2 &p_shape_motion); - Array collide_and_get_contacts(const Transform2D &p_local_xform, const Ref<Shape2D> &p_shape, const Transform2D &p_shape_xform); + PackedVector2Array collide_with_motion_and_get_contacts(const Transform2D &p_local_xform, const Vector2 &p_local_motion, const Ref<Shape2D> &p_shape, const Transform2D &p_shape_xform, const Vector2 &p_shape_motion); + PackedVector2Array collide_and_get_contacts(const Transform2D &p_local_xform, const Ref<Shape2D> &p_shape, const Transform2D &p_shape_xform); virtual void draw(const RID &p_to_rid, const Color &p_color) {} virtual Rect2 get_rect() const { return Rect2(); } diff --git a/scene/resources/shape_3d.h b/scene/resources/shape_3d.h index 77e79a269d..58fe723d89 100644 --- a/scene/resources/shape_3d.h +++ b/scene/resources/shape_3d.h @@ -73,4 +73,4 @@ public: ~Shape3D(); }; -#endif // SHAPE_H +#endif // SHAPE_3D_H diff --git a/scene/resources/skeleton_modification_2d.h b/scene/resources/skeleton_modification_2d.h index 3b9235ffd8..4c49119df0 100644 --- a/scene/resources/skeleton_modification_2d.h +++ b/scene/resources/skeleton_modification_2d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SKELETONMODIFICATION2D_H -#define SKELETONMODIFICATION2D_H +#ifndef SKELETON_MODIFICATION_2D_H +#define SKELETON_MODIFICATION_2D_H #include "scene/2d/skeleton_2d.h" #include "scene/resources/skeleton_modification_stack_2d.h" @@ -86,4 +86,4 @@ public: SkeletonModification2D(); }; -#endif // SKELETONMODIFICATION2D_H +#endif // SKELETON_MODIFICATION_2D_H diff --git a/scene/resources/skeleton_modification_2d_ccdik.cpp b/scene/resources/skeleton_modification_2d_ccdik.cpp index 7adaf1452c..96961a1fe4 100644 --- a/scene/resources/skeleton_modification_2d_ccdik.cpp +++ b/scene/resources/skeleton_modification_2d_ccdik.cpp @@ -52,9 +52,9 @@ bool SkeletonModification2DCCDIK::_set(const StringName &p_path, const Variant & } else if (what == "enable_constraint") { set_ccdik_joint_enable_constraint(which, p_value); } else if (what == "constraint_angle_min") { - set_ccdik_joint_constraint_angle_min(which, Math::deg2rad(float(p_value))); + set_ccdik_joint_constraint_angle_min(which, Math::deg_to_rad(float(p_value))); } else if (what == "constraint_angle_max") { - set_ccdik_joint_constraint_angle_max(which, Math::deg2rad(float(p_value))); + set_ccdik_joint_constraint_angle_max(which, Math::deg_to_rad(float(p_value))); } else if (what == "constraint_angle_invert") { set_ccdik_joint_constraint_angle_invert(which, p_value); } else if (what == "constraint_in_localspace") { @@ -96,9 +96,9 @@ bool SkeletonModification2DCCDIK::_get(const StringName &p_path, Variant &r_ret) } else if (what == "enable_constraint") { r_ret = get_ccdik_joint_enable_constraint(which); } else if (what == "constraint_angle_min") { - r_ret = Math::rad2deg(get_ccdik_joint_constraint_angle_min(which)); + r_ret = Math::rad_to_deg(get_ccdik_joint_constraint_angle_min(which)); } else if (what == "constraint_angle_max") { - r_ret = Math::rad2deg(get_ccdik_joint_constraint_angle_max(which)); + r_ret = Math::rad_to_deg(get_ccdik_joint_constraint_angle_max(which)); } else if (what == "constraint_angle_invert") { r_ret = get_ccdik_joint_constraint_angle_invert(which); } else if (what == "constraint_in_localspace") { diff --git a/scene/resources/skeleton_modification_2d_ccdik.h b/scene/resources/skeleton_modification_2d_ccdik.h index 31485fa31f..e49dfca5b5 100644 --- a/scene/resources/skeleton_modification_2d_ccdik.h +++ b/scene/resources/skeleton_modification_2d_ccdik.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SKELETONMODIFICATION2DCCDIK_H -#define SKELETONMODIFICATION2DCCDIK_H +#ifndef SKELETON_MODIFICATION_2D_CCDIK_H +#define SKELETON_MODIFICATION_2D_CCDIK_H #include "scene/2d/skeleton_2d.h" #include "scene/resources/skeleton_modification_2d.h" @@ -113,4 +113,4 @@ public: ~SkeletonModification2DCCDIK(); }; -#endif // SKELETONMODIFICATION2DCCDIK_H +#endif // SKELETON_MODIFICATION_2D_CCDIK_H diff --git a/scene/resources/skeleton_modification_2d_fabrik.h b/scene/resources/skeleton_modification_2d_fabrik.h index d5077084ef..4a875d039f 100644 --- a/scene/resources/skeleton_modification_2d_fabrik.h +++ b/scene/resources/skeleton_modification_2d_fabrik.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SKELETONMODIFICATION2DFABRIK_H -#define SKELETONMODIFICATION2DFABRIK_H +#ifndef SKELETON_MODIFICATION_2D_FABRIK_H +#define SKELETON_MODIFICATION_2D_FABRIK_H #include "scene/2d/skeleton_2d.h" #include "scene/resources/skeleton_modification_2d.h" @@ -105,4 +105,4 @@ public: ~SkeletonModification2DFABRIK(); }; -#endif // SKELETONMODIFICATION2DFABRIK_H +#endif // SKELETON_MODIFICATION_2D_FABRIK_H diff --git a/scene/resources/skeleton_modification_2d_jiggle.h b/scene/resources/skeleton_modification_2d_jiggle.h index a1abca6564..b9080eafa9 100644 --- a/scene/resources/skeleton_modification_2d_jiggle.h +++ b/scene/resources/skeleton_modification_2d_jiggle.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SKELETONMODIFICATION2DJIGGLE_H -#define SKELETONMODIFICATION2DJIGGLE_H +#ifndef SKELETON_MODIFICATION_2D_JIGGLE_H +#define SKELETON_MODIFICATION_2D_JIGGLE_H #include "scene/2d/skeleton_2d.h" #include "scene/resources/skeleton_modification_2d.h" @@ -136,4 +136,4 @@ public: ~SkeletonModification2DJiggle(); }; -#endif // SKELETONMODIFICATION2DJIGGLE_H +#endif // SKELETON_MODIFICATION_2D_JIGGLE_H diff --git a/scene/resources/skeleton_modification_2d_lookat.cpp b/scene/resources/skeleton_modification_2d_lookat.cpp index 23e1c579dc..d3cfffb1de 100644 --- a/scene/resources/skeleton_modification_2d_lookat.cpp +++ b/scene/resources/skeleton_modification_2d_lookat.cpp @@ -41,15 +41,15 @@ bool SkeletonModification2DLookAt::_set(const StringName &p_path, const Variant if (path.begins_with("enable_constraint")) { set_enable_constraint(p_value); } else if (path.begins_with("constraint_angle_min")) { - set_constraint_angle_min(Math::deg2rad(float(p_value))); + set_constraint_angle_min(Math::deg_to_rad(float(p_value))); } else if (path.begins_with("constraint_angle_max")) { - set_constraint_angle_max(Math::deg2rad(float(p_value))); + set_constraint_angle_max(Math::deg_to_rad(float(p_value))); } else if (path.begins_with("constraint_angle_invert")) { set_constraint_angle_invert(p_value); } else if (path.begins_with("constraint_in_localspace")) { set_constraint_in_localspace(p_value); } else if (path.begins_with("additional_rotation")) { - set_additional_rotation(Math::deg2rad(float(p_value))); + set_additional_rotation(Math::deg_to_rad(float(p_value))); } #ifdef TOOLS_ENABLED @@ -67,15 +67,15 @@ bool SkeletonModification2DLookAt::_get(const StringName &p_path, Variant &r_ret if (path.begins_with("enable_constraint")) { r_ret = get_enable_constraint(); } else if (path.begins_with("constraint_angle_min")) { - r_ret = Math::rad2deg(get_constraint_angle_min()); + r_ret = Math::rad_to_deg(get_constraint_angle_min()); } else if (path.begins_with("constraint_angle_max")) { - r_ret = Math::rad2deg(get_constraint_angle_max()); + r_ret = Math::rad_to_deg(get_constraint_angle_max()); } else if (path.begins_with("constraint_angle_invert")) { r_ret = get_constraint_angle_invert(); } else if (path.begins_with("constraint_in_localspace")) { r_ret = get_constraint_in_localspace(); } else if (path.begins_with("additional_rotation")) { - r_ret = Math::rad2deg(get_additional_rotation()); + r_ret = Math::rad_to_deg(get_additional_rotation()); } #ifdef TOOLS_ENABLED diff --git a/scene/resources/skeleton_modification_2d_lookat.h b/scene/resources/skeleton_modification_2d_lookat.h index ff91b92e7d..e4d7afa605 100644 --- a/scene/resources/skeleton_modification_2d_lookat.h +++ b/scene/resources/skeleton_modification_2d_lookat.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SKELETONMODIFICATION2DLOOKAT_H -#define SKELETONMODIFICATION2DLOOKAT_H +#ifndef SKELETON_MODIFICATION_2D_LOOKAT_H +#define SKELETON_MODIFICATION_2D_LOOKAT_H #include "scene/2d/skeleton_2d.h" #include "scene/resources/skeleton_modification_2d.h" @@ -97,4 +97,4 @@ public: ~SkeletonModification2DLookAt(); }; -#endif // SKELETONMODIFICATION2DLOOKAT_H +#endif // SKELETON_MODIFICATION_2D_LOOKAT_H diff --git a/scene/resources/skeleton_modification_2d_physicalbones.h b/scene/resources/skeleton_modification_2d_physicalbones.h index 373ff666ee..55482b2cca 100644 --- a/scene/resources/skeleton_modification_2d_physicalbones.h +++ b/scene/resources/skeleton_modification_2d_physicalbones.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SKELETONMODIFICATION2DPHYSICALBONES_H -#define SKELETONMODIFICATION2DPHYSICALBONES_H +#ifndef SKELETON_MODIFICATION_2D_PHYSICALBONES_H +#define SKELETON_MODIFICATION_2D_PHYSICALBONES_H #include "scene/2d/skeleton_2d.h" #include "scene/resources/skeleton_modification_2d.h" @@ -79,4 +79,4 @@ public: ~SkeletonModification2DPhysicalBones(); }; -#endif // SKELETONMODIFICATION2DPHYSICALBONES_H +#endif // SKELETON_MODIFICATION_2D_PHYSICALBONES_H diff --git a/scene/resources/skeleton_modification_2d_stackholder.h b/scene/resources/skeleton_modification_2d_stackholder.h index 99d9f6f381..4da9fcc1f6 100644 --- a/scene/resources/skeleton_modification_2d_stackholder.h +++ b/scene/resources/skeleton_modification_2d_stackholder.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SKELETONMODIFICATION2DSTACKHOLDER_H -#define SKELETONMODIFICATION2DSTACKHOLDER_H +#ifndef SKELETON_MODIFICATION_2D_STACKHOLDER_H +#define SKELETON_MODIFICATION_2D_STACKHOLDER_H #include "scene/2d/skeleton_2d.h" #include "scene/resources/skeleton_modification_2d.h" @@ -61,4 +61,4 @@ public: ~SkeletonModification2DStackHolder(); }; -#endif // SKELETONMODIFICATION2DSTACKHOLDER_H +#endif // SKELETON_MODIFICATION_2D_STACKHOLDER_H diff --git a/scene/resources/skeleton_modification_2d_twoboneik.h b/scene/resources/skeleton_modification_2d_twoboneik.h index fc14d35f70..e1dbb6cfda 100644 --- a/scene/resources/skeleton_modification_2d_twoboneik.h +++ b/scene/resources/skeleton_modification_2d_twoboneik.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SKELETONMODIFICATION2DTWOBONEIK_H -#define SKELETONMODIFICATION2DTWOBONEIK_H +#ifndef SKELETON_MODIFICATION_2D_TWOBONEIK_H +#define SKELETON_MODIFICATION_2D_TWOBONEIK_H #include "scene/2d/skeleton_2d.h" #include "scene/resources/skeleton_modification_2d.h" @@ -104,4 +104,4 @@ public: ~SkeletonModification2DTwoBoneIK(); }; -#endif // SKELETONMODIFICATION2DTWOBONEIK_H +#endif // SKELETON_MODIFICATION_2D_TWOBONEIK_H diff --git a/scene/resources/skeleton_modification_3d.h b/scene/resources/skeleton_modification_3d.h index ab736fcbd2..6daf5efcd9 100644 --- a/scene/resources/skeleton_modification_3d.h +++ b/scene/resources/skeleton_modification_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SKELETONMODIFICATION3D_H -#define SKELETONMODIFICATION3D_H +#ifndef SKELETON_MODIFICATION_3D_H +#define SKELETON_MODIFICATION_3D_H #include "scene/3d/skeleton_3d.h" #include "scene/resources/skeleton_modification_stack_3d.h" @@ -76,4 +76,4 @@ public: SkeletonModification3D(); }; -#endif // SKELETONMODIFICATION3D_H +#endif // SKELETON_MODIFICATION_3D_H diff --git a/scene/resources/skeleton_modification_3d_ccdik.cpp b/scene/resources/skeleton_modification_3d_ccdik.cpp index f19be47db2..3251ee4189 100644 --- a/scene/resources/skeleton_modification_3d_ccdik.cpp +++ b/scene/resources/skeleton_modification_3d_ccdik.cpp @@ -50,9 +50,9 @@ bool SkeletonModification3DCCDIK::_set(const StringName &p_path, const Variant & } else if (what == "enable_joint_constraint") { set_ccdik_joint_enable_constraint(which, p_value); } else if (what == "joint_constraint_angle_min") { - set_ccdik_joint_constraint_angle_min(which, Math::deg2rad(real_t(p_value))); + set_ccdik_joint_constraint_angle_min(which, Math::deg_to_rad(real_t(p_value))); } else if (what == "joint_constraint_angle_max") { - set_ccdik_joint_constraint_angle_max(which, Math::deg2rad(real_t(p_value))); + set_ccdik_joint_constraint_angle_max(which, Math::deg_to_rad(real_t(p_value))); } else if (what == "joint_constraint_angles_invert") { set_ccdik_joint_constraint_invert(which, p_value); } @@ -79,9 +79,9 @@ bool SkeletonModification3DCCDIK::_get(const StringName &p_path, Variant &r_ret) } else if (what == "enable_joint_constraint") { r_ret = get_ccdik_joint_enable_constraint(which); } else if (what == "joint_constraint_angle_min") { - r_ret = Math::rad2deg(get_ccdik_joint_constraint_angle_min(which)); + r_ret = Math::rad_to_deg(get_ccdik_joint_constraint_angle_min(which)); } else if (what == "joint_constraint_angle_max") { - r_ret = Math::rad2deg(get_ccdik_joint_constraint_angle_max(which)); + r_ret = Math::rad_to_deg(get_ccdik_joint_constraint_angle_max(which)); } else if (what == "joint_constraint_angles_invert") { r_ret = get_ccdik_joint_constraint_invert(which); } diff --git a/scene/resources/skeleton_modification_3d_ccdik.h b/scene/resources/skeleton_modification_3d_ccdik.h index 873ab8aa5a..1fe53e94b6 100644 --- a/scene/resources/skeleton_modification_3d_ccdik.h +++ b/scene/resources/skeleton_modification_3d_ccdik.h @@ -28,13 +28,13 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#ifndef SKELETON_MODIFICATION_3D_CCDIK_H +#define SKELETON_MODIFICATION_3D_CCDIK_H + #include "core/templates/local_vector.h" #include "scene/3d/skeleton_3d.h" #include "scene/resources/skeleton_modification_3d.h" -#ifndef SKELETONMODIFICATION3DCCDIK_H -#define SKELETONMODIFICATION3DCCDIK_H - class SkeletonModification3DCCDIK : public SkeletonModification3D { GDCLASS(SkeletonModification3DCCDIK, SkeletonModification3D); @@ -111,4 +111,4 @@ public: ~SkeletonModification3DCCDIK(); }; -#endif //SKELETONMODIFICATION3DCCDIK_H +#endif // SKELETON_MODIFICATION_3D_CCDIK_H diff --git a/scene/resources/skeleton_modification_3d_fabrik.cpp b/scene/resources/skeleton_modification_3d_fabrik.cpp index b62dda3f4f..4099208f44 100644 --- a/scene/resources/skeleton_modification_3d_fabrik.cpp +++ b/scene/resources/skeleton_modification_3d_fabrik.cpp @@ -58,7 +58,7 @@ bool SkeletonModification3DFABRIK::_set(const StringName &p_path, const Variant } else if (what == "use_target_basis") { set_fabrik_joint_use_target_basis(which, p_value); } else if (what == "roll") { - set_fabrik_joint_roll(which, Math::deg2rad(real_t(p_value))); + set_fabrik_joint_roll(which, Math::deg_to_rad(real_t(p_value))); } return true; } @@ -91,7 +91,7 @@ bool SkeletonModification3DFABRIK::_get(const StringName &p_path, Variant &r_ret } else if (what == "use_target_basis") { r_ret = get_fabrik_joint_use_target_basis(which); } else if (what == "roll") { - r_ret = Math::rad2deg(get_fabrik_joint_roll(which)); + r_ret = Math::rad_to_deg(get_fabrik_joint_roll(which)); } return true; } diff --git a/scene/resources/skeleton_modification_3d_fabrik.h b/scene/resources/skeleton_modification_3d_fabrik.h index cc4d3a5e20..e2e490d636 100644 --- a/scene/resources/skeleton_modification_3d_fabrik.h +++ b/scene/resources/skeleton_modification_3d_fabrik.h @@ -28,13 +28,13 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#ifndef SKELETON_MODIFICATION_3D_FABRIK_H +#define SKELETON_MODIFICATION_3D_FABRIK_H + #include "core/templates/local_vector.h" #include "scene/3d/skeleton_3d.h" #include "scene/resources/skeleton_modification_3d.h" -#ifndef SKELETONMODIFICATION3DFABRIK_H -#define SKELETONMODIFICATION3DFABRIK_H - class SkeletonModification3DFABRIK : public SkeletonModification3D { GDCLASS(SkeletonModification3DFABRIK, SkeletonModification3D); @@ -121,4 +121,4 @@ public: ~SkeletonModification3DFABRIK(); }; -#endif //SKELETONMODIFICATION3DFABRIK_H +#endif // SKELETON_MODIFICATION_3D_FABRIK_H diff --git a/scene/resources/skeleton_modification_3d_jiggle.cpp b/scene/resources/skeleton_modification_3d_jiggle.cpp index 3e36c241f7..64f26f3fda 100644 --- a/scene/resources/skeleton_modification_3d_jiggle.cpp +++ b/scene/resources/skeleton_modification_3d_jiggle.cpp @@ -58,7 +58,7 @@ bool SkeletonModification3DJiggle::_set(const StringName &p_path, const Variant } else if (what == "gravity") { set_jiggle_joint_gravity(which, p_value); } else if (what == "roll") { - set_jiggle_joint_roll(which, Math::deg2rad(real_t(p_value))); + set_jiggle_joint_roll(which, Math::deg_to_rad(real_t(p_value))); } return true; } else { @@ -98,7 +98,7 @@ bool SkeletonModification3DJiggle::_get(const StringName &p_path, Variant &r_ret } else if (what == "gravity") { r_ret = get_jiggle_joint_gravity(which); } else if (what == "roll") { - r_ret = Math::rad2deg(get_jiggle_joint_roll(which)); + r_ret = Math::rad_to_deg(get_jiggle_joint_roll(which)); } return true; } else { diff --git a/scene/resources/skeleton_modification_3d_jiggle.h b/scene/resources/skeleton_modification_3d_jiggle.h index 7ec5ed4f11..bd1ee51d93 100644 --- a/scene/resources/skeleton_modification_3d_jiggle.h +++ b/scene/resources/skeleton_modification_3d_jiggle.h @@ -28,13 +28,13 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#ifndef SKELETON_MODIFICATION_3D_JIGGLE_H +#define SKELETON_MODIFICATION_3D_JIGGLE_H + #include "core/templates/local_vector.h" #include "scene/3d/skeleton_3d.h" #include "scene/resources/skeleton_modification_3d.h" -#ifndef SKELETONMODIFICATION3DJIGGLE_H -#define SKELETONMODIFICATION3DJIGGLE_H - class SkeletonModification3DJiggle : public SkeletonModification3D { GDCLASS(SkeletonModification3DJiggle, SkeletonModification3D); @@ -135,4 +135,4 @@ public: ~SkeletonModification3DJiggle(); }; -#endif //SKELETONMODIFICATION3DJIGGLE_H +#endif // SKELETON_MODIFICATION_3D_JIGGLE_H diff --git a/scene/resources/skeleton_modification_3d_lookat.cpp b/scene/resources/skeleton_modification_3d_lookat.cpp index 3e8c1e3a77..69167cb308 100644 --- a/scene/resources/skeleton_modification_3d_lookat.cpp +++ b/scene/resources/skeleton_modification_3d_lookat.cpp @@ -39,9 +39,9 @@ bool SkeletonModification3DLookAt::_set(const StringName &p_path, const Variant set_lock_rotation_plane(p_value); } else if (p_path == "additional_rotation") { Vector3 tmp = p_value; - tmp.x = Math::deg2rad(tmp.x); - tmp.y = Math::deg2rad(tmp.y); - tmp.z = Math::deg2rad(tmp.z); + tmp.x = Math::deg_to_rad(tmp.x); + tmp.y = Math::deg_to_rad(tmp.y); + tmp.z = Math::deg_to_rad(tmp.z); set_additional_rotation(tmp); } @@ -55,9 +55,9 @@ bool SkeletonModification3DLookAt::_get(const StringName &p_path, Variant &r_ret r_ret = get_lock_rotation_plane(); } else if (p_path == "additional_rotation") { Vector3 tmp = get_additional_rotation(); - tmp.x = Math::rad2deg(tmp.x); - tmp.y = Math::rad2deg(tmp.y); - tmp.z = Math::rad2deg(tmp.z); + tmp.x = Math::rad_to_deg(tmp.x); + tmp.y = Math::rad_to_deg(tmp.y); + tmp.z = Math::rad_to_deg(tmp.z); r_ret = tmp; } diff --git a/scene/resources/skeleton_modification_3d_lookat.h b/scene/resources/skeleton_modification_3d_lookat.h index 9f5120a0fd..cea63fc34f 100644 --- a/scene/resources/skeleton_modification_3d_lookat.h +++ b/scene/resources/skeleton_modification_3d_lookat.h @@ -28,12 +28,12 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#ifndef SKELETON_MODIFICATION_3D_LOOKAT_H +#define SKELETON_MODIFICATION_3D_LOOKAT_H + #include "scene/3d/skeleton_3d.h" #include "scene/resources/skeleton_modification_3d.h" -#ifndef SKELETONMODIFICATION3DLOOKAT_H -#define SKELETONMODIFICATION3DLOOKAT_H - class SkeletonModification3DLookAt : public SkeletonModification3D { GDCLASS(SkeletonModification3DLookAt, SkeletonModification3D); @@ -86,4 +86,4 @@ public: ~SkeletonModification3DLookAt(); }; -#endif //SKELETONMODIFICATION3DLOOKAT_H +#endif // SKELETON_MODIFICATION_3D_LOOKAT_H diff --git a/scene/resources/skeleton_modification_3d_stackholder.h b/scene/resources/skeleton_modification_3d_stackholder.h index 5780d7d43f..2071de5457 100644 --- a/scene/resources/skeleton_modification_3d_stackholder.h +++ b/scene/resources/skeleton_modification_3d_stackholder.h @@ -28,12 +28,12 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#ifndef SKELETON_MODIFICATION_3D_STACKHOLDER_H +#define SKELETON_MODIFICATION_3D_STACKHOLDER_H + #include "scene/3d/skeleton_3d.h" #include "scene/resources/skeleton_modification_3d.h" -#ifndef SKELETONMODIFICATION3DSTACKHOLDER_H -#define SKELETONMODIFICATION3DSTACKHOLDER_H - class SkeletonModification3DStackHolder : public SkeletonModification3D { GDCLASS(SkeletonModification3DStackHolder, SkeletonModification3D); @@ -56,4 +56,4 @@ public: ~SkeletonModification3DStackHolder(); }; -#endif //SKELETONMODIFICATION3DSTACKHOLDER_H +#endif // SKELETON_MODIFICATION_3D_STACKHOLDER_H diff --git a/scene/resources/skeleton_modification_3d_twoboneik.cpp b/scene/resources/skeleton_modification_3d_twoboneik.cpp index acc5ff716c..366fcc30b7 100644 --- a/scene/resources/skeleton_modification_3d_twoboneik.cpp +++ b/scene/resources/skeleton_modification_3d_twoboneik.cpp @@ -54,13 +54,13 @@ bool SkeletonModification3DTwoBoneIK::_set(const StringName &p_path, const Varia } else if (path == "joint_one/bone_idx") { set_joint_one_bone_idx(p_value); } else if (path == "joint_one/roll") { - set_joint_one_roll(Math::deg2rad(real_t(p_value))); + set_joint_one_roll(Math::deg_to_rad(real_t(p_value))); } else if (path == "joint_two/bone_name") { set_joint_two_bone_name(p_value); } else if (path == "joint_two/bone_idx") { set_joint_two_bone_idx(p_value); } else if (path == "joint_two/roll") { - set_joint_two_roll(Math::deg2rad(real_t(p_value))); + set_joint_two_roll(Math::deg_to_rad(real_t(p_value))); } return true; @@ -88,13 +88,13 @@ bool SkeletonModification3DTwoBoneIK::_get(const StringName &p_path, Variant &r_ } else if (path == "joint_one/bone_idx") { r_ret = get_joint_one_bone_idx(); } else if (path == "joint_one/roll") { - r_ret = Math::rad2deg(get_joint_one_roll()); + r_ret = Math::rad_to_deg(get_joint_one_roll()); } else if (path == "joint_two/bone_name") { r_ret = get_joint_two_bone_name(); } else if (path == "joint_two/bone_idx") { r_ret = get_joint_two_bone_idx(); } else if (path == "joint_two/roll") { - r_ret = Math::rad2deg(get_joint_two_roll()); + r_ret = Math::rad_to_deg(get_joint_two_roll()); } return true; diff --git a/scene/resources/skeleton_modification_3d_twoboneik.h b/scene/resources/skeleton_modification_3d_twoboneik.h index a4ddc6cee7..7bd7c8291d 100644 --- a/scene/resources/skeleton_modification_3d_twoboneik.h +++ b/scene/resources/skeleton_modification_3d_twoboneik.h @@ -28,12 +28,12 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#ifndef SKELETON_MODIFICATION_3D_TWOBONEIK_H +#define SKELETON_MODIFICATION_3D_TWOBONEIK_H + #include "scene/3d/skeleton_3d.h" #include "scene/resources/skeleton_modification_3d.h" -#ifndef SKELETONMODIFICATION3DTWOBONEIK_H -#define SKELETONMODIFICATION3DTWOBONEIK_H - class SkeletonModification3DTwoBoneIK : public SkeletonModification3D { GDCLASS(SkeletonModification3DTwoBoneIK, SkeletonModification3D); @@ -115,4 +115,4 @@ public: ~SkeletonModification3DTwoBoneIK(); }; -#endif //SKELETONMODIFICATION3DTWOBONEIK_H +#endif // SKELETON_MODIFICATION_3D_TWOBONEIK_H diff --git a/scene/resources/skeleton_modification_stack_2d.cpp b/scene/resources/skeleton_modification_stack_2d.cpp index 38ec19828f..56234a8a14 100644 --- a/scene/resources/skeleton_modification_stack_2d.cpp +++ b/scene/resources/skeleton_modification_stack_2d.cpp @@ -138,7 +138,7 @@ void SkeletonModificationStack2D::set_editor_gizmos_dirty(bool p_dirty) { if (!editor_gizmo_dirty && p_dirty) { editor_gizmo_dirty = p_dirty; if (skeleton) { - skeleton->update(); + skeleton->queue_redraw(); } } else { editor_gizmo_dirty = p_dirty; @@ -182,11 +182,11 @@ void SkeletonModificationStack2D::delete_modification(int p_mod_idx) { void SkeletonModificationStack2D::set_modification(int p_mod_idx, Ref<SkeletonModification2D> p_mod) { ERR_FAIL_INDEX(p_mod_idx, modifications.size()); - if (p_mod == nullptr) { - modifications.insert(p_mod_idx, nullptr); + if (p_mod.is_null()) { + modifications.write[p_mod_idx] = Ref<SkeletonModification2D>(); } else { + modifications.write[p_mod_idx] = p_mod; p_mod->_setup_modification(this); - modifications.insert(p_mod_idx, p_mod); } #ifdef TOOLS_ENABLED diff --git a/scene/resources/skeleton_modification_stack_2d.h b/scene/resources/skeleton_modification_stack_2d.h index 7b5f8e0322..ceffd71368 100644 --- a/scene/resources/skeleton_modification_stack_2d.h +++ b/scene/resources/skeleton_modification_stack_2d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SKELETONMODIFICATIONSTACK2D_H -#define SKELETONMODIFICATIONSTACK2D_H +#ifndef SKELETON_MODIFICATION_STACK_2D_H +#define SKELETON_MODIFICATION_STACK_2D_H #include "scene/2d/skeleton_2d.h" #include "scene/resources/skeleton_modification_2d.h" @@ -96,4 +96,4 @@ public: SkeletonModificationStack2D(); }; -#endif // SKELETONMODIFICATION2D_H +#endif // SKELETON_MODIFICATION_STACK_2D_H diff --git a/scene/resources/skeleton_modification_stack_3d.h b/scene/resources/skeleton_modification_stack_3d.h index 2c17fba2c5..4ff1bd1e33 100644 --- a/scene/resources/skeleton_modification_stack_3d.h +++ b/scene/resources/skeleton_modification_stack_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SKELETONMODIFICATIONSTACK3D_H -#define SKELETONMODIFICATIONSTACK3D_H +#ifndef SKELETON_MODIFICATION_STACK_3D_H +#define SKELETON_MODIFICATION_STACK_3D_H #include "core/templates/local_vector.h" #include "scene/3d/skeleton_3d.h" @@ -88,4 +88,4 @@ public: SkeletonModificationStack3D(); }; -#endif // SKELETONMODIFICATIONSTACK3D_H +#endif // SKELETON_MODIFICATION_STACK_3D_H diff --git a/scene/resources/skeleton_profile.cpp b/scene/resources/skeleton_profile.cpp index 05d48f9545..61a0350440 100644 --- a/scene/resources/skeleton_profile.cpp +++ b/scene/resources/skeleton_profile.cpp @@ -34,7 +34,7 @@ bool SkeletonProfile::_set(const StringName &p_path, const Variant &p_value) { ERR_FAIL_COND_V(is_read_only, false); String path = p_path; - if (path.begins_with("group/")) { + if (path.begins_with("groups/")) { int which = path.get_slicec('/', 1).to_int(); String what = path.get_slicec('/', 2); ERR_FAIL_INDEX_V(which, groups.size(), false); @@ -43,23 +43,35 @@ bool SkeletonProfile::_set(const StringName &p_path, const Variant &p_value) { set_group_name(which, p_value); } else if (what == "texture") { set_texture(which, p_value); + } else { + return false; } - return true; } - if (path.begins_with("bone/")) { + if (path.begins_with("bones/")) { int which = path.get_slicec('/', 1).to_int(); String what = path.get_slicec('/', 2); ERR_FAIL_INDEX_V(which, bones.size(), false); if (what == "bone_name") { set_bone_name(which, p_value); + } else if (what == "bone_parent") { + set_bone_parent(which, p_value); + } else if (what == "tail_direction") { + set_tail_direction(which, static_cast<TailDirection>((int)p_value)); + } else if (what == "bone_tail") { + set_bone_tail(which, p_value); + } else if (what == "reference_pose") { + set_reference_pose(which, p_value); } else if (what == "handle_offset") { set_handle_offset(which, p_value); } else if (what == "group") { set_group(which, p_value); + } else if (what == "require") { + set_require(which, p_value); + } else { + return false; } - return true; } return true; } @@ -67,7 +79,7 @@ bool SkeletonProfile::_set(const StringName &p_path, const Variant &p_value) { bool SkeletonProfile::_get(const StringName &p_path, Variant &r_ret) const { String path = p_path; - if (path.begins_with("group/")) { + if (path.begins_with("groups/")) { int which = path.get_slicec('/', 1).to_int(); String what = path.get_slicec('/', 2); ERR_FAIL_INDEX_V(which, groups.size(), false); @@ -76,34 +88,61 @@ bool SkeletonProfile::_get(const StringName &p_path, Variant &r_ret) const { r_ret = get_group_name(which); } else if (what == "texture") { r_ret = get_texture(which); + } else { + return false; } - return true; } - if (path.begins_with("bone/")) { + if (path.begins_with("bones/")) { int which = path.get_slicec('/', 1).to_int(); String what = path.get_slicec('/', 2); ERR_FAIL_INDEX_V(which, bones.size(), false); if (what == "bone_name") { r_ret = get_bone_name(which); + } else if (what == "bone_parent") { + r_ret = get_bone_parent(which); + } else if (what == "tail_direction") { + r_ret = get_tail_direction(which); + } else if (what == "bone_tail") { + r_ret = get_bone_tail(which); + } else if (what == "reference_pose") { + r_ret = get_reference_pose(which); } else if (what == "handle_offset") { r_ret = get_handle_offset(which); } else if (what == "group") { r_ret = get_group(which); + } else if (what == "require") { + r_ret = is_require(which); + } else { + return false; } - return true; } return true; } -void SkeletonProfile::_validate_property(PropertyInfo &property) const { +void SkeletonProfile::_validate_property(PropertyInfo &p_property) const { if (is_read_only) { - if (property.name == ("group_size") || property.name == ("bone_size")) { - property.usage = PROPERTY_USAGE_NO_EDITOR; + if (p_property.name == ("group_size") || p_property.name == ("bone_size") || p_property.name == ("root_bone") || p_property.name == ("scale_base_bone")) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; return; } } + + if (p_property.name == ("root_bone") || p_property.name == ("scale_base_bone")) { + String hint = ""; + for (int i = 0; i < bones.size(); i++) { + hint += i == 0 ? String(bones[i].bone_name) : "," + String(bones[i].bone_name); + } + p_property.hint_string = hint; + } + + PackedStringArray split = p_property.name.split("/"); + if (split.size() == 3 && split[0] == "bones") { + if (split[2] == "bone_tail" && get_tail_direction(split[1].to_int()) != TAIL_DIRECTION_SPECIFIC_CHILD) { + p_property.usage = PROPERTY_USAGE_NONE; + } + } } void SkeletonProfile::_get_property_list(List<PropertyInfo> *p_list) const { @@ -112,7 +151,7 @@ void SkeletonProfile::_get_property_list(List<PropertyInfo> *p_list) const { } String group_names = ""; for (int i = 0; i < groups.size(); i++) { - String path = "group/" + itos(i) + "/"; + String path = "groups/" + itos(i) + "/"; p_list->push_back(PropertyInfo(Variant::STRING_NAME, path + "group_name")); p_list->push_back(PropertyInfo(Variant::OBJECT, path + "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D")); if (i > 0) { @@ -121,11 +160,42 @@ void SkeletonProfile::_get_property_list(List<PropertyInfo> *p_list) const { group_names = group_names + groups[i].group_name; } for (int i = 0; i < bones.size(); i++) { - String path = "bone/" + itos(i) + "/"; + String path = "bones/" + itos(i) + "/"; p_list->push_back(PropertyInfo(Variant::STRING_NAME, path + "bone_name")); + p_list->push_back(PropertyInfo(Variant::STRING_NAME, path + "bone_parent")); + p_list->push_back(PropertyInfo(Variant::INT, path + "tail_direction", PROPERTY_HINT_ENUM, "AverageChildren,SpecificChild,End")); + p_list->push_back(PropertyInfo(Variant::STRING_NAME, path + "bone_tail")); + p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, path + "reference_pose")); p_list->push_back(PropertyInfo(Variant::VECTOR2, path + "handle_offset")); p_list->push_back(PropertyInfo(Variant::STRING_NAME, path + "group", PROPERTY_HINT_ENUM, group_names)); + p_list->push_back(PropertyInfo(Variant::BOOL, path + "require")); + } + + for (PropertyInfo &E : *p_list) { + _validate_property(E); + } +} + +StringName SkeletonProfile::get_root_bone() { + return root_bone; +} + +void SkeletonProfile::set_root_bone(StringName p_bone_name) { + if (is_read_only) { + return; + } + root_bone = p_bone_name; +} + +StringName SkeletonProfile::get_scale_base_bone() { + return scale_base_bone; +} + +void SkeletonProfile::set_scale_base_bone(StringName p_bone_name) { + if (is_read_only) { + return; } + scale_base_bone = p_bone_name; } int SkeletonProfile::get_group_size() { @@ -184,6 +254,18 @@ void SkeletonProfile::set_bone_size(int p_size) { notify_property_list_changed(); } +int SkeletonProfile::find_bone(StringName p_bone_name) const { + if (p_bone_name == StringName()) { + return -1; + } + for (int i = 0; i < bones.size(); i++) { + if (bones[i].bone_name == p_bone_name) { + return i; + } + } + return -1; +} + StringName SkeletonProfile::get_bone_name(int p_bone_idx) const { ERR_FAIL_INDEX_V(p_bone_idx, bones.size(), StringName()); return bones[p_bone_idx].bone_name; @@ -198,6 +280,63 @@ void SkeletonProfile::set_bone_name(int p_bone_idx, const StringName p_bone_name emit_signal("profile_updated"); } +StringName SkeletonProfile::get_bone_parent(int p_bone_idx) const { + ERR_FAIL_INDEX_V(p_bone_idx, bones.size(), StringName()); + return bones[p_bone_idx].bone_parent; +} + +void SkeletonProfile::set_bone_parent(int p_bone_idx, const StringName p_bone_parent) { + if (is_read_only) { + return; + } + ERR_FAIL_INDEX(p_bone_idx, bones.size()); + bones.write[p_bone_idx].bone_parent = p_bone_parent; + emit_signal("profile_updated"); +} + +SkeletonProfile::TailDirection SkeletonProfile::get_tail_direction(int p_bone_idx) const { + ERR_FAIL_INDEX_V(p_bone_idx, bones.size(), TAIL_DIRECTION_AVERAGE_CHILDREN); + return bones[p_bone_idx].tail_direction; +} + +void SkeletonProfile::set_tail_direction(int p_bone_idx, const TailDirection p_tail_direction) { + if (is_read_only) { + return; + } + ERR_FAIL_INDEX(p_bone_idx, bones.size()); + bones.write[p_bone_idx].tail_direction = p_tail_direction; + emit_signal("profile_updated"); + notify_property_list_changed(); +} + +StringName SkeletonProfile::get_bone_tail(int p_bone_idx) const { + ERR_FAIL_INDEX_V(p_bone_idx, bones.size(), StringName()); + return bones[p_bone_idx].bone_tail; +} + +void SkeletonProfile::set_bone_tail(int p_bone_idx, const StringName p_bone_tail) { + if (is_read_only) { + return; + } + ERR_FAIL_INDEX(p_bone_idx, bones.size()); + bones.write[p_bone_idx].bone_tail = p_bone_tail; + emit_signal("profile_updated"); +} + +Transform3D SkeletonProfile::get_reference_pose(int p_bone_idx) const { + ERR_FAIL_INDEX_V(p_bone_idx, bones.size(), Transform3D()); + return bones[p_bone_idx].reference_pose; +} + +void SkeletonProfile::set_reference_pose(int p_bone_idx, const Transform3D p_reference_pose) { + if (is_read_only) { + return; + } + ERR_FAIL_INDEX(p_bone_idx, bones.size()); + bones.write[p_bone_idx].reference_pose = p_reference_pose; + emit_signal("profile_updated"); +} + Vector2 SkeletonProfile::get_handle_offset(int p_bone_idx) const { ERR_FAIL_INDEX_V(p_bone_idx, bones.size(), Vector2()); return bones[p_bone_idx].handle_offset; @@ -226,6 +365,20 @@ void SkeletonProfile::set_group(int p_bone_idx, const StringName p_group) { emit_signal("profile_updated"); } +bool SkeletonProfile::is_require(int p_bone_idx) const { + ERR_FAIL_INDEX_V(p_bone_idx, bones.size(), false); + return bones[p_bone_idx].require; +} + +void SkeletonProfile::set_require(int p_bone_idx, const bool p_require) { + if (is_read_only) { + return; + } + ERR_FAIL_INDEX(p_bone_idx, bones.size()); + bones.write[p_bone_idx].require = p_require; + emit_signal("profile_updated"); +} + bool SkeletonProfile::has_bone(StringName p_bone_name) { bool is_found = false; for (int i = 0; i < bones.size(); i++) { @@ -238,6 +391,12 @@ bool SkeletonProfile::has_bone(StringName p_bone_name) { } void SkeletonProfile::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_root_bone", "bone_name"), &SkeletonProfile::set_root_bone); + ClassDB::bind_method(D_METHOD("get_root_bone"), &SkeletonProfile::get_root_bone); + + ClassDB::bind_method(D_METHOD("set_scale_base_bone", "bone_name"), &SkeletonProfile::set_scale_base_bone); + ClassDB::bind_method(D_METHOD("get_scale_base_bone"), &SkeletonProfile::get_scale_base_bone); + ClassDB::bind_method(D_METHOD("set_group_size", "size"), &SkeletonProfile::set_group_size); ClassDB::bind_method(D_METHOD("get_group_size"), &SkeletonProfile::get_group_size); @@ -250,19 +409,40 @@ void SkeletonProfile::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bone_size", "size"), &SkeletonProfile::set_bone_size); ClassDB::bind_method(D_METHOD("get_bone_size"), &SkeletonProfile::get_bone_size); + ClassDB::bind_method(D_METHOD("find_bone", "bone_name"), &SkeletonProfile::find_bone); + ClassDB::bind_method(D_METHOD("get_bone_name", "bone_idx"), &SkeletonProfile::get_bone_name); ClassDB::bind_method(D_METHOD("set_bone_name", "bone_idx", "bone_name"), &SkeletonProfile::set_bone_name); + ClassDB::bind_method(D_METHOD("get_bone_parent", "bone_idx"), &SkeletonProfile::get_bone_parent); + ClassDB::bind_method(D_METHOD("set_bone_parent", "bone_idx", "bone_parent"), &SkeletonProfile::set_bone_parent); + + ClassDB::bind_method(D_METHOD("get_tail_direction", "bone_idx"), &SkeletonProfile::get_tail_direction); + ClassDB::bind_method(D_METHOD("set_tail_direction", "bone_idx", "tail_direction"), &SkeletonProfile::set_tail_direction); + + ClassDB::bind_method(D_METHOD("get_bone_tail", "bone_idx"), &SkeletonProfile::get_bone_tail); + ClassDB::bind_method(D_METHOD("set_bone_tail", "bone_idx", "bone_tail"), &SkeletonProfile::set_bone_tail); + + ClassDB::bind_method(D_METHOD("get_reference_pose", "bone_idx"), &SkeletonProfile::get_reference_pose); + ClassDB::bind_method(D_METHOD("set_reference_pose", "bone_idx", "bone_name"), &SkeletonProfile::set_reference_pose); + ClassDB::bind_method(D_METHOD("get_handle_offset", "bone_idx"), &SkeletonProfile::get_handle_offset); ClassDB::bind_method(D_METHOD("set_handle_offset", "bone_idx", "handle_offset"), &SkeletonProfile::set_handle_offset); ClassDB::bind_method(D_METHOD("get_group", "bone_idx"), &SkeletonProfile::get_group); ClassDB::bind_method(D_METHOD("set_group", "bone_idx", "group"), &SkeletonProfile::set_group); - ADD_PROPERTY(PropertyInfo(Variant::INT, "group_size", PROPERTY_HINT_RANGE, "0,100,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ARRAY, "Groups,group/"), "set_group_size", "get_group_size"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_size", PROPERTY_HINT_RANGE, "0,100,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ARRAY, "Bones,bone/"), "set_bone_size", "get_bone_size"); + ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "root_bone", PROPERTY_HINT_ENUM_SUGGESTION, ""), "set_root_bone", "get_root_bone"); + ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "scale_base_bone", PROPERTY_HINT_ENUM_SUGGESTION, ""), "set_scale_base_bone", "get_scale_base_bone"); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "group_size", PROPERTY_HINT_RANGE, "0,100,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ARRAY, "Groups,groups/"), "set_group_size", "get_group_size"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_size", PROPERTY_HINT_RANGE, "0,100,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ARRAY, "Bones,bones/"), "set_bone_size", "get_bone_size"); ADD_SIGNAL(MethodInfo("profile_updated")); + + BIND_ENUM_CONSTANT(TAIL_DIRECTION_AVERAGE_CHILDREN); + BIND_ENUM_CONSTANT(TAIL_DIRECTION_SPECIFIC_CHILD); + BIND_ENUM_CONSTANT(TAIL_DIRECTION_END); } SkeletonProfile::SkeletonProfile() { @@ -274,6 +454,9 @@ SkeletonProfile::~SkeletonProfile() { SkeletonProfileHumanoid::SkeletonProfileHumanoid() { is_read_only = true; + root_bone = "Root"; + scale_base_bone = "Hips"; + groups.resize(4); groups.write[0].group_name = "Body"; @@ -284,226 +467,364 @@ SkeletonProfileHumanoid::SkeletonProfileHumanoid() { bones.resize(56); bones.write[0].bone_name = "Root"; + bones.write[0].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0); bones.write[0].handle_offset = Vector2(0.5, 0.91); bones.write[0].group = "Body"; bones.write[1].bone_name = "Hips"; + bones.write[1].bone_parent = "Root"; + bones.write[1].tail_direction = TAIL_DIRECTION_SPECIFIC_CHILD; + bones.write[1].bone_tail = "Spine"; + bones.write[1].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.75, 0); bones.write[1].handle_offset = Vector2(0.5, 0.5); bones.write[1].group = "Body"; + bones.write[1].require = true; bones.write[2].bone_name = "Spine"; + bones.write[2].bone_parent = "Hips"; + bones.write[2].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 0); bones.write[2].handle_offset = Vector2(0.5, 0.43); bones.write[2].group = "Body"; + bones.write[2].require = true; bones.write[3].bone_name = "Chest"; + bones.write[3].bone_parent = "Spine"; + bones.write[3].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 0); bones.write[3].handle_offset = Vector2(0.5, 0.36); bones.write[3].group = "Body"; bones.write[4].bone_name = "UpperChest"; + bones.write[4].bone_parent = "Chest"; + bones.write[4].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 0); bones.write[4].handle_offset = Vector2(0.5, 0.29); bones.write[4].group = "Body"; bones.write[5].bone_name = "Neck"; + bones.write[5].bone_parent = "UpperChest"; + bones.write[5].tail_direction = TAIL_DIRECTION_SPECIFIC_CHILD; + bones.write[5].bone_tail = "Head"; + bones.write[5].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 0); bones.write[5].handle_offset = Vector2(0.5, 0.23); bones.write[5].group = "Body"; + bones.write[5].require = false; bones.write[6].bone_name = "Head"; + bones.write[6].bone_parent = "Neck"; + bones.write[6].tail_direction = TAIL_DIRECTION_END; + bones.write[6].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 0); bones.write[6].handle_offset = Vector2(0.5, 0.18); bones.write[6].group = "Body"; + bones.write[6].require = true; bones.write[7].bone_name = "LeftEye"; + bones.write[7].bone_parent = "Head"; + bones.write[7].reference_pose = Transform3D(1, 0, 0, 0, 0, -1, 0, 1, 0, 0.05, 0.15, 0); bones.write[7].handle_offset = Vector2(0.6, 0.46); bones.write[7].group = "Face"; bones.write[8].bone_name = "RightEye"; + bones.write[8].bone_parent = "Head"; + bones.write[8].reference_pose = Transform3D(1, 0, 0, 0, 0, -1, 0, 1, 0, -0.05, 0.15, 0); bones.write[8].handle_offset = Vector2(0.37, 0.46); bones.write[8].group = "Face"; bones.write[9].bone_name = "Jaw"; + bones.write[9].bone_parent = "Head"; + bones.write[9].reference_pose = Transform3D(-1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0.05, 0.05); bones.write[9].handle_offset = Vector2(0.46, 0.75); bones.write[9].group = "Face"; bones.write[10].bone_name = "LeftShoulder"; + bones.write[10].bone_parent = "UpperChest"; + bones.write[10].reference_pose = Transform3D(0, 1, 0, 0, 0, 1, 1, 0, 0, 0.05, 0.1, 0); bones.write[10].handle_offset = Vector2(0.55, 0.235); bones.write[10].group = "Body"; + bones.write[10].require = true; bones.write[11].bone_name = "LeftUpperArm"; + bones.write[11].bone_parent = "LeftShoulder"; + bones.write[11].reference_pose = Transform3D(-1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0.05, 0); bones.write[11].handle_offset = Vector2(0.6, 0.24); bones.write[11].group = "Body"; + bones.write[11].require = true; bones.write[12].bone_name = "LeftLowerArm"; + bones.write[12].bone_parent = "LeftUpperArm"; + bones.write[12].reference_pose = Transform3D(0, 0, -1, 0, 1, 0, 1, 0, 0, 0, 0.25, 0); bones.write[12].handle_offset = Vector2(0.7, 0.24); bones.write[12].group = "Body"; + bones.write[12].require = true; bones.write[13].bone_name = "LeftHand"; + bones.write[13].bone_parent = "LeftLowerArm"; + bones.write[13].tail_direction = TAIL_DIRECTION_SPECIFIC_CHILD; + bones.write[13].bone_tail = "LeftMiddleProximal"; + bones.write[13].reference_pose = Transform3D(0, 0, 1, 0, 1, 0, -1, 0, 0, 0, 0.25, 0); bones.write[13].handle_offset = Vector2(0.82, 0.235); bones.write[13].group = "Body"; + bones.write[13].require = true; - bones.write[14].bone_name = "LeftThumbProximal"; + bones.write[14].bone_name = "LeftThumbMetacarpal"; + bones.write[14].bone_parent = "LeftHand"; + bones.write[14].reference_pose = Transform3D(0, -0.577, 0.816, 0, 0.816, 0.577, -1, 0, 0, -0.025, 0.025, 0); bones.write[14].handle_offset = Vector2(0.4, 0.8); bones.write[14].group = "LeftHand"; - bones.write[15].bone_name = "LeftThumbIntermediate"; + bones.write[15].bone_name = "LeftThumbProximal"; + bones.write[15].bone_parent = "LeftThumbMetacarpal"; + bones.write[15].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.043, 0); bones.write[15].handle_offset = Vector2(0.3, 0.69); bones.write[15].group = "LeftHand"; bones.write[16].bone_name = "LeftThumbDistal"; + bones.write[16].bone_parent = "LeftThumbProximal"; + bones.write[16].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.043, 0); bones.write[16].handle_offset = Vector2(0.23, 0.555); bones.write[16].group = "LeftHand"; bones.write[17].bone_name = "LeftIndexProximal"; + bones.write[17].bone_parent = "LeftHand"; + bones.write[17].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.025, 0.075, 0); bones.write[17].handle_offset = Vector2(0.413, 0.52); bones.write[17].group = "LeftHand"; bones.write[18].bone_name = "LeftIndexIntermediate"; + bones.write[18].bone_parent = "LeftIndexProximal"; + bones.write[18].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.05, 0); bones.write[18].handle_offset = Vector2(0.403, 0.36); bones.write[18].group = "LeftHand"; bones.write[19].bone_name = "LeftIndexDistal"; + bones.write[19].bone_parent = "LeftIndexIntermediate"; + bones.write[19].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.025, 0); bones.write[19].handle_offset = Vector2(0.403, 0.255); bones.write[19].group = "LeftHand"; bones.write[20].bone_name = "LeftMiddleProximal"; + bones.write[20].bone_parent = "LeftHand"; + bones.write[20].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.075, 0); bones.write[20].handle_offset = Vector2(0.5, 0.51); bones.write[20].group = "LeftHand"; bones.write[21].bone_name = "LeftMiddleIntermediate"; + bones.write[21].bone_parent = "LeftMiddleProximal"; + bones.write[21].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.075, 0); bones.write[21].handle_offset = Vector2(0.5, 0.345); bones.write[21].group = "LeftHand"; bones.write[22].bone_name = "LeftMiddleDistal"; + bones.write[22].bone_parent = "LeftMiddleIntermediate"; + bones.write[22].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.025, 0); bones.write[22].handle_offset = Vector2(0.5, 0.22); bones.write[22].group = "LeftHand"; bones.write[23].bone_name = "LeftRingProximal"; + bones.write[23].bone_parent = "LeftHand"; + bones.write[23].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.025, 0.075, 0); bones.write[23].handle_offset = Vector2(0.586, 0.52); bones.write[23].group = "LeftHand"; bones.write[24].bone_name = "LeftRingIntermediate"; + bones.write[24].bone_parent = "LeftRingProximal"; + bones.write[24].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.05, 0); bones.write[24].handle_offset = Vector2(0.59, 0.36); bones.write[24].group = "LeftHand"; bones.write[25].bone_name = "LeftRingDistal"; + bones.write[25].bone_parent = "LeftRingIntermediate"; + bones.write[25].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.025, 0); bones.write[25].handle_offset = Vector2(0.591, 0.25); bones.write[25].group = "LeftHand"; bones.write[26].bone_name = "LeftLittleProximal"; + bones.write[26].bone_parent = "LeftHand"; + bones.write[26].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.05, 0.05, 0); bones.write[26].handle_offset = Vector2(0.663, 0.543); bones.write[26].group = "LeftHand"; bones.write[27].bone_name = "LeftLittleIntermediate"; + bones.write[27].bone_parent = "LeftLittleProximal"; + bones.write[27].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.05, 0); bones.write[27].handle_offset = Vector2(0.672, 0.415); bones.write[27].group = "LeftHand"; bones.write[28].bone_name = "LeftLittleDistal"; + bones.write[28].bone_parent = "LeftLittleIntermediate"; + bones.write[28].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.025, 0); bones.write[28].handle_offset = Vector2(0.672, 0.32); bones.write[28].group = "LeftHand"; bones.write[29].bone_name = "RightShoulder"; + bones.write[29].bone_parent = "UpperChest"; + bones.write[29].reference_pose = Transform3D(0, -1, 0, 0, 0, 1, -1, 0, 0, -0.05, 0.1, 0); bones.write[29].handle_offset = Vector2(0.45, 0.235); bones.write[29].group = "Body"; + bones.write[29].require = true; bones.write[30].bone_name = "RightUpperArm"; + bones.write[30].bone_parent = "RightShoulder"; + bones.write[30].reference_pose = Transform3D(-1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0.05, 0); bones.write[30].handle_offset = Vector2(0.4, 0.24); bones.write[30].group = "Body"; + bones.write[30].require = true; bones.write[31].bone_name = "RightLowerArm"; + bones.write[31].bone_parent = "RightUpperArm"; + bones.write[31].reference_pose = Transform3D(0, 0, 1, 0, 1, 0, -1, 0, 0, 0, 0.25, 0); bones.write[31].handle_offset = Vector2(0.3, 0.24); bones.write[31].group = "Body"; + bones.write[31].require = true; bones.write[32].bone_name = "RightHand"; + bones.write[32].bone_parent = "RightLowerArm"; + bones.write[32].tail_direction = TAIL_DIRECTION_SPECIFIC_CHILD; + bones.write[32].bone_tail = "RightMiddleProximal"; + bones.write[32].reference_pose = Transform3D(0, 0, -1, 0, 1, 0, 1, 0, 0, 0, 0.25, 0); bones.write[32].handle_offset = Vector2(0.18, 0.235); bones.write[32].group = "Body"; + bones.write[32].require = true; - bones.write[33].bone_name = "RightThumbProximal"; + bones.write[33].bone_name = "RightThumbMetacarpal"; + bones.write[33].bone_parent = "RightHand"; + bones.write[33].reference_pose = Transform3D(0, 0.577, -0.816, 0, 0.816, 0.577, 1, 0, 0, 0.025, 0.025, 0); bones.write[33].handle_offset = Vector2(0.6, 0.8); bones.write[33].group = "RightHand"; - bones.write[34].bone_name = "RightThumbIntermediate"; + bones.write[34].bone_name = "RightThumbProximal"; + bones.write[34].bone_parent = "RightThumbMetacarpal"; + bones.write[34].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.043, 0); bones.write[34].handle_offset = Vector2(0.7, 0.69); bones.write[34].group = "RightHand"; bones.write[35].bone_name = "RightThumbDistal"; + bones.write[35].bone_parent = "RightThumbProximal"; + bones.write[35].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.043, 0); bones.write[35].handle_offset = Vector2(0.77, 0.555); bones.write[35].group = "RightHand"; bones.write[36].bone_name = "RightIndexProximal"; + bones.write[36].bone_parent = "RightHand"; + bones.write[36].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.025, 0.075, 0); bones.write[36].handle_offset = Vector2(0.587, 0.52); bones.write[36].group = "RightHand"; bones.write[37].bone_name = "RightIndexIntermediate"; + bones.write[37].bone_parent = "RightIndexProximal"; + bones.write[37].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.05, 0); bones.write[37].handle_offset = Vector2(0.597, 0.36); bones.write[37].group = "RightHand"; bones.write[38].bone_name = "RightIndexDistal"; + bones.write[38].bone_parent = "RightIndexIntermediate"; + bones.write[38].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.025, 0); bones.write[38].handle_offset = Vector2(0.597, 0.255); bones.write[38].group = "RightHand"; bones.write[39].bone_name = "RightMiddleProximal"; + bones.write[39].bone_parent = "RightHand"; + bones.write[39].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.075, 0); bones.write[39].handle_offset = Vector2(0.5, 0.51); bones.write[39].group = "RightHand"; bones.write[40].bone_name = "RightMiddleIntermediate"; + bones.write[40].bone_parent = "RightMiddleProximal"; + bones.write[40].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.075, 0); bones.write[40].handle_offset = Vector2(0.5, 0.345); bones.write[40].group = "RightHand"; bones.write[41].bone_name = "RightMiddleDistal"; + bones.write[41].bone_parent = "RightMiddleIntermediate"; + bones.write[41].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.025, 0); bones.write[41].handle_offset = Vector2(0.5, 0.22); bones.write[41].group = "RightHand"; bones.write[42].bone_name = "RightRingProximal"; + bones.write[42].bone_parent = "RightHand"; + bones.write[42].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.025, 0.075, 0); bones.write[42].handle_offset = Vector2(0.414, 0.52); bones.write[42].group = "RightHand"; bones.write[43].bone_name = "RightRingIntermediate"; + bones.write[43].bone_parent = "RightRingProximal"; + bones.write[43].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.05, 0); bones.write[43].handle_offset = Vector2(0.41, 0.36); bones.write[43].group = "RightHand"; bones.write[44].bone_name = "RightRingDistal"; + bones.write[44].bone_parent = "RightRingIntermediate"; + bones.write[44].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.025, 0); bones.write[44].handle_offset = Vector2(0.409, 0.25); bones.write[44].group = "RightHand"; bones.write[45].bone_name = "RightLittleProximal"; + bones.write[45].bone_parent = "RightHand"; + bones.write[45].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.05, 0.05, 0); bones.write[45].handle_offset = Vector2(0.337, 0.543); bones.write[45].group = "RightHand"; bones.write[46].bone_name = "RightLittleIntermediate"; + bones.write[46].bone_parent = "RightLittleProximal"; + bones.write[46].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.05, 0); bones.write[46].handle_offset = Vector2(0.328, 0.415); bones.write[46].group = "RightHand"; bones.write[47].bone_name = "RightLittleDistal"; + bones.write[47].bone_parent = "RightLittleIntermediate"; + bones.write[47].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.025, 0); bones.write[47].handle_offset = Vector2(0.328, 0.32); bones.write[47].group = "RightHand"; bones.write[48].bone_name = "LeftUpperLeg"; + bones.write[48].bone_parent = "Hips"; + bones.write[48].reference_pose = Transform3D(-1, 0, 0, 0, -1, 0, 0, 0, 1, 0.1, 0, 0); bones.write[48].handle_offset = Vector2(0.549, 0.49); bones.write[48].group = "Body"; + bones.write[48].require = true; bones.write[49].bone_name = "LeftLowerLeg"; + bones.write[49].bone_parent = "LeftUpperLeg"; + bones.write[49].reference_pose = Transform3D(-1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0.375, 0); bones.write[49].handle_offset = Vector2(0.548, 0.683); bones.write[49].group = "Body"; + bones.write[49].require = true; bones.write[50].bone_name = "LeftFoot"; + bones.write[50].bone_parent = "LeftLowerLeg"; + bones.write[50].reference_pose = Transform3D(-1, 0, 0, 0, 0, -1, 0, -1, 0, 0, 0.375, 0); bones.write[50].handle_offset = Vector2(0.545, 0.9); bones.write[50].group = "Body"; + bones.write[50].require = true; bones.write[51].bone_name = "LeftToes"; + bones.write[51].bone_parent = "LeftFoot"; + bones.write[51].reference_pose = Transform3D(-1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0.15, 0); bones.write[51].handle_offset = Vector2(0.545, 0.95); bones.write[51].group = "Body"; bones.write[52].bone_name = "RightUpperLeg"; + bones.write[52].bone_parent = "Hips"; + bones.write[52].reference_pose = Transform3D(-1, 0, 0, 0, -1, 0, 0, 0, 1, -0.1, 0, 0); bones.write[52].handle_offset = Vector2(0.451, 0.49); bones.write[52].group = "Body"; + bones.write[52].require = true; bones.write[53].bone_name = "RightLowerLeg"; + bones.write[53].bone_parent = "RightUpperLeg"; + bones.write[53].reference_pose = Transform3D(-1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0.375, 0); bones.write[53].handle_offset = Vector2(0.452, 0.683); bones.write[53].group = "Body"; + bones.write[53].require = true; bones.write[54].bone_name = "RightFoot"; + bones.write[54].bone_parent = "RightLowerLeg"; + bones.write[54].reference_pose = Transform3D(-1, 0, 0, 0, 0, -1, 0, -1, 0, 0, 0.375, 0); bones.write[54].handle_offset = Vector2(0.455, 0.9); bones.write[54].group = "Body"; + bones.write[54].require = true; bones.write[55].bone_name = "RightToes"; + bones.write[55].bone_parent = "RightFoot"; + bones.write[55].reference_pose = Transform3D(-1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0.15, 0); bones.write[55].handle_offset = Vector2(0.455, 0.95); bones.write[55].group = "Body"; } diff --git a/scene/resources/skeleton_profile.h b/scene/resources/skeleton_profile.h index 920aaa2b8d..66344d954d 100644 --- a/scene/resources/skeleton_profile.h +++ b/scene/resources/skeleton_profile.h @@ -36,6 +36,13 @@ class SkeletonProfile : public Resource { GDCLASS(SkeletonProfile, Resource); +public: + enum TailDirection { + TAIL_DIRECTION_AVERAGE_CHILDREN, + TAIL_DIRECTION_SPECIFIC_CHILD, + TAIL_DIRECTION_END + }; + protected: // Note: SkeletonProfileHumanoid which extends SkeletonProfile exists to unify standard bone names. // That is what is_read_only is for, so don't make it public. @@ -48,20 +55,34 @@ protected: struct SkeletonProfileBone { StringName bone_name; + StringName bone_parent; + TailDirection tail_direction = TAIL_DIRECTION_AVERAGE_CHILDREN; + StringName bone_tail; + Transform3D reference_pose; Vector2 handle_offset; StringName group; + bool require = false; }; + StringName root_bone; + StringName scale_base_bone; + Vector<SkeletonProfileGroup> groups; Vector<SkeletonProfileBone> bones; bool _get(const StringName &p_path, Variant &r_ret) const; bool _set(const StringName &p_path, const Variant &p_value); - virtual void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; void _get_property_list(List<PropertyInfo> *p_list) const; static void _bind_methods(); public: + StringName get_root_bone(); + void set_root_bone(StringName p_bone_name); + + StringName get_scale_base_bone(); + void set_scale_base_bone(StringName p_bone_name); + int get_group_size(); void set_group_size(int p_size); @@ -74,15 +95,32 @@ public: int get_bone_size(); void set_bone_size(int p_size); + int find_bone(const StringName p_bone_name) const; + StringName get_bone_name(int p_bone_idx) const; void set_bone_name(int p_bone_idx, const StringName p_bone_name); + StringName get_bone_parent(int p_bone_idx) const; + void set_bone_parent(int p_bone_idx, const StringName p_bone_parent); + + TailDirection get_tail_direction(int p_bone_idx) const; + void set_tail_direction(int p_bone_idx, const TailDirection p_tail_direction); + + StringName get_bone_tail(int p_bone_idx) const; + void set_bone_tail(int p_bone_idx, const StringName p_bone_tail); + + Transform3D get_reference_pose(int p_bone_idx) const; + void set_reference_pose(int p_bone_idx, const Transform3D p_reference_pose); + Vector2 get_handle_offset(int p_bone_idx) const; void set_handle_offset(int p_bone_idx, const Vector2 p_handle_offset); StringName get_group(int p_bone_idx) const; void set_group(int p_bone_idx, const StringName p_group); + bool is_require(int p_bone_idx) const; + void set_require(int p_bone_idx, const bool p_require); + bool has_bone(StringName p_bone_name); SkeletonProfile(); @@ -97,4 +135,6 @@ public: ~SkeletonProfileHumanoid(); }; +VARIANT_ENUM_CAST(SkeletonProfile::TailDirection); + #endif // SKELETON_PROFILE_H diff --git a/scene/resources/sky_material.cpp b/scene/resources/sky_material.cpp index 5d1a223cc7..d21f04fab8 100644 --- a/scene/resources/sky_material.cpp +++ b/scene/resources/sky_material.cpp @@ -30,10 +30,11 @@ #include "sky_material.h" +#include "core/config/project_settings.h" #include "core/version.h" Mutex ProceduralSkyMaterial::shader_mutex; -RID ProceduralSkyMaterial::shader; +RID ProceduralSkyMaterial::shader_cache[2]; void ProceduralSkyMaterial::set_sky_top_color(const Color &p_sky_top) { sky_top_color = p_sky_top; @@ -62,13 +63,13 @@ float ProceduralSkyMaterial::get_sky_curve() const { return sky_curve; } -void ProceduralSkyMaterial::set_sky_energy(float p_energy) { - sky_energy = p_energy; - RS::get_singleton()->material_set_param(_get_material(), "sky_energy", sky_energy); +void ProceduralSkyMaterial::set_sky_energy_multiplier(float p_multiplier) { + sky_energy_multiplier = p_multiplier; + RS::get_singleton()->material_set_param(_get_material(), "sky_energy", sky_energy_multiplier); } -float ProceduralSkyMaterial::get_sky_energy() const { - return sky_energy; +float ProceduralSkyMaterial::get_sky_energy_multiplier() const { + return sky_energy_multiplier; } void ProceduralSkyMaterial::set_sky_cover(const Ref<Texture2D> &p_sky_cover) { @@ -117,18 +118,18 @@ float ProceduralSkyMaterial::get_ground_curve() const { return ground_curve; } -void ProceduralSkyMaterial::set_ground_energy(float p_energy) { - ground_energy = p_energy; - RS::get_singleton()->material_set_param(_get_material(), "ground_energy", ground_energy); +void ProceduralSkyMaterial::set_ground_energy_multiplier(float p_multiplier) { + ground_energy_multiplier = p_multiplier; + RS::get_singleton()->material_set_param(_get_material(), "ground_energy", ground_energy_multiplier); } -float ProceduralSkyMaterial::get_ground_energy() const { - return ground_energy; +float ProceduralSkyMaterial::get_ground_energy_multiplier() const { + return ground_energy_multiplier; } void ProceduralSkyMaterial::set_sun_angle_max(float p_angle) { sun_angle_max = p_angle; - RS::get_singleton()->material_set_param(_get_material(), "sun_angle_max", Math::deg2rad(sun_angle_max)); + RS::get_singleton()->material_set_param(_get_material(), "sun_angle_max", Math::deg_to_rad(sun_angle_max)); } float ProceduralSkyMaterial::get_sun_angle_max() const { @@ -146,7 +147,11 @@ float ProceduralSkyMaterial::get_sun_curve() const { void ProceduralSkyMaterial::set_use_debanding(bool p_use_debanding) { use_debanding = p_use_debanding; - RS::get_singleton()->material_set_param(_get_material(), "use_debanding", use_debanding); + _update_shader(); + // Only set if shader already compiled + if (shader_set) { + RS::get_singleton()->material_set_shader(_get_material(), shader_cache[int(use_debanding)]); + } } bool ProceduralSkyMaterial::get_use_debanding() const { @@ -160,7 +165,8 @@ Shader::Mode ProceduralSkyMaterial::get_shader_mode() const { RID ProceduralSkyMaterial::get_rid() const { _update_shader(); if (!shader_set) { - RS::get_singleton()->material_set_shader(_get_material(), shader); + RS::get_singleton()->material_set_shader(_get_material(), shader_cache[1 - int(use_debanding)]); + RS::get_singleton()->material_set_shader(_get_material(), shader_cache[int(use_debanding)]); shader_set = true; } return _get_material(); @@ -168,7 +174,13 @@ RID ProceduralSkyMaterial::get_rid() const { RID ProceduralSkyMaterial::get_shader_rid() const { _update_shader(); - return shader; + return shader_cache[int(use_debanding)]; +} + +void ProceduralSkyMaterial::_validate_property(PropertyInfo &p_property) const { + if ((p_property.name == "sky_luminance" || p_property.name == "ground_luminance") && !GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } } void ProceduralSkyMaterial::_bind_methods() { @@ -181,8 +193,8 @@ void ProceduralSkyMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_sky_curve", "curve"), &ProceduralSkyMaterial::set_sky_curve); ClassDB::bind_method(D_METHOD("get_sky_curve"), &ProceduralSkyMaterial::get_sky_curve); - ClassDB::bind_method(D_METHOD("set_sky_energy", "energy"), &ProceduralSkyMaterial::set_sky_energy); - ClassDB::bind_method(D_METHOD("get_sky_energy"), &ProceduralSkyMaterial::get_sky_energy); + ClassDB::bind_method(D_METHOD("set_sky_energy_multiplier", "multiplier"), &ProceduralSkyMaterial::set_sky_energy_multiplier); + ClassDB::bind_method(D_METHOD("get_sky_energy_multiplier"), &ProceduralSkyMaterial::get_sky_energy_multiplier); ClassDB::bind_method(D_METHOD("set_sky_cover", "sky_cover"), &ProceduralSkyMaterial::set_sky_cover); ClassDB::bind_method(D_METHOD("get_sky_cover"), &ProceduralSkyMaterial::get_sky_cover); @@ -199,8 +211,8 @@ void ProceduralSkyMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_ground_curve", "curve"), &ProceduralSkyMaterial::set_ground_curve); ClassDB::bind_method(D_METHOD("get_ground_curve"), &ProceduralSkyMaterial::get_ground_curve); - ClassDB::bind_method(D_METHOD("set_ground_energy", "energy"), &ProceduralSkyMaterial::set_ground_energy); - ClassDB::bind_method(D_METHOD("get_ground_energy"), &ProceduralSkyMaterial::get_ground_energy); + ClassDB::bind_method(D_METHOD("set_ground_energy_multiplier", "energy"), &ProceduralSkyMaterial::set_ground_energy_multiplier); + ClassDB::bind_method(D_METHOD("get_ground_energy_multiplier"), &ProceduralSkyMaterial::get_ground_energy_multiplier); ClassDB::bind_method(D_METHOD("set_sun_angle_max", "degrees"), &ProceduralSkyMaterial::set_sun_angle_max); ClassDB::bind_method(D_METHOD("get_sun_angle_max"), &ProceduralSkyMaterial::get_sun_angle_max); @@ -215,7 +227,7 @@ void ProceduralSkyMaterial::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::COLOR, "sky_top_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_sky_top_color", "get_sky_top_color"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "sky_horizon_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_sky_horizon_color", "get_sky_horizon_color"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sky_curve", PROPERTY_HINT_EXP_EASING), "set_sky_curve", "get_sky_curve"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sky_energy", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_sky_energy", "get_sky_energy"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sky_energy_multiplier", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_sky_energy_multiplier", "get_sky_energy_multiplier"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "sky_cover", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_sky_cover", "get_sky_cover"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "sky_cover_modulate"), "set_sky_cover_modulate", "get_sky_cover_modulate"); @@ -223,7 +235,7 @@ void ProceduralSkyMaterial::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ground_bottom_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_ground_bottom_color", "get_ground_bottom_color"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ground_horizon_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_ground_horizon_color", "get_ground_horizon_color"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ground_curve", PROPERTY_HINT_EXP_EASING), "set_ground_curve", "get_ground_curve"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ground_energy", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_ground_energy", "get_ground_energy"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ground_energy_multiplier", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_ground_energy_multiplier", "get_ground_energy_multiplier"); ADD_GROUP("Sun", "sun_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sun_angle_max", PROPERTY_HINT_RANGE, "0,360,0.01,degrees"), "set_sun_angle_max", "get_sun_angle_max"); @@ -234,26 +246,29 @@ void ProceduralSkyMaterial::_bind_methods() { } void ProceduralSkyMaterial::cleanup_shader() { - if (shader.is_valid()) { - RS::get_singleton()->free(shader); + if (shader_cache[0].is_valid()) { + RS::get_singleton()->free(shader_cache[0]); + RS::get_singleton()->free(shader_cache[1]); } } void ProceduralSkyMaterial::_update_shader() { shader_mutex.lock(); - if (shader.is_null()) { - shader = RS::get_singleton()->shader_create(); + if (shader_cache[0].is_null()) { + for (int i = 0; i < 2; i++) { + shader_cache[i] = RS::get_singleton()->shader_create(); - // Add a comment to describe the shader origin (useful when converting to ShaderMaterial). - RS::get_singleton()->shader_set_code(shader, R"( + // Add a comment to describe the shader origin (useful when converting to ShaderMaterial). + RS::get_singleton()->shader_set_code(shader_cache[i], vformat(R"( // NOTE: Shader automatically converted from )" VERSION_NAME " " VERSION_FULL_CONFIG R"('s ProceduralSkyMaterial. shader_type sky; +%s uniform vec4 sky_top_color : source_color = vec4(0.385, 0.454, 0.55, 1.0); uniform vec4 sky_horizon_color : source_color = vec4(0.646, 0.656, 0.67, 1.0); uniform float sky_curve : hint_range(0, 1) = 0.15; -uniform float sky_energy = 1.0; +uniform float sky_energy = 1.0; // In Lux. uniform sampler2D sky_cover : source_color, hint_default_black; uniform vec4 sky_cover_modulate : source_color = vec4(1.0, 1.0, 1.0, 1.0); uniform vec4 ground_bottom_color : source_color = vec4(0.2, 0.169, 0.133, 1.0); @@ -262,14 +277,6 @@ uniform float ground_curve : hint_range(0, 1) = 0.02; uniform float ground_energy = 1.0; uniform float sun_angle_max = 30.0; uniform float sun_curve : hint_range(0, 1) = 0.15; -uniform bool use_debanding = true; - -// https://www.iryoku.com/next-generation-post-processing-in-call-of-duty-advanced-warfare -vec3 interleaved_gradient_noise(vec2 pos) { - const vec3 magic = vec3(0.06711056f, 0.00583715f, 52.9829189f); - float res = fract(magic.z * fract(dot(pos, magic.xy))) * 2.0 - 1.0; - return vec3(res, -res, res) / 255.0; -} void sky() { float v_angle = acos(clamp(EYEDIR.y, -1.0, 1.0)); @@ -325,11 +332,10 @@ void sky() { ground *= ground_energy; COLOR = mix(ground, sky, step(0.0, EYEDIR.y)); - if (use_debanding) { - COLOR += interleaved_gradient_noise(FRAGCOORD.xy); - } } -)"); +)", + i ? "render_mode use_debanding;" : "")); + } } shader_mutex.unlock(); } @@ -338,13 +344,13 @@ ProceduralSkyMaterial::ProceduralSkyMaterial() { set_sky_top_color(Color(0.385, 0.454, 0.55)); set_sky_horizon_color(Color(0.6463, 0.6558, 0.6708)); set_sky_curve(0.15); - set_sky_energy(1.0); + set_sky_energy_multiplier(1.0); set_sky_cover_modulate(Color(1, 1, 1)); set_ground_bottom_color(Color(0.2, 0.169, 0.133)); set_ground_horizon_color(Color(0.6463, 0.6558, 0.6708)); set_ground_curve(0.02); - set_ground_energy(1.0); + set_ground_energy_multiplier(1.0); set_sun_angle_max(30.0); set_sun_curve(0.15); @@ -528,18 +534,22 @@ Color PhysicalSkyMaterial::get_ground_color() const { return ground_color; } -void PhysicalSkyMaterial::set_exposure(float p_exposure) { - exposure = p_exposure; - RS::get_singleton()->material_set_param(_get_material(), "exposure", exposure); +void PhysicalSkyMaterial::set_energy_multiplier(float p_multiplier) { + energy_multiplier = p_multiplier; + RS::get_singleton()->material_set_param(_get_material(), "exposure", energy_multiplier); } -float PhysicalSkyMaterial::get_exposure() const { - return exposure; +float PhysicalSkyMaterial::get_energy_multiplier() const { + return energy_multiplier; } void PhysicalSkyMaterial::set_use_debanding(bool p_use_debanding) { use_debanding = p_use_debanding; - RS::get_singleton()->material_set_param(_get_material(), "use_debanding", use_debanding); + _update_shader(); + // Only set if shader already compiled + if (shader_set) { + RS::get_singleton()->material_set_shader(_get_material(), shader_cache[int(use_debanding)]); + } } bool PhysicalSkyMaterial::get_use_debanding() const { @@ -563,7 +573,8 @@ Shader::Mode PhysicalSkyMaterial::get_shader_mode() const { RID PhysicalSkyMaterial::get_rid() const { _update_shader(); if (!shader_set) { - RS::get_singleton()->material_set_shader(_get_material(), shader); + RS::get_singleton()->material_set_shader(_get_material(), shader_cache[1 - int(use_debanding)]); + RS::get_singleton()->material_set_shader(_get_material(), shader_cache[int(use_debanding)]); shader_set = true; } return _get_material(); @@ -571,11 +582,17 @@ RID PhysicalSkyMaterial::get_rid() const { RID PhysicalSkyMaterial::get_shader_rid() const { _update_shader(); - return shader; + return shader_cache[int(use_debanding)]; +} + +void PhysicalSkyMaterial::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "exposure_value" && !GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } } Mutex PhysicalSkyMaterial::shader_mutex; -RID PhysicalSkyMaterial::shader; +RID PhysicalSkyMaterial::shader_cache[2]; void PhysicalSkyMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_rayleigh_coefficient", "rayleigh"), &PhysicalSkyMaterial::set_rayleigh_coefficient); @@ -602,8 +619,8 @@ void PhysicalSkyMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_ground_color", "color"), &PhysicalSkyMaterial::set_ground_color); ClassDB::bind_method(D_METHOD("get_ground_color"), &PhysicalSkyMaterial::get_ground_color); - ClassDB::bind_method(D_METHOD("set_exposure", "exposure"), &PhysicalSkyMaterial::set_exposure); - ClassDB::bind_method(D_METHOD("get_exposure"), &PhysicalSkyMaterial::get_exposure); + ClassDB::bind_method(D_METHOD("set_energy_multiplier", "multiplier"), &PhysicalSkyMaterial::set_energy_multiplier); + ClassDB::bind_method(D_METHOD("get_energy_multiplier"), &PhysicalSkyMaterial::get_energy_multiplier); ClassDB::bind_method(D_METHOD("set_use_debanding", "use_debanding"), &PhysicalSkyMaterial::set_use_debanding); ClassDB::bind_method(D_METHOD("get_use_debanding"), &PhysicalSkyMaterial::get_use_debanding); @@ -623,27 +640,30 @@ void PhysicalSkyMaterial::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "turbidity", PROPERTY_HINT_RANGE, "0,1000,0.01"), "set_turbidity", "get_turbidity"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sun_disk_scale", PROPERTY_HINT_RANGE, "0,360,0.01"), "set_sun_disk_scale", "get_sun_disk_scale"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ground_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_ground_color", "get_ground_color"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "exposure", PROPERTY_HINT_RANGE, "0,128,0.01"), "set_exposure", "get_exposure"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "energy_multiplier", PROPERTY_HINT_RANGE, "0,128,0.01"), "set_energy_multiplier", "get_energy_multiplier"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_debanding"), "set_use_debanding", "get_use_debanding"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "night_sky", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_night_sky", "get_night_sky"); } void PhysicalSkyMaterial::cleanup_shader() { - if (shader.is_valid()) { - RS::get_singleton()->free(shader); + if (shader_cache[0].is_valid()) { + RS::get_singleton()->free(shader_cache[0]); + RS::get_singleton()->free(shader_cache[1]); } } void PhysicalSkyMaterial::_update_shader() { shader_mutex.lock(); - if (shader.is_null()) { - shader = RS::get_singleton()->shader_create(); + if (shader_cache[0].is_null()) { + for (int i = 0; i < 2; i++) { + shader_cache[i] = RS::get_singleton()->shader_create(); - // Add a comment to describe the shader origin (useful when converting to ShaderMaterial). - RS::get_singleton()->shader_set_code(shader, R"( + // Add a comment to describe the shader origin (useful when converting to ShaderMaterial). + RS::get_singleton()->shader_set_code(shader_cache[i], vformat(R"( // NOTE: Shader automatically converted from )" VERSION_NAME " " VERSION_FULL_CONFIG R"('s PhysicalSkyMaterial. shader_type sky; +%s uniform float rayleigh : hint_range(0, 64) = 2.0; uniform vec4 rayleigh_color : source_color = vec4(0.3, 0.405, 0.6, 1.0); @@ -654,16 +674,12 @@ uniform vec4 mie_color : source_color = vec4(0.69, 0.729, 0.812, 1.0); uniform float turbidity : hint_range(0, 1000) = 10.0; uniform float sun_disk_scale : hint_range(0, 360) = 1.0; uniform vec4 ground_color : source_color = vec4(0.1, 0.07, 0.034, 1.0); -uniform float exposure : hint_range(0, 128) = 0.1; -uniform bool use_debanding = true; +uniform float exposure : hint_range(0, 128) = 1.0; uniform sampler2D night_sky : source_color, hint_default_black; const vec3 UP = vec3( 0.0, 1.0, 0.0 ); -// Sun constants -const float SUN_ENERGY = 1000.0; - // Optical length at zenith for molecules. const float rayleigh_zenith_size = 8.4e3; const float mie_zenith_size = 1.25e3; @@ -673,17 +689,10 @@ float henyey_greenstein(float cos_theta, float g) { return k * (1.0 - g * g) / (pow(1.0 + g * g - 2.0 * g * cos_theta, 1.5)); } -// https://www.iryoku.com/next-generation-post-processing-in-call-of-duty-advanced-warfare -vec3 interleaved_gradient_noise(vec2 pos) { - const vec3 magic = vec3(0.06711056f, 0.00583715f, 52.9829189f); - float res = fract(magic.z * fract(dot(pos, magic.xy))) * 2.0 - 1.0; - return vec3(res, -res, res) / 255.0; -} - void sky() { if (LIGHT0_ENABLED) { float zenith_angle = clamp( dot(UP, normalize(LIGHT0_DIRECTION)), -1.0, 1.0 ); - float sun_energy = max(0.0, 1.0 - exp(-((PI * 0.5) - acos(zenith_angle)))) * SUN_ENERGY * LIGHT0_ENERGY; + float sun_energy = max(0.0, 1.0 - exp(-((PI * 0.5) - acos(zenith_angle)))) * LIGHT0_ENERGY; float sun_fade = 1.0 - clamp(1.0 - exp(LIGHT0_DIRECTION.y), 0.0, 1.0); // Rayleigh coefficients. @@ -721,22 +730,21 @@ void sky() { float sunAngularDiameterCos = cos(LIGHT0_SIZE * sun_disk_scale); float sunAngularDiameterCos2 = cos(LIGHT0_SIZE * sun_disk_scale*0.5); float sundisk = smoothstep(sunAngularDiameterCos, sunAngularDiameterCos2, cos_theta); - vec3 L0 = (sun_energy * 1900.0 * extinction) * sundisk * LIGHT0_COLOR; + vec3 L0 = (sun_energy * extinction) * sundisk * LIGHT0_COLOR; L0 += texture(night_sky, SKY_COORDS).xyz * extinction; - vec3 color = (Lin + L0) * 0.04; + vec3 color = Lin + L0; COLOR = pow(color, vec3(1.0 / (1.2 + (1.2 * sun_fade)))); COLOR *= exposure; - if (use_debanding) { - COLOR += interleaved_gradient_noise(FRAGCOORD.xy); - } } else { // There is no sun, so display night_sky and nothing else. - COLOR = texture(night_sky, SKY_COORDS).xyz * 0.04; + COLOR = texture(night_sky, SKY_COORDS).xyz; COLOR *= exposure; } } -)"); +)", + i ? "render_mode use_debanding;" : "")); + } } shader_mutex.unlock(); @@ -751,7 +759,7 @@ PhysicalSkyMaterial::PhysicalSkyMaterial() { set_turbidity(10.0); set_sun_disk_scale(1.0); set_ground_color(Color(0.1, 0.07, 0.034)); - set_exposure(0.1); + set_energy_multiplier(1.0); set_use_debanding(true); } diff --git a/scene/resources/sky_material.h b/scene/resources/sky_material.h index 5be8922ba4..3de1a4b26f 100644 --- a/scene/resources/sky_material.h +++ b/scene/resources/sky_material.h @@ -28,12 +28,12 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "core/templates/rid.h" -#include "scene/resources/material.h" - #ifndef SKY_MATERIAL_H #define SKY_MATERIAL_H +#include "core/templates/rid.h" +#include "scene/resources/material.h" + class ProceduralSkyMaterial : public Material { GDCLASS(ProceduralSkyMaterial, Material); @@ -41,26 +41,27 @@ private: Color sky_top_color; Color sky_horizon_color; float sky_curve = 0.0f; - float sky_energy = 0.0f; + float sky_energy_multiplier = 0.0f; Ref<Texture2D> sky_cover; Color sky_cover_modulate; Color ground_bottom_color; Color ground_horizon_color; float ground_curve = 0.0f; - float ground_energy = 0.0f; + float ground_energy_multiplier = 0.0f; float sun_angle_max = 0.0f; float sun_curve = 0.0f; bool use_debanding = true; static Mutex shader_mutex; - static RID shader; + static RID shader_cache[2]; static void _update_shader(); mutable bool shader_set = false; protected: static void _bind_methods(); + void _validate_property(PropertyInfo &property) const; public: void set_sky_top_color(const Color &p_sky_top); @@ -72,8 +73,8 @@ public: void set_sky_curve(float p_curve); float get_sky_curve() const; - void set_sky_energy(float p_energy); - float get_sky_energy() const; + void set_sky_energy_multiplier(float p_multiplier); + float get_sky_energy_multiplier() const; void set_sky_cover(const Ref<Texture2D> &p_sky_cover); Ref<Texture2D> get_sky_cover() const; @@ -90,8 +91,8 @@ public: void set_ground_curve(float p_curve); float get_ground_curve() const; - void set_ground_energy(float p_energy); - float get_ground_energy() const; + void set_ground_energy_multiplier(float p_energy); + float get_ground_energy_multiplier() const; void set_sun_angle_max(float p_angle); float get_sun_angle_max() const; @@ -138,6 +139,9 @@ public: void set_filtering_enabled(bool p_enabled); bool is_filtering_enabled() const; + void set_energy_multiplier(float p_multiplier); + float get_energy_multiplier() const; + virtual Shader::Mode get_shader_mode() const override; virtual RID get_shader_rid() const override; virtual RID get_rid() const override; @@ -156,7 +160,7 @@ class PhysicalSkyMaterial : public Material { private: static Mutex shader_mutex; - static RID shader; + static RID shader_cache[2]; float rayleigh = 0.0f; Color rayleigh_color; @@ -166,7 +170,7 @@ private: float turbidity = 0.0f; float sun_disk_scale = 0.0f; Color ground_color; - float exposure = 0.0f; + float energy_multiplier = 1.0f; bool use_debanding = true; Ref<Texture2D> night_sky; static void _update_shader(); @@ -174,6 +178,7 @@ private: protected: static void _bind_methods(); + void _validate_property(PropertyInfo &property) const; public: void set_rayleigh_coefficient(float p_rayleigh); @@ -200,8 +205,11 @@ public: void set_ground_color(Color p_ground_color); Color get_ground_color() const; - void set_exposure(float p_exposure); - float get_exposure() const; + void set_energy_multiplier(float p_multiplier); + float get_energy_multiplier() const; + + void set_exposure_value(float p_exposure); + float get_exposure_value() const; void set_use_debanding(bool p_use_debanding); bool get_use_debanding() const; @@ -219,4 +227,4 @@ public: ~PhysicalSkyMaterial(); }; -#endif /* !SKY_MATERIAL_H */ +#endif // SKY_MATERIAL_H diff --git a/scene/resources/sphere_shape_3d.cpp b/scene/resources/sphere_shape_3d.cpp index 92efe3ce6f..340d0fe370 100644 --- a/scene/resources/sphere_shape_3d.cpp +++ b/scene/resources/sphere_shape_3d.cpp @@ -38,8 +38,8 @@ Vector<Vector3> SphereShape3D::get_debug_mesh_lines() const { Vector<Vector3> points; for (int i = 0; i <= 360; i++) { - float ra = Math::deg2rad((float)i); - float rb = Math::deg2rad((float)i + 1); + float ra = Math::deg_to_rad((float)i); + float rb = Math::deg_to_rad((float)i + 1); Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r; Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r; diff --git a/scene/resources/sphere_shape_3d.h b/scene/resources/sphere_shape_3d.h index 8f77378ef4..91d72e01ec 100644 --- a/scene/resources/sphere_shape_3d.h +++ b/scene/resources/sphere_shape_3d.h @@ -52,4 +52,4 @@ public: SphereShape3D(); }; -#endif // SPHERE_SHAPE_H +#endif // SPHERE_SHAPE_3D_H diff --git a/scene/resources/sprite_frames.cpp b/scene/resources/sprite_frames.cpp index ba21b9fd17..3533e86c3a 100644 --- a/scene/resources/sprite_frames.cpp +++ b/scene/resources/sprite_frames.cpp @@ -96,17 +96,6 @@ void SpriteFrames::rename_animation(const StringName &p_prev, const StringName & animations[p_next] = anim; } -Vector<String> SpriteFrames::_get_animation_list() const { - Vector<String> ret; - List<StringName> al; - get_animation_list(&al); - for (const StringName &E : al) { - ret.push_back(E); - } - - return ret; -} - void SpriteFrames::get_animation_list(List<StringName> *r_animations) const { for (const KeyValue<StringName, Anim> &E : animations) { r_animations->push_back(E.key); @@ -147,31 +136,22 @@ bool SpriteFrames::get_animation_loop(const StringName &p_anim) const { return E->value.loop; } -void SpriteFrames::_set_frames(const Array &p_frames) { - clear_all(); - HashMap<StringName, Anim>::Iterator E = animations.find(SceneStringNames::get_singleton()->_default); - ERR_FAIL_COND(!E); - - E->value.frames.resize(p_frames.size()); - for (int i = 0; i < E->value.frames.size(); i++) { - E->value.frames.write[i] = p_frames[i]; - } -} - -Array SpriteFrames::_get_frames() const { - return Array(); -} - Array SpriteFrames::_get_animations() const { Array anims; - for (const KeyValue<StringName, Anim> &E : animations) { + + List<StringName> sorted_names; + get_animation_list(&sorted_names); + sorted_names.sort_custom<StringName::AlphCompare>(); + + for (const StringName &name : sorted_names) { + const Anim &anim = animations[name]; Dictionary d; - d["name"] = E.key; - d["speed"] = E.value.speed; - d["loop"] = E.value.loop; + d["name"] = name; + d["speed"] = anim.speed; + d["loop"] = anim.loop; Array frames; - for (int i = 0; i < E.value.frames.size(); i++) { - frames.push_back(E.value.frames[i]); + for (int i = 0; i < anim.frames.size(); i++) { + frames.push_back(anim.frames[i]); } d["frames"] = frames; anims.push_back(d); @@ -225,15 +205,12 @@ void SpriteFrames::_bind_methods() { ClassDB::bind_method(D_METHOD("clear", "anim"), &SpriteFrames::clear); ClassDB::bind_method(D_METHOD("clear_all"), &SpriteFrames::clear_all); - ClassDB::bind_method(D_METHOD("_set_frames"), &SpriteFrames::_set_frames); - ClassDB::bind_method(D_METHOD("_get_frames"), &SpriteFrames::_get_frames); - - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "frames", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "_set_frames", "_get_frames"); //compatibility + // `animations` property is for serialization. - ClassDB::bind_method(D_METHOD("_set_animations"), &SpriteFrames::_set_animations); + ClassDB::bind_method(D_METHOD("_set_animations", "animations"), &SpriteFrames::_set_animations); ClassDB::bind_method(D_METHOD("_get_animations"), &SpriteFrames::_get_animations); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "animations", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_animations", "_get_animations"); //compatibility + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "animations", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_animations", "_get_animations"); } SpriteFrames::SpriteFrames() { diff --git a/scene/resources/sprite_frames.h b/scene/resources/sprite_frames.h index e32ccc1336..87d84b70c0 100644 --- a/scene/resources/sprite_frames.h +++ b/scene/resources/sprite_frames.h @@ -44,14 +44,9 @@ class SpriteFrames : public Resource { HashMap<StringName, Anim> animations; - Array _get_frames() const; - void _set_frames(const Array &p_frames); - Array _get_animations() const; void _set_animations(const Array &p_animations); - Vector<String> _get_animation_list() const; - protected: static void _bind_methods(); diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp index a53c299d00..cd893d8c23 100644 --- a/scene/resources/style_box.cpp +++ b/scene/resources/style_box.cpp @@ -41,6 +41,7 @@ float StyleBox::get_style_margin(Side p_side) const { } return 0; } + bool StyleBox::test_mask(const Point2 &p_point, const Rect2 &p_rect) const { bool ret; if (GDVIRTUAL_CALL(_test_mask, p_point, p_rect, ret)) { @@ -63,6 +64,21 @@ void StyleBox::set_default_margin(Side p_side, float p_value) { emit_changed(); } +void StyleBox::set_default_margin_all(float p_value) { + for (int i = 0; i < 4; i++) { + margin[i] = p_value; + } + emit_changed(); +} + +void StyleBox::set_default_margin_individual(float p_left, float p_top, float p_right, float p_bottom) { + margin[SIDE_LEFT] = p_left; + margin[SIDE_TOP] = p_top; + margin[SIDE_RIGHT] = p_right; + margin[SIDE_BOTTOM] = p_bottom; + emit_changed(); +} + float StyleBox::get_default_margin(Side p_side) const { ERR_FAIL_INDEX_V((int)p_side, 4, 0.0); @@ -112,6 +128,7 @@ void StyleBox::_bind_methods() { ClassDB::bind_method(D_METHOD("test_mask", "point", "rect"), &StyleBox::test_mask); ClassDB::bind_method(D_METHOD("set_default_margin", "margin", "offset"), &StyleBox::set_default_margin); + ClassDB::bind_method(D_METHOD("set_default_margin_all", "offset"), &StyleBox::set_default_margin_all); ClassDB::bind_method(D_METHOD("get_default_margin", "margin"), &StyleBox::get_default_margin); ClassDB::bind_method(D_METHOD("get_margin", "margin"), &StyleBox::get_margin); @@ -165,6 +182,21 @@ void StyleBoxTexture::set_margin_size(Side p_side, float p_size) { emit_changed(); } +void StyleBoxTexture::set_margin_size_all(float p_size) { + for (int i = 0; i < 4; i++) { + margin[i] = p_size; + } + emit_changed(); +} + +void StyleBoxTexture::set_margin_size_individual(float p_left, float p_top, float p_right, float p_bottom) { + margin[SIDE_LEFT] = p_left; + margin[SIDE_TOP] = p_top; + margin[SIDE_RIGHT] = p_right; + margin[SIDE_BOTTOM] = p_bottom; + emit_changed(); +} + float StyleBoxTexture::get_margin_size(Side p_side) const { ERR_FAIL_INDEX_V((int)p_side, 4, 0.0); @@ -292,11 +324,11 @@ void StyleBoxTexture::_bind_methods() { ClassDB::bind_method(D_METHOD("get_texture"), &StyleBoxTexture::get_texture); ClassDB::bind_method(D_METHOD("set_margin_size", "margin", "size"), &StyleBoxTexture::set_margin_size); + ClassDB::bind_method(D_METHOD("set_margin_size_all", "size"), &StyleBoxTexture::set_margin_size_all); ClassDB::bind_method(D_METHOD("get_margin_size", "margin"), &StyleBoxTexture::get_margin_size); ClassDB::bind_method(D_METHOD("set_expand_margin_size", "margin", "size"), &StyleBoxTexture::set_expand_margin_size); ClassDB::bind_method(D_METHOD("set_expand_margin_all", "size"), &StyleBoxTexture::set_expand_margin_size_all); - ClassDB::bind_method(D_METHOD("set_expand_margin_individual", "size_left", "size_top", "size_right", "size_bottom"), &StyleBoxTexture::set_expand_margin_size_individual); ClassDB::bind_method(D_METHOD("get_expand_margin_size", "margin"), &StyleBoxTexture::get_expand_margin_size); ClassDB::bind_method(D_METHOD("set_region_rect", "region"), &StyleBoxTexture::set_region_rect); @@ -687,7 +719,7 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const { const bool rounded_corners = (corner_radius[0] > 0) || (corner_radius[1] > 0) || (corner_radius[2] > 0) || (corner_radius[3] > 0); // Only enable antialiasing if it is actually needed. This improve performances // and maximizes sharpness for non-skewed StyleBoxes with sharp corners. - const bool aa_on = (rounded_corners || !skew.is_equal_approx(Vector2())) && anti_aliased; + const bool aa_on = (rounded_corners || !skew.is_zero_approx()) && anti_aliased; const bool blend_on = blend_border && draw_border; @@ -842,9 +874,9 @@ float StyleBoxFlat::get_style_margin(Side p_side) const { return border_width[p_side]; } -void StyleBoxFlat::_validate_property(PropertyInfo &property) const { - if (!anti_aliased && property.name == "anti_aliasing_size") { - property.usage = PROPERTY_USAGE_NO_EDITOR; +void StyleBoxFlat::_validate_property(PropertyInfo &p_property) const { + if (!anti_aliased && p_property.name == "anti_aliasing_size") { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } } @@ -864,7 +896,6 @@ void StyleBoxFlat::_bind_methods() { ClassDB::bind_method(D_METHOD("set_border_blend", "blend"), &StyleBoxFlat::set_border_blend); ClassDB::bind_method(D_METHOD("get_border_blend"), &StyleBoxFlat::get_border_blend); - ClassDB::bind_method(D_METHOD("set_corner_radius_individual", "radius_top_left", "radius_top_right", "radius_bottom_right", "radius_bottom_left"), &StyleBoxFlat::set_corner_radius_individual); ClassDB::bind_method(D_METHOD("set_corner_radius_all", "radius"), &StyleBoxFlat::set_corner_radius_all); ClassDB::bind_method(D_METHOD("set_corner_radius", "corner", "radius"), &StyleBoxFlat::set_corner_radius); @@ -872,7 +903,6 @@ void StyleBoxFlat::_bind_methods() { ClassDB::bind_method(D_METHOD("set_expand_margin", "margin", "size"), &StyleBoxFlat::set_expand_margin_size); ClassDB::bind_method(D_METHOD("set_expand_margin_all", "size"), &StyleBoxFlat::set_expand_margin_size_all); - ClassDB::bind_method(D_METHOD("set_expand_margin_individual", "size_left", "size_top", "size_right", "size_bottom"), &StyleBoxFlat::set_expand_margin_size_individual); ClassDB::bind_method(D_METHOD("get_expand_margin", "margin"), &StyleBoxFlat::get_expand_margin_size); ClassDB::bind_method(D_METHOD("set_draw_center", "draw_center"), &StyleBoxFlat::set_draw_center); diff --git a/scene/resources/style_box.h b/scene/resources/style_box.h index 3b3654775f..2c72446567 100644 --- a/scene/resources/style_box.h +++ b/scene/resources/style_box.h @@ -57,7 +57,10 @@ public: virtual bool test_mask(const Point2 &p_point, const Rect2 &p_rect) const; void set_default_margin(Side p_side, float p_value); + void set_default_margin_all(float p_value); + void set_default_margin_individual(float p_left, float p_top, float p_right, float p_bottom); float get_default_margin(Side p_side) const; + float get_margin(Side p_side) const; virtual Size2 get_center_size() const; @@ -112,6 +115,8 @@ public: float get_expand_margin_size(Side p_expand_side) const; void set_margin_size(Side p_side, float p_size); + void set_margin_size_all(float p_size); + void set_margin_size_individual(float p_left, float p_top, float p_right, float p_bottom); float get_margin_size(Side p_side) const; void set_region_rect(const Rect2 &p_region_rect); @@ -166,7 +171,7 @@ class StyleBoxFlat : public StyleBox { protected: virtual float get_style_margin(Side p_side) const override; static void _bind_methods(); - void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; public: void set_bg_color(const Color &p_color); @@ -264,4 +269,4 @@ public: ~StyleBoxLine(); }; -#endif +#endif // STYLE_BOX_H diff --git a/scene/resources/surface_tool.h b/scene/resources/surface_tool.h index 2d399ca3bf..6735d6623f 100644 --- a/scene/resources/surface_tool.h +++ b/scene/resources/surface_tool.h @@ -207,4 +207,4 @@ public: VARIANT_ENUM_CAST(SurfaceTool::CustomFormat) VARIANT_ENUM_CAST(SurfaceTool::SkinWeightCount) -#endif +#endif // SURFACE_TOOL_H diff --git a/scene/resources/syntax_highlighter.h b/scene/resources/syntax_highlighter.h index 1243a9dbf7..69131ffbee 100644 --- a/scene/resources/syntax_highlighter.h +++ b/scene/resources/syntax_highlighter.h @@ -142,4 +142,4 @@ public: Color get_member_variable_color() const; }; -#endif +#endif // SYNTAX_HIGHLIGHTER_H diff --git a/scene/resources/text_file.h b/scene/resources/text_file.h index 0c8cf855f0..168e4f5879 100644 --- a/scene/resources/text_file.h +++ b/scene/resources/text_file.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef TEXTFILE_H -#define TEXTFILE_H +#ifndef TEXT_FILE_H +#define TEXT_FILE_H #include "core/io/resource_loader.h" #include "core/io/resource_saver.h" @@ -51,4 +51,4 @@ public: Error load_text(const String &p_path); }; -#endif // TEXTFILE_H +#endif // TEXT_FILE_H diff --git a/scene/resources/text_line.cpp b/scene/resources/text_line.cpp index f32b7feb4b..823d742d72 100644 --- a/scene/resources/text_line.cpp +++ b/scene/resources/text_line.cpp @@ -74,7 +74,7 @@ void TextLine::_bind_methods() { ClassDB::bind_method(D_METHOD("set_flags", "flags"), &TextLine::set_flags); ClassDB::bind_method(D_METHOD("get_flags"), &TextLine::get_flags); - ADD_PROPERTY(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Kashida Justify,Word Justify,Trim Edge Spaces After Justify,Justify Only After Last Tab"), "set_flags", "get_flags"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Kashida Justification,Word Justification,Trim Edge Spaces After Justification,Justify Only After Last Tab,Constrain Ellipsis"), "set_flags", "get_flags"); ClassDB::bind_method(D_METHOD("set_text_overrun_behavior", "overrun_behavior"), &TextLine::set_text_overrun_behavior); ClassDB::bind_method(D_METHOD("get_text_overrun_behavior"), &TextLine::get_text_overrun_behavior); @@ -106,24 +106,24 @@ void TextLine::_shape() { TS->shaped_text_tab_align(rid, tab_stops); } - uint16_t overrun_flags = TextServer::OVERRUN_NO_TRIM; + BitField<TextServer::TextOverrunFlag> overrun_flags = TextServer::OVERRUN_NO_TRIM; if (overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) { switch (overrun_behavior) { case TextServer::OVERRUN_TRIM_WORD_ELLIPSIS: - overrun_flags |= TextServer::OVERRUN_TRIM; - overrun_flags |= TextServer::OVERRUN_TRIM_WORD_ONLY; - overrun_flags |= TextServer::OVERRUN_ADD_ELLIPSIS; + overrun_flags.set_flag(TextServer::OVERRUN_TRIM); + overrun_flags.set_flag(TextServer::OVERRUN_TRIM_WORD_ONLY); + overrun_flags.set_flag(TextServer::OVERRUN_ADD_ELLIPSIS); break; case TextServer::OVERRUN_TRIM_ELLIPSIS: - overrun_flags |= TextServer::OVERRUN_TRIM; - overrun_flags |= TextServer::OVERRUN_ADD_ELLIPSIS; + overrun_flags.set_flag(TextServer::OVERRUN_TRIM); + overrun_flags.set_flag(TextServer::OVERRUN_ADD_ELLIPSIS); break; case TextServer::OVERRUN_TRIM_WORD: - overrun_flags |= TextServer::OVERRUN_TRIM; - overrun_flags |= TextServer::OVERRUN_TRIM_WORD_ONLY; + overrun_flags.set_flag(TextServer::OVERRUN_TRIM); + overrun_flags.set_flag(TextServer::OVERRUN_TRIM_WORD_ONLY); break; case TextServer::OVERRUN_TRIM_CHAR: - overrun_flags |= TextServer::OVERRUN_TRIM; + overrun_flags.set_flag(TextServer::OVERRUN_TRIM); break; case TextServer::OVERRUN_NO_TRIMMING: break; @@ -131,7 +131,7 @@ void TextLine::_shape() { if (alignment == HORIZONTAL_ALIGNMENT_FILL) { TS->shaped_text_fit_to_width(rid, width, flags); - overrun_flags |= TextServer::OVERRUN_JUSTIFICATION_AWARE; + overrun_flags.set_flag(TextServer::OVERRUN_JUSTIFICATION_AWARE); TS->shaped_text_overrun_trim_to_width(rid, width, overrun_flags); } else { TS->shaped_text_overrun_trim_to_width(rid, width, overrun_flags); @@ -241,14 +241,14 @@ void TextLine::tab_align(const Vector<float> &p_tab_stops) { dirty = true; } -void TextLine::set_flags(uint16_t p_flags) { +void TextLine::set_flags(BitField<TextServer::JustificationFlag> p_flags) { if (flags != p_flags) { flags = p_flags; dirty = true; } } -uint16_t TextLine::get_flags() const { +BitField<TextServer::JustificationFlag> TextLine::get_flags() const { return flags; } diff --git a/scene/resources/text_line.h b/scene/resources/text_line.h index 2d1548d079..e70e82cf2b 100644 --- a/scene/resources/text_line.h +++ b/scene/resources/text_line.h @@ -45,7 +45,7 @@ private: bool dirty = true; float width = -1.0; - uint16_t flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA; + BitField<TextServer::JustificationFlag> flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA; HorizontalAlignment alignment = HORIZONTAL_ALIGNMENT_LEFT; TextServer::OverrunBehavior overrun_behavior = TextServer::OVERRUN_TRIM_ELLIPSIS; @@ -84,8 +84,8 @@ public: void tab_align(const Vector<float> &p_tab_stops); - void set_flags(uint16_t p_flags); - uint16_t get_flags() const; + void set_flags(BitField<TextServer::JustificationFlag> p_flags); + BitField<TextServer::JustificationFlag> get_flags() const; void set_text_overrun_behavior(TextServer::OverrunBehavior p_behavior); TextServer::OverrunBehavior get_text_overrun_behavior() const; diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp index c8b9e895fc..7e9a2591e4 100644 --- a/scene/resources/text_paragraph.cpp +++ b/scene/resources/text_paragraph.cpp @@ -74,10 +74,15 @@ void TextParagraph::_bind_methods() { ClassDB::bind_method(D_METHOD("tab_align", "tab_stops"), &TextParagraph::tab_align); - ClassDB::bind_method(D_METHOD("set_flags", "flags"), &TextParagraph::set_flags); - ClassDB::bind_method(D_METHOD("get_flags"), &TextParagraph::get_flags); + ClassDB::bind_method(D_METHOD("set_break_flags", "flags"), &TextParagraph::set_break_flags); + ClassDB::bind_method(D_METHOD("get_break_flags"), &TextParagraph::get_break_flags); - ADD_PROPERTY(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Kashida Justify,Word Justify,Trim Edge Spaces After Justify,Justify Only After Last Tab,Break Mandatory,Break Words,Break Graphemes"), "set_flags", "get_flags"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "break_flags", PROPERTY_HINT_FLAGS, "Mandatory,Word Bound,Grapheme Bound,Adaptive,Trim Spaces"), "set_break_flags", "get_break_flags"); + + ClassDB::bind_method(D_METHOD("set_justification_flags", "flags"), &TextParagraph::set_justification_flags); + ClassDB::bind_method(D_METHOD("get_justification_flags"), &TextParagraph::get_justification_flags); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "justification_flags", PROPERTY_HINT_FLAGS, "Kashida Justification,Word Justification,Trim Edge Spaces After Justification,Justify Only After Last Tab,Constrain Ellipsis"), "set_justification_flags", "get_justification_flags"); ClassDB::bind_method(D_METHOD("set_text_overrun_behavior", "overrun_behavior"), &TextParagraph::set_text_overrun_behavior); ClassDB::bind_method(D_METHOD("get_text_overrun_behavior"), &TextParagraph::get_text_overrun_behavior); @@ -154,7 +159,7 @@ void TextParagraph::_shape_lines() { if (h_offset > 0) { // Dropcap, flow around. - PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(rid, width - h_offset, 0, flags); + PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(rid, width - h_offset, 0, brk_flags); for (int i = 0; i < line_breaks.size(); i = i + 2) { RID line = TS->shaped_text_substr(rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]); float h = (TS->shaped_text_get_orientation(line) == TextServer::ORIENTATION_HORIZONTAL) ? TS->shaped_text_get_size(line).y : TS->shaped_text_get_size(line).x; @@ -172,7 +177,7 @@ void TextParagraph::_shape_lines() { } } // Use fixed for the rest of lines. - PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(rid, width, start, flags); + PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(rid, width, start, brk_flags); for (int i = 0; i < line_breaks.size(); i = i + 2) { RID line = TS->shaped_text_substr(rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]); if (!tab_stops.is_empty()) { @@ -181,43 +186,43 @@ void TextParagraph::_shape_lines() { lines_rid.push_back(line); } - uint16_t overrun_flags = TextServer::OVERRUN_NO_TRIM; + BitField<TextServer::TextOverrunFlag> overrun_flags = TextServer::OVERRUN_NO_TRIM; if (overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) { switch (overrun_behavior) { case TextServer::OVERRUN_TRIM_WORD_ELLIPSIS: - overrun_flags |= TextServer::OVERRUN_TRIM; - overrun_flags |= TextServer::OVERRUN_TRIM_WORD_ONLY; - overrun_flags |= TextServer::OVERRUN_ADD_ELLIPSIS; + overrun_flags.set_flag(TextServer::OVERRUN_TRIM); + overrun_flags.set_flag(TextServer::OVERRUN_TRIM_WORD_ONLY); + overrun_flags.set_flag(TextServer::OVERRUN_ADD_ELLIPSIS); break; case TextServer::OVERRUN_TRIM_ELLIPSIS: - overrun_flags |= TextServer::OVERRUN_TRIM; - overrun_flags |= TextServer::OVERRUN_ADD_ELLIPSIS; + overrun_flags.set_flag(TextServer::OVERRUN_TRIM); + overrun_flags.set_flag(TextServer::OVERRUN_ADD_ELLIPSIS); break; case TextServer::OVERRUN_TRIM_WORD: - overrun_flags |= TextServer::OVERRUN_TRIM; - overrun_flags |= TextServer::OVERRUN_TRIM_WORD_ONLY; + overrun_flags.set_flag(TextServer::OVERRUN_TRIM); + overrun_flags.set_flag(TextServer::OVERRUN_TRIM_WORD_ONLY); break; case TextServer::OVERRUN_TRIM_CHAR: - overrun_flags |= TextServer::OVERRUN_TRIM; + overrun_flags.set_flag(TextServer::OVERRUN_TRIM); break; case TextServer::OVERRUN_NO_TRIMMING: break; } } - bool autowrap_enabled = ((flags & TextServer::BREAK_WORD_BOUND) == TextServer::BREAK_WORD_BOUND) || ((flags & TextServer::BREAK_GRAPHEME_BOUND) == TextServer::BREAK_GRAPHEME_BOUND); + bool autowrap_enabled = brk_flags.has_flag(TextServer::BREAK_WORD_BOUND) || brk_flags.has_flag(TextServer::BREAK_GRAPHEME_BOUND); // Fill after min_size calculation. if (autowrap_enabled) { int visible_lines = (max_lines_visible >= 0) ? MIN(max_lines_visible, (int)lines_rid.size()) : (int)lines_rid.size(); bool lines_hidden = visible_lines > 0 && visible_lines < (int)lines_rid.size(); if (lines_hidden) { - overrun_flags |= TextServer::OVERRUN_ENFORCE_ELLIPSIS; + overrun_flags.set_flag(TextServer::OVERRUN_ENFORCE_ELLIPSIS); } if (alignment == HORIZONTAL_ALIGNMENT_FILL) { for (int i = 0; i < (int)lines_rid.size(); i++) { if (i < visible_lines - 1 || (int)lines_rid.size() == 1) { - TS->shaped_text_fit_to_width(lines_rid[i], width, flags); + TS->shaped_text_fit_to_width(lines_rid[i], width, jst_flags); } else if (i == (visible_lines - 1)) { TS->shaped_text_overrun_trim_to_width(lines_rid[visible_lines - 1], width, overrun_flags); } @@ -231,10 +236,10 @@ void TextParagraph::_shape_lines() { // Autowrap disabled. for (int i = 0; i < (int)lines_rid.size(); i++) { if (alignment == HORIZONTAL_ALIGNMENT_FILL) { - TS->shaped_text_fit_to_width(lines_rid[i], width, flags); - overrun_flags |= TextServer::OVERRUN_JUSTIFICATION_AWARE; + TS->shaped_text_fit_to_width(lines_rid[i], width, jst_flags); + overrun_flags.set_flag(TextServer::OVERRUN_JUSTIFICATION_AWARE); TS->shaped_text_overrun_trim_to_width(lines_rid[i], width, overrun_flags); - TS->shaped_text_fit_to_width(lines_rid[i], width, flags | TextServer::JUSTIFICATION_CONSTRAIN_ELLIPSIS); + TS->shaped_text_fit_to_width(lines_rid[i], width, jst_flags | TextServer::JUSTIFICATION_CONSTRAIN_ELLIPSIS); } else { TS->shaped_text_overrun_trim_to_width(lines_rid[i], width, overrun_flags); } @@ -420,17 +425,30 @@ void TextParagraph::tab_align(const Vector<float> &p_tab_stops) { lines_dirty = true; } -void TextParagraph::set_flags(uint16_t p_flags) { +void TextParagraph::set_justification_flags(BitField<TextServer::JustificationFlag> p_flags) { + _THREAD_SAFE_METHOD_ + + if (jst_flags != p_flags) { + jst_flags = p_flags; + lines_dirty = true; + } +} + +BitField<TextServer::JustificationFlag> TextParagraph::get_justification_flags() const { + return jst_flags; +} + +void TextParagraph::set_break_flags(BitField<TextServer::LineBreakFlag> p_flags) { _THREAD_SAFE_METHOD_ - if (flags != p_flags) { - flags = p_flags; + if (brk_flags != p_flags) { + brk_flags = p_flags; lines_dirty = true; } } -uint16_t TextParagraph::get_flags() const { - return flags; +BitField<TextServer::LineBreakFlag> TextParagraph::get_break_flags() const { + return brk_flags; } void TextParagraph::set_text_overrun_behavior(TextServer::OverrunBehavior p_behavior) { diff --git a/scene/resources/text_paragraph.h b/scene/resources/text_paragraph.h index f161cb5b8c..0fe82b4364 100644 --- a/scene/resources/text_paragraph.h +++ b/scene/resources/text_paragraph.h @@ -54,7 +54,8 @@ private: float width = -1.0; int max_lines_visible = -1; - uint16_t flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA; + BitField<TextServer::LineBreakFlag> brk_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND; + BitField<TextServer::JustificationFlag> jst_flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA; TextServer::OverrunBehavior overrun_behavior = TextServer::OVERRUN_NO_TRIMMING; HorizontalAlignment alignment = HORIZONTAL_ALIGNMENT_LEFT; @@ -102,8 +103,11 @@ public: void tab_align(const Vector<float> &p_tab_stops); - void set_flags(uint16_t p_flags); - uint16_t get_flags() const; + void set_justification_flags(BitField<TextServer::JustificationFlag> p_flags); + BitField<TextServer::JustificationFlag> get_justification_flags() const; + + void set_break_flags(BitField<TextServer::LineBreakFlag> p_flags); + BitField<TextServer::LineBreakFlag> get_break_flags() const; void set_text_overrun_behavior(TextServer::OverrunBehavior p_behavior); TextServer::OverrunBehavior get_text_overrun_behavior() const; diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index 8c175e9ced..15678c9281 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -35,9 +35,10 @@ #include "core/io/marshalls.h" #include "core/math/geometry_2d.h" #include "core/os/os.h" -#include "mesh.h" #include "scene/resources/bit_map.h" +#include "scene/resources/mesh.h" #include "servers/camera/camera_feed.h" + int Texture2D::get_width() const { int ret; if (GDVIRTUAL_REQUIRED_CALL(_get_width, ret)) { @@ -294,14 +295,14 @@ bool ImageTexture::is_pixel_opaque(int p_x, int p_y) const { x = CLAMP(x, 0, aw); y = CLAMP(y, 0, ah); - return alpha_cache->get_bit(Point2(x, y)); + return alpha_cache->get_bit(x, y); } return true; } -void ImageTexture::set_size_override(const Size2 &p_size) { - Size2 s = p_size; +void ImageTexture::set_size_override(const Size2i &p_size) { + Size2i s = p_size; if (s.x != 0) { w = s.x; } @@ -323,6 +324,7 @@ void ImageTexture::_bind_methods() { ClassDB::bind_static_method("ImageTexture", D_METHOD("create_from_image", "image"), &ImageTexture::create_from_image); ClassDB::bind_method(D_METHOD("get_format"), &ImageTexture::get_format); + ClassDB::bind_method(D_METHOD("set_image", "image"), &ImageTexture::set_image); ClassDB::bind_method(D_METHOD("update", "image"), &ImageTexture::update); ClassDB::bind_method(D_METHOD("set_size_override", "size"), &ImageTexture::set_size_override); } @@ -560,7 +562,7 @@ bool PortableCompressedTexture2D::is_pixel_opaque(int p_x, int p_y) const { x = CLAMP(x, 0, aw); y = CLAMP(y, 0, ah); - return alpha_cache->get_bit(Point2(x, y)); + return alpha_cache->get_bit(x, y); } return true; @@ -1016,7 +1018,7 @@ bool CompressedTexture2D::is_pixel_opaque(int p_x, int p_y) const { x = CLAMP(x, 0, aw); y = CLAMP(y, 0, ah); - return alpha_cache->get_bit(Point2(x, y)); + return alpha_cache->get_bit(x, y); } return true; @@ -1037,7 +1039,7 @@ void CompressedTexture2D::reload_from_file() { load(path); } -void CompressedTexture2D::_validate_property(PropertyInfo &property) const { +void CompressedTexture2D::_validate_property(PropertyInfo &p_property) const { } void CompressedTexture2D::_bind_methods() { @@ -1393,7 +1395,7 @@ void CompressedTexture3D::reload_from_file() { load(path); } -void CompressedTexture3D::_validate_property(PropertyInfo &property) const { +void CompressedTexture3D::_validate_property(PropertyInfo &p_property) const { } void CompressedTexture3D::_bind_methods() { @@ -1865,11 +1867,11 @@ void CurveTexture::_update() { for (int i = 0; i < _width; ++i) { float t = i / static_cast<float>(_width); if (texture_mode == TEXTURE_MODE_RGB) { - wd[i * 3 + 0] = curve.interpolate_baked(t); + wd[i * 3 + 0] = curve.sample_baked(t); wd[i * 3 + 1] = wd[i * 3 + 0]; wd[i * 3 + 2] = wd[i * 3 + 0]; } else { - wd[i] = curve.interpolate_baked(t); + wd[i] = curve.sample_baked(t); } } @@ -2008,7 +2010,7 @@ void CurveXYZTexture::set_curve_x(Ref<Curve> p_curve) { } _curve_x = p_curve; if (_curve_x.is_valid()) { - _curve_x->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveXYZTexture::_update), varray(), CONNECT_REFERENCE_COUNTED); + _curve_x->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveXYZTexture::_update), CONNECT_REFERENCE_COUNTED); } _update(); } @@ -2021,7 +2023,7 @@ void CurveXYZTexture::set_curve_y(Ref<Curve> p_curve) { } _curve_y = p_curve; if (_curve_y.is_valid()) { - _curve_y->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveXYZTexture::_update), varray(), CONNECT_REFERENCE_COUNTED); + _curve_y->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveXYZTexture::_update), CONNECT_REFERENCE_COUNTED); } _update(); } @@ -2034,7 +2036,7 @@ void CurveXYZTexture::set_curve_z(Ref<Curve> p_curve) { } _curve_z = p_curve; if (_curve_z.is_valid()) { - _curve_z->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveXYZTexture::_update), varray(), CONNECT_REFERENCE_COUNTED); + _curve_z->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveXYZTexture::_update), CONNECT_REFERENCE_COUNTED); } _update(); } @@ -2053,7 +2055,7 @@ void CurveXYZTexture::_update() { Curve &curve_x = **_curve_x; for (int i = 0; i < _width; ++i) { float t = i / static_cast<float>(_width); - wd[i * 3 + 0] = curve_x.interpolate_baked(t); + wd[i * 3 + 0] = curve_x.sample_baked(t); } } else { @@ -2066,7 +2068,7 @@ void CurveXYZTexture::_update() { Curve &curve_y = **_curve_y; for (int i = 0; i < _width; ++i) { float t = i / static_cast<float>(_width); - wd[i * 3 + 1] = curve_y.interpolate_baked(t); + wd[i * 3 + 1] = curve_y.sample_baked(t); } } else { @@ -2079,7 +2081,7 @@ void CurveXYZTexture::_update() { Curve &curve_z = **_curve_z; for (int i = 0; i < _width; ++i) { float t = i / static_cast<float>(_width); - wd[i * 3 + 2] = curve_z.interpolate_baked(t); + wd[i * 3 + 2] = curve_z.sample_baked(t); } } else { @@ -2157,7 +2159,7 @@ void GradientTexture1D::_bind_methods() { ClassDB::bind_method(D_METHOD("_update"), &GradientTexture1D::_update); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gradient", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_gradient", "get_gradient"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gradient", PROPERTY_HINT_RESOURCE_TYPE, "Gradient", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT), "set_gradient", "get_gradient"); ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,16384,suffix:px"), "set_width", "get_width"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_hdr"), "set_use_hdr", "is_using_hdr"); } @@ -2536,13 +2538,6 @@ void GradientTexture2D::_bind_methods() { ////////////////////////////////////// -void ProxyTexture::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_base", "base"), &ProxyTexture::set_base); - ClassDB::bind_method(D_METHOD("get_base"), &ProxyTexture::get_base); - - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "base", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_base", "get_base"); -} - void ProxyTexture::set_base(const Ref<Texture2D> &p_texture) { ERR_FAIL_COND(p_texture == this); @@ -2623,26 +2618,30 @@ void AnimatedTexture::_update_proxy() { time += delta; - float limit; - - if (fps == 0) { - limit = 0; - } else { - limit = 1.0 / fps; - } + float speed = speed_scale == 0 ? 0 : abs(1.0 / speed_scale); int iter_max = frame_count; while (iter_max && !pause) { - float frame_limit = limit + frames[current_frame].delay_sec; + float frame_limit = frames[current_frame].duration * speed; if (time > frame_limit) { - current_frame++; + if (speed_scale > 0.0) { + current_frame++; + } else { + current_frame--; + } if (current_frame >= frame_count) { - if (oneshot) { + if (one_shot) { current_frame = frame_count - 1; } else { current_frame = 0; } + } else if (current_frame < 0) { + if (one_shot) { + current_frame = 0; + } else { + current_frame = frame_count - 1; + } } time -= frame_limit; @@ -2690,13 +2689,13 @@ bool AnimatedTexture::get_pause() const { return pause; } -void AnimatedTexture::set_oneshot(bool p_oneshot) { +void AnimatedTexture::set_one_shot(bool p_one_shot) { RWLockWrite r(rw_lock); - oneshot = p_oneshot; + one_shot = p_one_shot; } -bool AnimatedTexture::get_oneshot() const { - return oneshot; +bool AnimatedTexture::get_one_shot() const { + return one_shot; } void AnimatedTexture::set_frame_texture(int p_frame, const Ref<Texture2D> &p_texture) { @@ -2716,30 +2715,30 @@ Ref<Texture2D> AnimatedTexture::get_frame_texture(int p_frame) const { return frames[p_frame].texture; } -void AnimatedTexture::set_frame_delay(int p_frame, float p_delay_sec) { +void AnimatedTexture::set_frame_duration(int p_frame, float p_duration) { ERR_FAIL_INDEX(p_frame, MAX_FRAMES); RWLockRead r(rw_lock); - frames[p_frame].delay_sec = p_delay_sec; + frames[p_frame].duration = p_duration; } -float AnimatedTexture::get_frame_delay(int p_frame) const { +float AnimatedTexture::get_frame_duration(int p_frame) const { ERR_FAIL_INDEX_V(p_frame, MAX_FRAMES, 0); RWLockRead r(rw_lock); - return frames[p_frame].delay_sec; + return frames[p_frame].duration; } -void AnimatedTexture::set_fps(float p_fps) { - ERR_FAIL_COND(p_fps < 0 || p_fps >= 1000); +void AnimatedTexture::set_speed_scale(float p_scale) { + ERR_FAIL_COND(p_scale < -1000 || p_scale >= 1000); - fps = p_fps; + speed_scale = p_scale; } -float AnimatedTexture::get_fps() const { - return fps; +float AnimatedTexture::get_speed_scale() const { + return speed_scale; } int AnimatedTexture::get_width() const { @@ -2795,12 +2794,12 @@ bool AnimatedTexture::is_pixel_opaque(int p_x, int p_y) const { return true; } -void AnimatedTexture::_validate_property(PropertyInfo &property) const { - String prop = property.name; +void AnimatedTexture::_validate_property(PropertyInfo &p_property) const { + String prop = p_property.name; if (prop.begins_with("frame_")) { int frame = prop.get_slicec('/', 0).get_slicec('_', 1).to_int(); if (frame >= frame_count) { - property.usage = PROPERTY_USAGE_NONE; + p_property.usage = PROPERTY_USAGE_NONE; } } } @@ -2815,27 +2814,27 @@ void AnimatedTexture::_bind_methods() { ClassDB::bind_method(D_METHOD("set_pause", "pause"), &AnimatedTexture::set_pause); ClassDB::bind_method(D_METHOD("get_pause"), &AnimatedTexture::get_pause); - ClassDB::bind_method(D_METHOD("set_oneshot", "oneshot"), &AnimatedTexture::set_oneshot); - ClassDB::bind_method(D_METHOD("get_oneshot"), &AnimatedTexture::get_oneshot); + ClassDB::bind_method(D_METHOD("set_one_shot", "one_shot"), &AnimatedTexture::set_one_shot); + ClassDB::bind_method(D_METHOD("get_one_shot"), &AnimatedTexture::get_one_shot); - ClassDB::bind_method(D_METHOD("set_fps", "fps"), &AnimatedTexture::set_fps); - ClassDB::bind_method(D_METHOD("get_fps"), &AnimatedTexture::get_fps); + ClassDB::bind_method(D_METHOD("set_speed_scale", "scale"), &AnimatedTexture::set_speed_scale); + ClassDB::bind_method(D_METHOD("get_speed_scale"), &AnimatedTexture::get_speed_scale); ClassDB::bind_method(D_METHOD("set_frame_texture", "frame", "texture"), &AnimatedTexture::set_frame_texture); ClassDB::bind_method(D_METHOD("get_frame_texture", "frame"), &AnimatedTexture::get_frame_texture); - ClassDB::bind_method(D_METHOD("set_frame_delay", "frame", "delay"), &AnimatedTexture::set_frame_delay); - ClassDB::bind_method(D_METHOD("get_frame_delay", "frame"), &AnimatedTexture::get_frame_delay); + ClassDB::bind_method(D_METHOD("set_frame_duration", "frame", "duration"), &AnimatedTexture::set_frame_duration); + ClassDB::bind_method(D_METHOD("get_frame_duration", "frame"), &AnimatedTexture::get_frame_duration); ADD_PROPERTY(PropertyInfo(Variant::INT, "frames", PROPERTY_HINT_RANGE, "1," + itos(MAX_FRAMES), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_frames", "get_frames"); ADD_PROPERTY(PropertyInfo(Variant::INT, "current_frame", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_current_frame", "get_current_frame"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "pause"), "set_pause", "get_pause"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "oneshot"), "set_oneshot", "get_oneshot"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fps", PROPERTY_HINT_RANGE, "0,1024,0.1"), "set_fps", "get_fps"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed_scale", PROPERTY_HINT_RANGE, "-60,60,0.1,or_greater,or_lesser"), "set_speed_scale", "get_speed_scale"); for (int i = 0; i < MAX_FRAMES; i++) { ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "frame_" + itos(i) + "/texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "set_frame_texture", "get_frame_texture", i); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "frame_" + itos(i) + "/delay_sec", PROPERTY_HINT_RANGE, "0.0,16.0,0.01,suffix:s", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "set_frame_delay", "get_frame_delay", i); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "frame_" + itos(i) + "/duration", PROPERTY_HINT_RANGE, "0.0,16.0,0.01,or_greater,suffix:s", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "set_frame_duration", "get_frame_duration", i); } BIND_CONSTANT(MAX_FRAMES); @@ -2961,7 +2960,7 @@ ImageTextureLayered::LayeredType ImageTextureLayered::get_layered_type() const { return layered_type; } -Error ImageTextureLayered::_create_from_images(const Array &p_images) { +Error ImageTextureLayered::_create_from_images(const TypedArray<Image> &p_images) { Vector<Ref<Image>> images; for (int i = 0; i < p_images.size(); i++) { Ref<Image> img = p_images[i]; @@ -2972,8 +2971,8 @@ Error ImageTextureLayered::_create_from_images(const Array &p_images) { return create_from_images(images); } -Array ImageTextureLayered::_get_images() const { - Array images; +TypedArray<Image> ImageTextureLayered::_get_images() const { + TypedArray<Image> images; for (int i = 0; i < layers; i++) { images.push_back(get_layer_data(i)); } @@ -3060,7 +3059,7 @@ void ImageTextureLayered::_bind_methods() { ClassDB::bind_method(D_METHOD("_get_images"), &ImageTextureLayered::_get_images); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "_images", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_INTERNAL), "create_from_images", "_get_images"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "_images", PROPERTY_HINT_ARRAY_TYPE, "Image", PROPERTY_USAGE_INTERNAL), "create_from_images", "_get_images"); } ImageTextureLayered::ImageTextureLayered(LayeredType p_layered_type) { @@ -3107,7 +3106,7 @@ Error CompressedTextureLayered::_load_data(const String &p_path, Vector<Ref<Imag uint32_t layer_count = f->get_32(); //layer count uint32_t type = f->get_32(); //layer count - ERR_FAIL_COND_V(type != layered_type, ERR_INVALID_DATA); + ERR_FAIL_COND_V((int)type != layered_type, ERR_INVALID_DATA); uint32_t df = f->get_32(); //data format mipmap_limit = int(f->get_32()); @@ -3220,7 +3219,7 @@ void CompressedTextureLayered::reload_from_file() { load(path); } -void CompressedTextureLayered::_validate_property(PropertyInfo &property) const { +void CompressedTextureLayered::_validate_property(PropertyInfo &p_property) const { } void CompressedTextureLayered::_bind_methods() { diff --git a/scene/resources/texture.h b/scene/resources/texture.h index 5973643034..4e529de8ee 100644 --- a/scene/resources/texture.h +++ b/scene/resources/texture.h @@ -130,7 +130,7 @@ public: bool is_pixel_opaque(int p_x, int p_y) const override; - void set_size_override(const Size2 &p_size); + void set_size_override(const Size2i &p_size); virtual void set_path(const String &p_path, bool p_take_over = false) override; @@ -251,7 +251,7 @@ private: protected: static void _bind_methods(); - void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; public: static Ref<Image> load_image_from_file(Ref<FileAccess> p_file, int p_size_limit); @@ -422,9 +422,9 @@ class ImageTextureLayered : public TextureLayered { int layers = 0; bool mipmaps = false; - Error _create_from_images(const Array &p_images); + Error _create_from_images(const TypedArray<Image> &p_images); - Array _get_images() const; + TypedArray<Image> _get_images() const; protected: static void _bind_methods(); @@ -506,7 +506,7 @@ private: protected: static void _bind_methods(); - void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; public: Image::Format get_format() const override; @@ -651,7 +651,7 @@ private: protected: static void _bind_methods(); - void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; public: Image::Format get_format() const override; @@ -769,15 +769,6 @@ public: class GradientTexture1D : public Texture2D { GDCLASS(GradientTexture1D, Texture2D); -public: - struct Point { - float offset = 0.0; - Color color; - bool operator<(const Point &p_ponit) const { - return offset < p_ponit.offset; - } - }; - private: Ref<Gradient> gradient; bool update_pending = false; @@ -883,8 +874,6 @@ VARIANT_ENUM_CAST(GradientTexture2D::Fill); VARIANT_ENUM_CAST(GradientTexture2D::Repeat); class ProxyTexture : public Texture2D { - GDCLASS(ProxyTexture, Texture2D); - private: mutable RID proxy_ph; mutable RID proxy; @@ -924,15 +913,15 @@ private: struct Frame { Ref<Texture2D> texture; - float delay_sec = 0.0; + float duration = 1.0; }; Frame frames[MAX_FRAMES]; int frame_count = 1.0; int current_frame = 0; bool pause = false; - bool oneshot = false; - float fps = 4.0; + bool one_shot = false; + float speed_scale = 1.0; float time = 0.0; @@ -942,7 +931,7 @@ private: protected: static void _bind_methods(); - void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; public: void set_frames(int p_frames); @@ -954,17 +943,17 @@ public: void set_pause(bool p_pause); bool get_pause() const; - void set_oneshot(bool p_oneshot); - bool get_oneshot() const; + void set_one_shot(bool p_one_shot); + bool get_one_shot() const; void set_frame_texture(int p_frame, const Ref<Texture2D> &p_texture); Ref<Texture2D> get_frame_texture(int p_frame) const; - void set_frame_delay(int p_frame, float p_delay_sec); - float get_frame_delay(int p_frame) const; + void set_frame_duration(int p_frame, float p_duration); + float get_frame_duration(int p_frame) const; - void set_fps(float p_fps); - float get_fps() const; + void set_speed_scale(float p_scale); + float get_speed_scale() const; virtual int get_width() const override; virtual int get_height() const override; @@ -1106,4 +1095,4 @@ public: PlaceholderTextureLayered(LAYERED_TYPE_CUBEMAP_ARRAY) {} }; -#endif +#endif // TEXTURE_H diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp index 39b77568cf..3321392821 100644 --- a/scene/resources/theme.cpp +++ b/scene/resources/theme.cpp @@ -31,17 +31,7 @@ #include "theme.h" #include "core/string/print_string.h" - -// Universal Theme resources used when no other theme has the item. -Ref<Theme> Theme::default_theme; -Ref<Theme> Theme::project_default_theme; - -// Universal default values, final fallback for every theme. -float Theme::fallback_base_scale = 1.0; -Ref<Texture2D> Theme::fallback_icon; -Ref<StyleBox> Theme::fallback_style; -Ref<Font> Theme::fallback_font; -int Theme::fallback_font_size = 16; +#include "scene/theme/theme_db.h" // Dynamic properties. bool Theme::_set(const StringName &p_name, const Variant &p_value) { @@ -185,64 +175,7 @@ void Theme::_get_property_list(List<PropertyInfo> *p_list) const { } } -// Universal fallback Theme resources. -Ref<Theme> Theme::get_default() { - return default_theme; -} - -void Theme::set_default(const Ref<Theme> &p_default) { - default_theme = p_default; -} - -Ref<Theme> Theme::get_project_default() { - return project_default_theme; -} - -void Theme::set_project_default(const Ref<Theme> &p_project_default) { - project_default_theme = p_project_default; -} - -// Universal fallback values for theme item types. -void Theme::set_fallback_base_scale(float p_base_scale) { - fallback_base_scale = p_base_scale; -} - -void Theme::set_fallback_icon(const Ref<Texture2D> &p_icon) { - fallback_icon = p_icon; -} - -void Theme::set_fallback_style(const Ref<StyleBox> &p_style) { - fallback_style = p_style; -} - -void Theme::set_fallback_font(const Ref<Font> &p_font) { - fallback_font = p_font; -} - -void Theme::set_fallback_font_size(int p_font_size) { - fallback_font_size = p_font_size; -} - -float Theme::get_fallback_base_scale() { - return fallback_base_scale; -} - -Ref<Texture2D> Theme::get_fallback_icon() { - return fallback_icon; -} - -Ref<StyleBox> Theme::get_fallback_style() { - return fallback_style; -} - -Ref<Font> Theme::get_fallback_font() { - return fallback_font; -} - -int Theme::get_fallback_font_size() { - return fallback_font_size; -} - +// Static helpers. bool Theme::is_valid_type_name(const String &p_name) { for (int i = 0; i < p_name.length(); i++) { if (!is_ascii_identifier_char(p_name[i])) { @@ -295,7 +228,7 @@ void Theme::set_default_font(const Ref<Font> &p_default_font) { default_font = p_default_font; if (default_font.is_valid()) { - default_font->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(false), CONNECT_REFERENCE_COUNTED); + default_font->connect("changed", callable_mp(this, &Theme::_emit_theme_changed).bind(false), CONNECT_REFERENCE_COUNTED); } _emit_theme_changed(); @@ -341,7 +274,7 @@ void Theme::set_icon(const StringName &p_name, const StringName &p_theme_type, c icon_map[p_theme_type][p_name] = p_icon; if (p_icon.is_valid()) { - icon_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(false), CONNECT_REFERENCE_COUNTED); + icon_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed).bind(false), CONNECT_REFERENCE_COUNTED); } _emit_theme_changed(!existing); @@ -351,7 +284,7 @@ Ref<Texture2D> Theme::get_icon(const StringName &p_name, const StringName &p_the if (icon_map.has(p_theme_type) && icon_map[p_theme_type].has(p_name) && icon_map[p_theme_type][p_name].is_valid()) { return icon_map[p_theme_type][p_name]; } else { - return fallback_icon; + return ThemeDB::get_singleton()->get_fallback_icon(); } } @@ -451,7 +384,7 @@ void Theme::set_stylebox(const StringName &p_name, const StringName &p_theme_typ style_map[p_theme_type][p_name] = p_style; if (p_style.is_valid()) { - style_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(false), CONNECT_REFERENCE_COUNTED); + style_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed).bind(false), CONNECT_REFERENCE_COUNTED); } _emit_theme_changed(!existing); @@ -461,7 +394,7 @@ Ref<StyleBox> Theme::get_stylebox(const StringName &p_name, const StringName &p_ if (style_map.has(p_theme_type) && style_map[p_theme_type].has(p_name) && style_map[p_theme_type][p_name].is_valid()) { return style_map[p_theme_type][p_name]; } else { - return fallback_style; + return ThemeDB::get_singleton()->get_fallback_stylebox(); } } @@ -561,7 +494,7 @@ void Theme::set_font(const StringName &p_name, const StringName &p_theme_type, c font_map[p_theme_type][p_name] = p_font; if (p_font.is_valid()) { - font_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(false), CONNECT_REFERENCE_COUNTED); + font_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed).bind(false), CONNECT_REFERENCE_COUNTED); } _emit_theme_changed(!existing); @@ -573,7 +506,7 @@ Ref<Font> Theme::get_font(const StringName &p_name, const StringName &p_theme_ty } else if (has_default_font()) { return default_font; } else { - return fallback_font; + return ThemeDB::get_singleton()->get_fallback_font(); } } @@ -676,7 +609,7 @@ int Theme::get_font_size(const StringName &p_name, const StringName &p_theme_typ } else if (has_default_font_size()) { return default_font_size; } else { - return fallback_font_size; + return ThemeDB::get_singleton()->get_fallback_font_size(); } } diff --git a/scene/resources/theme.h b/scene/resources/theme.h index 87d7d2fdea..ed1dc7c938 100644 --- a/scene/resources/theme.h +++ b/scene/resources/theme.h @@ -102,17 +102,6 @@ protected: bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; - // Universal Theme resources used when no other theme has the item. - static Ref<Theme> default_theme; - static Ref<Theme> project_default_theme; - - // Universal default values, final fallback for every theme. - static float fallback_base_scale; - static Ref<Texture2D> fallback_icon; - static Ref<StyleBox> fallback_style; - static Ref<Font> fallback_font; - static int fallback_font_size; - // Default values configurable for each individual theme. float default_base_scale = 0.0; Ref<Font> default_font; @@ -126,24 +115,6 @@ protected: virtual void reset_state() override; public: - static Ref<Theme> get_default(); - static void set_default(const Ref<Theme> &p_default); - - static Ref<Theme> get_project_default(); - static void set_project_default(const Ref<Theme> &p_project_default); - - static void set_fallback_base_scale(float p_base_scale); - static void set_fallback_icon(const Ref<Texture2D> &p_icon); - static void set_fallback_style(const Ref<StyleBox> &p_style); - static void set_fallback_font(const Ref<Font> &p_font); - static void set_fallback_font_size(int p_font_size); - - static float get_fallback_base_scale(); - static Ref<Texture2D> get_fallback_icon(); - static Ref<StyleBox> get_fallback_style(); - static Ref<Font> get_fallback_font(); - static int get_fallback_font_size(); - static bool is_valid_type_name(const String &p_name); static bool is_valid_item_name(const String &p_name); @@ -256,4 +227,4 @@ public: VARIANT_ENUM_CAST(Theme::DataType); -#endif +#endif // THEME_H diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index 05483db1e4..65f3767449 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -118,7 +118,7 @@ void TileMapPattern::remove_cell(const Vector2i &p_coords, bool p_update_size) { pattern.erase(p_coords); if (p_update_size) { - size = Vector2i(); + size = Size2i(); for (const KeyValue<Vector2i, TileMapCell> &E : pattern) { size = size.max(E.key + Vector2i(1, 1)); } @@ -157,11 +157,11 @@ TypedArray<Vector2i> TileMapPattern::get_used_cells() const { return a; } -Vector2i TileMapPattern::get_size() const { +Size2i TileMapPattern::get_size() const { return size; } -void TileMapPattern::set_size(const Vector2i &p_size) { +void TileMapPattern::set_size(const Size2i &p_size) { for (const KeyValue<Vector2i, TileMapCell> &E : pattern) { Vector2i coords = E.key; if (p_size.x <= coords.x || p_size.y <= coords.y) { @@ -178,7 +178,7 @@ bool TileMapPattern::is_empty() const { }; void TileMapPattern::clear() { - size = Vector2i(); + size = Size2i(); pattern.clear(); emit_changed(); }; @@ -1048,13 +1048,13 @@ int TileSet::get_custom_data_layer_by_name(String p_value) const { } } -void TileSet::set_custom_data_name(int p_layer_id, String p_value) { +void TileSet::set_custom_data_layer_name(int p_layer_id, String p_value) { ERR_FAIL_INDEX(p_layer_id, custom_data_layers.size()); // Exit if another property has the same name. if (!p_value.is_empty()) { for (int other_layer_id = 0; other_layer_id < get_custom_data_layers_count(); other_layer_id++) { - if (other_layer_id != p_layer_id && get_custom_data_name(other_layer_id) == p_value) { + if (other_layer_id != p_layer_id && get_custom_data_layer_name(other_layer_id) == p_value) { ERR_FAIL_MSG(vformat("There is already a custom property named %s", p_value)); } } @@ -1070,12 +1070,12 @@ void TileSet::set_custom_data_name(int p_layer_id, String p_value) { emit_changed(); } -String TileSet::get_custom_data_name(int p_layer_id) const { +String TileSet::get_custom_data_layer_name(int p_layer_id) const { ERR_FAIL_INDEX_V(p_layer_id, custom_data_layers.size(), ""); return custom_data_layers[p_layer_id].name; } -void TileSet::set_custom_data_type(int p_layer_id, Variant::Type p_value) { +void TileSet::set_custom_data_layer_type(int p_layer_id, Variant::Type p_value) { ERR_FAIL_INDEX(p_layer_id, custom_data_layers.size()); custom_data_layers.write[p_layer_id].type = p_value; @@ -1086,7 +1086,7 @@ void TileSet::set_custom_data_type(int p_layer_id, Variant::Type p_value) { emit_changed(); } -Variant::Type TileSet::get_custom_data_type(int p_layer_id) const { +Variant::Type TileSet::get_custom_data_layer_type(int p_layer_id) const { ERR_FAIL_INDEX_V(p_layer_id, custom_data_layers.size(), Variant::NIL); return custom_data_layers[p_layer_id].type; } @@ -1537,7 +1537,6 @@ Vector<Point2> TileSet::get_terrain_polygon(int p_terrain_set) { } return _get_half_offset_terrain_polygon(tile_size, overlap, tile_offset_axis); } - return Vector<Point2>(); } Vector<Point2> TileSet::get_terrain_peering_bit_polygon(int p_terrain_set, TileSet::CellNeighbor p_bit) { @@ -1801,9 +1800,9 @@ Vector<Vector<Ref<Texture2D>>> TileSet::generate_terrains_icons(Size2i p_size) { if (counts[terrain_set][terrain].count > 0) { // Get the best tile. Ref<Texture2D> texture = counts[terrain_set][terrain].texture; - Rect2 region = counts[terrain_set][terrain].region; + Rect2i region = counts[terrain_set][terrain].region; image->create(region.size.x, region.size.y, false, Image::FORMAT_RGBA8); - image->blit_rect(texture->get_image(), region, Point2()); + image->blit_rect(texture->get_image(), region, Point2i()); image->resize(p_size.x, p_size.y, Image::INTERPOLATE_NEAREST); } else { image->create(1, 1, false, Image::FORMAT_RGBA8); @@ -2467,9 +2466,42 @@ Vector<Point2> TileSet::_get_half_offset_side_terrain_peering_bit_polygon(Vector } void TileSet::reset_state() { + // Rendering occlusion_layers.clear(); + tile_lines_mesh.instantiate(); + tile_filled_mesh.instantiate(); + tile_meshes_dirty = true; + + // Physics physics_layers.clear(); + + // Terrains + terrain_sets.clear(); + terrain_meshes.clear(); + terrain_peering_bits_meshes.clear(); + per_terrain_pattern_tiles.clear(); + terrains_cache_dirty = true; + + // Navigation + navigation_layers.clear(); + custom_data_layers.clear(); + custom_data_layers_by_name.clear(); + + // Proxies + source_level_proxies.clear(); + coords_level_proxies.clear(); + alternative_level_proxies.clear(); + +#ifndef DISABLE_DEPRECATED + for (const KeyValue<int, CompatibilityTileData *> &E : compatibility_data) { + memdelete(E.value); + } + compatibility_data.clear(); +#endif // DISABLE_DEPRECATED + while (!source_ids.is_empty()) { + remove_source(source_ids[0]); + } } const Vector2i TileSetSource::INVALID_ATLAS_COORDS = Vector2i(-1, -1); @@ -3003,14 +3035,14 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) { while (index >= custom_data_layers.size()) { add_custom_data_layer(); } - set_custom_data_name(index, p_value); + set_custom_data_layer_name(index, p_value); return true; } else if (components[1] == "type") { ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false); while (index >= custom_data_layers.size()) { add_custom_data_layer(); } - set_custom_data_type(index, Variant::Type(int(p_value))); + set_custom_data_layer_type(index, Variant::Type(int(p_value))); return true; } } else if (components.size() == 2 && components[0] == "sources" && components[1].is_valid_int()) { @@ -3132,10 +3164,10 @@ bool TileSet::_get(const StringName &p_name, Variant &r_ret) const { return false; } if (components[1] == "name") { - r_ret = get_custom_data_name(index); + r_ret = get_custom_data_layer_name(index); return true; } else if (components[1] == "type") { - r_ret = get_custom_data_type(index); + r_ret = get_custom_data_layer_type(index); return true; } } else if (components.size() == 2 && components[0] == "sources" && components[1].is_valid_int()) { @@ -3269,11 +3301,11 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const { } } -void TileSet::_validate_property(PropertyInfo &property) const { - if (property.name == "tile_layout" && tile_shape == TILE_SHAPE_SQUARE) { - property.usage ^= PROPERTY_USAGE_READ_ONLY; - } else if (property.name == "tile_offset_axis" && tile_shape == TILE_SHAPE_SQUARE) { - property.usage ^= PROPERTY_USAGE_READ_ONLY; +void TileSet::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "tile_layout" && tile_shape == TILE_SHAPE_SQUARE) { + p_property.usage ^= PROPERTY_USAGE_READ_ONLY; + } else if (p_property.name == "tile_offset_axis" && tile_shape == TILE_SHAPE_SQUARE) { + p_property.usage ^= PROPERTY_USAGE_READ_ONLY; } } @@ -3358,6 +3390,11 @@ void TileSet::_bind_methods() { ClassDB::bind_method(D_METHOD("add_custom_data_layer", "to_position"), &TileSet::add_custom_data_layer, DEFVAL(-1)); ClassDB::bind_method(D_METHOD("move_custom_data_layer", "layer_index", "to_position"), &TileSet::move_custom_data_layer); ClassDB::bind_method(D_METHOD("remove_custom_data_layer", "layer_index"), &TileSet::remove_custom_data_layer); + ClassDB::bind_method(D_METHOD("get_custom_data_layer_by_name", "layer_name"), &TileSet::get_custom_data_layer_by_name); + ClassDB::bind_method(D_METHOD("set_custom_data_layer_name", "layer_index", "layer_name"), &TileSet::set_custom_data_layer_name); + ClassDB::bind_method(D_METHOD("get_custom_data_layer_name", "layer_index"), &TileSet::get_custom_data_layer_name); + ClassDB::bind_method(D_METHOD("set_custom_data_layer_type", "layer_index", "layer_type"), &TileSet::set_custom_data_layer_type); + ClassDB::bind_method(D_METHOD("get_custom_data_layer_type", "layer_index"), &TileSet::get_custom_data_layer_type); // Tile proxies ClassDB::bind_method(D_METHOD("set_source_level_tile_proxy", "source_from", "source_to"), &TileSet::set_source_level_tile_proxy); @@ -3457,6 +3494,10 @@ void TileSetSource::set_tile_set(const TileSet *p_tile_set) { tile_set = p_tile_set; } +void TileSetSource::reset_state() { + tile_set = nullptr; +}; + void TileSetSource::_bind_methods() { // Base tiles ClassDB::bind_method(D_METHOD("get_tiles_count"), &TileSetSource::get_tiles_count); @@ -3640,12 +3681,17 @@ void TileSetAtlasSource::remove_custom_data_layer(int p_index) { } void TileSetAtlasSource::reset_state() { - // Reset all TileData. + tile_set = nullptr; + for (KeyValue<Vector2i, TileAlternativesData> &E_tile : tiles) { - for (KeyValue<int, TileData *> &E_alternative : E_tile.value.alternatives) { - E_alternative.value->reset_state(); + for (const KeyValue<int, TileData *> &E_tile_data : E_tile.value.alternatives) { + memdelete(E_tile_data.value); } } + _coords_mapping_cache.clear(); + tiles.clear(); + tiles_ids.clear(); + _queue_update_padded_texture(); } void TileSetAtlasSource::set_texture(Ref<Texture2D> p_texture) { @@ -3927,7 +3973,7 @@ void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const { tile_property_list.push_back(property_info); // animation_frames_count. - tile_property_list.push_back(PropertyInfo(Variant::INT, "animation_frames_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NETWORK)); + tile_property_list.push_back(PropertyInfo(Variant::INT, "animation_frames_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); // animation_frame_*. bool store_durations = tiles[E_tile.key].animation_frames_durations.size() >= 2; @@ -4669,14 +4715,19 @@ void TileSetScenesCollectionSource::set_scene_tile_id(int p_id, int p_new_id) { void TileSetScenesCollectionSource::set_scene_tile_scene(int p_id, Ref<PackedScene> p_packed_scene) { ERR_FAIL_COND(!scenes.has(p_id)); if (p_packed_scene.is_valid()) { - // Make sure we have a root node. Supposed to be at 0 index because find_node_by_path() does not seem to work. - ERR_FAIL_COND(!p_packed_scene->get_state().is_valid()); - ERR_FAIL_COND(p_packed_scene->get_state()->get_node_count() < 1); - // Check if it extends CanvasItem. - String type = p_packed_scene->get_state()->get_node_type(0); + Ref<SceneState> scene_state = p_packed_scene->get_state(); + String type; + while (scene_state.is_valid() && type.is_empty()) { + // Make sure we have a root node. Supposed to be at 0 index because find_node_by_path() does not seem to work. + ERR_FAIL_COND(scene_state->get_node_count() < 1); + + type = scene_state->get_node_type(0); + scene_state = scene_state->get_base_scene_state(); + } + ERR_FAIL_COND_MSG(type.is_empty(), vformat("Invalid PackedScene for TileSetScenesCollectionSource: %s. Could not get the type of the root node.", p_packed_scene->get_path())); bool extends_correct_class = ClassDB::is_parent_class(type, "Control") || ClassDB::is_parent_class(type, "Node2D"); - ERR_FAIL_COND_MSG(!extends_correct_class, vformat("Invalid PackedScene for TileSetScenesCollectionSource: %s. Root node should extend Control or Node2D.", p_packed_scene->get_path())); + ERR_FAIL_COND_MSG(!extends_correct_class, vformat("Invalid PackedScene for TileSetScenesCollectionSource: %s. Root node should extend Control or Node2D. Found %s instead.", p_packed_scene->get_path(), type)); scenes[p_id].scene = p_packed_scene; } else { @@ -4805,14 +4856,14 @@ void TileData::notify_tile_data_properties_should_change() { // Convert custom data to the new type. custom_data.resize(tile_set->get_custom_data_layers_count()); for (int i = 0; i < custom_data.size(); i++) { - if (custom_data[i].get_type() != tile_set->get_custom_data_type(i)) { + if (custom_data[i].get_type() != tile_set->get_custom_data_layer_type(i)) { Variant new_val; Callable::CallError error; - if (Variant::can_convert(custom_data[i].get_type(), tile_set->get_custom_data_type(i))) { + if (Variant::can_convert(custom_data[i].get_type(), tile_set->get_custom_data_layer_type(i))) { const Variant *args[] = { &custom_data[i] }; - Variant::construct(tile_set->get_custom_data_type(i), new_val, args, 1, error); + Variant::construct(tile_set->get_custom_data_layer_type(i), new_val, args, 1, error); } else { - Variant::construct(tile_set->get_custom_data_type(i), new_val, nullptr, 0, error); + Variant::construct(tile_set->get_custom_data_layer_type(i), new_val, nullptr, 0, error); } custom_data.write[i] = new_val; } @@ -4975,13 +5026,6 @@ void TileData::remove_custom_data_layer(int p_index) { custom_data.remove_at(p_index); } -void TileData::reset_state() { - occluders.clear(); - physics.clear(); - navigation.clear(); - custom_data.clear(); -} - void TileData::set_allow_transform(bool p_allow_transform) { allow_transform = p_allow_transform; } @@ -5231,6 +5275,7 @@ void TileData::set_terrain_set(int p_terrain_set) { } if (tile_set) { ERR_FAIL_COND(p_terrain_set >= tile_set->get_terrain_sets_count()); + terrain = -1; for (int i = 0; i < 16; i++) { terrain_peering_bits[i] = -1; } @@ -5626,7 +5671,7 @@ void TileData::_get_property_list(List<PropertyInfo> *p_list) const { Variant default_val; Callable::CallError error; Variant::construct(custom_data[i].get_type(), default_val, nullptr, 0, error); - property_info = PropertyInfo(tile_set->get_custom_data_type(i), vformat("custom_data_%d", i), PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT); + property_info = PropertyInfo(tile_set->get_custom_data_layer_type(i), vformat("custom_data_%d", i), PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT); if (custom_data[i] == default_val) { property_info.usage ^= PROPERTY_USAGE_STORAGE; } diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index 7368d2bd87..e156679711 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -117,7 +117,7 @@ union TileMapCell { class TileMapPattern : public Resource { GDCLASS(TileMapPattern, Resource); - Vector2i size; + Size2i size; HashMap<Vector2i, TileMapCell> pattern; void _set_tile_data(const Vector<int> &p_data); @@ -140,8 +140,8 @@ public: TypedArray<Vector2i> get_used_cells() const; - Vector2i get_size() const; - void set_size(const Vector2i &p_size); + Size2i get_size() const; + void set_size(const Size2i &p_size); bool is_empty() const; void clear(); @@ -295,7 +295,7 @@ protected: bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; - virtual void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; private: // --- TileSet data --- @@ -479,10 +479,10 @@ public: void move_custom_data_layer(int p_from_index, int p_to_pos); void remove_custom_data_layer(int p_index); int get_custom_data_layer_by_name(String p_value) const; - void set_custom_data_name(int p_layer_id, String p_value); - String get_custom_data_name(int p_layer_id) const; - void set_custom_data_type(int p_layer_id, Variant::Type p_value); - Variant::Type get_custom_data_type(int p_layer_id) const; + void set_custom_data_layer_name(int p_layer_id, String p_value); + String get_custom_data_layer_name(int p_layer_id) const; + void set_custom_data_layer_type(int p_layer_id, Variant::Type p_value); + Variant::Type get_custom_data_layer_type(int p_layer_id) const; // Tiles proxies. void set_source_level_tile_proxy(int p_source_from, int p_source_to); @@ -569,7 +569,7 @@ public: virtual void add_custom_data_layer(int p_index){}; virtual void move_custom_data_layer(int p_from_index, int p_to_pos){}; virtual void remove_custom_data_layer(int p_index){}; - virtual void reset_state() override{}; + virtual void reset_state() override; // Tiles. virtual int get_tiles_count() const = 0; @@ -847,7 +847,6 @@ public: void add_custom_data_layer(int p_index); void move_custom_data_layer(int p_from_index, int p_to_pos); void remove_custom_data_layer(int p_index); - void reset_state(); void set_allow_transform(bool p_allow_transform); bool is_allowing_transform() const; diff --git a/scene/resources/video_stream.h b/scene/resources/video_stream.h index 3e154d539b..e14081c681 100644 --- a/scene/resources/video_stream.h +++ b/scene/resources/video_stream.h @@ -50,16 +50,16 @@ public: virtual void set_loop(bool p_enable) = 0; virtual bool has_loop() const = 0; - virtual float get_length() const = 0; + virtual double get_length() const = 0; - virtual float get_playback_position() const = 0; - virtual void seek(float p_time) = 0; + virtual double get_playback_position() const = 0; + virtual void seek(double p_time) = 0; virtual void set_audio_track(int p_idx) = 0; virtual Ref<Texture2D> get_texture() const = 0; - virtual void update(float p_delta) = 0; + virtual void update(double p_delta) = 0; virtual void set_mix_callback(AudioMixCallback p_callback, void *p_userdata) = 0; virtual int get_channels() const = 0; @@ -72,7 +72,7 @@ class VideoStream : public Resource { public: virtual void set_audio_track(int p_track) = 0; - virtual Ref<VideoStreamPlayback> instance_playback() = 0; + virtual Ref<VideoStreamPlayback> instantiate_playback() = 0; }; -#endif +#endif // VIDEO_STREAM_H diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index b8eac6de00..9174fcd9e3 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -30,6 +30,7 @@ #include "visual_shader.h" +#include "core/templates/rb_map.h" #include "core/templates/vmap.h" #include "servers/rendering/shader_types.h" #include "visual_shader_nodes.h" @@ -722,10 +723,10 @@ void VisualShader::add_node(Type p_type, const Ref<VisualShaderNode> &p_node, co n.node = p_node; n.position = p_position; - Ref<VisualShaderNodeUniform> uniform = n.node; - if (uniform.is_valid()) { - String valid_name = validate_uniform_name(uniform->get_uniform_name(), uniform); - uniform->set_uniform_name(valid_name); + Ref<VisualShaderNodeParameter> parameter = n.node; + if (parameter.is_valid()) { + String valid_name = validate_parameter_name(parameter->get_parameter_name(), parameter); + parameter->set_parameter_name(valid_name); } Ref<VisualShaderNodeInput> input = n.node; @@ -954,7 +955,7 @@ bool VisualShader::can_connect_nodes(Type p_type, int p_from_node, int p_from_po } bool VisualShader::is_port_types_compatible(int p_a, int p_b) const { - return MAX(0, p_a - 4) == (MAX(0, p_b - 4)); + return MAX(0, p_a - 5) == (MAX(0, p_b - 5)); } void VisualShader::connect_nodes_forced(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) { @@ -966,6 +967,12 @@ void VisualShader::connect_nodes_forced(Type p_type, int p_from_node, int p_from ERR_FAIL_COND(!g->nodes.has(p_to_node)); ERR_FAIL_INDEX(p_to_port, g->nodes[p_to_node].node->get_input_port_count()); + for (const Connection &E : g->connections) { + if (E.from_node == p_from_node && E.from_port == p_from_port && E.to_node == p_to_node && E.to_port == p_to_port) { + return; + } + } + Connection c; c.from_node = p_from_node; c.from_port = p_from_port; @@ -1029,11 +1036,11 @@ void VisualShader::disconnect_nodes(Type p_type, int p_from_node, int p_from_por } } -Array VisualShader::_get_node_connections(Type p_type) const { +TypedArray<Dictionary> VisualShader::_get_node_connections(Type p_type) const { ERR_FAIL_INDEX_V(p_type, TYPE_MAX, Array()); const Graph *g = &graph[p_type]; - Array ret; + TypedArray<Dictionary> ret; for (const Connection &E : g->connections) { Dictionary d; d["from_node"] = E.from_node; @@ -1272,7 +1279,7 @@ String VisualShader::validate_port_name(const String &p_port_name, VisualShaderN return name; } -String VisualShader::validate_uniform_name(const String &p_name, const Ref<VisualShaderNodeUniform> &p_uniform) const { +String VisualShader::validate_parameter_name(const String &p_name, const Ref<VisualShaderNodeParameter> &p_parameter) const { String name = p_name; //validate name first while (name.length() && !is_ascii_char(name[0])) { name = name.substr(1, name.length() - 1); @@ -1292,7 +1299,7 @@ String VisualShader::validate_uniform_name(const String &p_name, const Ref<Visua } if (name.is_empty()) { - name = p_uniform->get_caption(); + name = p_parameter->get_caption(); } int attempt = 1; @@ -1301,11 +1308,11 @@ String VisualShader::validate_uniform_name(const String &p_name, const Ref<Visua bool exists = false; for (int i = 0; i < TYPE_MAX; i++) { for (const KeyValue<int, Node> &E : graph[i].nodes) { - Ref<VisualShaderNodeUniform> node = E.value.node; - if (node == p_uniform) { //do not test on self + Ref<VisualShaderNodeParameter> node = E.value.node; + if (node == p_parameter) { //do not test on self continue; } - if (node.is_valid() && node->get_uniform_name() == name) { + if (node.is_valid() && node->get_parameter_name() == name) { exists = true; break; } @@ -1613,8 +1620,8 @@ Error VisualShader::_write_node(Type type, StringBuilder *global_code, StringBui bool skip_global = input.is_valid() && for_preview; if (!skip_global) { - Ref<VisualShaderNodeUniform> uniform = vsnode; - if (!uniform.is_valid() || !uniform->is_global_code_generated()) { + Ref<VisualShaderNodeParameter> parameter = vsnode; + if (!parameter.is_valid() || !parameter->is_global_code_generated()) { if (global_code) { *global_code += vsnode->generate_global(get_mode(), type, node); } @@ -1673,8 +1680,8 @@ Error VisualShader::_write_node(Type type, StringBuilder *global_code, StringBui VisualShaderNode *ptr = const_cast<VisualShaderNode *>(graph[type].nodes[from_node].node.ptr()); if (ptr->has_method("get_input_real_name")) { inputs[i] = ptr->call("get_input_real_name"); - } else if (ptr->has_method("get_uniform_name")) { - inputs[i] = ptr->call("get_uniform_name"); + } else if (ptr->has_method("get_parameter_name")) { + inputs[i] = ptr->call("get_parameter_name"); } else { inputs[i] = ""; } @@ -1691,13 +1698,13 @@ Error VisualShader::_write_node(Type type, StringBuilder *global_code, StringBui inputs[i] = "(" + src_var + " ? 1.0 : 0.0)"; } break; case VisualShaderNode::PORT_TYPE_VECTOR_2D: { - inputs[i] = "dot(" + src_var + ", vec2(0.5, 0.5))"; + inputs[i] = src_var + ".x"; } break; case VisualShaderNode::PORT_TYPE_VECTOR_3D: { - inputs[i] = "dot(" + src_var + ", vec3(0.333333, 0.333333, 0.333333))"; + inputs[i] = src_var + ".x"; } break; case VisualShaderNode::PORT_TYPE_VECTOR_4D: { - inputs[i] = "dot(" + src_var + ", vec4(0.25, 0.25, 0.25, 0.25))"; + inputs[i] = src_var + ".x"; } break; default: break; @@ -2143,8 +2150,8 @@ void VisualShader::_update_shader() const { static const char *func_name[TYPE_MAX] = { "vertex", "fragment", "light", "start", "process", "collide", "start_custom", "process_custom", "sky", "fog" }; String global_expressions; - HashSet<String> used_uniform_names; - List<VisualShaderNodeUniform *> uniforms; + HashSet<String> used_parameter_names; + List<VisualShaderNodeParameter *> parameters; HashMap<int, List<int>> emitters; HashMap<int, List<int>> varying_setters; @@ -2163,13 +2170,13 @@ void VisualShader::_update_shader() const { expr += "\n"; global_expressions += expr; } - Ref<VisualShaderNodeUniformRef> uniform_ref = E.value.node; - if (uniform_ref.is_valid()) { - used_uniform_names.insert(uniform_ref->get_uniform_name()); + Ref<VisualShaderNodeParameterRef> parameter_ref = E.value.node; + if (parameter_ref.is_valid()) { + used_parameter_names.insert(parameter_ref->get_parameter_name()); } - Ref<VisualShaderNodeUniform> uniform = E.value.node; - if (uniform.is_valid()) { - uniforms.push_back(uniform.ptr()); + Ref<VisualShaderNodeParameter> parameter = E.value.node; + if (parameter.is_valid()) { + parameters.push_back(parameter.ptr()); } Ref<VisualShaderNodeVaryingSetter> varying_setter = E.value.node; if (varying_setter.is_valid() && varying_setter->is_input_port_connected(0)) { @@ -2188,13 +2195,13 @@ void VisualShader::_update_shader() const { } } - for (int i = 0; i < uniforms.size(); i++) { - VisualShaderNodeUniform *uniform = uniforms[i]; - if (used_uniform_names.has(uniform->get_uniform_name())) { - global_code += uniform->generate_global(get_mode(), Type(i), -1); - const_cast<VisualShaderNodeUniform *>(uniform)->set_global_code_generated(true); + for (int i = 0; i < parameters.size(); i++) { + VisualShaderNodeParameter *parameter = parameters[i]; + if (used_parameter_names.has(parameter->get_parameter_name())) { + global_code += parameter->generate_global(get_mode(), Type(i), -1); + const_cast<VisualShaderNodeParameter *>(parameter)->set_global_code_generated(true); } else { - const_cast<VisualShaderNodeUniform *>(uniform)->set_global_code_generated(false); + const_cast<VisualShaderNodeParameter *>(parameter)->set_global_code_generated(false); } } @@ -2207,6 +2214,12 @@ void VisualShader::_update_shader() const { case VaryingType::VARYING_TYPE_FLOAT: global_code += "float "; break; + case VaryingType::VARYING_TYPE_INT: + if (E.value.mode == VaryingMode::VARYING_MODE_VERTEX_TO_FRAG_LIGHT) { + global_code += "flat "; + } + global_code += "int "; + break; case VaryingType::VARYING_TYPE_VECTOR_2D: global_code += "vec2 "; break; @@ -2216,8 +2229,11 @@ void VisualShader::_update_shader() const { case VaryingType::VARYING_TYPE_VECTOR_4D: global_code += "vec4 "; break; - case VaryingType::VARYING_TYPE_COLOR: - global_code += "vec4 "; + case VaryingType::VARYING_TYPE_BOOLEAN: + if (E.value.mode == VaryingMode::VARYING_MODE_VERTEX_TO_FRAG_LIGHT) { + global_code += "flat "; + } + global_code += "bool "; break; case VaryingType::VARYING_TYPE_TRANSFORM: global_code += "mat4 "; @@ -2270,6 +2286,9 @@ void VisualShader::_update_shader() const { case VaryingType::VARYING_TYPE_FLOAT: code2 += "0.0"; break; + case VaryingType::VARYING_TYPE_INT: + code2 += "0"; + break; case VaryingType::VARYING_TYPE_VECTOR_2D: code2 += "vec2(0.0)"; break; @@ -2279,8 +2298,8 @@ void VisualShader::_update_shader() const { case VaryingType::VARYING_TYPE_VECTOR_4D: code2 += "vec4(0.0)"; break; - case VaryingType::VARYING_TYPE_COLOR: - code2 += "vec4(0.0)"; + case VaryingType::VARYING_TYPE_BOOLEAN: + code2 += "false"; break; case VaryingType::VARYING_TYPE_TRANSFORM: code2 += "mat4(1.0)"; @@ -2501,7 +2520,7 @@ void VisualShader::_update_shader() const { const_cast<VisualShader *>(this)->set_code(final_code); for (int i = 0; i < default_tex_params.size(); i++) { for (int j = 0; j < default_tex_params[i].params.size(); j++) { - const_cast<VisualShader *>(this)->set_default_texture_param(default_tex_params[i].name, default_tex_params[i].params[j], j); + const_cast<VisualShader *>(this)->set_default_texture_parameter(default_tex_params[i].name, default_tex_params[i].params[j], j); } } if (previous_code != final_code) { @@ -2578,10 +2597,11 @@ void VisualShader::_bind_methods() { BIND_ENUM_CONSTANT(VARYING_MODE_MAX); BIND_ENUM_CONSTANT(VARYING_TYPE_FLOAT); + BIND_ENUM_CONSTANT(VARYING_TYPE_INT); BIND_ENUM_CONSTANT(VARYING_TYPE_VECTOR_2D); BIND_ENUM_CONSTANT(VARYING_TYPE_VECTOR_3D); BIND_ENUM_CONSTANT(VARYING_TYPE_VECTOR_4D); - BIND_ENUM_CONSTANT(VARYING_TYPE_COLOR); + BIND_ENUM_CONSTANT(VARYING_TYPE_BOOLEAN); BIND_ENUM_CONSTANT(VARYING_TYPE_TRANSFORM); BIND_ENUM_CONSTANT(VARYING_TYPE_MAX); @@ -2640,6 +2660,10 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR_INT, "view_index", "VIEW_INDEX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR_INT, "view_mono_left", "VIEW_MONO_LEFT" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR_INT, "view_right", "VIEW_RIGHT" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "node_position_world", "NODE_POSITION_WORLD" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "camera_position_world", "CAMERA_POSITION_WORLD" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "camera_direction_world", "CAMERA_DIRECTION_WORLD" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "node_position_view", "NODE_POSITION_VIEW" }, // Node3D, Fragment { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "fragcoord", "FRAGCOORD" }, @@ -2668,6 +2692,10 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR_INT, "view_index", "VIEW_INDEX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR_INT, "view_mono_left", "VIEW_MONO_LEFT" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR_INT, "view_right", "VIEW_RIGHT" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "node_position_world", "NODE_POSITION_WORLD" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "camera_position_world", "CAMERA_POSITION_WORLD" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "camera_direction_world", "CAMERA_DIRECTION_WORLD" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "node_position_view", "NODE_POSITION_VIEW" }, // Node3D, Light { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "fragcoord", "FRAGCOORD" }, @@ -2735,6 +2763,9 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "light", "LIGHT" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "light_color", "LIGHT_COLOR" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light_position", "LIGHT_POSITION" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light_direction", "LIGHT_DIRECTION" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_BOOLEAN, "light_is_directional", "LIGHT_IS_DIRECTIONAL" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_energy", "LIGHT_ENERGY" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light_vertex", "LIGHT_VERTEX" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "shadow", "SHADOW_MODULATE" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" }, @@ -3138,8 +3169,8 @@ String VisualShaderNodeInput::get_input_index_name(int p_index) const { return ""; } -void VisualShaderNodeInput::_validate_property(PropertyInfo &property) const { - if (property.name == "input_name") { +void VisualShaderNodeInput::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "input_name") { String port_list; int idx = 0; @@ -3157,7 +3188,7 @@ void VisualShaderNodeInput::_validate_property(PropertyInfo &property) const { if (port_list.is_empty()) { port_list = RTR("None"); } - property.hint_string = port_list; + p_property.hint_string = port_list; } } @@ -3187,20 +3218,20 @@ void VisualShaderNodeInput::_bind_methods() { VisualShaderNodeInput::VisualShaderNodeInput() { } -////////////// UniformRef +////////////// ParameterRef -List<VisualShaderNodeUniformRef::Uniform> uniforms; +RBMap<RID, List<VisualShaderNodeParameterRef::Parameter>> parameters; -void VisualShaderNodeUniformRef::add_uniform(const String &p_name, UniformType p_type) { - uniforms.push_back({ p_name, p_type }); +void VisualShaderNodeParameterRef::add_parameter(RID p_shader_rid, const String &p_name, ParameterType p_type) { + parameters[p_shader_rid].push_back({ p_name, p_type }); } -void VisualShaderNodeUniformRef::clear_uniforms() { - uniforms.clear(); +void VisualShaderNodeParameterRef::clear_parameters(RID p_shader_rid) { + parameters[p_shader_rid].clear(); } -bool VisualShaderNodeUniformRef::has_uniform(const String &p_name) { - for (const VisualShaderNodeUniformRef::Uniform &E : uniforms) { +bool VisualShaderNodeParameterRef::has_parameter(RID p_shader_rid, const String &p_name) { + for (const VisualShaderNodeParameterRef::Parameter &E : parameters[p_shader_rid]) { if (E.name == p_name) { return true; } @@ -3208,41 +3239,41 @@ bool VisualShaderNodeUniformRef::has_uniform(const String &p_name) { return false; } -String VisualShaderNodeUniformRef::get_caption() const { - return "UniformRef"; +String VisualShaderNodeParameterRef::get_caption() const { + return "ParameterRef"; } -int VisualShaderNodeUniformRef::get_input_port_count() const { +int VisualShaderNodeParameterRef::get_input_port_count() const { return 0; } -VisualShaderNodeUniformRef::PortType VisualShaderNodeUniformRef::get_input_port_type(int p_port) const { +VisualShaderNodeParameterRef::PortType VisualShaderNodeParameterRef::get_input_port_type(int p_port) const { return PortType::PORT_TYPE_SCALAR; } -String VisualShaderNodeUniformRef::get_input_port_name(int p_port) const { +String VisualShaderNodeParameterRef::get_input_port_name(int p_port) const { return ""; } -int VisualShaderNodeUniformRef::get_output_port_count() const { - switch (uniform_type) { - case UniformType::UNIFORM_TYPE_FLOAT: +int VisualShaderNodeParameterRef::get_output_port_count() const { + switch (param_type) { + case PARAMETER_TYPE_FLOAT: return 1; - case UniformType::UNIFORM_TYPE_INT: + case PARAMETER_TYPE_INT: return 1; - case UniformType::UNIFORM_TYPE_BOOLEAN: + case PARAMETER_TYPE_BOOLEAN: return 1; - case UniformType::UNIFORM_TYPE_VECTOR2: + case PARAMETER_TYPE_VECTOR2: return 1; - case UniformType::UNIFORM_TYPE_VECTOR3: + case PARAMETER_TYPE_VECTOR3: return 1; - case UniformType::UNIFORM_TYPE_VECTOR4: + case PARAMETER_TYPE_VECTOR4: return 1; - case UniformType::UNIFORM_TYPE_TRANSFORM: + case PARAMETER_TYPE_TRANSFORM: return 1; - case UniformType::UNIFORM_TYPE_COLOR: + case PARAMETER_TYPE_COLOR: return 2; - case UniformType::UNIFORM_TYPE_SAMPLER: + case UNIFORM_TYPE_SAMPLER: return 1; default: break; @@ -3250,30 +3281,30 @@ int VisualShaderNodeUniformRef::get_output_port_count() const { return 1; } -VisualShaderNodeUniformRef::PortType VisualShaderNodeUniformRef::get_output_port_type(int p_port) const { - switch (uniform_type) { - case UniformType::UNIFORM_TYPE_FLOAT: +VisualShaderNodeParameterRef::PortType VisualShaderNodeParameterRef::get_output_port_type(int p_port) const { + switch (param_type) { + case PARAMETER_TYPE_FLOAT: return PortType::PORT_TYPE_SCALAR; - case UniformType::UNIFORM_TYPE_INT: + case PARAMETER_TYPE_INT: return PortType::PORT_TYPE_SCALAR_INT; - case UniformType::UNIFORM_TYPE_BOOLEAN: + case PARAMETER_TYPE_BOOLEAN: return PortType::PORT_TYPE_BOOLEAN; - case UniformType::UNIFORM_TYPE_VECTOR2: + case PARAMETER_TYPE_VECTOR2: return PortType::PORT_TYPE_VECTOR_2D; - case UniformType::UNIFORM_TYPE_VECTOR3: + case PARAMETER_TYPE_VECTOR3: return PortType::PORT_TYPE_VECTOR_3D; - case UniformType::UNIFORM_TYPE_VECTOR4: + case PARAMETER_TYPE_VECTOR4: return PortType::PORT_TYPE_VECTOR_4D; - case UniformType::UNIFORM_TYPE_TRANSFORM: + case PARAMETER_TYPE_TRANSFORM: return PortType::PORT_TYPE_TRANSFORM; - case UniformType::UNIFORM_TYPE_COLOR: + case PARAMETER_TYPE_COLOR: if (p_port == 0) { return PortType::PORT_TYPE_VECTOR_3D; } else if (p_port == 1) { return PORT_TYPE_SCALAR; } break; - case UniformType::UNIFORM_TYPE_SAMPLER: + case UNIFORM_TYPE_SAMPLER: return PortType::PORT_TYPE_SAMPLER; default: break; @@ -3281,30 +3312,30 @@ VisualShaderNodeUniformRef::PortType VisualShaderNodeUniformRef::get_output_port return PORT_TYPE_SCALAR; } -String VisualShaderNodeUniformRef::get_output_port_name(int p_port) const { - switch (uniform_type) { - case UniformType::UNIFORM_TYPE_FLOAT: +String VisualShaderNodeParameterRef::get_output_port_name(int p_port) const { + switch (param_type) { + case PARAMETER_TYPE_FLOAT: return ""; - case UniformType::UNIFORM_TYPE_INT: + case PARAMETER_TYPE_INT: return ""; - case UniformType::UNIFORM_TYPE_BOOLEAN: + case PARAMETER_TYPE_BOOLEAN: return ""; - case UniformType::UNIFORM_TYPE_VECTOR2: + case PARAMETER_TYPE_VECTOR2: return ""; - case UniformType::UNIFORM_TYPE_VECTOR3: + case PARAMETER_TYPE_VECTOR3: return ""; - case UniformType::UNIFORM_TYPE_VECTOR4: + case PARAMETER_TYPE_VECTOR4: return ""; - case UniformType::UNIFORM_TYPE_TRANSFORM: + case PARAMETER_TYPE_TRANSFORM: return ""; - case UniformType::UNIFORM_TYPE_COLOR: + case PARAMETER_TYPE_COLOR: if (p_port == 0) { return "rgb"; } else if (p_port == 1) { return "alpha"; } break; - case UniformType::UNIFORM_TYPE_SAMPLER: + case UNIFORM_TYPE_SAMPLER: return ""; break; default: @@ -3313,65 +3344,85 @@ String VisualShaderNodeUniformRef::get_output_port_name(int p_port) const { return ""; } -void VisualShaderNodeUniformRef::set_uniform_name(const String &p_name) { - uniform_name = p_name; - if (uniform_name != "[None]") { - uniform_type = get_uniform_type_by_name(uniform_name); - } else { - uniform_type = UniformType::UNIFORM_TYPE_FLOAT; +void VisualShaderNodeParameterRef::set_shader_rid(const RID &p_shader_rid) { + shader_rid = p_shader_rid; +} + +void VisualShaderNodeParameterRef::set_parameter_name(const String &p_name) { + parameter_name = p_name; + if (shader_rid.is_valid()) { + update_parameter_type(); } emit_changed(); } -String VisualShaderNodeUniformRef::get_uniform_name() const { - return uniform_name; +void VisualShaderNodeParameterRef::update_parameter_type() { + if (parameter_name != "[None]") { + param_type = get_parameter_type_by_name(parameter_name); + } else { + param_type = PARAMETER_TYPE_FLOAT; + } } -int VisualShaderNodeUniformRef::get_uniforms_count() const { - return uniforms.size(); +String VisualShaderNodeParameterRef::get_parameter_name() const { + return parameter_name; } -String VisualShaderNodeUniformRef::get_uniform_name_by_index(int p_idx) const { - if (p_idx >= 0 && p_idx < uniforms.size()) { - return uniforms[p_idx].name; +int VisualShaderNodeParameterRef::get_parameters_count() const { + ERR_FAIL_COND_V(!shader_rid.is_valid(), 0); + + return parameters[shader_rid].size(); +} + +String VisualShaderNodeParameterRef::get_parameter_name_by_index(int p_idx) const { + ERR_FAIL_COND_V(!shader_rid.is_valid(), String()); + + if (p_idx >= 0 && p_idx < parameters[shader_rid].size()) { + return parameters[shader_rid][p_idx].name; } return ""; } -VisualShaderNodeUniformRef::UniformType VisualShaderNodeUniformRef::get_uniform_type_by_name(const String &p_name) const { - for (int i = 0; i < uniforms.size(); i++) { - if (uniforms[i].name == p_name) { - return uniforms[i].type; +VisualShaderNodeParameterRef::ParameterType VisualShaderNodeParameterRef::get_parameter_type_by_name(const String &p_name) const { + ERR_FAIL_COND_V(!shader_rid.is_valid(), PARAMETER_TYPE_FLOAT); + + for (int i = 0; i < parameters[shader_rid].size(); i++) { + if (parameters[shader_rid][i].name == p_name) { + return parameters[shader_rid][i].type; } } - return UniformType::UNIFORM_TYPE_FLOAT; + return PARAMETER_TYPE_FLOAT; } -VisualShaderNodeUniformRef::UniformType VisualShaderNodeUniformRef::get_uniform_type_by_index(int p_idx) const { - if (p_idx >= 0 && p_idx < uniforms.size()) { - return uniforms[p_idx].type; +VisualShaderNodeParameterRef::ParameterType VisualShaderNodeParameterRef::get_parameter_type_by_index(int p_idx) const { + ERR_FAIL_COND_V(!shader_rid.is_valid(), PARAMETER_TYPE_FLOAT); + + if (p_idx >= 0 && p_idx < parameters[shader_rid].size()) { + return parameters[shader_rid][p_idx].type; } - return UniformType::UNIFORM_TYPE_FLOAT; + return PARAMETER_TYPE_FLOAT; } -VisualShaderNodeUniformRef::PortType VisualShaderNodeUniformRef::get_port_type_by_index(int p_idx) const { - if (p_idx >= 0 && p_idx < uniforms.size()) { - switch (uniforms[p_idx].type) { - case UniformType::UNIFORM_TYPE_FLOAT: +VisualShaderNodeParameterRef::PortType VisualShaderNodeParameterRef::get_port_type_by_index(int p_idx) const { + ERR_FAIL_COND_V(!shader_rid.is_valid(), PORT_TYPE_SCALAR); + + if (p_idx >= 0 && p_idx < parameters[shader_rid].size()) { + switch (parameters[shader_rid][p_idx].type) { + case PARAMETER_TYPE_FLOAT: return PORT_TYPE_SCALAR; - case UniformType::UNIFORM_TYPE_INT: + case PARAMETER_TYPE_INT: return PORT_TYPE_SCALAR_INT; - case UniformType::UNIFORM_TYPE_SAMPLER: + case UNIFORM_TYPE_SAMPLER: return PORT_TYPE_SAMPLER; - case UniformType::UNIFORM_TYPE_VECTOR2: + case PARAMETER_TYPE_VECTOR2: return PORT_TYPE_VECTOR_2D; - case UniformType::UNIFORM_TYPE_VECTOR3: + case PARAMETER_TYPE_VECTOR3: return PORT_TYPE_VECTOR_3D; - case UniformType::UNIFORM_TYPE_VECTOR4: + case PARAMETER_TYPE_VECTOR4: return PORT_TYPE_VECTOR_4D; - case UniformType::UNIFORM_TYPE_TRANSFORM: + case PARAMETER_TYPE_TRANSFORM: return PORT_TYPE_TRANSFORM; - case UniformType::UNIFORM_TYPE_COLOR: + case PARAMETER_TYPE_COLOR: return PORT_TYPE_VECTOR_3D; default: break; @@ -3380,54 +3431,54 @@ VisualShaderNodeUniformRef::PortType VisualShaderNodeUniformRef::get_port_type_b return PORT_TYPE_SCALAR; } -String VisualShaderNodeUniformRef::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 { - switch (uniform_type) { - case UniformType::UNIFORM_TYPE_FLOAT: - if (uniform_name == "[None]") { +String VisualShaderNodeParameterRef::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 { + switch (param_type) { + case PARAMETER_TYPE_FLOAT: + if (parameter_name == "[None]") { return " " + p_output_vars[0] + " = 0.0;\n"; } break; - case UniformType::UNIFORM_TYPE_COLOR: { - String code = " " + p_output_vars[0] + " = " + get_uniform_name() + ".rgb;\n"; - code += " " + p_output_vars[1] + " = " + get_uniform_name() + ".a;\n"; + case PARAMETER_TYPE_COLOR: { + String code = " " + p_output_vars[0] + " = " + get_parameter_name() + ".rgb;\n"; + code += " " + p_output_vars[1] + " = " + get_parameter_name() + ".a;\n"; return code; } break; - case UniformType::UNIFORM_TYPE_SAMPLER: + case UNIFORM_TYPE_SAMPLER: return String(); default: break; } - return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; + return " " + p_output_vars[0] + " = " + get_parameter_name() + ";\n"; } -void VisualShaderNodeUniformRef::_set_uniform_type(int p_uniform_type) { - uniform_type = (UniformType)p_uniform_type; +void VisualShaderNodeParameterRef::_set_parameter_type(int p_type) { + param_type = (ParameterType)p_type; } -int VisualShaderNodeUniformRef::_get_uniform_type() const { - return (int)uniform_type; +int VisualShaderNodeParameterRef::_get_parameter_type() const { + return (int)param_type; } -void VisualShaderNodeUniformRef::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_uniform_name", "name"), &VisualShaderNodeUniformRef::set_uniform_name); - ClassDB::bind_method(D_METHOD("get_uniform_name"), &VisualShaderNodeUniformRef::get_uniform_name); +void VisualShaderNodeParameterRef::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_parameter_name", "name"), &VisualShaderNodeParameterRef::set_parameter_name); + ClassDB::bind_method(D_METHOD("get_parameter_name"), &VisualShaderNodeParameterRef::get_parameter_name); - ClassDB::bind_method(D_METHOD("_set_uniform_type", "type"), &VisualShaderNodeUniformRef::_set_uniform_type); - ClassDB::bind_method(D_METHOD("_get_uniform_type"), &VisualShaderNodeUniformRef::_get_uniform_type); + ClassDB::bind_method(D_METHOD("_set_parameter_type", "type"), &VisualShaderNodeParameterRef::_set_parameter_type); + ClassDB::bind_method(D_METHOD("_get_parameter_type"), &VisualShaderNodeParameterRef::_get_parameter_type); - ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "uniform_name", PROPERTY_HINT_ENUM, ""), "set_uniform_name", "get_uniform_name"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "uniform_type", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_uniform_type", "_get_uniform_type"); + ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "parameter_name", PROPERTY_HINT_ENUM, ""), "set_parameter_name", "get_parameter_name"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "param_type", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_parameter_type", "_get_parameter_type"); } -Vector<StringName> VisualShaderNodeUniformRef::get_editable_properties() const { +Vector<StringName> VisualShaderNodeParameterRef::get_editable_properties() const { Vector<StringName> props; - props.push_back("uniform_name"); - props.push_back("uniform_type"); + props.push_back("parameter_name"); + props.push_back("param_type"); return props; } -VisualShaderNodeUniformRef::VisualShaderNodeUniformRef() { +VisualShaderNodeParameterRef::VisualShaderNodeParameterRef() { } //////////////////////////////////////////// @@ -3639,17 +3690,17 @@ VisualShaderNodeOutput::VisualShaderNodeOutput() { /////////////////////////// -void VisualShaderNodeUniform::set_uniform_name(const String &p_name) { - uniform_name = p_name; +void VisualShaderNodeParameter::set_parameter_name(const String &p_name) { + parameter_name = p_name; emit_signal(SNAME("name_changed")); emit_changed(); } -String VisualShaderNodeUniform::get_uniform_name() const { - return uniform_name; +String VisualShaderNodeParameter::get_parameter_name() const { + return parameter_name; } -void VisualShaderNodeUniform::set_qualifier(VisualShaderNodeUniform::Qualifier p_qual) { +void VisualShaderNodeParameter::set_qualifier(VisualShaderNodeParameter::Qualifier p_qual) { ERR_FAIL_INDEX(int(p_qual), int(QUAL_MAX)); if (qualifier == p_qual) { return; @@ -3658,26 +3709,37 @@ void VisualShaderNodeUniform::set_qualifier(VisualShaderNodeUniform::Qualifier p emit_changed(); } -VisualShaderNodeUniform::Qualifier VisualShaderNodeUniform::get_qualifier() const { +VisualShaderNodeParameter::Qualifier VisualShaderNodeParameter::get_qualifier() const { return qualifier; } -void VisualShaderNodeUniform::set_global_code_generated(bool p_enabled) { +void VisualShaderNodeParameter::set_global_code_generated(bool p_enabled) { global_code_generated = p_enabled; } -bool VisualShaderNodeUniform::is_global_code_generated() const { +bool VisualShaderNodeParameter::is_global_code_generated() const { return global_code_generated; } -void VisualShaderNodeUniform::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_uniform_name", "name"), &VisualShaderNodeUniform::set_uniform_name); - ClassDB::bind_method(D_METHOD("get_uniform_name"), &VisualShaderNodeUniform::get_uniform_name); +#ifndef DISABLE_DEPRECATED +// Kept for compatibility from 3.x to 4.0. +bool VisualShaderNodeParameter::_set(const StringName &p_name, const Variant &p_value) { + if (p_name == "uniform_name") { + set_parameter_name(p_value); + return true; + } + return false; +} +#endif + +void VisualShaderNodeParameter::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_parameter_name", "name"), &VisualShaderNodeParameter::set_parameter_name); + ClassDB::bind_method(D_METHOD("get_parameter_name"), &VisualShaderNodeParameter::get_parameter_name); - ClassDB::bind_method(D_METHOD("set_qualifier", "qualifier"), &VisualShaderNodeUniform::set_qualifier); - ClassDB::bind_method(D_METHOD("get_qualifier"), &VisualShaderNodeUniform::get_qualifier); + ClassDB::bind_method(D_METHOD("set_qualifier", "qualifier"), &VisualShaderNodeParameter::set_qualifier); + ClassDB::bind_method(D_METHOD("get_qualifier"), &VisualShaderNodeParameter::get_qualifier); - ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "uniform_name"), "set_uniform_name", "get_uniform_name"); + ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "parameter_name"), "set_parameter_name", "get_parameter_name"); ADD_PROPERTY(PropertyInfo(Variant::INT, "qualifier", PROPERTY_HINT_ENUM, "None,Global,Instance"), "set_qualifier", "get_qualifier"); BIND_ENUM_CONSTANT(QUAL_NONE); @@ -3686,7 +3748,7 @@ void VisualShaderNodeUniform::_bind_methods() { BIND_ENUM_CONSTANT(QUAL_MAX); } -String VisualShaderNodeUniform::_get_qual_str() const { +String VisualShaderNodeParameter::_get_qual_str() const { if (is_qualifier_supported(qualifier)) { switch (qualifier) { case QUAL_NONE: @@ -3702,11 +3764,11 @@ String VisualShaderNodeUniform::_get_qual_str() const { return String(); } -String VisualShaderNodeUniform::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const { +String VisualShaderNodeParameter::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const { List<String> keyword_list; ShaderLanguage::get_keyword_list(&keyword_list); - if (keyword_list.find(uniform_name)) { - return RTR("Shader keywords cannot be used as uniform names.\nChoose another name."); + if (keyword_list.find(parameter_name)) { + return RTR("Shader keywords cannot be used as parameter names.\nChoose another name."); } if (!is_qualifier_supported(qualifier)) { String qualifier_str; @@ -3722,66 +3784,66 @@ String VisualShaderNodeUniform::get_warning(Shader::Mode p_mode, VisualShader::T default: break; } - return vformat(RTR("This uniform type does not support the '%s' qualifier."), qualifier_str); + return vformat(RTR("This parameter type does not support the '%s' qualifier."), qualifier_str); } else if (qualifier == Qualifier::QUAL_GLOBAL) { - RS::GlobalVariableType gvt = RS::get_singleton()->global_variable_get_type(uniform_name); + RS::GlobalShaderParameterType gvt = RS::get_singleton()->global_shader_parameter_get_type(parameter_name); if (gvt == RS::GLOBAL_VAR_TYPE_MAX) { - return vformat(RTR("Global uniform '%s' does not exist.\nCreate it in the Project Settings."), uniform_name); + return vformat(RTR("Global parameter '%s' does not exist.\nCreate it in the Project Settings."), parameter_name); } bool incompatible_type = false; switch (gvt) { case RS::GLOBAL_VAR_TYPE_FLOAT: { - if (!Object::cast_to<VisualShaderNodeFloatUniform>(this)) { + if (!Object::cast_to<VisualShaderNodeFloatParameter>(this)) { incompatible_type = true; } } break; case RS::GLOBAL_VAR_TYPE_INT: { - if (!Object::cast_to<VisualShaderNodeIntUniform>(this)) { + if (!Object::cast_to<VisualShaderNodeIntParameter>(this)) { incompatible_type = true; } } break; case RS::GLOBAL_VAR_TYPE_BOOL: { - if (!Object::cast_to<VisualShaderNodeBooleanUniform>(this)) { + if (!Object::cast_to<VisualShaderNodeBooleanParameter>(this)) { incompatible_type = true; } } break; case RS::GLOBAL_VAR_TYPE_COLOR: { - if (!Object::cast_to<VisualShaderNodeColorUniform>(this)) { + if (!Object::cast_to<VisualShaderNodeColorParameter>(this)) { incompatible_type = true; } } break; case RS::GLOBAL_VAR_TYPE_VEC3: { - if (!Object::cast_to<VisualShaderNodeVec3Uniform>(this)) { + if (!Object::cast_to<VisualShaderNodeVec3Parameter>(this)) { incompatible_type = true; } } break; case RS::GLOBAL_VAR_TYPE_VEC4: { - if (!Object::cast_to<VisualShaderNodeVec4Uniform>(this)) { + if (!Object::cast_to<VisualShaderNodeVec4Parameter>(this)) { incompatible_type = true; } } break; case RS::GLOBAL_VAR_TYPE_TRANSFORM: { - if (!Object::cast_to<VisualShaderNodeTransformUniform>(this)) { + if (!Object::cast_to<VisualShaderNodeTransformParameter>(this)) { incompatible_type = true; } } break; case RS::GLOBAL_VAR_TYPE_SAMPLER2D: { - if (!Object::cast_to<VisualShaderNodeTextureUniform>(this)) { + if (!Object::cast_to<VisualShaderNodeTextureParameter>(this)) { incompatible_type = true; } } break; case RS::GLOBAL_VAR_TYPE_SAMPLER3D: { - if (!Object::cast_to<VisualShaderNodeTexture3DUniform>(this)) { + if (!Object::cast_to<VisualShaderNodeTexture3DParameter>(this)) { incompatible_type = true; } } break; case RS::GLOBAL_VAR_TYPE_SAMPLER2DARRAY: { - if (!Object::cast_to<VisualShaderNodeTexture2DArrayUniform>(this)) { + if (!Object::cast_to<VisualShaderNodeTexture2DArrayParameter>(this)) { incompatible_type = true; } } break; case RS::GLOBAL_VAR_TYPE_SAMPLERCUBE: { - if (!Object::cast_to<VisualShaderNodeCubemapUniform>(this)) { + if (!Object::cast_to<VisualShaderNodeCubemapParameter>(this)) { incompatible_type = true; } } break; @@ -3789,29 +3851,29 @@ String VisualShaderNodeUniform::get_warning(Shader::Mode p_mode, VisualShader::T break; } if (incompatible_type) { - return vformat(RTR("Global uniform '%s' has an incompatible type for this kind of node.\nChange it in the Project Settings."), uniform_name); + return vformat(RTR("Global parameter '%s' has an incompatible type for this kind of node.\nChange it in the Project Settings."), parameter_name); } } return String(); } -Vector<StringName> VisualShaderNodeUniform::get_editable_properties() const { +Vector<StringName> VisualShaderNodeParameter::get_editable_properties() const { Vector<StringName> props; props.push_back("qualifier"); return props; } -VisualShaderNodeUniform::VisualShaderNodeUniform() { +VisualShaderNodeParameter::VisualShaderNodeParameter() { } ////////////// ResizeableBase -void VisualShaderNodeResizableBase::set_size(const Vector2 &p_size) { +void VisualShaderNodeResizableBase::set_size(const Size2 &p_size) { size = p_size; } -Vector2 VisualShaderNodeResizableBase::get_size() const { +Size2 VisualShaderNodeResizableBase::get_size() const { return size; } @@ -4597,21 +4659,23 @@ void VisualShaderNodeVarying::_bind_methods() { ClassDB::bind_method(D_METHOD("get_varying_type"), &VisualShaderNodeVarying::get_varying_type); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "varying_name"), "set_varying_name", "get_varying_name"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "varying_type", PROPERTY_HINT_ENUM, "Float,Vector,Transform"), "set_varying_type", "get_varying_type"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "varying_type", PROPERTY_HINT_ENUM, "Float,Int,Vector2,Vector3,Vector4,Boolean,Transform"), "set_varying_type", "get_varying_type"); } String VisualShaderNodeVarying::get_type_str() const { switch (varying_type) { case VisualShader::VARYING_TYPE_FLOAT: return "float"; + case VisualShader::VARYING_TYPE_INT: + return "int"; case VisualShader::VARYING_TYPE_VECTOR_2D: return "vec2"; case VisualShader::VARYING_TYPE_VECTOR_3D: return "vec3"; case VisualShader::VARYING_TYPE_VECTOR_4D: return "vec4"; - case VisualShader::VARYING_TYPE_COLOR: - return "vec4"; + case VisualShader::VARYING_TYPE_BOOLEAN: + return "bool"; case VisualShader::VARYING_TYPE_TRANSFORM: return "mat4"; default: @@ -4622,17 +4686,16 @@ String VisualShaderNodeVarying::get_type_str() const { VisualShaderNodeVarying::PortType VisualShaderNodeVarying::get_port_type(VisualShader::VaryingType p_type, int p_port) const { switch (p_type) { + case VisualShader::VARYING_TYPE_INT: + return PORT_TYPE_SCALAR_INT; case VisualShader::VARYING_TYPE_VECTOR_2D: return PORT_TYPE_VECTOR_2D; case VisualShader::VARYING_TYPE_VECTOR_3D: return PORT_TYPE_VECTOR_3D; case VisualShader::VARYING_TYPE_VECTOR_4D: return PORT_TYPE_VECTOR_4D; - case VisualShader::VARYING_TYPE_COLOR: - if (p_port == 1) { - break; // scalar - } - return PORT_TYPE_VECTOR_3D; + case VisualShader::VARYING_TYPE_BOOLEAN: + return PORT_TYPE_BOOLEAN; case VisualShader::VARYING_TYPE_TRANSFORM: return PORT_TYPE_TRANSFORM; default: @@ -4676,9 +4739,6 @@ String VisualShaderNodeVaryingSetter::get_caption() const { } int VisualShaderNodeVaryingSetter::get_input_port_count() const { - if (varying_type == VisualShader::VARYING_TYPE_COLOR) { - return 2; - } return 1; } @@ -4687,13 +4747,6 @@ VisualShaderNodeVaryingSetter::PortType VisualShaderNodeVaryingSetter::get_input } String VisualShaderNodeVaryingSetter::get_input_port_name(int p_port) const { - if (varying_type == VisualShader::VARYING_TYPE_COLOR) { - if (p_port == 0) { - return "color"; - } else { - return "alpha"; - } - } return ""; } @@ -4709,20 +4762,12 @@ String VisualShaderNodeVaryingSetter::get_output_port_name(int p_port) const { return ""; } -String VisualShaderNodeVaryingSetter::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { - return vformat("varying %s %s;\n", get_type_str(), varying_name); -} - String VisualShaderNodeVaryingSetter::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; if (varying_name == "[None]") { return code; } - if (varying_type == VisualShader::VARYING_TYPE_COLOR) { - code += vformat(" %s = vec4(%s, %s);\n", varying_name, p_input_vars[0], p_input_vars[1]); - } else { - code += vformat(" %s = %s;\n", varying_name, p_input_vars[0]); - } + code += vformat(" %s = %s;\n", varying_name, p_input_vars[0]); return code; } @@ -4748,9 +4793,6 @@ String VisualShaderNodeVaryingGetter::get_input_port_name(int p_port) const { } int VisualShaderNodeVaryingGetter::get_output_port_count() const { - if (varying_type == VisualShader::VARYING_TYPE_COLOR) { - return 2; - } return 1; } @@ -4759,13 +4801,6 @@ VisualShaderNodeVaryingGetter::PortType VisualShaderNodeVaryingGetter::get_outpu } String VisualShaderNodeVaryingGetter::get_output_port_name(int p_port) const { - if (varying_type == VisualShader::VARYING_TYPE_COLOR) { - if (p_port == 0) { - return "color"; - } else { - return "alpha"; - } - } return ""; } @@ -4782,6 +4817,9 @@ String VisualShaderNodeVaryingGetter::generate_code(Shader::Mode p_mode, VisualS case VisualShader::VARYING_TYPE_FLOAT: from = "0.0"; break; + case VisualShader::VARYING_TYPE_INT: + from = "0"; + break; case VisualShader::VARYING_TYPE_VECTOR_2D: from = "vec2(0.0)"; break; @@ -4791,9 +4829,8 @@ String VisualShaderNodeVaryingGetter::generate_code(Shader::Mode p_mode, VisualS case VisualShader::VARYING_TYPE_VECTOR_4D: from = "vec4(0.0)"; break; - case VisualShader::VARYING_TYPE_COLOR: - from = "vec3(0.0)"; - from2 = "0.0"; + case VisualShader::VARYING_TYPE_BOOLEAN: + from = "false"; break; case VisualShader::VARYING_TYPE_TRANSFORM: from = "mat4(1.0)"; @@ -4801,16 +4838,6 @@ String VisualShaderNodeVaryingGetter::generate_code(Shader::Mode p_mode, VisualS default: break; } - } else if (varying_type == VisualShader::VARYING_TYPE_COLOR) { - from = varying_name + ".rgb"; - from2 = varying_name + ".a"; - } - - if (varying_type == VisualShader::VARYING_TYPE_COLOR) { - String code; - code += vformat(" %s = %s;\n", p_output_vars[0], from); - code += vformat(" %s = %s;\n", p_output_vars[1], from2); - return code; } return vformat(" %s = %s;\n", p_output_vars[0], from); } diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h index afd84e49cc..3aba550f03 100644 --- a/scene/resources/visual_shader.h +++ b/scene/resources/visual_shader.h @@ -36,7 +36,7 @@ #include "scene/gui/control.h" #include "scene/resources/shader.h" -class VisualShaderNodeUniform; +class VisualShaderNodeParameter; class VisualShaderNode; class VisualShader : public Shader { @@ -79,21 +79,21 @@ public: enum VaryingType { VARYING_TYPE_FLOAT, + VARYING_TYPE_INT, VARYING_TYPE_VECTOR_2D, VARYING_TYPE_VECTOR_3D, VARYING_TYPE_VECTOR_4D, - VARYING_TYPE_COLOR, + VARYING_TYPE_BOOLEAN, VARYING_TYPE_TRANSFORM, VARYING_TYPE_MAX, }; struct Varying { String name; - VaryingMode mode; - VaryingType type; + VaryingMode mode = VARYING_MODE_MAX; + VaryingType type = VARYING_TYPE_MAX; - Varying() { - } + Varying() {} Varying(String p_name, VaryingMode p_mode, VaryingType p_type) : name(p_name), mode(p_mode), type(p_type) {} @@ -132,7 +132,7 @@ private: Shader::Mode shader_mode = Shader::MODE_SPATIAL; mutable String previous_code; - Array _get_node_connections(Type p_type) const; + TypedArray<Dictionary> _get_node_connections(Type p_type) const; Vector2 graph_offset; @@ -228,7 +228,7 @@ public: // internal methods String generate_preview_shader(Type p_type, int p_node, int p_port, Vector<DefaultTextureParam> &r_default_tex_params) const; String validate_port_name(const String &p_port_name, VisualShaderNode *p_node, int p_port_id, bool p_output) const; - String validate_uniform_name(const String &p_name, const Ref<VisualShaderNodeUniform> &p_uniform) const; + String validate_parameter_name(const String &p_name, const Ref<VisualShaderNodeParameter> &p_parameter) const; VisualShader(); }; @@ -428,7 +428,7 @@ public: protected: static void _bind_methods(); - void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; public: virtual int get_input_port_count() const override; @@ -498,8 +498,8 @@ public: VisualShaderNodeOutput(); }; -class VisualShaderNodeUniform : public VisualShaderNode { - GDCLASS(VisualShaderNodeUniform, VisualShaderNode); +class VisualShaderNodeParameter : public VisualShaderNode { + GDCLASS(VisualShaderNodeParameter, VisualShaderNode); public: enum Qualifier { @@ -510,7 +510,7 @@ public: }; private: - String uniform_name = ""; + String parameter_name = ""; Qualifier qualifier = QUAL_NONE; bool global_code_generated = false; @@ -518,9 +518,13 @@ protected: static void _bind_methods(); String _get_qual_str() const; +#ifndef DISABLE_DEPRECATED + bool _set(const StringName &p_name, const Variant &p_value); +#endif + public: - void set_uniform_name(const String &p_name); - String get_uniform_name() const; + void set_parameter_name(const String &p_name); + String get_parameter_name() const; void set_qualifier(Qualifier p_qual); Qualifier get_qualifier() const; @@ -534,43 +538,44 @@ public: virtual Vector<StringName> get_editable_properties() const override; virtual String get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const override; - VisualShaderNodeUniform(); + VisualShaderNodeParameter(); }; -VARIANT_ENUM_CAST(VisualShaderNodeUniform::Qualifier) +VARIANT_ENUM_CAST(VisualShaderNodeParameter::Qualifier) -class VisualShaderNodeUniformRef : public VisualShaderNode { - GDCLASS(VisualShaderNodeUniformRef, VisualShaderNode); +class VisualShaderNodeParameterRef : public VisualShaderNode { + GDCLASS(VisualShaderNodeParameterRef, VisualShaderNode); public: - enum UniformType { - UNIFORM_TYPE_FLOAT, - UNIFORM_TYPE_INT, - UNIFORM_TYPE_BOOLEAN, - UNIFORM_TYPE_VECTOR2, - UNIFORM_TYPE_VECTOR3, - UNIFORM_TYPE_VECTOR4, - UNIFORM_TYPE_TRANSFORM, - UNIFORM_TYPE_COLOR, + enum ParameterType { + PARAMETER_TYPE_FLOAT, + PARAMETER_TYPE_INT, + PARAMETER_TYPE_BOOLEAN, + PARAMETER_TYPE_VECTOR2, + PARAMETER_TYPE_VECTOR3, + PARAMETER_TYPE_VECTOR4, + PARAMETER_TYPE_TRANSFORM, + PARAMETER_TYPE_COLOR, UNIFORM_TYPE_SAMPLER, }; - struct Uniform { + struct Parameter { String name; - UniformType type; + ParameterType type; }; private: - String uniform_name = "[None]"; - UniformType uniform_type = UniformType::UNIFORM_TYPE_FLOAT; + RID shader_rid; + String parameter_name = "[None]"; + ParameterType param_type = ParameterType::PARAMETER_TYPE_FLOAT; protected: static void _bind_methods(); public: - static void add_uniform(const String &p_name, UniformType p_type); - static void clear_uniforms(); - static bool has_uniform(const String &p_name); + static void add_parameter(RID p_shader_rid, const String &p_name, ParameterType p_type); + static void clear_parameters(RID p_shader_rid); + static bool has_parameter(RID p_shader_rid, const String &p_name); public: virtual String get_caption() const override; @@ -583,38 +588,42 @@ public: virtual PortType get_output_port_type(int p_port) const override; virtual String get_output_port_name(int p_port) const override; - void set_uniform_name(const String &p_name); - String get_uniform_name() const; + void set_shader_rid(const RID &p_shader); - void _set_uniform_type(int p_uniform_type); - int _get_uniform_type() const; + void set_parameter_name(const String &p_name); + String get_parameter_name() const; - int get_uniforms_count() const; - String get_uniform_name_by_index(int p_idx) const; - UniformType get_uniform_type_by_name(const String &p_name) const; - UniformType get_uniform_type_by_index(int p_idx) const; + void update_parameter_type(); + + void _set_parameter_type(int p_parameter_type); + int _get_parameter_type() const; + + int get_parameters_count() const; + String get_parameter_name_by_index(int p_idx) const; + ParameterType get_parameter_type_by_name(const String &p_name) const; + ParameterType get_parameter_type_by_index(int p_idx) const; PortType get_port_type_by_index(int p_idx) const; virtual Vector<StringName> get_editable_properties() const override; virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; - VisualShaderNodeUniformRef(); + VisualShaderNodeParameterRef(); }; class VisualShaderNodeResizableBase : public VisualShaderNode { GDCLASS(VisualShaderNodeResizableBase, VisualShaderNode); protected: - Vector2 size = Size2(0, 0); + Size2 size = Size2(0, 0); bool allow_v_resize = true; protected: static void _bind_methods(); public: - void set_size(const Vector2 &p_size); - Vector2 get_size() const; + void set_size(const Size2 &p_size); + Size2 get_size() const; bool is_allow_v_resize() const; void set_allow_v_resize(bool p_enabled); @@ -823,7 +832,6 @@ public: virtual PortType get_output_port_type(int p_port) const override; virtual String get_output_port_name(int p_port) const override; - virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override; virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; VisualShaderNodeVaryingSetter(); diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp index b8667f07fe..de13912b75 100644 --- a/scene/resources/visual_shader_nodes.cpp +++ b/scene/resources/visual_shader_nodes.cpp @@ -1015,7 +1015,7 @@ Vector<StringName> VisualShaderNodeCurveTexture::get_editable_properties() const } String VisualShaderNodeCurveTexture::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { - return "uniform sampler2D " + make_unique_id(p_type, p_id, "curve") + ";\n"; + return "uniform sampler2D " + make_unique_id(p_type, p_id, "curve") + " : repeat_disable;\n"; } String VisualShaderNodeCurveTexture::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 { @@ -1606,6 +1606,55 @@ VisualShaderNodeCubemap::VisualShaderNodeCubemap() { simple_decl = false; } +////////////// Linear Depth + +String VisualShaderNodeLinearSceneDepth::get_caption() const { + return "LinearSceneDepth"; +} + +int VisualShaderNodeLinearSceneDepth::get_input_port_count() const { + return 0; +} + +VisualShaderNodeLinearSceneDepth::PortType VisualShaderNodeLinearSceneDepth::get_input_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeLinearSceneDepth::get_input_port_name(int p_port) const { + return ""; +} + +int VisualShaderNodeLinearSceneDepth::get_output_port_count() const { + return 1; +} + +VisualShaderNodeLinearSceneDepth::PortType VisualShaderNodeLinearSceneDepth::get_output_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeLinearSceneDepth::get_output_port_name(int p_port) const { + return "linear depth"; +} + +bool VisualShaderNodeLinearSceneDepth::has_output_port_preview(int p_port) const { + return false; +} + +String VisualShaderNodeLinearSceneDepth::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 += " float _log_depth = textureLod(DEPTH_TEXTURE, SCREEN_UV, 0.0).x;\n"; + code += " vec3 _depth_ndc = vec3(SCREEN_UV * 2.0 - 1.0, _log_depth);\n"; + code += " vec4 _depth_view = INV_PROJECTION_MATRIX * vec4(_depth_ndc, 1.0);\n"; + code += " _depth_view.xyz /= _depth_view.w;"; + code += vformat(" %s = -_depth_view.z;", p_output_vars[0]); + + return code; +} + +VisualShaderNodeLinearSceneDepth::VisualShaderNodeLinearSceneDepth() { +} + ////////////// Float Op String VisualShaderNodeFloatOp::get_caption() const { @@ -1901,13 +1950,7 @@ String VisualShaderNodeVectorOp::generate_code(Shader::Mode p_mode, VisualShader code += "atan(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; break; case OP_REFLECT: - if (op_type == OP_TYPE_VECTOR_2D) { // Not supported. - code += "vec2(0.0);\n"; - } else if (op_type == OP_TYPE_VECTOR_4D) { // Not supported. - code += "vec4(0.0);\n"; - } else { - code += "reflect(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; - } + code += "reflect(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; break; case OP_STEP: code += "step(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; @@ -1967,7 +2010,7 @@ String VisualShaderNodeVectorOp::get_warning(Shader::Mode p_mode, VisualShader:: bool invalid_type = false; if (op_type == OP_TYPE_VECTOR_2D || op_type == OP_TYPE_VECTOR_4D) { - if (op == OP_CROSS || op == OP_REFLECT) { + if (op == OP_CROSS) { invalid_type = true; } } @@ -3096,6 +3139,107 @@ VisualShaderNodeUVFunc::VisualShaderNodeUVFunc() { set_input_port_default_value(2, Vector2()); // offset } +////////////// UV PolarCoord + +String VisualShaderNodeUVPolarCoord::get_caption() const { + return "UVPolarCoord"; +} + +int VisualShaderNodeUVPolarCoord::get_input_port_count() const { + return 4; +} + +VisualShaderNodeUVPolarCoord::PortType VisualShaderNodeUVPolarCoord::get_input_port_type(int p_port) const { + switch (p_port) { + case 0: + return PORT_TYPE_VECTOR_2D; // uv + case 1: + return PORT_TYPE_VECTOR_2D; // center + case 2: + return PORT_TYPE_SCALAR; // zoom + case 3: + return PORT_TYPE_SCALAR; // repeat + default: + break; + } + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeUVPolarCoord::get_input_port_name(int p_port) const { + switch (p_port) { + case 0: + return "uv"; + case 1: + return "scale"; + case 2: + return "zoom strength"; + case 3: + return "repeat"; + default: + break; + } + return ""; +} + +bool VisualShaderNodeUVPolarCoord::is_input_port_default(int p_port, Shader::Mode p_mode) const { + if (p_mode == Shader::MODE_CANVAS_ITEM || p_mode == Shader::MODE_SPATIAL) { + if (p_port == 0) { + return true; + } + } + return false; +} + +int VisualShaderNodeUVPolarCoord::get_output_port_count() const { + return 1; +} + +VisualShaderNodeUVPolarCoord::PortType VisualShaderNodeUVPolarCoord::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR_2D; +} + +String VisualShaderNodeUVPolarCoord::get_output_port_name(int p_port) const { + return "uv"; +} + +String VisualShaderNodeUVPolarCoord::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; + + String uv; + if (p_input_vars[0].is_empty()) { + if (p_mode == Shader::MODE_CANVAS_ITEM || p_mode == Shader::MODE_SPATIAL) { + uv = "UV"; + } else { + uv = "vec2(0.0)"; + } + } else { + uv = vformat("%s", p_input_vars[0]); + } + String center = vformat("%s", p_input_vars[1]); + String zoom = vformat("%s", p_input_vars[2]); + String repeat = vformat("%s", p_input_vars[3]); + + if (p_mode == Shader::MODE_CANVAS_ITEM) { + code += vformat(" vec2 __dir = %s - %s;\n", uv, center); + code += " float __radius = length(__dir) * 2.0;\n"; + code += " float __angle = atan(__dir.y, __dir.x) * 1.0/(PI * 2.0);\n"; + code += vformat(" %s = mod(vec2(__radius * %s, __angle * %s), 1.0);\n", p_output_vars[0], zoom, repeat); + } else { + code += vformat(" vec2 __dir = %s - %s;\n", uv, center); + code += " float __radius = length(__dir) * 2.0;\n"; + code += " float __angle = atan(__dir.y, __dir.x) * 1.0/(PI * 2.0);\n"; + code += vformat(" %s = vec2(__radius * %s, __angle * %s);\n", p_output_vars[0], zoom, repeat); + } + + return code; +} + +VisualShaderNodeUVPolarCoord::VisualShaderNodeUVPolarCoord() { + set_input_port_default_value(1, Vector2(0.5, 0.5)); // center + set_input_port_default_value(2, 1.0); // zoom + set_input_port_default_value(3, 1.0); // repeat +} + ////////////// Dot Product String VisualShaderNodeDotProduct::get_caption() const { @@ -4006,14 +4150,6 @@ int VisualShaderNodeVectorRefract::get_input_port_count() const { return 3; } -VisualShaderNodeVectorRefract::PortType VisualShaderNodeVectorRefract::get_input_port_type(int p_port) const { - if (p_port == 2) { - return PORT_TYPE_SCALAR; - } - - return PORT_TYPE_VECTOR_3D; -} - String VisualShaderNodeVectorRefract::get_input_port_name(int p_port) const { switch (p_port) { case 0: @@ -4030,10 +4166,6 @@ int VisualShaderNodeVectorRefract::get_output_port_count() const { return 1; } -VisualShaderNodeVectorRefract::PortType VisualShaderNodeVectorRefract::get_output_port_type(int p_port) const { - return PORT_TYPE_VECTOR_3D; -} - String VisualShaderNodeVectorRefract::get_output_port_name(int p_port) const { return ""; } @@ -4042,6 +4174,31 @@ String VisualShaderNodeVectorRefract::generate_code(Shader::Mode p_mode, VisualS return " " + p_output_vars[0] + " = refract(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n"; } +void VisualShaderNodeVectorRefract::set_op_type(OpType p_op_type) { + ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX)); + if (op_type == p_op_type) { + return; + } + switch (p_op_type) { + case OP_TYPE_VECTOR_2D: { + set_input_port_default_value(0, Vector2(), get_input_port_default_value(0)); + set_input_port_default_value(1, Vector2(), get_input_port_default_value(1)); + } break; + case OP_TYPE_VECTOR_3D: { + set_input_port_default_value(0, Vector3(), get_input_port_default_value(0)); + set_input_port_default_value(1, Vector3(), get_input_port_default_value(1)); + } break; + case OP_TYPE_VECTOR_4D: { + set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0)); + set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1)); + } break; + default: + break; + } + op_type = p_op_type; + emit_changed(); +} + VisualShaderNodeVectorRefract::VisualShaderNodeVectorRefract() { set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0)); set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0)); @@ -4559,44 +4716,44 @@ VisualShaderNodeTransformDecompose::VisualShaderNodeTransformDecompose() { set_input_port_default_value(0, Transform3D()); } -////////////// Float Uniform +////////////// Float Parameter -String VisualShaderNodeFloatUniform::get_caption() const { - return "FloatUniform"; +String VisualShaderNodeFloatParameter::get_caption() const { + return "FloatParameter"; } -int VisualShaderNodeFloatUniform::get_input_port_count() const { +int VisualShaderNodeFloatParameter::get_input_port_count() const { return 0; } -VisualShaderNodeFloatUniform::PortType VisualShaderNodeFloatUniform::get_input_port_type(int p_port) const { +VisualShaderNodeFloatParameter::PortType VisualShaderNodeFloatParameter::get_input_port_type(int p_port) const { return PORT_TYPE_SCALAR; } -String VisualShaderNodeFloatUniform::get_input_port_name(int p_port) const { +String VisualShaderNodeFloatParameter::get_input_port_name(int p_port) const { return String(); } -int VisualShaderNodeFloatUniform::get_output_port_count() const { +int VisualShaderNodeFloatParameter::get_output_port_count() const { return 1; } -VisualShaderNodeFloatUniform::PortType VisualShaderNodeFloatUniform::get_output_port_type(int p_port) const { +VisualShaderNodeFloatParameter::PortType VisualShaderNodeFloatParameter::get_output_port_type(int p_port) const { return PORT_TYPE_SCALAR; } -String VisualShaderNodeFloatUniform::get_output_port_name(int p_port) const { +String VisualShaderNodeFloatParameter::get_output_port_name(int p_port) const { return ""; //no output port means the editor will be used as port } -String VisualShaderNodeFloatUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { +String VisualShaderNodeFloatParameter::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { String code = ""; if (hint == HINT_RANGE) { - code += _get_qual_str() + "uniform float " + get_uniform_name() + " : hint_range(" + rtos(hint_range_min) + ", " + rtos(hint_range_max) + ")"; + code += _get_qual_str() + "uniform float " + get_parameter_name() + " : hint_range(" + rtos(hint_range_min) + ", " + rtos(hint_range_max) + ")"; } else if (hint == HINT_RANGE_STEP) { - code += _get_qual_str() + "uniform float " + get_uniform_name() + " : hint_range(" + rtos(hint_range_min) + ", " + rtos(hint_range_max) + ", " + rtos(hint_range_step) + ")"; + code += _get_qual_str() + "uniform float " + get_parameter_name() + " : hint_range(" + rtos(hint_range_min) + ", " + rtos(hint_range_max) + ", " + rtos(hint_range_step) + ")"; } else { - code += _get_qual_str() + "uniform float " + get_uniform_name(); + code += _get_qual_str() + "uniform float " + get_parameter_name(); } if (default_value_enabled) { code += " = " + rtos(default_value); @@ -4605,19 +4762,19 @@ String VisualShaderNodeFloatUniform::generate_global(Shader::Mode p_mode, Visual return code; } -String VisualShaderNodeFloatUniform::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 { - return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; +String VisualShaderNodeFloatParameter::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 { + return " " + p_output_vars[0] + " = " + get_parameter_name() + ";\n"; } -bool VisualShaderNodeFloatUniform::is_show_prop_names() const { +bool VisualShaderNodeFloatParameter::is_show_prop_names() const { return true; } -bool VisualShaderNodeFloatUniform::is_use_prop_slots() const { +bool VisualShaderNodeFloatParameter::is_use_prop_slots() const { return true; } -void VisualShaderNodeFloatUniform::set_hint(Hint p_hint) { +void VisualShaderNodeFloatParameter::set_hint(Hint p_hint) { ERR_FAIL_INDEX(int(p_hint), int(HINT_MAX)); if (hint == p_hint) { return; @@ -4626,11 +4783,11 @@ void VisualShaderNodeFloatUniform::set_hint(Hint p_hint) { emit_changed(); } -VisualShaderNodeFloatUniform::Hint VisualShaderNodeFloatUniform::get_hint() const { +VisualShaderNodeFloatParameter::Hint VisualShaderNodeFloatParameter::get_hint() const { return hint; } -void VisualShaderNodeFloatUniform::set_min(float p_value) { +void VisualShaderNodeFloatParameter::set_min(float p_value) { if (Math::is_equal_approx(hint_range_min, p_value)) { return; } @@ -4638,11 +4795,11 @@ void VisualShaderNodeFloatUniform::set_min(float p_value) { emit_changed(); } -float VisualShaderNodeFloatUniform::get_min() const { +float VisualShaderNodeFloatParameter::get_min() const { return hint_range_min; } -void VisualShaderNodeFloatUniform::set_max(float p_value) { +void VisualShaderNodeFloatParameter::set_max(float p_value) { if (Math::is_equal_approx(hint_range_max, p_value)) { return; } @@ -4650,11 +4807,11 @@ void VisualShaderNodeFloatUniform::set_max(float p_value) { emit_changed(); } -float VisualShaderNodeFloatUniform::get_max() const { +float VisualShaderNodeFloatParameter::get_max() const { return hint_range_max; } -void VisualShaderNodeFloatUniform::set_step(float p_value) { +void VisualShaderNodeFloatParameter::set_step(float p_value) { if (Math::is_equal_approx(hint_range_step, p_value)) { return; } @@ -4662,11 +4819,11 @@ void VisualShaderNodeFloatUniform::set_step(float p_value) { emit_changed(); } -float VisualShaderNodeFloatUniform::get_step() const { +float VisualShaderNodeFloatParameter::get_step() const { return hint_range_step; } -void VisualShaderNodeFloatUniform::set_default_value_enabled(bool p_enabled) { +void VisualShaderNodeFloatParameter::set_default_value_enabled(bool p_enabled) { if (default_value_enabled == p_enabled) { return; } @@ -4674,11 +4831,11 @@ void VisualShaderNodeFloatUniform::set_default_value_enabled(bool p_enabled) { emit_changed(); } -bool VisualShaderNodeFloatUniform::is_default_value_enabled() const { +bool VisualShaderNodeFloatParameter::is_default_value_enabled() const { return default_value_enabled; } -void VisualShaderNodeFloatUniform::set_default_value(float p_value) { +void VisualShaderNodeFloatParameter::set_default_value(float p_value) { if (Math::is_equal_approx(default_value, p_value)) { return; } @@ -4686,28 +4843,28 @@ void VisualShaderNodeFloatUniform::set_default_value(float p_value) { emit_changed(); } -float VisualShaderNodeFloatUniform::get_default_value() const { +float VisualShaderNodeFloatParameter::get_default_value() const { return default_value; } -void VisualShaderNodeFloatUniform::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_hint", "hint"), &VisualShaderNodeFloatUniform::set_hint); - ClassDB::bind_method(D_METHOD("get_hint"), &VisualShaderNodeFloatUniform::get_hint); +void VisualShaderNodeFloatParameter::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_hint", "hint"), &VisualShaderNodeFloatParameter::set_hint); + ClassDB::bind_method(D_METHOD("get_hint"), &VisualShaderNodeFloatParameter::get_hint); - ClassDB::bind_method(D_METHOD("set_min", "value"), &VisualShaderNodeFloatUniform::set_min); - ClassDB::bind_method(D_METHOD("get_min"), &VisualShaderNodeFloatUniform::get_min); + ClassDB::bind_method(D_METHOD("set_min", "value"), &VisualShaderNodeFloatParameter::set_min); + ClassDB::bind_method(D_METHOD("get_min"), &VisualShaderNodeFloatParameter::get_min); - ClassDB::bind_method(D_METHOD("set_max", "value"), &VisualShaderNodeFloatUniform::set_max); - ClassDB::bind_method(D_METHOD("get_max"), &VisualShaderNodeFloatUniform::get_max); + ClassDB::bind_method(D_METHOD("set_max", "value"), &VisualShaderNodeFloatParameter::set_max); + ClassDB::bind_method(D_METHOD("get_max"), &VisualShaderNodeFloatParameter::get_max); - ClassDB::bind_method(D_METHOD("set_step", "value"), &VisualShaderNodeFloatUniform::set_step); - ClassDB::bind_method(D_METHOD("get_step"), &VisualShaderNodeFloatUniform::get_step); + ClassDB::bind_method(D_METHOD("set_step", "value"), &VisualShaderNodeFloatParameter::set_step); + ClassDB::bind_method(D_METHOD("get_step"), &VisualShaderNodeFloatParameter::get_step); - ClassDB::bind_method(D_METHOD("set_default_value_enabled", "enabled"), &VisualShaderNodeFloatUniform::set_default_value_enabled); - ClassDB::bind_method(D_METHOD("is_default_value_enabled"), &VisualShaderNodeFloatUniform::is_default_value_enabled); + ClassDB::bind_method(D_METHOD("set_default_value_enabled", "enabled"), &VisualShaderNodeFloatParameter::set_default_value_enabled); + ClassDB::bind_method(D_METHOD("is_default_value_enabled"), &VisualShaderNodeFloatParameter::is_default_value_enabled); - ClassDB::bind_method(D_METHOD("set_default_value", "value"), &VisualShaderNodeFloatUniform::set_default_value); - ClassDB::bind_method(D_METHOD("get_default_value"), &VisualShaderNodeFloatUniform::get_default_value); + ClassDB::bind_method(D_METHOD("set_default_value", "value"), &VisualShaderNodeFloatParameter::set_default_value); + ClassDB::bind_method(D_METHOD("get_default_value"), &VisualShaderNodeFloatParameter::get_default_value); ADD_PROPERTY(PropertyInfo(Variant::INT, "hint", PROPERTY_HINT_ENUM, "None,Range,Range+Step"), "set_hint", "get_hint"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "min"), "set_min", "get_min"); @@ -4722,16 +4879,16 @@ void VisualShaderNodeFloatUniform::_bind_methods() { BIND_ENUM_CONSTANT(HINT_MAX); } -bool VisualShaderNodeFloatUniform::is_qualifier_supported(Qualifier p_qual) const { +bool VisualShaderNodeFloatParameter::is_qualifier_supported(Qualifier p_qual) const { return true; // all qualifiers are supported } -bool VisualShaderNodeFloatUniform::is_convertible_to_constant() const { +bool VisualShaderNodeFloatParameter::is_convertible_to_constant() const { return true; // conversion is allowed } -Vector<StringName> VisualShaderNodeFloatUniform::get_editable_properties() const { - Vector<StringName> props = VisualShaderNodeUniform::get_editable_properties(); +Vector<StringName> VisualShaderNodeFloatParameter::get_editable_properties() const { + Vector<StringName> props = VisualShaderNodeParameter::get_editable_properties(); props.push_back("hint"); if (hint == HINT_RANGE || hint == HINT_RANGE_STEP) { props.push_back("min"); @@ -4747,47 +4904,47 @@ Vector<StringName> VisualShaderNodeFloatUniform::get_editable_properties() const return props; } -VisualShaderNodeFloatUniform::VisualShaderNodeFloatUniform() { +VisualShaderNodeFloatParameter::VisualShaderNodeFloatParameter() { } -////////////// Integer Uniform +////////////// Integer Parametet -String VisualShaderNodeIntUniform::get_caption() const { - return "IntUniform"; +String VisualShaderNodeIntParameter::get_caption() const { + return "IntParameter"; } -int VisualShaderNodeIntUniform::get_input_port_count() const { +int VisualShaderNodeIntParameter::get_input_port_count() const { return 0; } -VisualShaderNodeIntUniform::PortType VisualShaderNodeIntUniform::get_input_port_type(int p_port) const { +VisualShaderNodeIntParameter::PortType VisualShaderNodeIntParameter::get_input_port_type(int p_port) const { return PORT_TYPE_SCALAR_INT; } -String VisualShaderNodeIntUniform::get_input_port_name(int p_port) const { +String VisualShaderNodeIntParameter::get_input_port_name(int p_port) const { return String(); } -int VisualShaderNodeIntUniform::get_output_port_count() const { +int VisualShaderNodeIntParameter::get_output_port_count() const { return 1; } -VisualShaderNodeIntUniform::PortType VisualShaderNodeIntUniform::get_output_port_type(int p_port) const { +VisualShaderNodeIntParameter::PortType VisualShaderNodeIntParameter::get_output_port_type(int p_port) const { return PORT_TYPE_SCALAR_INT; } -String VisualShaderNodeIntUniform::get_output_port_name(int p_port) const { +String VisualShaderNodeIntParameter::get_output_port_name(int p_port) const { return ""; //no output port means the editor will be used as port } -String VisualShaderNodeIntUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { +String VisualShaderNodeIntParameter::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { String code = ""; if (hint == HINT_RANGE) { - code += _get_qual_str() + "uniform int " + get_uniform_name() + " : hint_range(" + itos(hint_range_min) + ", " + itos(hint_range_max) + ")"; + code += _get_qual_str() + "uniform int " + get_parameter_name() + " : hint_range(" + itos(hint_range_min) + ", " + itos(hint_range_max) + ")"; } else if (hint == HINT_RANGE_STEP) { - code += _get_qual_str() + "uniform int " + get_uniform_name() + " : hint_range(" + itos(hint_range_min) + ", " + itos(hint_range_max) + ", " + itos(hint_range_step) + ")"; + code += _get_qual_str() + "uniform int " + get_parameter_name() + " : hint_range(" + itos(hint_range_min) + ", " + itos(hint_range_max) + ", " + itos(hint_range_step) + ")"; } else { - code += _get_qual_str() + "uniform int " + get_uniform_name(); + code += _get_qual_str() + "uniform int " + get_parameter_name(); } if (default_value_enabled) { code += " = " + itos(default_value); @@ -4796,19 +4953,19 @@ String VisualShaderNodeIntUniform::generate_global(Shader::Mode p_mode, VisualSh return code; } -String VisualShaderNodeIntUniform::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 { - return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; +String VisualShaderNodeIntParameter::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 { + return " " + p_output_vars[0] + " = " + get_parameter_name() + ";\n"; } -bool VisualShaderNodeIntUniform::is_show_prop_names() const { +bool VisualShaderNodeIntParameter::is_show_prop_names() const { return true; } -bool VisualShaderNodeIntUniform::is_use_prop_slots() const { +bool VisualShaderNodeIntParameter::is_use_prop_slots() const { return true; } -void VisualShaderNodeIntUniform::set_hint(Hint p_hint) { +void VisualShaderNodeIntParameter::set_hint(Hint p_hint) { ERR_FAIL_INDEX(int(p_hint), int(HINT_MAX)); if (hint == p_hint) { return; @@ -4817,11 +4974,11 @@ void VisualShaderNodeIntUniform::set_hint(Hint p_hint) { emit_changed(); } -VisualShaderNodeIntUniform::Hint VisualShaderNodeIntUniform::get_hint() const { +VisualShaderNodeIntParameter::Hint VisualShaderNodeIntParameter::get_hint() const { return hint; } -void VisualShaderNodeIntUniform::set_min(int p_value) { +void VisualShaderNodeIntParameter::set_min(int p_value) { if (hint_range_min == p_value) { return; } @@ -4829,11 +4986,11 @@ void VisualShaderNodeIntUniform::set_min(int p_value) { emit_changed(); } -int VisualShaderNodeIntUniform::get_min() const { +int VisualShaderNodeIntParameter::get_min() const { return hint_range_min; } -void VisualShaderNodeIntUniform::set_max(int p_value) { +void VisualShaderNodeIntParameter::set_max(int p_value) { if (hint_range_max == p_value) { return; } @@ -4841,11 +4998,11 @@ void VisualShaderNodeIntUniform::set_max(int p_value) { emit_changed(); } -int VisualShaderNodeIntUniform::get_max() const { +int VisualShaderNodeIntParameter::get_max() const { return hint_range_max; } -void VisualShaderNodeIntUniform::set_step(int p_value) { +void VisualShaderNodeIntParameter::set_step(int p_value) { if (hint_range_step == p_value) { return; } @@ -4853,11 +5010,11 @@ void VisualShaderNodeIntUniform::set_step(int p_value) { emit_changed(); } -int VisualShaderNodeIntUniform::get_step() const { +int VisualShaderNodeIntParameter::get_step() const { return hint_range_step; } -void VisualShaderNodeIntUniform::set_default_value_enabled(bool p_default_value_enabled) { +void VisualShaderNodeIntParameter::set_default_value_enabled(bool p_default_value_enabled) { if (default_value_enabled == p_default_value_enabled) { return; } @@ -4865,11 +5022,11 @@ void VisualShaderNodeIntUniform::set_default_value_enabled(bool p_default_value_ emit_changed(); } -bool VisualShaderNodeIntUniform::is_default_value_enabled() const { +bool VisualShaderNodeIntParameter::is_default_value_enabled() const { return default_value_enabled; } -void VisualShaderNodeIntUniform::set_default_value(int p_default_value) { +void VisualShaderNodeIntParameter::set_default_value(int p_default_value) { if (default_value == p_default_value) { return; } @@ -4877,28 +5034,28 @@ void VisualShaderNodeIntUniform::set_default_value(int p_default_value) { emit_changed(); } -int VisualShaderNodeIntUniform::get_default_value() const { +int VisualShaderNodeIntParameter::get_default_value() const { return default_value; } -void VisualShaderNodeIntUniform::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_hint", "hint"), &VisualShaderNodeIntUniform::set_hint); - ClassDB::bind_method(D_METHOD("get_hint"), &VisualShaderNodeIntUniform::get_hint); +void VisualShaderNodeIntParameter::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_hint", "hint"), &VisualShaderNodeIntParameter::set_hint); + ClassDB::bind_method(D_METHOD("get_hint"), &VisualShaderNodeIntParameter::get_hint); - ClassDB::bind_method(D_METHOD("set_min", "value"), &VisualShaderNodeIntUniform::set_min); - ClassDB::bind_method(D_METHOD("get_min"), &VisualShaderNodeIntUniform::get_min); + ClassDB::bind_method(D_METHOD("set_min", "value"), &VisualShaderNodeIntParameter::set_min); + ClassDB::bind_method(D_METHOD("get_min"), &VisualShaderNodeIntParameter::get_min); - ClassDB::bind_method(D_METHOD("set_max", "value"), &VisualShaderNodeIntUniform::set_max); - ClassDB::bind_method(D_METHOD("get_max"), &VisualShaderNodeIntUniform::get_max); + ClassDB::bind_method(D_METHOD("set_max", "value"), &VisualShaderNodeIntParameter::set_max); + ClassDB::bind_method(D_METHOD("get_max"), &VisualShaderNodeIntParameter::get_max); - ClassDB::bind_method(D_METHOD("set_step", "value"), &VisualShaderNodeIntUniform::set_step); - ClassDB::bind_method(D_METHOD("get_step"), &VisualShaderNodeIntUniform::get_step); + ClassDB::bind_method(D_METHOD("set_step", "value"), &VisualShaderNodeIntParameter::set_step); + ClassDB::bind_method(D_METHOD("get_step"), &VisualShaderNodeIntParameter::get_step); - ClassDB::bind_method(D_METHOD("set_default_value_enabled", "enabled"), &VisualShaderNodeIntUniform::set_default_value_enabled); - ClassDB::bind_method(D_METHOD("is_default_value_enabled"), &VisualShaderNodeIntUniform::is_default_value_enabled); + ClassDB::bind_method(D_METHOD("set_default_value_enabled", "enabled"), &VisualShaderNodeIntParameter::set_default_value_enabled); + ClassDB::bind_method(D_METHOD("is_default_value_enabled"), &VisualShaderNodeIntParameter::is_default_value_enabled); - ClassDB::bind_method(D_METHOD("set_default_value", "value"), &VisualShaderNodeIntUniform::set_default_value); - ClassDB::bind_method(D_METHOD("get_default_value"), &VisualShaderNodeIntUniform::get_default_value); + ClassDB::bind_method(D_METHOD("set_default_value", "value"), &VisualShaderNodeIntParameter::set_default_value); + ClassDB::bind_method(D_METHOD("get_default_value"), &VisualShaderNodeIntParameter::get_default_value); ADD_PROPERTY(PropertyInfo(Variant::INT, "hint", PROPERTY_HINT_ENUM, "None,Range,Range + Step"), "set_hint", "get_hint"); ADD_PROPERTY(PropertyInfo(Variant::INT, "min"), "set_min", "get_min"); @@ -4913,16 +5070,16 @@ void VisualShaderNodeIntUniform::_bind_methods() { BIND_ENUM_CONSTANT(HINT_MAX); } -bool VisualShaderNodeIntUniform::is_qualifier_supported(Qualifier p_qual) const { +bool VisualShaderNodeIntParameter::is_qualifier_supported(Qualifier p_qual) const { return true; // all qualifiers are supported } -bool VisualShaderNodeIntUniform::is_convertible_to_constant() const { +bool VisualShaderNodeIntParameter::is_convertible_to_constant() const { return true; // conversion is allowed } -Vector<StringName> VisualShaderNodeIntUniform::get_editable_properties() const { - Vector<StringName> props = VisualShaderNodeUniform::get_editable_properties(); +Vector<StringName> VisualShaderNodeIntParameter::get_editable_properties() const { + Vector<StringName> props = VisualShaderNodeParameter::get_editable_properties(); props.push_back("hint"); if (hint == HINT_RANGE || hint == HINT_RANGE_STEP) { props.push_back("min"); @@ -4938,40 +5095,40 @@ Vector<StringName> VisualShaderNodeIntUniform::get_editable_properties() const { return props; } -VisualShaderNodeIntUniform::VisualShaderNodeIntUniform() { +VisualShaderNodeIntParameter::VisualShaderNodeIntParameter() { } -////////////// Boolean Uniform +////////////// Boolean Parameter -String VisualShaderNodeBooleanUniform::get_caption() const { - return "BooleanUniform"; +String VisualShaderNodeBooleanParameter::get_caption() const { + return "BooleanParameter"; } -int VisualShaderNodeBooleanUniform::get_input_port_count() const { +int VisualShaderNodeBooleanParameter::get_input_port_count() const { return 0; } -VisualShaderNodeBooleanUniform::PortType VisualShaderNodeBooleanUniform::get_input_port_type(int p_port) const { +VisualShaderNodeBooleanParameter::PortType VisualShaderNodeBooleanParameter::get_input_port_type(int p_port) const { return PORT_TYPE_BOOLEAN; } -String VisualShaderNodeBooleanUniform::get_input_port_name(int p_port) const { +String VisualShaderNodeBooleanParameter::get_input_port_name(int p_port) const { return String(); } -int VisualShaderNodeBooleanUniform::get_output_port_count() const { +int VisualShaderNodeBooleanParameter::get_output_port_count() const { return 1; } -VisualShaderNodeBooleanUniform::PortType VisualShaderNodeBooleanUniform::get_output_port_type(int p_port) const { +VisualShaderNodeBooleanParameter::PortType VisualShaderNodeBooleanParameter::get_output_port_type(int p_port) const { return PORT_TYPE_BOOLEAN; } -String VisualShaderNodeBooleanUniform::get_output_port_name(int p_port) const { +String VisualShaderNodeBooleanParameter::get_output_port_name(int p_port) const { return ""; //no output port means the editor will be used as port } -void VisualShaderNodeBooleanUniform::set_default_value_enabled(bool p_default_value_enabled) { +void VisualShaderNodeBooleanParameter::set_default_value_enabled(bool p_default_value_enabled) { if (default_value_enabled == p_default_value_enabled) { return; } @@ -4979,11 +5136,11 @@ void VisualShaderNodeBooleanUniform::set_default_value_enabled(bool p_default_va emit_changed(); } -bool VisualShaderNodeBooleanUniform::is_default_value_enabled() const { +bool VisualShaderNodeBooleanParameter::is_default_value_enabled() const { return default_value_enabled; } -void VisualShaderNodeBooleanUniform::set_default_value(bool p_default_value) { +void VisualShaderNodeBooleanParameter::set_default_value(bool p_default_value) { if (default_value == p_default_value) { return; } @@ -4991,12 +5148,12 @@ void VisualShaderNodeBooleanUniform::set_default_value(bool p_default_value) { emit_changed(); } -bool VisualShaderNodeBooleanUniform::get_default_value() const { +bool VisualShaderNodeBooleanParameter::get_default_value() const { return default_value; } -String VisualShaderNodeBooleanUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { - String code = _get_qual_str() + "uniform bool " + get_uniform_name(); +String VisualShaderNodeBooleanParameter::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + String code = _get_qual_str() + "uniform bool " + get_parameter_name(); if (default_value_enabled) { if (default_value) { code += " = true"; @@ -5008,39 +5165,39 @@ String VisualShaderNodeBooleanUniform::generate_global(Shader::Mode p_mode, Visu return code; } -String VisualShaderNodeBooleanUniform::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 { - return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; +String VisualShaderNodeBooleanParameter::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 { + return " " + p_output_vars[0] + " = " + get_parameter_name() + ";\n"; } -bool VisualShaderNodeBooleanUniform::is_show_prop_names() const { +bool VisualShaderNodeBooleanParameter::is_show_prop_names() const { return true; } -bool VisualShaderNodeBooleanUniform::is_use_prop_slots() const { +bool VisualShaderNodeBooleanParameter::is_use_prop_slots() const { return true; } -void VisualShaderNodeBooleanUniform::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_default_value_enabled", "enabled"), &VisualShaderNodeBooleanUniform::set_default_value_enabled); - ClassDB::bind_method(D_METHOD("is_default_value_enabled"), &VisualShaderNodeBooleanUniform::is_default_value_enabled); +void VisualShaderNodeBooleanParameter::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_default_value_enabled", "enabled"), &VisualShaderNodeBooleanParameter::set_default_value_enabled); + ClassDB::bind_method(D_METHOD("is_default_value_enabled"), &VisualShaderNodeBooleanParameter::is_default_value_enabled); - ClassDB::bind_method(D_METHOD("set_default_value", "value"), &VisualShaderNodeBooleanUniform::set_default_value); - ClassDB::bind_method(D_METHOD("get_default_value"), &VisualShaderNodeBooleanUniform::get_default_value); + ClassDB::bind_method(D_METHOD("set_default_value", "value"), &VisualShaderNodeBooleanParameter::set_default_value); + ClassDB::bind_method(D_METHOD("get_default_value"), &VisualShaderNodeBooleanParameter::get_default_value); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "default_value_enabled"), "set_default_value_enabled", "is_default_value_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "default_value"), "set_default_value", "get_default_value"); } -bool VisualShaderNodeBooleanUniform::is_qualifier_supported(Qualifier p_qual) const { +bool VisualShaderNodeBooleanParameter::is_qualifier_supported(Qualifier p_qual) const { return true; // all qualifiers are supported } -bool VisualShaderNodeBooleanUniform::is_convertible_to_constant() const { +bool VisualShaderNodeBooleanParameter::is_convertible_to_constant() const { return true; // conversion is allowed } -Vector<StringName> VisualShaderNodeBooleanUniform::get_editable_properties() const { - Vector<StringName> props = VisualShaderNodeUniform::get_editable_properties(); +Vector<StringName> VisualShaderNodeBooleanParameter::get_editable_properties() const { + Vector<StringName> props = VisualShaderNodeParameter::get_editable_properties(); props.push_back("default_value_enabled"); if (default_value_enabled) { props.push_back("default_value"); @@ -5048,47 +5205,47 @@ Vector<StringName> VisualShaderNodeBooleanUniform::get_editable_properties() con return props; } -VisualShaderNodeBooleanUniform::VisualShaderNodeBooleanUniform() { +VisualShaderNodeBooleanParameter::VisualShaderNodeBooleanParameter() { } -////////////// Color Uniform +////////////// Color Parameter -String VisualShaderNodeColorUniform::get_caption() const { - return "ColorUniform"; +String VisualShaderNodeColorParameter::get_caption() const { + return "ColorParameter"; } -int VisualShaderNodeColorUniform::get_input_port_count() const { +int VisualShaderNodeColorParameter::get_input_port_count() const { return 0; } -VisualShaderNodeColorUniform::PortType VisualShaderNodeColorUniform::get_input_port_type(int p_port) const { +VisualShaderNodeColorParameter::PortType VisualShaderNodeColorParameter::get_input_port_type(int p_port) const { return PORT_TYPE_SCALAR; } -String VisualShaderNodeColorUniform::get_input_port_name(int p_port) const { +String VisualShaderNodeColorParameter::get_input_port_name(int p_port) const { return String(); } -int VisualShaderNodeColorUniform::get_output_port_count() const { +int VisualShaderNodeColorParameter::get_output_port_count() const { return 1; } -VisualShaderNodeColorUniform::PortType VisualShaderNodeColorUniform::get_output_port_type(int p_port) const { +VisualShaderNodeColorParameter::PortType VisualShaderNodeColorParameter::get_output_port_type(int p_port) const { return p_port == 0 ? PORT_TYPE_VECTOR_4D : PORT_TYPE_SCALAR; } -String VisualShaderNodeColorUniform::get_output_port_name(int p_port) const { +String VisualShaderNodeColorParameter::get_output_port_name(int p_port) const { return "color"; } -bool VisualShaderNodeColorUniform::is_output_port_expandable(int p_port) const { +bool VisualShaderNodeColorParameter::is_output_port_expandable(int p_port) const { if (p_port == 0) { return true; } return false; } -void VisualShaderNodeColorUniform::set_default_value_enabled(bool p_enabled) { +void VisualShaderNodeColorParameter::set_default_value_enabled(bool p_enabled) { if (default_value_enabled == p_enabled) { return; } @@ -5096,11 +5253,11 @@ void VisualShaderNodeColorUniform::set_default_value_enabled(bool p_enabled) { emit_changed(); } -bool VisualShaderNodeColorUniform::is_default_value_enabled() const { +bool VisualShaderNodeColorParameter::is_default_value_enabled() const { return default_value_enabled; } -void VisualShaderNodeColorUniform::set_default_value(const Color &p_value) { +void VisualShaderNodeColorParameter::set_default_value(const Color &p_value) { if (default_value.is_equal_approx(p_value)) { return; } @@ -5108,12 +5265,12 @@ void VisualShaderNodeColorUniform::set_default_value(const Color &p_value) { emit_changed(); } -Color VisualShaderNodeColorUniform::get_default_value() const { +Color VisualShaderNodeColorParameter::get_default_value() const { return default_value; } -String VisualShaderNodeColorUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { - String code = _get_qual_str() + "uniform vec4 " + get_uniform_name() + " : source_color"; +String VisualShaderNodeColorParameter::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + String code = _get_qual_str() + "uniform vec4 " + get_parameter_name() + " : source_color"; if (default_value_enabled) { code += vformat(" = vec4(%.6f, %.6f, %.6f, %.6f)", default_value.r, default_value.g, default_value.b, default_value.a); } @@ -5121,35 +5278,35 @@ String VisualShaderNodeColorUniform::generate_global(Shader::Mode p_mode, Visual return code; } -String VisualShaderNodeColorUniform::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 { - return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; +String VisualShaderNodeColorParameter::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 { + return " " + p_output_vars[0] + " = " + get_parameter_name() + ";\n"; } -bool VisualShaderNodeColorUniform::is_show_prop_names() const { +bool VisualShaderNodeColorParameter::is_show_prop_names() const { return true; } -void VisualShaderNodeColorUniform::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_default_value_enabled", "enabled"), &VisualShaderNodeColorUniform::set_default_value_enabled); - ClassDB::bind_method(D_METHOD("is_default_value_enabled"), &VisualShaderNodeColorUniform::is_default_value_enabled); +void VisualShaderNodeColorParameter::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_default_value_enabled", "enabled"), &VisualShaderNodeColorParameter::set_default_value_enabled); + ClassDB::bind_method(D_METHOD("is_default_value_enabled"), &VisualShaderNodeColorParameter::is_default_value_enabled); - ClassDB::bind_method(D_METHOD("set_default_value", "value"), &VisualShaderNodeColorUniform::set_default_value); - ClassDB::bind_method(D_METHOD("get_default_value"), &VisualShaderNodeColorUniform::get_default_value); + ClassDB::bind_method(D_METHOD("set_default_value", "value"), &VisualShaderNodeColorParameter::set_default_value); + ClassDB::bind_method(D_METHOD("get_default_value"), &VisualShaderNodeColorParameter::get_default_value); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "default_value_enabled"), "set_default_value_enabled", "is_default_value_enabled"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "default_value"), "set_default_value", "get_default_value"); } -bool VisualShaderNodeColorUniform::is_qualifier_supported(Qualifier p_qual) const { +bool VisualShaderNodeColorParameter::is_qualifier_supported(Qualifier p_qual) const { return true; // all qualifiers are supported } -bool VisualShaderNodeColorUniform::is_convertible_to_constant() const { +bool VisualShaderNodeColorParameter::is_convertible_to_constant() const { return true; // conversion is allowed } -Vector<StringName> VisualShaderNodeColorUniform::get_editable_properties() const { - Vector<StringName> props = VisualShaderNodeUniform::get_editable_properties(); +Vector<StringName> VisualShaderNodeColorParameter::get_editable_properties() const { + Vector<StringName> props = VisualShaderNodeParameter::get_editable_properties(); props.push_back("default_value_enabled"); if (default_value_enabled) { props.push_back("default_value"); @@ -5157,59 +5314,59 @@ Vector<StringName> VisualShaderNodeColorUniform::get_editable_properties() const return props; } -VisualShaderNodeColorUniform::VisualShaderNodeColorUniform() { +VisualShaderNodeColorParameter::VisualShaderNodeColorParameter() { } -////////////// Vector2 Uniform +////////////// Vector2 Parameter -String VisualShaderNodeVec2Uniform::get_caption() const { - return "Vector2Uniform"; +String VisualShaderNodeVec2Parameter::get_caption() const { + return "Vector2Parameter"; } -int VisualShaderNodeVec2Uniform::get_input_port_count() const { +int VisualShaderNodeVec2Parameter::get_input_port_count() const { return 0; } -VisualShaderNodeVec2Uniform::PortType VisualShaderNodeVec2Uniform::get_input_port_type(int p_port) const { +VisualShaderNodeVec2Parameter::PortType VisualShaderNodeVec2Parameter::get_input_port_type(int p_port) const { return PORT_TYPE_VECTOR_2D; } -String VisualShaderNodeVec2Uniform::get_input_port_name(int p_port) const { +String VisualShaderNodeVec2Parameter::get_input_port_name(int p_port) const { return String(); } -int VisualShaderNodeVec2Uniform::get_output_port_count() const { +int VisualShaderNodeVec2Parameter::get_output_port_count() const { return 1; } -VisualShaderNodeVec2Uniform::PortType VisualShaderNodeVec2Uniform::get_output_port_type(int p_port) const { +VisualShaderNodeVec2Parameter::PortType VisualShaderNodeVec2Parameter::get_output_port_type(int p_port) const { return PORT_TYPE_VECTOR_2D; } -String VisualShaderNodeVec2Uniform::get_output_port_name(int p_port) const { +String VisualShaderNodeVec2Parameter::get_output_port_name(int p_port) const { return String(); } -void VisualShaderNodeVec2Uniform::set_default_value_enabled(bool p_enabled) { +void VisualShaderNodeVec2Parameter::set_default_value_enabled(bool p_enabled) { default_value_enabled = p_enabled; emit_changed(); } -bool VisualShaderNodeVec2Uniform::is_default_value_enabled() const { +bool VisualShaderNodeVec2Parameter::is_default_value_enabled() const { return default_value_enabled; } -void VisualShaderNodeVec2Uniform::set_default_value(const Vector2 &p_value) { +void VisualShaderNodeVec2Parameter::set_default_value(const Vector2 &p_value) { default_value = p_value; emit_changed(); } -Vector2 VisualShaderNodeVec2Uniform::get_default_value() const { +Vector2 VisualShaderNodeVec2Parameter::get_default_value() const { return default_value; } -String VisualShaderNodeVec2Uniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { - String code = _get_qual_str() + "uniform vec2 " + get_uniform_name(); +String VisualShaderNodeVec2Parameter::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + String code = _get_qual_str() + "uniform vec2 " + get_parameter_name(); if (default_value_enabled) { code += vformat(" = vec2(%.6f, %.6f)", default_value.x, default_value.y); } @@ -5217,39 +5374,39 @@ String VisualShaderNodeVec2Uniform::generate_global(Shader::Mode p_mode, VisualS return code; } -String VisualShaderNodeVec2Uniform::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 { - return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; +String VisualShaderNodeVec2Parameter::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 { + return " " + p_output_vars[0] + " = " + get_parameter_name() + ";\n"; } -void VisualShaderNodeVec2Uniform::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_default_value_enabled", "enabled"), &VisualShaderNodeVec2Uniform::set_default_value_enabled); - ClassDB::bind_method(D_METHOD("is_default_value_enabled"), &VisualShaderNodeVec2Uniform::is_default_value_enabled); +void VisualShaderNodeVec2Parameter::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_default_value_enabled", "enabled"), &VisualShaderNodeVec2Parameter::set_default_value_enabled); + ClassDB::bind_method(D_METHOD("is_default_value_enabled"), &VisualShaderNodeVec2Parameter::is_default_value_enabled); - ClassDB::bind_method(D_METHOD("set_default_value", "value"), &VisualShaderNodeVec2Uniform::set_default_value); - ClassDB::bind_method(D_METHOD("get_default_value"), &VisualShaderNodeVec2Uniform::get_default_value); + ClassDB::bind_method(D_METHOD("set_default_value", "value"), &VisualShaderNodeVec2Parameter::set_default_value); + ClassDB::bind_method(D_METHOD("get_default_value"), &VisualShaderNodeVec2Parameter::get_default_value); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "default_value_enabled"), "set_default_value_enabled", "is_default_value_enabled"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "default_value"), "set_default_value", "get_default_value"); } -bool VisualShaderNodeVec2Uniform::is_show_prop_names() const { +bool VisualShaderNodeVec2Parameter::is_show_prop_names() const { return true; } -bool VisualShaderNodeVec2Uniform::is_use_prop_slots() const { +bool VisualShaderNodeVec2Parameter::is_use_prop_slots() const { return true; } -bool VisualShaderNodeVec2Uniform::is_qualifier_supported(Qualifier p_qual) const { +bool VisualShaderNodeVec2Parameter::is_qualifier_supported(Qualifier p_qual) const { return true; // all qualifiers are supported } -bool VisualShaderNodeVec2Uniform::is_convertible_to_constant() const { +bool VisualShaderNodeVec2Parameter::is_convertible_to_constant() const { return true; // conversion is allowed } -Vector<StringName> VisualShaderNodeVec2Uniform::get_editable_properties() const { - Vector<StringName> props = VisualShaderNodeUniform::get_editable_properties(); +Vector<StringName> VisualShaderNodeVec2Parameter::get_editable_properties() const { + Vector<StringName> props = VisualShaderNodeParameter::get_editable_properties(); props.push_back("default_value_enabled"); if (default_value_enabled) { props.push_back("default_value"); @@ -5257,59 +5414,59 @@ Vector<StringName> VisualShaderNodeVec2Uniform::get_editable_properties() const return props; } -VisualShaderNodeVec2Uniform::VisualShaderNodeVec2Uniform() { +VisualShaderNodeVec2Parameter::VisualShaderNodeVec2Parameter() { } -////////////// Vector3 Uniform +////////////// Vector3 Parameter -String VisualShaderNodeVec3Uniform::get_caption() const { - return "Vector3Uniform"; +String VisualShaderNodeVec3Parameter::get_caption() const { + return "Vector3Parameter"; } -int VisualShaderNodeVec3Uniform::get_input_port_count() const { +int VisualShaderNodeVec3Parameter::get_input_port_count() const { return 0; } -VisualShaderNodeVec3Uniform::PortType VisualShaderNodeVec3Uniform::get_input_port_type(int p_port) const { +VisualShaderNodeVec3Parameter::PortType VisualShaderNodeVec3Parameter::get_input_port_type(int p_port) const { return PORT_TYPE_VECTOR_3D; } -String VisualShaderNodeVec3Uniform::get_input_port_name(int p_port) const { +String VisualShaderNodeVec3Parameter::get_input_port_name(int p_port) const { return String(); } -int VisualShaderNodeVec3Uniform::get_output_port_count() const { +int VisualShaderNodeVec3Parameter::get_output_port_count() const { return 1; } -VisualShaderNodeVec3Uniform::PortType VisualShaderNodeVec3Uniform::get_output_port_type(int p_port) const { +VisualShaderNodeVec3Parameter::PortType VisualShaderNodeVec3Parameter::get_output_port_type(int p_port) const { return PORT_TYPE_VECTOR_3D; } -String VisualShaderNodeVec3Uniform::get_output_port_name(int p_port) const { +String VisualShaderNodeVec3Parameter::get_output_port_name(int p_port) const { return ""; //no output port means the editor will be used as port } -void VisualShaderNodeVec3Uniform::set_default_value_enabled(bool p_enabled) { +void VisualShaderNodeVec3Parameter::set_default_value_enabled(bool p_enabled) { default_value_enabled = p_enabled; emit_changed(); } -bool VisualShaderNodeVec3Uniform::is_default_value_enabled() const { +bool VisualShaderNodeVec3Parameter::is_default_value_enabled() const { return default_value_enabled; } -void VisualShaderNodeVec3Uniform::set_default_value(const Vector3 &p_value) { +void VisualShaderNodeVec3Parameter::set_default_value(const Vector3 &p_value) { default_value = p_value; emit_changed(); } -Vector3 VisualShaderNodeVec3Uniform::get_default_value() const { +Vector3 VisualShaderNodeVec3Parameter::get_default_value() const { return default_value; } -String VisualShaderNodeVec3Uniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { - String code = _get_qual_str() + "uniform vec3 " + get_uniform_name(); +String VisualShaderNodeVec3Parameter::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + String code = _get_qual_str() + "uniform vec3 " + get_parameter_name(); if (default_value_enabled) { code += vformat(" = vec3(%.6f, %.6f, %.6f)", default_value.x, default_value.y, default_value.z); } @@ -5317,39 +5474,39 @@ String VisualShaderNodeVec3Uniform::generate_global(Shader::Mode p_mode, VisualS return code; } -String VisualShaderNodeVec3Uniform::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 { - return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; +String VisualShaderNodeVec3Parameter::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 { + return " " + p_output_vars[0] + " = " + get_parameter_name() + ";\n"; } -void VisualShaderNodeVec3Uniform::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_default_value_enabled", "enabled"), &VisualShaderNodeVec3Uniform::set_default_value_enabled); - ClassDB::bind_method(D_METHOD("is_default_value_enabled"), &VisualShaderNodeVec3Uniform::is_default_value_enabled); +void VisualShaderNodeVec3Parameter::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_default_value_enabled", "enabled"), &VisualShaderNodeVec3Parameter::set_default_value_enabled); + ClassDB::bind_method(D_METHOD("is_default_value_enabled"), &VisualShaderNodeVec3Parameter::is_default_value_enabled); - ClassDB::bind_method(D_METHOD("set_default_value", "value"), &VisualShaderNodeVec3Uniform::set_default_value); - ClassDB::bind_method(D_METHOD("get_default_value"), &VisualShaderNodeVec3Uniform::get_default_value); + ClassDB::bind_method(D_METHOD("set_default_value", "value"), &VisualShaderNodeVec3Parameter::set_default_value); + ClassDB::bind_method(D_METHOD("get_default_value"), &VisualShaderNodeVec3Parameter::get_default_value); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "default_value_enabled"), "set_default_value_enabled", "is_default_value_enabled"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "default_value"), "set_default_value", "get_default_value"); } -bool VisualShaderNodeVec3Uniform::is_show_prop_names() const { +bool VisualShaderNodeVec3Parameter::is_show_prop_names() const { return true; } -bool VisualShaderNodeVec3Uniform::is_use_prop_slots() const { +bool VisualShaderNodeVec3Parameter::is_use_prop_slots() const { return true; } -bool VisualShaderNodeVec3Uniform::is_qualifier_supported(Qualifier p_qual) const { +bool VisualShaderNodeVec3Parameter::is_qualifier_supported(Qualifier p_qual) const { return true; // all qualifiers are supported } -bool VisualShaderNodeVec3Uniform::is_convertible_to_constant() const { +bool VisualShaderNodeVec3Parameter::is_convertible_to_constant() const { return true; // conversion is allowed } -Vector<StringName> VisualShaderNodeVec3Uniform::get_editable_properties() const { - Vector<StringName> props = VisualShaderNodeUniform::get_editable_properties(); +Vector<StringName> VisualShaderNodeVec3Parameter::get_editable_properties() const { + Vector<StringName> props = VisualShaderNodeParameter::get_editable_properties(); props.push_back("default_value_enabled"); if (default_value_enabled) { props.push_back("default_value"); @@ -5357,59 +5514,59 @@ Vector<StringName> VisualShaderNodeVec3Uniform::get_editable_properties() const return props; } -VisualShaderNodeVec3Uniform::VisualShaderNodeVec3Uniform() { +VisualShaderNodeVec3Parameter::VisualShaderNodeVec3Parameter() { } -////////////// Vector4 Uniform +////////////// Vector4 Parameter -String VisualShaderNodeVec4Uniform::get_caption() const { - return "Vector4Uniform"; +String VisualShaderNodeVec4Parameter::get_caption() const { + return "Vector4Parameter"; } -int VisualShaderNodeVec4Uniform::get_input_port_count() const { +int VisualShaderNodeVec4Parameter::get_input_port_count() const { return 0; } -VisualShaderNodeVec4Uniform::PortType VisualShaderNodeVec4Uniform::get_input_port_type(int p_port) const { +VisualShaderNodeVec4Parameter::PortType VisualShaderNodeVec4Parameter::get_input_port_type(int p_port) const { return PORT_TYPE_VECTOR_4D; } -String VisualShaderNodeVec4Uniform::get_input_port_name(int p_port) const { +String VisualShaderNodeVec4Parameter::get_input_port_name(int p_port) const { return String(); } -int VisualShaderNodeVec4Uniform::get_output_port_count() const { +int VisualShaderNodeVec4Parameter::get_output_port_count() const { return 1; } -VisualShaderNodeVec4Uniform::PortType VisualShaderNodeVec4Uniform::get_output_port_type(int p_port) const { +VisualShaderNodeVec4Parameter::PortType VisualShaderNodeVec4Parameter::get_output_port_type(int p_port) const { return PORT_TYPE_VECTOR_4D; } -String VisualShaderNodeVec4Uniform::get_output_port_name(int p_port) const { +String VisualShaderNodeVec4Parameter::get_output_port_name(int p_port) const { return ""; // No output port means the editor will be used as port. } -void VisualShaderNodeVec4Uniform::set_default_value_enabled(bool p_enabled) { +void VisualShaderNodeVec4Parameter::set_default_value_enabled(bool p_enabled) { default_value_enabled = p_enabled; emit_changed(); } -bool VisualShaderNodeVec4Uniform::is_default_value_enabled() const { +bool VisualShaderNodeVec4Parameter::is_default_value_enabled() const { return default_value_enabled; } -void VisualShaderNodeVec4Uniform::set_default_value(const Quaternion &p_value) { +void VisualShaderNodeVec4Parameter::set_default_value(const Vector4 &p_value) { default_value = p_value; emit_changed(); } -Quaternion VisualShaderNodeVec4Uniform::get_default_value() const { +Vector4 VisualShaderNodeVec4Parameter::get_default_value() const { return default_value; } -String VisualShaderNodeVec4Uniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { - String code = _get_qual_str() + "uniform vec4 " + get_uniform_name(); +String VisualShaderNodeVec4Parameter::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + String code = _get_qual_str() + "uniform vec4 " + get_parameter_name(); if (default_value_enabled) { code += vformat(" = vec4(%.6f, %.6f, %.6f, %.6f)", default_value.x, default_value.y, default_value.z, default_value.w); } @@ -5417,39 +5574,39 @@ String VisualShaderNodeVec4Uniform::generate_global(Shader::Mode p_mode, VisualS return code; } -String VisualShaderNodeVec4Uniform::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 { - return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; +String VisualShaderNodeVec4Parameter::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 { + return " " + p_output_vars[0] + " = " + get_parameter_name() + ";\n"; } -void VisualShaderNodeVec4Uniform::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_default_value_enabled", "enabled"), &VisualShaderNodeVec4Uniform::set_default_value_enabled); - ClassDB::bind_method(D_METHOD("is_default_value_enabled"), &VisualShaderNodeVec4Uniform::is_default_value_enabled); +void VisualShaderNodeVec4Parameter::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_default_value_enabled", "enabled"), &VisualShaderNodeVec4Parameter::set_default_value_enabled); + ClassDB::bind_method(D_METHOD("is_default_value_enabled"), &VisualShaderNodeVec4Parameter::is_default_value_enabled); - ClassDB::bind_method(D_METHOD("set_default_value", "value"), &VisualShaderNodeVec4Uniform::set_default_value); - ClassDB::bind_method(D_METHOD("get_default_value"), &VisualShaderNodeVec4Uniform::get_default_value); + ClassDB::bind_method(D_METHOD("set_default_value", "value"), &VisualShaderNodeVec4Parameter::set_default_value); + ClassDB::bind_method(D_METHOD("get_default_value"), &VisualShaderNodeVec4Parameter::get_default_value); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "default_value_enabled"), "set_default_value_enabled", "is_default_value_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::QUATERNION, "default_value"), "set_default_value", "get_default_value"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR4, "default_value"), "set_default_value", "get_default_value"); } -bool VisualShaderNodeVec4Uniform::is_show_prop_names() const { +bool VisualShaderNodeVec4Parameter::is_show_prop_names() const { return true; } -bool VisualShaderNodeVec4Uniform::is_use_prop_slots() const { +bool VisualShaderNodeVec4Parameter::is_use_prop_slots() const { return true; } -bool VisualShaderNodeVec4Uniform::is_qualifier_supported(Qualifier p_qual) const { +bool VisualShaderNodeVec4Parameter::is_qualifier_supported(Qualifier p_qual) const { return true; // All qualifiers are supported. } -bool VisualShaderNodeVec4Uniform::is_convertible_to_constant() const { +bool VisualShaderNodeVec4Parameter::is_convertible_to_constant() const { return true; // Conversion is allowed. } -Vector<StringName> VisualShaderNodeVec4Uniform::get_editable_properties() const { - Vector<StringName> props = VisualShaderNodeUniform::get_editable_properties(); +Vector<StringName> VisualShaderNodeVec4Parameter::get_editable_properties() const { + Vector<StringName> props = VisualShaderNodeParameter::get_editable_properties(); props.push_back("default_value_enabled"); if (default_value_enabled) { props.push_back("default_value"); @@ -5457,59 +5614,59 @@ Vector<StringName> VisualShaderNodeVec4Uniform::get_editable_properties() const return props; } -VisualShaderNodeVec4Uniform::VisualShaderNodeVec4Uniform() { +VisualShaderNodeVec4Parameter::VisualShaderNodeVec4Parameter() { } -////////////// Transform Uniform +////////////// Transform Parameter -String VisualShaderNodeTransformUniform::get_caption() const { - return "TransformUniform"; +String VisualShaderNodeTransformParameter::get_caption() const { + return "TransformParameter"; } -int VisualShaderNodeTransformUniform::get_input_port_count() const { +int VisualShaderNodeTransformParameter::get_input_port_count() const { return 0; } -VisualShaderNodeTransformUniform::PortType VisualShaderNodeTransformUniform::get_input_port_type(int p_port) const { +VisualShaderNodeTransformParameter::PortType VisualShaderNodeTransformParameter::get_input_port_type(int p_port) const { return PORT_TYPE_VECTOR_3D; } -String VisualShaderNodeTransformUniform::get_input_port_name(int p_port) const { +String VisualShaderNodeTransformParameter::get_input_port_name(int p_port) const { return String(); } -int VisualShaderNodeTransformUniform::get_output_port_count() const { +int VisualShaderNodeTransformParameter::get_output_port_count() const { return 1; } -VisualShaderNodeTransformUniform::PortType VisualShaderNodeTransformUniform::get_output_port_type(int p_port) const { +VisualShaderNodeTransformParameter::PortType VisualShaderNodeTransformParameter::get_output_port_type(int p_port) const { return PORT_TYPE_TRANSFORM; } -String VisualShaderNodeTransformUniform::get_output_port_name(int p_port) const { +String VisualShaderNodeTransformParameter::get_output_port_name(int p_port) const { return ""; //no output port means the editor will be used as port } -void VisualShaderNodeTransformUniform::set_default_value_enabled(bool p_enabled) { +void VisualShaderNodeTransformParameter::set_default_value_enabled(bool p_enabled) { default_value_enabled = p_enabled; emit_changed(); } -bool VisualShaderNodeTransformUniform::is_default_value_enabled() const { +bool VisualShaderNodeTransformParameter::is_default_value_enabled() const { return default_value_enabled; } -void VisualShaderNodeTransformUniform::set_default_value(const Transform3D &p_value) { +void VisualShaderNodeTransformParameter::set_default_value(const Transform3D &p_value) { default_value = p_value; emit_changed(); } -Transform3D VisualShaderNodeTransformUniform::get_default_value() const { +Transform3D VisualShaderNodeTransformParameter::get_default_value() const { return default_value; } -String VisualShaderNodeTransformUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { - String code = _get_qual_str() + "uniform mat4 " + get_uniform_name(); +String VisualShaderNodeTransformParameter::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + String code = _get_qual_str() + "uniform mat4 " + get_parameter_name(); if (default_value_enabled) { Vector3 row0 = default_value.basis.rows[0]; Vector3 row1 = default_value.basis.rows[1]; @@ -5521,42 +5678,42 @@ String VisualShaderNodeTransformUniform::generate_global(Shader::Mode p_mode, Vi return code; } -String VisualShaderNodeTransformUniform::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 { - return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; +String VisualShaderNodeTransformParameter::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 { + return " " + p_output_vars[0] + " = " + get_parameter_name() + ";\n"; } -void VisualShaderNodeTransformUniform::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_default_value_enabled", "enabled"), &VisualShaderNodeTransformUniform::set_default_value_enabled); - ClassDB::bind_method(D_METHOD("is_default_value_enabled"), &VisualShaderNodeTransformUniform::is_default_value_enabled); +void VisualShaderNodeTransformParameter::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_default_value_enabled", "enabled"), &VisualShaderNodeTransformParameter::set_default_value_enabled); + ClassDB::bind_method(D_METHOD("is_default_value_enabled"), &VisualShaderNodeTransformParameter::is_default_value_enabled); - ClassDB::bind_method(D_METHOD("set_default_value", "value"), &VisualShaderNodeTransformUniform::set_default_value); - ClassDB::bind_method(D_METHOD("get_default_value"), &VisualShaderNodeTransformUniform::get_default_value); + ClassDB::bind_method(D_METHOD("set_default_value", "value"), &VisualShaderNodeTransformParameter::set_default_value); + ClassDB::bind_method(D_METHOD("get_default_value"), &VisualShaderNodeTransformParameter::get_default_value); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "default_value_enabled"), "set_default_value_enabled", "is_default_value_enabled"); ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "default_value"), "set_default_value", "get_default_value"); } -bool VisualShaderNodeTransformUniform::is_show_prop_names() const { +bool VisualShaderNodeTransformParameter::is_show_prop_names() const { return true; } -bool VisualShaderNodeTransformUniform::is_use_prop_slots() const { +bool VisualShaderNodeTransformParameter::is_use_prop_slots() const { return true; } -bool VisualShaderNodeTransformUniform::is_qualifier_supported(Qualifier p_qual) const { +bool VisualShaderNodeTransformParameter::is_qualifier_supported(Qualifier p_qual) const { if (p_qual == Qualifier::QUAL_INSTANCE) { return false; } return true; } -bool VisualShaderNodeTransformUniform::is_convertible_to_constant() const { +bool VisualShaderNodeTransformParameter::is_convertible_to_constant() const { return true; // conversion is allowed } -Vector<StringName> VisualShaderNodeTransformUniform::get_editable_properties() const { - Vector<StringName> props = VisualShaderNodeUniform::get_editable_properties(); +Vector<StringName> VisualShaderNodeTransformParameter::get_editable_properties() const { + Vector<StringName> props = VisualShaderNodeParameter::get_editable_properties(); props.push_back("default_value_enabled"); if (default_value_enabled) { props.push_back("default_value"); @@ -5564,12 +5721,12 @@ Vector<StringName> VisualShaderNodeTransformUniform::get_editable_properties() c return props; } -VisualShaderNodeTransformUniform::VisualShaderNodeTransformUniform() { +VisualShaderNodeTransformParameter::VisualShaderNodeTransformParameter() { } ////////////// -String get_sampler_hint(VisualShaderNodeTextureUniform::TextureType p_texture_type, VisualShaderNodeTextureUniform::ColorDefault p_color_default, VisualShaderNodeTextureUniform::TextureFilter p_texture_filter, VisualShaderNodeTextureUniform::TextureRepeat p_texture_repeat) { +String get_sampler_hint(VisualShaderNodeTextureParameter::TextureType p_texture_type, VisualShaderNodeTextureParameter::ColorDefault p_color_default, VisualShaderNodeTextureParameter::TextureFilter p_texture_filter, VisualShaderNodeTextureParameter::TextureRepeat p_texture_repeat) { String code; bool has_colon = false; @@ -5578,21 +5735,25 @@ String get_sampler_hint(VisualShaderNodeTextureUniform::TextureType p_texture_ty String type_code; switch (p_texture_type) { - case VisualShaderNodeTextureUniform::TYPE_DATA: - if (p_color_default == VisualShaderNodeTextureUniform::COLOR_DEFAULT_BLACK) { + case VisualShaderNodeTextureParameter::TYPE_DATA: + if (p_color_default == VisualShaderNodeTextureParameter::COLOR_DEFAULT_BLACK) { type_code = "hint_default_black"; + } else if (p_color_default == VisualShaderNodeTextureParameter::COLOR_DEFAULT_TRANSPARENT) { + type_code = "hint_default_transparent"; } break; - case VisualShaderNodeTextureUniform::TYPE_COLOR: + case VisualShaderNodeTextureParameter::TYPE_COLOR: type_code = "source_color"; - if (p_color_default == VisualShaderNodeTextureUniform::COLOR_DEFAULT_BLACK) { + if (p_color_default == VisualShaderNodeTextureParameter::COLOR_DEFAULT_BLACK) { type_code += ", hint_default_black"; + } else if (p_color_default == VisualShaderNodeTextureParameter::COLOR_DEFAULT_TRANSPARENT) { + type_code += ", hint_default_transparent"; } break; - case VisualShaderNodeTextureUniform::TYPE_NORMAL_MAP: + case VisualShaderNodeTextureParameter::TYPE_NORMAL_MAP: type_code = "hint_normal"; break; - case VisualShaderNodeTextureUniform::TYPE_ANISOTROPY: + case VisualShaderNodeTextureParameter::TYPE_ANISOTROPY: type_code = "hint_anisotropy"; break; default: @@ -5610,22 +5771,22 @@ String get_sampler_hint(VisualShaderNodeTextureUniform::TextureType p_texture_ty String filter_code; switch (p_texture_filter) { - case VisualShaderNodeTextureUniform::FILTER_NEAREST: + case VisualShaderNodeTextureParameter::FILTER_NEAREST: filter_code = "filter_nearest"; break; - case VisualShaderNodeTextureUniform::FILTER_LINEAR: + case VisualShaderNodeTextureParameter::FILTER_LINEAR: filter_code = "filter_linear"; break; - case VisualShaderNodeTextureUniform::FILTER_NEAREST_MIPMAP: + case VisualShaderNodeTextureParameter::FILTER_NEAREST_MIPMAP: filter_code = "filter_nearest_mipmap"; break; - case VisualShaderNodeTextureUniform::FILTER_LINEAR_MIPMAP: + case VisualShaderNodeTextureParameter::FILTER_LINEAR_MIPMAP: filter_code = "filter_linear_mipmap"; break; - case VisualShaderNodeTextureUniform::FILTER_NEAREST_MIPMAP_ANISOTROPIC: + case VisualShaderNodeTextureParameter::FILTER_NEAREST_MIPMAP_ANISOTROPIC: filter_code = "filter_nearest_mipmap_anisotropic"; break; - case VisualShaderNodeTextureUniform::FILTER_LINEAR_MIPMAP_ANISOTROPIC: + case VisualShaderNodeTextureParameter::FILTER_LINEAR_MIPMAP_ANISOTROPIC: filter_code = "filter_linear_mipmap_anisotropic"; break; default: @@ -5648,10 +5809,10 @@ String get_sampler_hint(VisualShaderNodeTextureUniform::TextureType p_texture_ty String repeat_code; switch (p_texture_repeat) { - case VisualShaderNodeTextureUniform::REPEAT_ENABLED: + case VisualShaderNodeTextureParameter::REPEAT_ENABLED: repeat_code = "repeat_enable"; break; - case VisualShaderNodeTextureUniform::REPEAT_DISABLED: + case VisualShaderNodeTextureParameter::REPEAT_DISABLED: repeat_code = "repeat_disable"; break; default: @@ -5671,29 +5832,25 @@ String get_sampler_hint(VisualShaderNodeTextureUniform::TextureType p_texture_ty return code; } -////////////// Texture Uniform - -String VisualShaderNodeTextureUniform::get_caption() const { - return "TextureUniform"; -} +////////////// Texture Parameter -int VisualShaderNodeTextureUniform::get_input_port_count() const { +int VisualShaderNodeTextureParameter::get_input_port_count() const { return 0; } -VisualShaderNodeTextureUniform::PortType VisualShaderNodeTextureUniform::get_input_port_type(int p_port) const { +VisualShaderNodeTextureParameter::PortType VisualShaderNodeTextureParameter::get_input_port_type(int p_port) const { return PORT_TYPE_SCALAR; } -String VisualShaderNodeTextureUniform::get_input_port_name(int p_port) const { +String VisualShaderNodeTextureParameter::get_input_port_name(int p_port) const { return ""; } -int VisualShaderNodeTextureUniform::get_output_port_count() const { +int VisualShaderNodeTextureParameter::get_output_port_count() const { return 1; } -VisualShaderNodeTextureUniform::PortType VisualShaderNodeTextureUniform::get_output_port_type(int p_port) const { +VisualShaderNodeTextureParameter::PortType VisualShaderNodeTextureParameter::get_output_port_type(int p_port) const { switch (p_port) { case 0: return PORT_TYPE_SAMPLER; @@ -5702,27 +5859,11 @@ VisualShaderNodeTextureUniform::PortType VisualShaderNodeTextureUniform::get_out } } -String VisualShaderNodeTextureUniform::get_output_port_name(int p_port) const { - switch (p_port) { - case 0: - return "sampler2D"; - default: - return ""; - } -} - -String VisualShaderNodeTextureUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { - String code = _get_qual_str() + "uniform sampler2D " + get_uniform_name(); - code += get_sampler_hint(texture_type, color_default, texture_filter, texture_repeat); - code += ";\n"; - return code; -} - -String VisualShaderNodeTextureUniform::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 VisualShaderNodeTextureParameter::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 { return ""; } -void VisualShaderNodeTextureUniform::set_texture_type(TextureType p_texture_type) { +void VisualShaderNodeTextureParameter::set_texture_type(TextureType p_texture_type) { ERR_FAIL_INDEX(int(p_texture_type), int(TYPE_MAX)); if (texture_type == p_texture_type) { return; @@ -5731,11 +5872,11 @@ void VisualShaderNodeTextureUniform::set_texture_type(TextureType p_texture_type emit_changed(); } -VisualShaderNodeTextureUniform::TextureType VisualShaderNodeTextureUniform::get_texture_type() const { +VisualShaderNodeTextureParameter::TextureType VisualShaderNodeTextureParameter::get_texture_type() const { return texture_type; } -void VisualShaderNodeTextureUniform::set_color_default(ColorDefault p_color_default) { +void VisualShaderNodeTextureParameter::set_color_default(ColorDefault p_color_default) { ERR_FAIL_INDEX(int(p_color_default), int(COLOR_DEFAULT_MAX)); if (color_default == p_color_default) { return; @@ -5744,11 +5885,11 @@ void VisualShaderNodeTextureUniform::set_color_default(ColorDefault p_color_defa emit_changed(); } -VisualShaderNodeTextureUniform::ColorDefault VisualShaderNodeTextureUniform::get_color_default() const { +VisualShaderNodeTextureParameter::ColorDefault VisualShaderNodeTextureParameter::get_color_default() const { return color_default; } -void VisualShaderNodeTextureUniform::set_texture_filter(TextureFilter p_filter) { +void VisualShaderNodeTextureParameter::set_texture_filter(TextureFilter p_filter) { ERR_FAIL_INDEX(int(p_filter), int(FILTER_MAX)); if (texture_filter == p_filter) { return; @@ -5757,11 +5898,11 @@ void VisualShaderNodeTextureUniform::set_texture_filter(TextureFilter p_filter) emit_changed(); } -VisualShaderNodeTextureUniform::TextureFilter VisualShaderNodeTextureUniform::get_texture_filter() const { +VisualShaderNodeTextureParameter::TextureFilter VisualShaderNodeTextureParameter::get_texture_filter() const { return texture_filter; } -void VisualShaderNodeTextureUniform::set_texture_repeat(TextureRepeat p_repeat) { +void VisualShaderNodeTextureParameter::set_texture_repeat(TextureRepeat p_repeat) { ERR_FAIL_INDEX(int(p_repeat), int(REPEAT_MAX)); if (texture_repeat == p_repeat) { return; @@ -5770,12 +5911,12 @@ void VisualShaderNodeTextureUniform::set_texture_repeat(TextureRepeat p_repeat) emit_changed(); } -VisualShaderNodeTextureUniform::TextureRepeat VisualShaderNodeTextureUniform::get_texture_repeat() const { +VisualShaderNodeTextureParameter::TextureRepeat VisualShaderNodeTextureParameter::get_texture_repeat() const { return texture_repeat; } -Vector<StringName> VisualShaderNodeTextureUniform::get_editable_properties() const { - Vector<StringName> props = VisualShaderNodeUniform::get_editable_properties(); +Vector<StringName> VisualShaderNodeTextureParameter::get_editable_properties() const { + Vector<StringName> props = VisualShaderNodeParameter::get_editable_properties(); props.push_back("texture_type"); if (texture_type == TYPE_DATA || texture_type == TYPE_COLOR) { props.push_back("color_default"); @@ -5785,11 +5926,11 @@ Vector<StringName> VisualShaderNodeTextureUniform::get_editable_properties() con return props; } -bool VisualShaderNodeTextureUniform::is_show_prop_names() const { +bool VisualShaderNodeTextureParameter::is_show_prop_names() const { return true; } -HashMap<StringName, String> VisualShaderNodeTextureUniform::get_editable_properties_names() const { +HashMap<StringName, String> VisualShaderNodeTextureParameter::get_editable_properties_names() const { HashMap<StringName, String> names; names.insert("texture_type", RTR("Type")); names.insert("color_default", RTR("Default Color")); @@ -5798,21 +5939,21 @@ HashMap<StringName, String> VisualShaderNodeTextureUniform::get_editable_propert return names; } -void VisualShaderNodeTextureUniform::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_texture_type", "type"), &VisualShaderNodeTextureUniform::set_texture_type); - ClassDB::bind_method(D_METHOD("get_texture_type"), &VisualShaderNodeTextureUniform::get_texture_type); +void VisualShaderNodeTextureParameter::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_texture_type", "type"), &VisualShaderNodeTextureParameter::set_texture_type); + ClassDB::bind_method(D_METHOD("get_texture_type"), &VisualShaderNodeTextureParameter::get_texture_type); - ClassDB::bind_method(D_METHOD("set_color_default", "type"), &VisualShaderNodeTextureUniform::set_color_default); - ClassDB::bind_method(D_METHOD("get_color_default"), &VisualShaderNodeTextureUniform::get_color_default); + ClassDB::bind_method(D_METHOD("set_color_default", "type"), &VisualShaderNodeTextureParameter::set_color_default); + ClassDB::bind_method(D_METHOD("get_color_default"), &VisualShaderNodeTextureParameter::get_color_default); - ClassDB::bind_method(D_METHOD("set_texture_filter", "filter"), &VisualShaderNodeTextureUniform::set_texture_filter); - ClassDB::bind_method(D_METHOD("get_texture_filter"), &VisualShaderNodeTextureUniform::get_texture_filter); + ClassDB::bind_method(D_METHOD("set_texture_filter", "filter"), &VisualShaderNodeTextureParameter::set_texture_filter); + ClassDB::bind_method(D_METHOD("get_texture_filter"), &VisualShaderNodeTextureParameter::get_texture_filter); - ClassDB::bind_method(D_METHOD("set_texture_repeat", "type"), &VisualShaderNodeTextureUniform::set_texture_repeat); - ClassDB::bind_method(D_METHOD("get_texture_repeat"), &VisualShaderNodeTextureUniform::get_texture_repeat); + ClassDB::bind_method(D_METHOD("set_texture_repeat", "type"), &VisualShaderNodeTextureParameter::set_texture_repeat); + ClassDB::bind_method(D_METHOD("get_texture_repeat"), &VisualShaderNodeTextureParameter::get_texture_repeat); ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_type", PROPERTY_HINT_ENUM, "Data,Color,Normal Map,Anisotropic"), "set_texture_type", "get_texture_type"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "color_default", PROPERTY_HINT_ENUM, "White,Black"), "set_color_default", "get_color_default"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "color_default", PROPERTY_HINT_ENUM, "White,Black,Transparent"), "set_color_default", "get_color_default"); ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Default,Nearest,Linear,Nearest Mipmap,Linear Mipmap,Nearest Mipmap Anisotropic,Linear Mipmap Anisotropic"), "set_texture_filter", "get_texture_filter"); ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_repeat", PROPERTY_HINT_ENUM, "Default,Enabled,Disabled"), "set_texture_repeat", "get_texture_repeat"); @@ -5824,6 +5965,7 @@ void VisualShaderNodeTextureUniform::_bind_methods() { BIND_ENUM_CONSTANT(COLOR_DEFAULT_WHITE); BIND_ENUM_CONSTANT(COLOR_DEFAULT_BLACK); + BIND_ENUM_CONSTANT(COLOR_DEFAULT_TRANSPARENT); BIND_ENUM_CONSTANT(COLOR_DEFAULT_MAX); BIND_ENUM_CONSTANT(FILTER_DEFAULT); @@ -5841,7 +5983,7 @@ void VisualShaderNodeTextureUniform::_bind_methods() { BIND_ENUM_CONSTANT(REPEAT_MAX); } -bool VisualShaderNodeTextureUniform::is_qualifier_supported(Qualifier p_qual) const { +bool VisualShaderNodeTextureParameter::is_qualifier_supported(Qualifier p_qual) const { switch (p_qual) { case Qualifier::QUAL_NONE: return true; @@ -5855,31 +5997,56 @@ bool VisualShaderNodeTextureUniform::is_qualifier_supported(Qualifier p_qual) co return false; } -bool VisualShaderNodeTextureUniform::is_convertible_to_constant() const { +bool VisualShaderNodeTextureParameter::is_convertible_to_constant() const { return false; // conversion is not allowed } -VisualShaderNodeTextureUniform::VisualShaderNodeTextureUniform() { +VisualShaderNodeTextureParameter::VisualShaderNodeTextureParameter() { +} + +////////////// Texture2D Parameter + +String VisualShaderNodeTexture2DParameter::get_caption() const { + return "Texture2DParameter"; +} + +String VisualShaderNodeTexture2DParameter::get_output_port_name(int p_port) const { + switch (p_port) { + case 0: + return "sampler2D"; + default: + return ""; + } +} + +String VisualShaderNodeTexture2DParameter::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + String code = _get_qual_str() + "uniform sampler2D " + get_parameter_name(); + code += get_sampler_hint(texture_type, color_default, texture_filter, texture_repeat); + code += ";\n"; + return code; } -////////////// Texture Uniform (Triplanar) +VisualShaderNodeTexture2DParameter::VisualShaderNodeTexture2DParameter() { +} + +////////////// Texture Parameter (Triplanar) -String VisualShaderNodeTextureUniformTriplanar::get_caption() const { +String VisualShaderNodeTextureParameterTriplanar::get_caption() const { return "TextureUniformTriplanar"; } -int VisualShaderNodeTextureUniformTriplanar::get_input_port_count() const { +int VisualShaderNodeTextureParameterTriplanar::get_input_port_count() const { return 2; } -VisualShaderNodeTextureUniformTriplanar::PortType VisualShaderNodeTextureUniformTriplanar::get_input_port_type(int p_port) const { +VisualShaderNodeTextureParameterTriplanar::PortType VisualShaderNodeTextureParameterTriplanar::get_input_port_type(int p_port) const { if (p_port == 0 || p_port == 1) { return PORT_TYPE_VECTOR_3D; } return PORT_TYPE_SCALAR; } -String VisualShaderNodeTextureUniformTriplanar::get_input_port_name(int p_port) const { +String VisualShaderNodeTextureParameterTriplanar::get_input_port_name(int p_port) const { if (p_port == 0) { return "weights"; } else if (p_port == 1) { @@ -5888,11 +6055,11 @@ String VisualShaderNodeTextureUniformTriplanar::get_input_port_name(int p_port) return ""; } -int VisualShaderNodeTextureUniformTriplanar::get_output_port_count() const { +int VisualShaderNodeTextureParameterTriplanar::get_output_port_count() const { return 2; } -VisualShaderNodeTextureUniformTriplanar::PortType VisualShaderNodeTextureUniformTriplanar::get_output_port_type(int p_port) const { +VisualShaderNodeTextureParameterTriplanar::PortType VisualShaderNodeTextureParameterTriplanar::get_output_port_type(int p_port) const { switch (p_port) { case 0: return PORT_TYPE_VECTOR_4D; @@ -5903,7 +6070,7 @@ VisualShaderNodeTextureUniformTriplanar::PortType VisualShaderNodeTextureUniform } } -String VisualShaderNodeTextureUniformTriplanar::get_output_port_name(int p_port) const { +String VisualShaderNodeTextureParameterTriplanar::get_output_port_name(int p_port) const { switch (p_port) { case 0: return "color"; @@ -5914,7 +6081,7 @@ String VisualShaderNodeTextureUniformTriplanar::get_output_port_name(int p_port) } } -String VisualShaderNodeTextureUniformTriplanar::generate_global_per_node(Shader::Mode p_mode, int p_id) const { +String VisualShaderNodeTextureParameterTriplanar::generate_global_per_node(Shader::Mode p_mode, int p_id) const { String code; code += "// " + get_caption() + "\n"; @@ -5936,7 +6103,7 @@ String VisualShaderNodeTextureUniformTriplanar::generate_global_per_node(Shader: return code; } -String VisualShaderNodeTextureUniformTriplanar::generate_global_per_func(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { +String VisualShaderNodeTextureParameterTriplanar::generate_global_per_func(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { String code; if (p_type == VisualShader::TYPE_VERTEX) { @@ -5952,8 +6119,15 @@ String VisualShaderNodeTextureUniformTriplanar::generate_global_per_func(Shader: return code; } -String VisualShaderNodeTextureUniformTriplanar::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 id = get_uniform_name(); +String VisualShaderNodeTextureParameterTriplanar::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + String code = _get_qual_str() + "uniform sampler2D " + get_parameter_name(); + code += get_sampler_hint(texture_type, color_default, texture_filter, texture_repeat); + code += ";\n"; + return code; +} + +String VisualShaderNodeTextureParameterTriplanar::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 id = get_parameter_name(); String code; if (p_input_vars[0].is_empty() && p_input_vars[1].is_empty()) { @@ -5969,7 +6143,7 @@ String VisualShaderNodeTextureUniformTriplanar::generate_code(Shader::Mode p_mod return code; } -bool VisualShaderNodeTextureUniformTriplanar::is_input_port_default(int p_port, Shader::Mode p_mode) const { +bool VisualShaderNodeTextureParameterTriplanar::is_input_port_default(int p_port, Shader::Mode p_mode) const { if (p_port == 0) { return true; } else if (p_port == 1) { @@ -5978,79 +6152,67 @@ bool VisualShaderNodeTextureUniformTriplanar::is_input_port_default(int p_port, return false; } -VisualShaderNodeTextureUniformTriplanar::VisualShaderNodeTextureUniformTriplanar() { +VisualShaderNodeTextureParameterTriplanar::VisualShaderNodeTextureParameterTriplanar() { } -////////////// Texture2DArray Uniform +////////////// Texture2DArray Parameter -String VisualShaderNodeTexture2DArrayUniform::get_caption() const { - return "Texture2DArrayUniform"; +String VisualShaderNodeTexture2DArrayParameter::get_caption() const { + return "Texture2DArrayParameter"; } -String VisualShaderNodeTexture2DArrayUniform::get_output_port_name(int p_port) const { +String VisualShaderNodeTexture2DArrayParameter::get_output_port_name(int p_port) const { return "sampler2DArray"; } -String VisualShaderNodeTexture2DArrayUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { - String code = _get_qual_str() + "uniform sampler2DArray " + get_uniform_name(); +String VisualShaderNodeTexture2DArrayParameter::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + String code = _get_qual_str() + "uniform sampler2DArray " + get_parameter_name(); code += get_sampler_hint(texture_type, color_default, texture_filter, texture_repeat); code += ";\n"; return code; } -String VisualShaderNodeTexture2DArrayUniform::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 { - return String(); -} - -VisualShaderNodeTexture2DArrayUniform::VisualShaderNodeTexture2DArrayUniform() { +VisualShaderNodeTexture2DArrayParameter::VisualShaderNodeTexture2DArrayParameter() { } -////////////// Texture3D Uniform +////////////// Texture3D Parameter -String VisualShaderNodeTexture3DUniform::get_caption() const { - return "Texture3DUniform"; +String VisualShaderNodeTexture3DParameter::get_caption() const { + return "Texture3DParameter"; } -String VisualShaderNodeTexture3DUniform::get_output_port_name(int p_port) const { +String VisualShaderNodeTexture3DParameter::get_output_port_name(int p_port) const { return "sampler3D"; } -String VisualShaderNodeTexture3DUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { - String code = _get_qual_str() + "uniform sampler3D " + get_uniform_name(); +String VisualShaderNodeTexture3DParameter::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + String code = _get_qual_str() + "uniform sampler3D " + get_parameter_name(); code += get_sampler_hint(texture_type, color_default, texture_filter, texture_repeat); code += ";\n"; return code; } -String VisualShaderNodeTexture3DUniform::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 { - return String(); -} - -VisualShaderNodeTexture3DUniform::VisualShaderNodeTexture3DUniform() { +VisualShaderNodeTexture3DParameter::VisualShaderNodeTexture3DParameter() { } -////////////// Cubemap Uniform +////////////// Cubemap Parameter -String VisualShaderNodeCubemapUniform::get_caption() const { - return "CubemapUniform"; +String VisualShaderNodeCubemapParameter::get_caption() const { + return "CubemapParameter"; } -String VisualShaderNodeCubemapUniform::get_output_port_name(int p_port) const { +String VisualShaderNodeCubemapParameter::get_output_port_name(int p_port) const { return "samplerCube"; } -String VisualShaderNodeCubemapUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { - String code = _get_qual_str() + "uniform samplerCube " + get_uniform_name(); +String VisualShaderNodeCubemapParameter::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + String code = _get_qual_str() + "uniform samplerCube " + get_parameter_name(); code += get_sampler_hint(texture_type, color_default, texture_filter, texture_repeat); code += ";\n"; return code; } -String VisualShaderNodeCubemapUniform::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 { - return String(); -} - -VisualShaderNodeCubemapUniform::VisualShaderNodeCubemapUniform() { +VisualShaderNodeCubemapParameter::VisualShaderNodeCubemapParameter() { } ////////////// If @@ -6818,23 +6980,23 @@ void VisualShaderNodeMultiplyAdd::set_op_type(OpType p_op_type) { switch (p_op_type) { case OP_TYPE_SCALAR: { set_input_port_default_value(0, 0.0, get_input_port_default_value(0)); - set_input_port_default_value(1, 0.0, get_input_port_default_value(1)); + set_input_port_default_value(1, 1.0, get_input_port_default_value(1)); set_input_port_default_value(2, 0.0, get_input_port_default_value(2)); } break; case OP_TYPE_VECTOR_2D: { set_input_port_default_value(0, Vector2(), get_input_port_default_value(0)); - set_input_port_default_value(1, Vector2(), get_input_port_default_value(1)); + set_input_port_default_value(1, Vector2(1.0, 1.0), get_input_port_default_value(1)); set_input_port_default_value(2, Vector2(), get_input_port_default_value(2)); } break; case OP_TYPE_VECTOR_3D: { set_input_port_default_value(0, Vector3(), get_input_port_default_value(0)); - set_input_port_default_value(1, Vector3(), get_input_port_default_value(1)); + set_input_port_default_value(1, Vector3(1.0, 1.0, 1.0), get_input_port_default_value(1)); set_input_port_default_value(2, Vector3(), get_input_port_default_value(2)); } break; case OP_TYPE_VECTOR_4D: { - set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0)); - set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1)); - set_input_port_default_value(2, Quaternion(), get_input_port_default_value(2)); + set_input_port_default_value(0, Vector4(), get_input_port_default_value(0)); + set_input_port_default_value(1, Vector4(1.0, 1.0, 1.0, 1.0), get_input_port_default_value(1)); + set_input_port_default_value(2, Vector4(), get_input_port_default_value(2)); } break; default: break; @@ -6868,7 +7030,7 @@ void VisualShaderNodeMultiplyAdd::_bind_methods() { VisualShaderNodeMultiplyAdd::VisualShaderNodeMultiplyAdd() { set_input_port_default_value(0, 0.0); - set_input_port_default_value(1, 0.0); + set_input_port_default_value(1, 1.0); set_input_port_default_value(2, 0.0); } @@ -6998,3 +7160,273 @@ void VisualShaderNodeBillboard::_bind_methods() { VisualShaderNodeBillboard::VisualShaderNodeBillboard() { simple_decl = false; } + +////////////// DistanceFade + +String VisualShaderNodeDistanceFade::get_caption() const { + return "DistanceFade"; +} + +int VisualShaderNodeDistanceFade::get_input_port_count() const { + return 2; +} + +VisualShaderNodeDistanceFade::PortType VisualShaderNodeDistanceFade::get_input_port_type(int p_port) const { + switch (p_port) { + case 0: + return PORT_TYPE_SCALAR; + case 1: + return PORT_TYPE_SCALAR; + } + + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeDistanceFade::get_input_port_name(int p_port) const { + switch (p_port) { + case 0: + return "min"; + case 1: + return "max"; + } + + return ""; +} + +int VisualShaderNodeDistanceFade::get_output_port_count() const { + return 1; +} + +VisualShaderNodeDistanceFade::PortType VisualShaderNodeDistanceFade::get_output_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeDistanceFade::get_output_port_name(int p_port) const { + return "amount"; +} + +bool VisualShaderNodeDistanceFade::has_output_port_preview(int p_port) const { + return false; +} + +String VisualShaderNodeDistanceFade::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 += vformat(" %s = clamp(smoothstep(%s, %s,-VERTEX.z),0.0,1.0);\n", p_output_vars[0], p_input_vars[0], p_input_vars[1]); + return code; +} + +VisualShaderNodeDistanceFade::VisualShaderNodeDistanceFade() { + set_input_port_default_value(0, 0.0); + set_input_port_default_value(1, 10.0); +} + +////////////// ProximityFade + +String VisualShaderNodeProximityFade::get_caption() const { + return "ProximityFade"; +} + +int VisualShaderNodeProximityFade::get_input_port_count() const { + return 1; +} + +VisualShaderNodeProximityFade::PortType VisualShaderNodeProximityFade::get_input_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeProximityFade::get_input_port_name(int p_port) const { + return "distance"; +} + +int VisualShaderNodeProximityFade::get_output_port_count() const { + return 1; +} + +VisualShaderNodeProximityFade::PortType VisualShaderNodeProximityFade::get_output_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeProximityFade::get_output_port_name(int p_port) const { + return "fade"; +} + +bool VisualShaderNodeProximityFade::has_output_port_preview(int p_port) const { + return false; +} + +String VisualShaderNodeProximityFade::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; + + String proximity_fade_distance = vformat("%s", p_input_vars[0]); + code += " float __depth_tex = textureLod(DEPTH_TEXTURE, SCREEN_UV, 0.0).r;\n"; + if (!RenderingServer::get_singleton()->is_low_end()) { + code += " vec4 __depth_world_pos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV * 2.0 - 1.0, __depth_tex, 1.0);\n"; + } else { + code += " vec4 __depth_world_pos = INV_PROJECTION_MATRIX * vec4(vec3(SCREEN_UV, __depth_tex) * 2.0 - 1.0, 1.0);\n"; + } + code += " __depth_world_pos.xyz /= __depth_world_pos.z;\n"; + code += vformat(" %s = clamp(1.0 - smoothstep(__depth_world_pos.z + %s, __depth_world_pos.z, VERTEX.z), 0.0, 1.0);\n", p_output_vars[0], p_input_vars[0]); + + return code; +} + +VisualShaderNodeProximityFade::VisualShaderNodeProximityFade() { + set_input_port_default_value(0, 1.0); +} + +////////////// Random Range + +String VisualShaderNodeRandomRange::get_caption() const { + return "RandomRange"; +} + +int VisualShaderNodeRandomRange::get_input_port_count() const { + return 3; +} + +VisualShaderNodeRandomRange::PortType VisualShaderNodeRandomRange::get_input_port_type(int p_port) const { + switch (p_port) { + case 0: + return PORT_TYPE_VECTOR_3D; + case 1: + return PORT_TYPE_SCALAR; + case 2: + return PORT_TYPE_SCALAR; + default: + break; + } + + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeRandomRange::get_input_port_name(int p_port) const { + switch (p_port) { + case 0: + return "seed"; + case 1: + return "min"; + case 2: + return "max"; + default: + break; + } + + return ""; +} + +int VisualShaderNodeRandomRange::get_output_port_count() const { + return 1; +} + +VisualShaderNodeRandomRange::PortType VisualShaderNodeRandomRange::get_output_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeRandomRange::get_output_port_name(int p_port) const { + return "value"; +} + +String VisualShaderNodeRandomRange::generate_global_per_node(Shader::Mode p_mode, int p_id) const { + String code; + + code += "\n\n"; + code += "// 3D Noise with friendly permission by Inigo Quilez\n"; + code += "vec3 hash_noise_range( vec3 p ) {\n"; + code += " p *= mat3(vec3(127.1, 311.7, -53.7), vec3(269.5, 183.3, 77.1), vec3(-301.7, 27.3, 215.3));\n"; + code += " return 2.0 * fract(fract(p)*4375.55) -1.;\n"; + code += "}\n"; + code += "\n"; + + return code; +} + +String VisualShaderNodeRandomRange::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 += vformat(" %s = mix(%s, %s, hash_noise_range(%s).x);\n", p_output_vars[0], p_input_vars[1], p_input_vars[2], p_input_vars[0]); + + return code; +} + +VisualShaderNodeRandomRange::VisualShaderNodeRandomRange() { + set_input_port_default_value(0, Vector3(1.0, 1.0, 1.0)); + set_input_port_default_value(1, 0.0); + set_input_port_default_value(2, 1.0); +} + +////////////// Remap + +String VisualShaderNodeRemap::get_caption() const { + return "Remap"; +} + +int VisualShaderNodeRemap::get_input_port_count() const { + return 5; +} + +VisualShaderNodeRemap::PortType VisualShaderNodeRemap::get_input_port_type(int p_port) const { + switch (p_port) { + case 0: + return PORT_TYPE_SCALAR; + case 1: + return PORT_TYPE_SCALAR; + case 2: + return PORT_TYPE_SCALAR; + case 3: + return PORT_TYPE_SCALAR; + case 4: + return PORT_TYPE_SCALAR; + default: + break; + } + + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeRemap::get_input_port_name(int p_port) const { + switch (p_port) { + case 0: + return "value"; + case 1: + return "input min"; + case 2: + return "input max"; + case 3: + return "output min"; + case 4: + return "output max"; + default: + break; + } + + return ""; +} + +int VisualShaderNodeRemap::get_output_port_count() const { + return 1; +} + +VisualShaderNodeRemap::PortType VisualShaderNodeRemap::get_output_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeRemap::get_output_port_name(int p_port) const { + return "value"; +} + +String VisualShaderNodeRemap::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { + String code; + + code += vformat(" float _input_range = %s - %s;\n", p_input_vars[2], p_input_vars[1]); + code += vformat(" float _output_range = %s - %s;\n", p_input_vars[4], p_input_vars[3]); + code += vformat(" %s = %s + _output_range * ((%s - %s) / _input_range);\n", p_output_vars[0], p_input_vars[3], p_input_vars[0], p_input_vars[1]); + + return code; +} + +VisualShaderNodeRemap::VisualShaderNodeRemap() { + set_input_port_default_value(1, 0.0); + set_input_port_default_value(2, 1.0); + set_input_port_default_value(3, 0.0); + set_input_port_default_value(4, 1.0); +} diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h index 1eb7b7240f..4b883c25cc 100644 --- a/scene/resources/visual_shader_nodes.h +++ b/scene/resources/visual_shader_nodes.h @@ -622,6 +622,28 @@ VARIANT_ENUM_CAST(VisualShaderNodeCubemap::TextureType) VARIANT_ENUM_CAST(VisualShaderNodeCubemap::Source) /////////////////////////////////////// + +class VisualShaderNodeLinearSceneDepth : public VisualShaderNode { + GDCLASS(VisualShaderNodeLinearSceneDepth, VisualShaderNode); + +public: + virtual String get_caption() const override; + + virtual int get_input_port_count() const override; + virtual PortType get_input_port_type(int p_port) const override; + virtual String get_input_port_name(int p_port) const override; + + virtual int get_output_port_count() const override; + virtual PortType get_output_port_type(int p_port) const override; + virtual String get_output_port_name(int p_port) const override; + virtual bool has_output_port_preview(int p_port) const override; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; + + VisualShaderNodeLinearSceneDepth(); +}; + +/////////////////////////////////////// /// OPS /////////////////////////////////////// @@ -1231,6 +1253,30 @@ public: VARIANT_ENUM_CAST(VisualShaderNodeUVFunc::Function) /////////////////////////////////////// +/// UV POLARCOORD +/////////////////////////////////////// + +class VisualShaderNodeUVPolarCoord : public VisualShaderNode { + GDCLASS(VisualShaderNodeUVPolarCoord, VisualShaderNode); + +public: + virtual String get_caption() const override; + + virtual int get_input_port_count() const override; + virtual PortType get_input_port_type(int p_port) const override; + virtual String get_input_port_name(int p_port) const override; + virtual bool is_input_port_default(int p_port, Shader::Mode p_mode) const override; + + virtual int get_output_port_count() const override; + virtual PortType get_output_port_type(int p_port) const override; + virtual String get_output_port_name(int p_port) const override; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; + + VisualShaderNodeUVPolarCoord(); +}; + +/////////////////////////////////////// /// DOT /////////////////////////////////////// @@ -1564,21 +1610,20 @@ public: /// REFRACT /////////////////////////////////////// -class VisualShaderNodeVectorRefract : public VisualShaderNode { - GDCLASS(VisualShaderNodeVectorRefract, VisualShaderNode); +class VisualShaderNodeVectorRefract : public VisualShaderNodeVectorBase { + GDCLASS(VisualShaderNodeVectorRefract, VisualShaderNodeVectorBase); public: virtual String get_caption() const override; virtual int get_input_port_count() const override; - virtual PortType get_input_port_type(int p_port) const override; virtual String get_input_port_name(int p_port) const override; virtual int get_output_port_count() const override; - virtual PortType get_output_port_type(int p_port) const override; virtual String get_output_port_name(int p_port) const override; virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; + virtual void set_op_type(OpType p_op_type) override; VisualShaderNodeVectorRefract(); }; @@ -1718,11 +1763,11 @@ public: }; /////////////////////////////////////// -/// UNIFORMS +/// PARAMETERS /////////////////////////////////////// -class VisualShaderNodeFloatUniform : public VisualShaderNodeUniform { - GDCLASS(VisualShaderNodeFloatUniform, VisualShaderNodeUniform); +class VisualShaderNodeFloatParameter : public VisualShaderNodeParameter { + GDCLASS(VisualShaderNodeFloatParameter, VisualShaderNodeParameter); public: enum Hint { @@ -1783,13 +1828,13 @@ public: virtual Vector<StringName> get_editable_properties() const override; - VisualShaderNodeFloatUniform(); + VisualShaderNodeFloatParameter(); }; -VARIANT_ENUM_CAST(VisualShaderNodeFloatUniform::Hint) +VARIANT_ENUM_CAST(VisualShaderNodeFloatParameter::Hint) -class VisualShaderNodeIntUniform : public VisualShaderNodeUniform { - GDCLASS(VisualShaderNodeIntUniform, VisualShaderNodeUniform); +class VisualShaderNodeIntParameter : public VisualShaderNodeParameter { + GDCLASS(VisualShaderNodeIntParameter, VisualShaderNodeParameter); public: enum Hint { @@ -1850,15 +1895,15 @@ public: virtual Vector<StringName> get_editable_properties() const override; - VisualShaderNodeIntUniform(); + VisualShaderNodeIntParameter(); }; -VARIANT_ENUM_CAST(VisualShaderNodeIntUniform::Hint) +VARIANT_ENUM_CAST(VisualShaderNodeIntParameter::Hint) /////////////////////////////////////// -class VisualShaderNodeBooleanUniform : public VisualShaderNodeUniform { - GDCLASS(VisualShaderNodeBooleanUniform, VisualShaderNodeUniform); +class VisualShaderNodeBooleanParameter : public VisualShaderNodeParameter { + GDCLASS(VisualShaderNodeBooleanParameter, VisualShaderNodeParameter); private: bool default_value_enabled = false; @@ -1895,13 +1940,13 @@ public: virtual Vector<StringName> get_editable_properties() const override; - VisualShaderNodeBooleanUniform(); + VisualShaderNodeBooleanParameter(); }; /////////////////////////////////////// -class VisualShaderNodeColorUniform : public VisualShaderNodeUniform { - GDCLASS(VisualShaderNodeColorUniform, VisualShaderNodeUniform); +class VisualShaderNodeColorParameter : public VisualShaderNodeParameter { + GDCLASS(VisualShaderNodeColorParameter, VisualShaderNodeParameter); private: bool default_value_enabled = false; @@ -1939,13 +1984,13 @@ public: virtual Vector<StringName> get_editable_properties() const override; - VisualShaderNodeColorUniform(); + VisualShaderNodeColorParameter(); }; /////////////////////////////////////// -class VisualShaderNodeVec2Uniform : public VisualShaderNodeUniform { - GDCLASS(VisualShaderNodeVec2Uniform, VisualShaderNodeUniform); +class VisualShaderNodeVec2Parameter : public VisualShaderNodeParameter { + GDCLASS(VisualShaderNodeVec2Parameter, VisualShaderNodeParameter); private: bool default_value_enabled = false; @@ -1982,13 +2027,13 @@ public: virtual Vector<StringName> get_editable_properties() const override; - VisualShaderNodeVec2Uniform(); + VisualShaderNodeVec2Parameter(); }; /////////////////////////////////////// -class VisualShaderNodeVec3Uniform : public VisualShaderNodeUniform { - GDCLASS(VisualShaderNodeVec3Uniform, VisualShaderNodeUniform); +class VisualShaderNodeVec3Parameter : public VisualShaderNodeParameter { + GDCLASS(VisualShaderNodeVec3Parameter, VisualShaderNodeParameter); private: bool default_value_enabled = false; @@ -2025,17 +2070,17 @@ public: virtual Vector<StringName> get_editable_properties() const override; - VisualShaderNodeVec3Uniform(); + VisualShaderNodeVec3Parameter(); }; /////////////////////////////////////// -class VisualShaderNodeVec4Uniform : public VisualShaderNodeUniform { - GDCLASS(VisualShaderNodeVec4Uniform, VisualShaderNodeUniform); +class VisualShaderNodeVec4Parameter : public VisualShaderNodeParameter { + GDCLASS(VisualShaderNodeVec4Parameter, VisualShaderNodeParameter); private: bool default_value_enabled = false; - Quaternion default_value; + Vector4 default_value; protected: static void _bind_methods(); @@ -2060,21 +2105,21 @@ public: void set_default_value_enabled(bool p_enabled); bool is_default_value_enabled() const; - void set_default_value(const Quaternion &p_value); - Quaternion get_default_value() const; + void set_default_value(const Vector4 &p_value); + Vector4 get_default_value() const; bool is_qualifier_supported(Qualifier p_qual) const override; bool is_convertible_to_constant() const override; virtual Vector<StringName> get_editable_properties() const override; - VisualShaderNodeVec4Uniform(); + VisualShaderNodeVec4Parameter(); }; /////////////////////////////////////// -class VisualShaderNodeTransformUniform : public VisualShaderNodeUniform { - GDCLASS(VisualShaderNodeTransformUniform, VisualShaderNodeUniform); +class VisualShaderNodeTransformParameter : public VisualShaderNodeParameter { + GDCLASS(VisualShaderNodeTransformParameter, VisualShaderNodeParameter); private: bool default_value_enabled = false; @@ -2111,13 +2156,13 @@ public: virtual Vector<StringName> get_editable_properties() const override; - VisualShaderNodeTransformUniform(); + VisualShaderNodeTransformParameter(); }; /////////////////////////////////////// -class VisualShaderNodeTextureUniform : public VisualShaderNodeUniform { - GDCLASS(VisualShaderNodeTextureUniform, VisualShaderNodeUniform); +class VisualShaderNodeTextureParameter : public VisualShaderNodeParameter { + GDCLASS(VisualShaderNodeTextureParameter, VisualShaderNodeParameter); public: enum TextureType { @@ -2131,6 +2176,7 @@ public: enum ColorDefault { COLOR_DEFAULT_WHITE, COLOR_DEFAULT_BLACK, + COLOR_DEFAULT_TRANSPARENT, COLOR_DEFAULT_MAX, }; @@ -2162,17 +2208,13 @@ protected: static void _bind_methods(); public: - virtual String get_caption() const override; - virtual int get_input_port_count() const override; virtual PortType get_input_port_type(int p_port) const override; virtual String get_input_port_name(int p_port) const override; virtual int get_output_port_count() const override; virtual PortType get_output_port_type(int p_port) const override; - virtual String get_output_port_name(int p_port) const override; - virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override; virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; virtual HashMap<StringName, String> get_editable_properties_names() const override; @@ -2195,18 +2237,32 @@ public: bool is_qualifier_supported(Qualifier p_qual) const override; bool is_convertible_to_constant() const override; - VisualShaderNodeTextureUniform(); + VisualShaderNodeTextureParameter(); }; -VARIANT_ENUM_CAST(VisualShaderNodeTextureUniform::TextureType) -VARIANT_ENUM_CAST(VisualShaderNodeTextureUniform::ColorDefault) -VARIANT_ENUM_CAST(VisualShaderNodeTextureUniform::TextureFilter) -VARIANT_ENUM_CAST(VisualShaderNodeTextureUniform::TextureRepeat) +VARIANT_ENUM_CAST(VisualShaderNodeTextureParameter::TextureType) +VARIANT_ENUM_CAST(VisualShaderNodeTextureParameter::ColorDefault) +VARIANT_ENUM_CAST(VisualShaderNodeTextureParameter::TextureFilter) +VARIANT_ENUM_CAST(VisualShaderNodeTextureParameter::TextureRepeat) + +/////////////////////////////////////// + +class VisualShaderNodeTexture2DParameter : public VisualShaderNodeTextureParameter { + GDCLASS(VisualShaderNodeTexture2DParameter, VisualShaderNodeTextureParameter); + +public: + virtual String get_caption() const override; + virtual String get_output_port_name(int p_port) const override; + + virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override; + + VisualShaderNodeTexture2DParameter(); +}; /////////////////////////////////////// -class VisualShaderNodeTextureUniformTriplanar : public VisualShaderNodeTextureUniform { - GDCLASS(VisualShaderNodeTextureUniformTriplanar, VisualShaderNodeTextureUniform); +class VisualShaderNodeTextureParameterTriplanar : public VisualShaderNodeTextureParameter { + GDCLASS(VisualShaderNodeTextureParameterTriplanar, VisualShaderNodeTextureParameter); public: virtual String get_caption() const override; @@ -2223,54 +2279,52 @@ public: virtual String generate_global_per_node(Shader::Mode p_mode, int p_id) const override; virtual String generate_global_per_func(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override; + virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override; virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; - VisualShaderNodeTextureUniformTriplanar(); + VisualShaderNodeTextureParameterTriplanar(); }; /////////////////////////////////////// -class VisualShaderNodeTexture2DArrayUniform : public VisualShaderNodeTextureUniform { - GDCLASS(VisualShaderNodeTexture2DArrayUniform, VisualShaderNodeTextureUniform); +class VisualShaderNodeTexture2DArrayParameter : public VisualShaderNodeTextureParameter { + GDCLASS(VisualShaderNodeTexture2DArrayParameter, VisualShaderNodeTextureParameter); public: virtual String get_caption() const override; virtual String get_output_port_name(int p_port) const override; virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override; - virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; - VisualShaderNodeTexture2DArrayUniform(); + VisualShaderNodeTexture2DArrayParameter(); }; /////////////////////////////////////// -class VisualShaderNodeTexture3DUniform : public VisualShaderNodeTextureUniform { - GDCLASS(VisualShaderNodeTexture3DUniform, VisualShaderNodeTextureUniform); +class VisualShaderNodeTexture3DParameter : public VisualShaderNodeTextureParameter { + GDCLASS(VisualShaderNodeTexture3DParameter, VisualShaderNodeTextureParameter); public: virtual String get_caption() const override; virtual String get_output_port_name(int p_port) const override; virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override; - virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; - VisualShaderNodeTexture3DUniform(); + VisualShaderNodeTexture3DParameter(); }; /////////////////////////////////////// -class VisualShaderNodeCubemapUniform : public VisualShaderNodeTextureUniform { - GDCLASS(VisualShaderNodeCubemapUniform, VisualShaderNodeTextureUniform); +class VisualShaderNodeCubemapParameter : public VisualShaderNodeTextureParameter { + GDCLASS(VisualShaderNodeCubemapParameter, VisualShaderNodeTextureParameter); public: virtual String get_caption() const override; virtual String get_output_port_name(int p_port) const override; virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override; - virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; - VisualShaderNodeCubemapUniform(); + VisualShaderNodeCubemapParameter(); }; /////////////////////////////////////// @@ -2574,4 +2628,87 @@ public: VARIANT_ENUM_CAST(VisualShaderNodeBillboard::BillboardType) +/////////////////////////////////////// +/// DistanceFade +/////////////////////////////////////// + +class VisualShaderNodeDistanceFade : public VisualShaderNode { + GDCLASS(VisualShaderNodeDistanceFade, VisualShaderNode); + +public: + virtual String get_caption() const override; + + virtual int get_input_port_count() const override; + virtual PortType get_input_port_type(int p_port) const override; + virtual String get_input_port_name(int p_port) const override; + + virtual int get_output_port_count() const override; + virtual PortType get_output_port_type(int p_port) const override; + virtual String get_output_port_name(int p_port) const override; + virtual bool has_output_port_preview(int p_port) const override; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; + + VisualShaderNodeDistanceFade(); +}; + +class VisualShaderNodeProximityFade : public VisualShaderNode { + GDCLASS(VisualShaderNodeProximityFade, VisualShaderNode); + +public: + virtual String get_caption() const override; + + virtual int get_input_port_count() const override; + virtual PortType get_input_port_type(int p_port) const override; + virtual String get_input_port_name(int p_port) const override; + + virtual int get_output_port_count() const override; + virtual PortType get_output_port_type(int p_port) const override; + virtual String get_output_port_name(int p_port) const override; + virtual bool has_output_port_preview(int p_port) const override; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; + + VisualShaderNodeProximityFade(); +}; + +class VisualShaderNodeRandomRange : public VisualShaderNode { + GDCLASS(VisualShaderNodeRandomRange, VisualShaderNode); + +public: + virtual String get_caption() const override; + + virtual int get_input_port_count() const override; + virtual PortType get_input_port_type(int p_port) const override; + virtual String get_input_port_name(int p_port) const override; + + virtual int get_output_port_count() const override; + virtual PortType get_output_port_type(int p_port) const override; + virtual String get_output_port_name(int p_port) const override; + + virtual String generate_global_per_node(Shader::Mode p_mode, int p_id) const override; + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; + + VisualShaderNodeRandomRange(); +}; + +class VisualShaderNodeRemap : public VisualShaderNode { + GDCLASS(VisualShaderNodeRemap, VisualShaderNode); + +public: + virtual String get_caption() const override; + + virtual int get_input_port_count() const override; + virtual PortType get_input_port_type(int p_port) const override; + virtual String get_input_port_name(int p_port) const override; + + virtual int get_output_port_count() const override; + virtual PortType get_output_port_type(int p_port) const override; + virtual String get_output_port_name(int p_port) const override; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; + + VisualShaderNodeRemap(); +}; + #endif // VISUAL_SHADER_NODES_H diff --git a/scene/resources/visual_shader_particle_nodes.cpp b/scene/resources/visual_shader_particle_nodes.cpp index bdfbb59fa6..df6abe161e 100644 --- a/scene/resources/visual_shader_particle_nodes.cpp +++ b/scene/resources/visual_shader_particle_nodes.cpp @@ -1130,31 +1130,38 @@ VisualShaderNodeParticleAccelerator::VisualShaderNodeParticleAccelerator() { // VisualShaderNodeParticleOutput String VisualShaderNodeParticleOutput::get_caption() const { - if (shader_type == VisualShader::TYPE_START) { - return "StartOutput"; - } else if (shader_type == VisualShader::TYPE_PROCESS) { - return "ProcessOutput"; - } else if (shader_type == VisualShader::TYPE_COLLIDE) { - return "CollideOutput"; - } else if (shader_type == VisualShader::TYPE_START_CUSTOM) { - return "CustomStartOutput"; - } else if (shader_type == VisualShader::TYPE_PROCESS_CUSTOM) { - return "CustomProcessOutput"; + switch (shader_type) { + case VisualShader::TYPE_START: + return "StartOutput"; + case VisualShader::TYPE_PROCESS: + return "ProcessOutput"; + case VisualShader::TYPE_COLLIDE: + return "CollideOutput"; + case VisualShader::TYPE_START_CUSTOM: + return "CustomStartOutput"; + case VisualShader::TYPE_PROCESS_CUSTOM: + return "CustomProcessOutput"; + default: + ERR_PRINT(vformat("Unexpected shader_type %d for VisualShaderNodeParticleOutput.", shader_type)); + return ""; } - return String(); } int VisualShaderNodeParticleOutput::get_input_port_count() const { - if (shader_type == VisualShader::TYPE_START) { - return 8; - } else if (shader_type == VisualShader::TYPE_COLLIDE) { - return 5; - } else if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) { - return 6; - } else { // TYPE_PROCESS - return 7; + switch (shader_type) { + case VisualShader::TYPE_START: + return 8; + case VisualShader::TYPE_PROCESS: + return 7; + case VisualShader::TYPE_COLLIDE: + return 5; + case VisualShader::TYPE_START_CUSTOM: + case VisualShader::TYPE_PROCESS_CUSTOM: + return 6; + default: + ERR_PRINT(vformat("Unexpected shader_type %d for VisualShaderNodeParticleOutput.", shader_type)); + return 0; } - return 0; } VisualShaderNodeParticleOutput::PortType VisualShaderNodeParticleOutput::get_input_port_type(int p_port) const { diff --git a/scene/resources/visual_shader_particle_nodes.h b/scene/resources/visual_shader_particle_nodes.h index 05a059373b..64acb6d18f 100644 --- a/scene/resources/visual_shader_particle_nodes.h +++ b/scene/resources/visual_shader_particle_nodes.h @@ -352,4 +352,4 @@ public: VARIANT_ENUM_CAST(VisualShaderNodeParticleEmit::EmitFlags) -#endif +#endif // VISUAL_SHADER_PARTICLE_NODES_H diff --git a/scene/resources/world_2d.cpp b/scene/resources/world_2d.cpp index 4dfbe5f079..75deb1e60b 100644 --- a/scene/resources/world_2d.cpp +++ b/scene/resources/world_2d.cpp @@ -85,6 +85,7 @@ World2D::World2D() { NavigationServer2D::get_singleton()->map_set_active(navigation_map, true); NavigationServer2D::get_singleton()->map_set_cell_size(navigation_map, GLOBAL_DEF("navigation/2d/default_cell_size", 1)); NavigationServer2D::get_singleton()->map_set_edge_connection_margin(navigation_map, GLOBAL_DEF("navigation/2d/default_edge_connection_margin", 1)); + NavigationServer2D::get_singleton()->map_set_link_connection_radius(navigation_map, GLOBAL_DEF("navigation/2d/default_link_connection_radius", 4)); } World2D::~World2D() { diff --git a/scene/resources/world_3d.cpp b/scene/resources/world_3d.cpp index fb6dcd3d57..ae8c9a182f 100644 --- a/scene/resources/world_3d.cpp +++ b/scene/resources/world_3d.cpp @@ -33,6 +33,8 @@ #include "core/config/project_settings.h" #include "scene/3d/camera_3d.h" #include "scene/3d/visible_on_screen_notifier_3d.h" +#include "scene/resources/camera_attributes.h" +#include "scene/resources/environment.h" #include "scene/scene_string_names.h" #include "servers/navigation_server_3d.h" @@ -98,17 +100,17 @@ Ref<Environment> World3D::get_fallback_environment() const { return fallback_environment; } -void World3D::set_camera_effects(const Ref<CameraEffects> &p_camera_effects) { - camera_effects = p_camera_effects; - if (camera_effects.is_valid()) { - RS::get_singleton()->scenario_set_camera_effects(scenario, camera_effects->get_rid()); +void World3D::set_camera_attributes(const Ref<CameraAttributes> &p_camera_attributes) { + camera_attributes = p_camera_attributes; + if (camera_attributes.is_valid()) { + RS::get_singleton()->scenario_set_camera_attributes(scenario, camera_attributes->get_rid()); } else { - RS::get_singleton()->scenario_set_camera_effects(scenario, RID()); + RS::get_singleton()->scenario_set_camera_attributes(scenario, RID()); } } -Ref<CameraEffects> World3D::get_camera_effects() const { - return camera_effects; +Ref<CameraAttributes> World3D::get_camera_attributes() const { + return camera_attributes; } PhysicsDirectSpaceState3D *World3D::get_direct_space_state() { @@ -123,12 +125,12 @@ void World3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_environment"), &World3D::get_environment); ClassDB::bind_method(D_METHOD("set_fallback_environment", "env"), &World3D::set_fallback_environment); ClassDB::bind_method(D_METHOD("get_fallback_environment"), &World3D::get_fallback_environment); - ClassDB::bind_method(D_METHOD("set_camera_effects", "effects"), &World3D::set_camera_effects); - ClassDB::bind_method(D_METHOD("get_camera_effects"), &World3D::get_camera_effects); + ClassDB::bind_method(D_METHOD("set_camera_attributes", "attributes"), &World3D::set_camera_attributes); + ClassDB::bind_method(D_METHOD("get_camera_attributes"), &World3D::get_camera_attributes); ClassDB::bind_method(D_METHOD("get_direct_space_state"), &World3D::get_direct_space_state); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "environment", PROPERTY_HINT_RESOURCE_TYPE, "Environment"), "set_environment", "get_environment"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "fallback_environment", PROPERTY_HINT_RESOURCE_TYPE, "Environment"), "set_fallback_environment", "get_fallback_environment"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "camera_effects", PROPERTY_HINT_RESOURCE_TYPE, "CameraEffects"), "set_camera_effects", "get_camera_effects"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "camera_attributes", PROPERTY_HINT_RESOURCE_TYPE, "CameraAttributesPractical,CameraAttributesPhysical"), "set_camera_attributes", "get_camera_attributes"); ADD_PROPERTY(PropertyInfo(Variant::RID, "space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "", "get_space"); ADD_PROPERTY(PropertyInfo(Variant::RID, "navigation_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "", "get_navigation_map"); ADD_PROPERTY(PropertyInfo(Variant::RID, "scenario", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "", "get_scenario"); @@ -151,6 +153,7 @@ World3D::World3D() { NavigationServer3D::get_singleton()->map_set_active(navigation_map, true); NavigationServer3D::get_singleton()->map_set_cell_size(navigation_map, GLOBAL_DEF("navigation/3d/default_cell_size", 0.25)); NavigationServer3D::get_singleton()->map_set_edge_connection_margin(navigation_map, GLOBAL_DEF("navigation/3d/default_edge_connection_margin", 0.25)); + NavigationServer3D::get_singleton()->map_set_link_connection_radius(navigation_map, GLOBAL_DEF("navigation/3d/default_link_connection_radius", 1.0)); } World3D::~World3D() { diff --git a/scene/resources/world_3d.h b/scene/resources/world_3d.h index 08bc050349..411b9aab37 100644 --- a/scene/resources/world_3d.h +++ b/scene/resources/world_3d.h @@ -32,11 +32,11 @@ #define WORLD_3D_H #include "core/io/resource.h" -#include "scene/resources/camera_effects.h" #include "scene/resources/environment.h" #include "servers/physics_server_3d.h" #include "servers/rendering_server.h" +class CameraAttributes; class Camera3D; class VisibleOnScreenNotifier3D; struct SpatialIndexer; @@ -51,7 +51,7 @@ private: Ref<Environment> environment; Ref<Environment> fallback_environment; - Ref<CameraEffects> camera_effects; + Ref<CameraAttributes> camera_attributes; HashSet<Camera3D *> cameras; @@ -74,8 +74,8 @@ public: void set_fallback_environment(const Ref<Environment> &p_environment); Ref<Environment> get_fallback_environment() const; - void set_camera_effects(const Ref<CameraEffects> &p_camera_effects); - Ref<CameraEffects> get_camera_effects() const; + void set_camera_attributes(const Ref<CameraAttributes> &p_camera_attributes); + Ref<CameraAttributes> get_camera_attributes() const; _FORCE_INLINE_ const HashSet<Camera3D *> &get_cameras() const { return cameras; } diff --git a/scene/resources/world_boundary_shape_3d.h b/scene/resources/world_boundary_shape_3d.h index 5378bc52c7..5c34767106 100644 --- a/scene/resources/world_boundary_shape_3d.h +++ b/scene/resources/world_boundary_shape_3d.h @@ -53,4 +53,5 @@ public: WorldBoundaryShape3D(); }; -#endif // WORLD_BOUNDARY_SHAPE_H + +#endif // WORLD_BOUNDARY_SHAPE_3D_H diff --git a/scene/multiplayer/SCsub b/scene/theme/SCsub index fc61250247..fc61250247 100644 --- a/scene/multiplayer/SCsub +++ b/scene/theme/SCsub diff --git a/scene/theme/theme_db.cpp b/scene/theme/theme_db.cpp new file mode 100644 index 0000000000..d6e892cd93 --- /dev/null +++ b/scene/theme/theme_db.cpp @@ -0,0 +1,237 @@ +/*************************************************************************/ +/* theme_db.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "theme_db.h" + +#include "core/config/project_settings.h" +#include "core/io/resource_loader.h" +#include "scene/resources/default_theme/default_theme.h" +#include "scene/resources/font.h" +#include "scene/resources/style_box.h" +#include "scene/resources/texture.h" +#include "scene/resources/theme.h" +#include "servers/text_server.h" + +// Default engine theme creation and configuration. +void ThemeDB::initialize_theme() { + // Allow creating the default theme at a different scale to suit higher/lower base resolutions. + float default_theme_scale = GLOBAL_DEF("gui/theme/default_theme_scale", 1.0); + ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/default_theme_scale", PropertyInfo(Variant::FLOAT, "gui/theme/default_theme_scale", PROPERTY_HINT_RANGE, "0.5,8,0.01", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)); + + String theme_path = GLOBAL_DEF_RST("gui/theme/custom", ""); + ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/custom", PropertyInfo(Variant::STRING, "gui/theme/custom", PROPERTY_HINT_FILE, "*.tres,*.res,*.theme", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)); + + String font_path = GLOBAL_DEF_RST("gui/theme/custom_font", ""); + ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/custom_font", PropertyInfo(Variant::STRING, "gui/theme/custom_font", PROPERTY_HINT_FILE, "*.tres,*.res", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)); + + TextServer::FontAntialiasing font_antialiasing = (TextServer::FontAntialiasing)(int)GLOBAL_DEF_RST("gui/theme/default_font_antialiasing", 1); + ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/default_font_antialiasing", PropertyInfo(Variant::INT, "gui/theme/default_font_antialiasing", PROPERTY_HINT_ENUM, "None,Grayscale,LCD sub-pixel", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)); + + TextServer::Hinting font_hinting = (TextServer::Hinting)(int)GLOBAL_DEF_RST("gui/theme/default_font_hinting", TextServer::HINTING_LIGHT); + ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/default_font_hinting", PropertyInfo(Variant::INT, "gui/theme/default_font_hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)); + + TextServer::SubpixelPositioning font_subpixel_positioning = (TextServer::SubpixelPositioning)(int)GLOBAL_DEF_RST("gui/theme/default_font_subpixel_positioning", TextServer::SUBPIXEL_POSITIONING_AUTO); + ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/default_font_subpixel_positioning", PropertyInfo(Variant::INT, "gui/theme/default_font_subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One half of a pixel,One quarter of a pixel", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)); + + const bool font_msdf = GLOBAL_DEF_RST("gui/theme/default_font_multichannel_signed_distance_field", false); + const bool font_generate_mipmaps = GLOBAL_DEF_RST("gui/theme/default_font_generate_mipmaps", false); + + GLOBAL_DEF_RST("gui/theme/lcd_subpixel_layout", 1); + ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/lcd_subpixel_layout", PropertyInfo(Variant::INT, "gui/theme/lcd_subpixel_layout", PROPERTY_HINT_ENUM, "Disabled,Horizontal RGB,Horizontal BGR,Vertical RGB,Vertical BGR")); + ProjectSettings::get_singleton()->set_restart_if_changed("gui/theme/lcd_subpixel_layout", false); + + Ref<Font> font; + if (!font_path.is_empty()) { + font = ResourceLoader::load(font_path); + if (!font.is_valid()) { + ERR_PRINT("Error loading custom font '" + font_path + "'"); + } + } + + // Always make the default theme to avoid invalid default font/icon/style in the given theme. + if (RenderingServer::get_singleton()) { + make_default_theme(default_theme_scale, font, font_subpixel_positioning, font_hinting, font_antialiasing, font_msdf, font_generate_mipmaps); + } + + if (!theme_path.is_empty()) { + Ref<Theme> theme = ResourceLoader::load(theme_path); + if (theme.is_valid()) { + set_project_theme(theme); + if (font.is_valid()) { + set_fallback_font(font); + } + } else { + ERR_PRINT("Error loading custom theme '" + theme_path + "'"); + } + } +} + +void ThemeDB::initialize_theme_noproject() { + if (RenderingServer::get_singleton()) { + make_default_theme(1.0, Ref<Font>()); + } +} + +// Universal fallback Theme resources. + +void ThemeDB::set_default_theme(const Ref<Theme> &p_default) { + default_theme = p_default; +} + +Ref<Theme> ThemeDB::get_default_theme() { + return default_theme; +} + +void ThemeDB::set_project_theme(const Ref<Theme> &p_project_default) { + project_theme = p_project_default; +} + +Ref<Theme> ThemeDB::get_project_theme() { + return project_theme; +} + +// Universal fallback values for theme item types. + +void ThemeDB::set_fallback_base_scale(float p_base_scale) { + if (fallback_base_scale == p_base_scale) { + return; + } + + fallback_base_scale = p_base_scale; + emit_signal(SNAME("fallback_changed")); +} + +float ThemeDB::get_fallback_base_scale() { + return fallback_base_scale; +} + +void ThemeDB::set_fallback_font(const Ref<Font> &p_font) { + if (fallback_font == p_font) { + return; + } + + fallback_font = p_font; + emit_signal(SNAME("fallback_changed")); +} + +Ref<Font> ThemeDB::get_fallback_font() { + return fallback_font; +} + +void ThemeDB::set_fallback_font_size(int p_font_size) { + if (fallback_font_size == p_font_size) { + return; + } + + fallback_font_size = p_font_size; + emit_signal(SNAME("fallback_changed")); +} + +int ThemeDB::get_fallback_font_size() { + return fallback_font_size; +} + +void ThemeDB::set_fallback_icon(const Ref<Texture2D> &p_icon) { + if (fallback_icon == p_icon) { + return; + } + + fallback_icon = p_icon; + emit_signal(SNAME("fallback_changed")); +} + +Ref<Texture2D> ThemeDB::get_fallback_icon() { + return fallback_icon; +} + +void ThemeDB::set_fallback_stylebox(const Ref<StyleBox> &p_stylebox) { + if (fallback_stylebox == p_stylebox) { + return; + } + + fallback_stylebox = p_stylebox; + emit_signal(SNAME("fallback_changed")); +} + +Ref<StyleBox> ThemeDB::get_fallback_stylebox() { + return fallback_stylebox; +} + +// Object methods. +void ThemeDB::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_default_theme"), &ThemeDB::get_default_theme); + ClassDB::bind_method(D_METHOD("get_project_theme"), &ThemeDB::get_project_theme); + + ClassDB::bind_method(D_METHOD("set_fallback_base_scale", "base_scale"), &ThemeDB::set_fallback_base_scale); + ClassDB::bind_method(D_METHOD("get_fallback_base_scale"), &ThemeDB::get_fallback_base_scale); + ClassDB::bind_method(D_METHOD("set_fallback_font", "font"), &ThemeDB::set_fallback_font); + ClassDB::bind_method(D_METHOD("get_fallback_font"), &ThemeDB::get_fallback_font); + ClassDB::bind_method(D_METHOD("set_fallback_font_size", "font_size"), &ThemeDB::set_fallback_font_size); + ClassDB::bind_method(D_METHOD("get_fallback_font_size"), &ThemeDB::get_fallback_font_size); + ClassDB::bind_method(D_METHOD("set_fallback_icon", "icon"), &ThemeDB::set_fallback_icon); + ClassDB::bind_method(D_METHOD("get_fallback_icon"), &ThemeDB::get_fallback_icon); + ClassDB::bind_method(D_METHOD("set_fallback_stylebox", "stylebox"), &ThemeDB::set_fallback_stylebox); + ClassDB::bind_method(D_METHOD("get_fallback_stylebox"), &ThemeDB::get_fallback_stylebox); + + ADD_GROUP("Fallback values", "fallback_"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fallback_base_scale", PROPERTY_HINT_RANGE, "0.0,2.0,0.01,or_greater"), "set_fallback_base_scale", "get_fallback_base_scale"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "fallback_font", PROPERTY_HINT_RESOURCE_TYPE, "Font", PROPERTY_USAGE_NONE), "set_fallback_font", "get_fallback_font"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "fallback_font_size", PROPERTY_HINT_RANGE, "0,256,1,or_greater,suffix:px"), "set_fallback_font_size", "get_fallback_font_size"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "fallback_icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_NONE), "set_fallback_icon", "get_fallback_icon"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "fallback_stylebox", PROPERTY_HINT_RESOURCE_TYPE, "StyleBox", PROPERTY_USAGE_NONE), "set_fallback_stylebox", "get_fallback_stylebox"); + + ADD_SIGNAL(MethodInfo("fallback_changed")); +} + +// Memory management, reference, and initialization +ThemeDB *ThemeDB::singleton = nullptr; + +ThemeDB *ThemeDB::get_singleton() { + return singleton; +} + +ThemeDB::ThemeDB() { + singleton = this; + + // Universal default values, final fallback for every theme. + fallback_base_scale = 1.0; + fallback_font_size = 16; +} + +ThemeDB::~ThemeDB() { + default_theme.unref(); + project_theme.unref(); + + fallback_font.unref(); + fallback_icon.unref(); + fallback_stylebox.unref(); + + singleton = nullptr; +} diff --git a/scene/gui/gradient_edit.h b/scene/theme/theme_db.h index 3badcd45ba..aace33d82c 100644 --- a/scene/gui/gradient_edit.h +++ b/scene/theme/theme_db.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* gradient_edit.h */ +/* theme_db.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,58 +28,68 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef GRADIENT_EDIT_H -#define GRADIENT_EDIT_H +#ifndef THEME_DB_H +#define THEME_DB_H -#include "scene/gui/color_picker.h" -#include "scene/gui/popup.h" -#include "scene/resources/gradient.h" +#include "core/object/class_db.h" +#include "core/object/ref_counted.h" -class GradientEdit : public Control { - GDCLASS(GradientEdit, Control); +class Font; +class StyleBox; +class Texture2D; +class Theme; - PopupPanel *popup = nullptr; - ColorPicker *picker = nullptr; +class ThemeDB : public Object { + GDCLASS(ThemeDB, Object); - bool grabbing = false; - int grabbed = -1; - Vector<Gradient::Point> points; - Gradient::InterpolationMode interpolation_mode = Gradient::GRADIENT_INTERPOLATE_LINEAR; + static ThemeDB *singleton; - Ref<Gradient> gradient_cache; - Ref<GradientTexture1D> preview_texture; + // Universal Theme resources used when no other theme has the item. + Ref<Theme> default_theme; + Ref<Theme> project_theme; - // Make sure to use the scaled value below. - const int BASE_SPACING = 3; - const int BASE_POINT_WIDTH = 8; - - int draw_spacing = BASE_SPACING; - int draw_point_width = BASE_POINT_WIDTH; - - void _draw_checker(int x, int y, int w, int h); - void _color_changed(const Color &p_color); - int _get_point_from_pos(int x); - void _show_color_picker(); + // Universal default values, final fallback for every theme. + float fallback_base_scale; + Ref<Font> fallback_font; + int fallback_font_size; + Ref<Texture2D> fallback_icon; + Ref<StyleBox> fallback_stylebox; protected: - virtual void gui_input(const Ref<InputEvent> &p_event) override; - void _notification(int p_what); static void _bind_methods(); public: - void set_ramp(const Vector<float> &p_offsets, const Vector<Color> &p_colors); - Vector<float> get_offsets() const; - Vector<Color> get_colors() const; - void set_points(Vector<Gradient::Point> &p_points); - Vector<Gradient::Point> &get_points(); - void set_interpolation_mode(Gradient::InterpolationMode p_interp_mode); - Gradient::InterpolationMode get_interpolation_mode(); - ColorPicker *get_picker(); - - virtual Size2 get_minimum_size() const override; - - GradientEdit(); - virtual ~GradientEdit(); + void initialize_theme(); + void initialize_theme_noproject(); + + // Universal Theme resources + + void set_default_theme(const Ref<Theme> &p_default); + Ref<Theme> get_default_theme(); + + void set_project_theme(const Ref<Theme> &p_project_default); + Ref<Theme> get_project_theme(); + + // Universal default values. + + void set_fallback_base_scale(float p_base_scale); + float get_fallback_base_scale(); + + void set_fallback_font(const Ref<Font> &p_font); + Ref<Font> get_fallback_font(); + + void set_fallback_font_size(int p_font_size); + int get_fallback_font_size(); + + void set_fallback_icon(const Ref<Texture2D> &p_icon); + Ref<Texture2D> get_fallback_icon(); + + void set_fallback_stylebox(const Ref<StyleBox> &p_stylebox); + Ref<StyleBox> get_fallback_stylebox(); + + static ThemeDB *get_singleton(); + ThemeDB(); + ~ThemeDB(); }; -#endif // GRADIENT_EDIT_H +#endif // THEME_DB_H diff --git a/scene/theme/theme_owner.cpp b/scene/theme/theme_owner.cpp new file mode 100644 index 0000000000..e89aa1b28d --- /dev/null +++ b/scene/theme/theme_owner.cpp @@ -0,0 +1,410 @@ +/*************************************************************************/ +/* theme_owner.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "theme_owner.h" + +#include "scene/gui/control.h" +#include "scene/main/window.h" +#include "scene/theme/theme_db.h" + +// Theme owner node. + +void ThemeOwner::set_owner_node(Node *p_node) { + owner_control = nullptr; + owner_window = nullptr; + + Control *c = Object::cast_to<Control>(p_node); + if (c) { + owner_control = c; + return; + } + + Window *w = Object::cast_to<Window>(p_node); + if (w) { + owner_window = w; + return; + } +} + +Node *ThemeOwner::get_owner_node() const { + if (owner_control) { + return owner_control; + } else if (owner_window) { + return owner_window; + } + return nullptr; +} + +bool ThemeOwner::has_owner_node() const { + return bool(owner_control || owner_window); +} + +// Theme propagation. + +void ThemeOwner::assign_theme_on_parented(Node *p_for_node) { + // We check if there are any themes affecting the parent. If that's the case + // its children also need to be affected. + // We don't notify here because `NOTIFICATION_THEME_CHANGED` will be handled + // a bit later by `NOTIFICATION_ENTER_TREE`. + + Node *parent = p_for_node->get_parent(); + + Control *parent_c = Object::cast_to<Control>(parent); + if (parent_c && parent_c->has_theme_owner_node()) { + propagate_theme_changed(p_for_node, parent_c->get_theme_owner_node(), false, true); + } else { + Window *parent_w = Object::cast_to<Window>(parent); + if (parent_w && parent_w->has_theme_owner_node()) { + propagate_theme_changed(p_for_node, parent_w->get_theme_owner_node(), false, true); + } + } +} + +void ThemeOwner::clear_theme_on_unparented(Node *p_for_node) { + // We check if there were any themes affecting the parent. If that's the case + // its children need were also affected and need to be updated. + // We don't notify because we're exiting the tree, and it's not important. + + Node *parent = p_for_node->get_parent(); + + Control *parent_c = Object::cast_to<Control>(parent); + if (parent_c && parent_c->has_theme_owner_node()) { + propagate_theme_changed(p_for_node, nullptr, false, true); + } else { + Window *parent_w = Object::cast_to<Window>(parent); + if (parent_w && parent_w->has_theme_owner_node()) { + propagate_theme_changed(p_for_node, nullptr, false, true); + } + } +} + +void ThemeOwner::propagate_theme_changed(Node *p_to_node, Node *p_owner_node, bool p_notify, bool p_assign) { + Control *c = Object::cast_to<Control>(p_to_node); + Window *w = c == nullptr ? Object::cast_to<Window>(p_to_node) : nullptr; + + if (!c && !w) { + // Theme inheritance chains are broken by nodes that aren't Control or Window. + return; + } + + bool assign = p_assign; + if (c) { + if (c != p_owner_node && c->get_theme().is_valid()) { + // Has a theme, so we don't want to change the theme owner, + // but we still want to propagate in case this child has theme items + // it inherits from the theme this node uses. + // See https://github.com/godotengine/godot/issues/62844. + assign = false; + } + + if (assign) { + c->set_theme_owner_node(p_owner_node); + } + + if (p_notify) { + c->notification(Control::NOTIFICATION_THEME_CHANGED); + } + } else if (w) { + if (w != p_owner_node && w->get_theme().is_valid()) { + // Same as above. + assign = false; + } + + if (assign) { + w->set_theme_owner_node(p_owner_node); + } + + if (p_notify) { + w->notification(Window::NOTIFICATION_THEME_CHANGED); + } + } + + for (int i = 0; i < p_to_node->get_child_count(); i++) { + propagate_theme_changed(p_to_node->get_child(i), p_owner_node, p_notify, assign); + } +} + +// Theme lookup. + +void ThemeOwner::get_theme_type_dependencies(const Node *p_for_node, const StringName &p_theme_type, List<StringName> *r_list) const { + const Control *for_c = Object::cast_to<Control>(p_for_node); + const Window *for_w = Object::cast_to<Window>(p_for_node); + ERR_FAIL_COND_MSG(!for_c && !for_w, "Only Control and Window nodes and derivatives can be polled for theming."); + + Ref<Theme> default_theme = ThemeDB::get_singleton()->get_default_theme(); + Ref<Theme> project_theme = ThemeDB::get_singleton()->get_project_theme(); + + StringName type_variation; + if (for_c) { + type_variation = for_c->get_theme_type_variation(); + } else if (for_w) { + type_variation = for_w->get_theme_type_variation(); + } + + if (p_theme_type == StringName() || p_theme_type == p_for_node->get_class_name() || p_theme_type == type_variation) { + if (project_theme.is_valid() && project_theme->get_type_variation_base(type_variation) != StringName()) { + project_theme->get_type_dependencies(p_for_node->get_class_name(), type_variation, r_list); + } else { + default_theme->get_type_dependencies(p_for_node->get_class_name(), type_variation, r_list); + } + } else { + default_theme->get_type_dependencies(p_theme_type, StringName(), r_list); + } +} + +Node *ThemeOwner::_get_next_owner_node(Node *p_from_node) const { + Node *parent = p_from_node->get_parent(); + + Control *parent_c = Object::cast_to<Control>(parent); + if (parent_c) { + return parent_c->get_theme_owner_node(); + } else { + Window *parent_w = Object::cast_to<Window>(parent); + if (parent_w) { + return parent_w->get_theme_owner_node(); + } + } + + return nullptr; +} + +Variant ThemeOwner::get_theme_item_in_types(Theme::DataType p_data_type, const StringName &p_name, List<StringName> p_theme_types) { + ERR_FAIL_COND_V_MSG(p_theme_types.size() == 0, Variant(), "At least one theme type must be specified."); + + // First, look through each control or window node in the branch, until no valid parent can be found. + // Only nodes with a theme resource attached are considered. + Node *owner_node = get_owner_node(); + + while (owner_node) { + // For each theme resource check the theme types provided and see if p_name exists with any of them. + for (const StringName &E : p_theme_types) { + Ref<Theme> owner_theme; + + Control *owner_c = Object::cast_to<Control>(owner_node); + if (owner_c) { + owner_theme = owner_c->get_theme(); + } + Window *owner_w = Object::cast_to<Window>(owner_node); + if (owner_w) { + owner_theme = owner_w->get_theme(); + } + + if (owner_theme.is_valid() && owner_theme->has_theme_item(p_data_type, p_name, E)) { + return owner_theme->get_theme_item(p_data_type, p_name, E); + } + } + + owner_node = _get_next_owner_node(owner_node); + } + + // Secondly, check the project-defined Theme resource. + if (ThemeDB::get_singleton()->get_project_theme().is_valid()) { + for (const StringName &E : p_theme_types) { + if (ThemeDB::get_singleton()->get_project_theme()->has_theme_item(p_data_type, p_name, E)) { + return ThemeDB::get_singleton()->get_project_theme()->get_theme_item(p_data_type, p_name, E); + } + } + } + + // Lastly, fall back on the items defined in the default Theme, if they exist. + for (const StringName &E : p_theme_types) { + if (ThemeDB::get_singleton()->get_default_theme()->has_theme_item(p_data_type, p_name, E)) { + return ThemeDB::get_singleton()->get_default_theme()->get_theme_item(p_data_type, p_name, E); + } + } + + // If they don't exist, use any type to return the default/empty value. + return ThemeDB::get_singleton()->get_default_theme()->get_theme_item(p_data_type, p_name, p_theme_types[0]); +} + +bool ThemeOwner::has_theme_item_in_types(Theme::DataType p_data_type, const StringName &p_name, List<StringName> p_theme_types) { + ERR_FAIL_COND_V_MSG(p_theme_types.size() == 0, false, "At least one theme type must be specified."); + + // First, look through each control or window node in the branch, until no valid parent can be found. + // Only nodes with a theme resource attached are considered. + Node *owner_node = get_owner_node(); + + while (owner_node) { + // For each theme resource check the theme types provided and see if p_name exists with any of them. + for (const StringName &E : p_theme_types) { + Ref<Theme> owner_theme; + + Control *owner_c = Object::cast_to<Control>(owner_node); + if (owner_c) { + owner_theme = owner_c->get_theme(); + } + Window *owner_w = Object::cast_to<Window>(owner_node); + if (owner_w) { + owner_theme = owner_w->get_theme(); + } + + if (owner_theme.is_valid() && owner_theme->has_theme_item(p_data_type, p_name, E)) { + return true; + } + } + + owner_node = _get_next_owner_node(owner_node); + } + + // Secondly, check the project-defined Theme resource. + if (ThemeDB::get_singleton()->get_project_theme().is_valid()) { + for (const StringName &E : p_theme_types) { + if (ThemeDB::get_singleton()->get_project_theme()->has_theme_item(p_data_type, p_name, E)) { + return true; + } + } + } + + // Lastly, fall back on the items defined in the default Theme, if they exist. + for (const StringName &E : p_theme_types) { + if (ThemeDB::get_singleton()->get_default_theme()->has_theme_item(p_data_type, p_name, E)) { + return true; + } + } + + return false; +} + +float ThemeOwner::get_theme_default_base_scale() { + // First, look through each control or window node in the branch, until no valid parent can be found. + // Only nodes with a theme resource attached are considered. + // For each theme resource see if their assigned theme has the default value defined and valid. + Node *owner_node = get_owner_node(); + + while (owner_node) { + Ref<Theme> owner_theme; + + Control *owner_c = Object::cast_to<Control>(owner_node); + if (owner_c) { + owner_theme = owner_c->get_theme(); + } + Window *owner_w = Object::cast_to<Window>(owner_node); + if (owner_w) { + owner_theme = owner_w->get_theme(); + } + + if (owner_theme.is_valid() && owner_theme->has_default_base_scale()) { + return owner_theme->get_default_base_scale(); + } + + owner_node = _get_next_owner_node(owner_node); + } + + // Secondly, check the project-defined Theme resource. + if (ThemeDB::get_singleton()->get_project_theme().is_valid()) { + if (ThemeDB::get_singleton()->get_project_theme()->has_default_base_scale()) { + return ThemeDB::get_singleton()->get_project_theme()->get_default_base_scale(); + } + } + + // Lastly, fall back on the default Theme. + if (ThemeDB::get_singleton()->get_default_theme()->has_default_base_scale()) { + return ThemeDB::get_singleton()->get_default_theme()->get_default_base_scale(); + } + return ThemeDB::get_singleton()->get_fallback_base_scale(); +} + +Ref<Font> ThemeOwner::get_theme_default_font() { + // First, look through each control or window node in the branch, until no valid parent can be found. + // Only nodes with a theme resource attached are considered. + // For each theme resource see if their assigned theme has the default value defined and valid. + Node *owner_node = get_owner_node(); + + while (owner_node) { + Ref<Theme> owner_theme; + + Control *owner_c = Object::cast_to<Control>(owner_node); + if (owner_c) { + owner_theme = owner_c->get_theme(); + } + Window *owner_w = Object::cast_to<Window>(owner_node); + if (owner_w) { + owner_theme = owner_w->get_theme(); + } + + if (owner_theme.is_valid() && owner_theme->has_default_font()) { + return owner_theme->get_default_font(); + } + + owner_node = _get_next_owner_node(owner_node); + } + + // Secondly, check the project-defined Theme resource. + if (ThemeDB::get_singleton()->get_project_theme().is_valid()) { + if (ThemeDB::get_singleton()->get_project_theme()->has_default_font()) { + return ThemeDB::get_singleton()->get_project_theme()->get_default_font(); + } + } + + // Lastly, fall back on the default Theme. + if (ThemeDB::get_singleton()->get_default_theme()->has_default_font()) { + return ThemeDB::get_singleton()->get_default_theme()->get_default_font(); + } + return ThemeDB::get_singleton()->get_fallback_font(); +} + +int ThemeOwner::get_theme_default_font_size() { + // First, look through each control or window node in the branch, until no valid parent can be found. + // Only nodes with a theme resource attached are considered. + // For each theme resource see if their assigned theme has the default value defined and valid. + Node *owner_node = get_owner_node(); + + while (owner_node) { + Ref<Theme> owner_theme; + + Control *owner_c = Object::cast_to<Control>(owner_node); + if (owner_c) { + owner_theme = owner_c->get_theme(); + } + Window *owner_w = Object::cast_to<Window>(owner_node); + if (owner_w) { + owner_theme = owner_w->get_theme(); + } + + if (owner_theme.is_valid() && owner_theme->has_default_font_size()) { + return owner_theme->get_default_font_size(); + } + + owner_node = _get_next_owner_node(owner_node); + } + + // Secondly, check the project-defined Theme resource. + if (ThemeDB::get_singleton()->get_project_theme().is_valid()) { + if (ThemeDB::get_singleton()->get_project_theme()->has_default_font_size()) { + return ThemeDB::get_singleton()->get_project_theme()->get_default_font_size(); + } + } + + // Lastly, fall back on the default Theme. + if (ThemeDB::get_singleton()->get_default_theme()->has_default_font_size()) { + return ThemeDB::get_singleton()->get_default_theme()->get_default_font_size(); + } + return ThemeDB::get_singleton()->get_fallback_font_size(); +} diff --git a/scene/theme/theme_owner.h b/scene/theme/theme_owner.h new file mode 100644 index 0000000000..59b72c1627 --- /dev/null +++ b/scene/theme/theme_owner.h @@ -0,0 +1,75 @@ +/*************************************************************************/ +/* theme_owner.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef THEME_OWNER_H +#define THEME_OWNER_H + +#include "core/object/object.h" +#include "scene/resources/theme.h" + +class Control; +class Node; +class Window; + +class ThemeOwner : public Object { + Control *owner_control = nullptr; + Window *owner_window = nullptr; + + Node *_get_next_owner_node(Node *p_from_node) const; + +public: + // Theme owner node. + + void set_owner_node(Node *p_node); + Node *get_owner_node() const; + bool has_owner_node() const; + + // Theme propagation. + + void assign_theme_on_parented(Node *p_for_node); + void clear_theme_on_unparented(Node *p_for_node); + void propagate_theme_changed(Node *p_to_node, Node *p_owner_node, bool p_notify, bool p_assign); + + // Theme lookup. + + void get_theme_type_dependencies(const Node *p_for_node, const StringName &p_theme_type, List<StringName> *r_list) const; + + Variant get_theme_item_in_types(Theme::DataType p_data_type, const StringName &p_name, List<StringName> p_theme_types); + bool has_theme_item_in_types(Theme::DataType p_data_type, const StringName &p_name, List<StringName> p_theme_types); + + float get_theme_default_base_scale(); + Ref<Font> get_theme_default_font(); + int get_theme_default_font_size(); + + ThemeOwner() {} + ~ThemeOwner() {} +}; + +#endif // THEME_OWNER_H |