diff options
Diffstat (limited to 'scene')
371 files changed, 8696 insertions, 3600 deletions
diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp index fb8bb3bb83..60a7961293 100644 --- a/scene/2d/animated_sprite.cpp +++ b/scene/2d/animated_sprite.cpp @@ -27,13 +27,62 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "animated_sprite.h" #include "os/os.h" #include "scene/scene_string_names.h" #define NORMAL_SUFFIX "_normal" -//////////////////////////// +Dictionary AnimatedSprite::_edit_get_state() const { + Dictionary state = Node2D::_edit_get_state(); + state["offset"] = offset; + return state; +} + +void AnimatedSprite::_edit_set_state(const Dictionary &p_state) { + Node2D::_edit_set_state(p_state); + set_offset(p_state["offset"]); +} + +void AnimatedSprite::_edit_set_pivot(const Point2 &p_pivot) { + set_offset(get_offset() - p_pivot); + set_position(get_transform().xform(p_pivot)); +} + +Point2 AnimatedSprite::_edit_get_pivot() const { + return Vector2(); +} + +bool AnimatedSprite::_edit_use_pivot() const { + return true; +} + +Rect2 AnimatedSprite::_edit_get_rect() const { + if (!frames.is_valid() || !frames->has_animation(animation) || frame < 0 || frame >= frames->get_frame_count(animation)) { + return Node2D::_edit_get_rect(); + } + + Ref<Texture> t; + if (animation) + t = frames->get_frame(animation, frame); + if (t.is_null()) + return Node2D::_edit_get_rect(); + Size2 s = t->get_size(); + + Point2 ofs = offset; + if (centered) + ofs -= s / 2; + + if (s == Size2(0, 0)) + s = Size2(1, 1); + + return Rect2(ofs, s); +} + +bool AnimatedSprite::_edit_use_rect() const { + return true; +} void SpriteFrames::add_frame(const StringName &p_anim, const Ref<Texture> &p_frame, int p_at_pos) { @@ -239,7 +288,7 @@ void SpriteFrames::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_animations"), &SpriteFrames::_set_animations); ClassDB::bind_method(D_METHOD("_get_animations"), &SpriteFrames::_get_animations); - ADD_PROPERTYNZ(PropertyInfo(Variant::ARRAY, "animations", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_animations", "_get_animations"); //compatibility + ADD_PROPERTYNZ(PropertyInfo(Variant::ARRAY, "animations", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_animations", "_get_animations"); //compatibility } SpriteFrames::SpriteFrames() { @@ -247,20 +296,6 @@ SpriteFrames::SpriteFrames() { add_animation(SceneStringNames::get_singleton()->_default); } -void AnimatedSprite::_edit_set_pivot(const Point2 &p_pivot) { - - set_offset(p_pivot); -} - -Point2 AnimatedSprite::_edit_get_pivot() const { - - return get_offset(); -} -bool AnimatedSprite::_edit_use_pivot() const { - - return true; -} - void AnimatedSprite::_validate_property(PropertyInfo &property) const { if (!frames.is_valid()) @@ -316,7 +351,7 @@ void AnimatedSprite::_notification(int p_what) { if (frame < 0) return; - float speed = frames->get_animation_speed(animation); + float speed = frames->get_animation_speed(animation) * speed_scale; if (speed == 0) return; //do nothing @@ -446,6 +481,16 @@ int AnimatedSprite::get_frame() const { return frame; } +void AnimatedSprite::set_speed_scale(float p_speed_scale) { + + speed_scale = MAX(p_speed_scale, 0.0f); +} + +float AnimatedSprite::get_speed_scale() const { + + return speed_scale; +} + void AnimatedSprite::set_centered(bool p_center) { centered = p_center; @@ -490,29 +535,6 @@ bool AnimatedSprite::is_flipped_v() const { return vflip; } -Rect2 AnimatedSprite::_edit_get_rect() const { - - if (!frames.is_valid() || !frames->has_animation(animation) || frame < 0 || frame >= frames->get_frame_count(animation)) { - return Node2D::_edit_get_rect(); - } - - Ref<Texture> t; - if (animation) - t = frames->get_frame(animation, frame); - if (t.is_null()) - return Node2D::_edit_get_rect(); - Size2i s = t->get_size(); - - Point2 ofs = offset; - if (centered) - ofs -= s / 2; - - if (s == Size2(0, 0)) - s = Size2(1, 1); - - return Rect2(ofs, s); -} - void AnimatedSprite::_res_changed() { set_frame(frame); @@ -558,7 +580,7 @@ void AnimatedSprite::_reset_timeout() { return; if (frames.is_valid() && frames->has_animation(animation)) { - float speed = frames->get_animation_speed(animation); + float speed = frames->get_animation_speed(animation) * speed_scale; if (speed > 0) { timeout = 1.0 / speed; } else { @@ -624,6 +646,9 @@ void AnimatedSprite::_bind_methods() { ClassDB::bind_method(D_METHOD("set_frame", "frame"), &AnimatedSprite::set_frame); ClassDB::bind_method(D_METHOD("get_frame"), &AnimatedSprite::get_frame); + ClassDB::bind_method(D_METHOD("set_speed_scale", "speed_scale"), &AnimatedSprite::set_speed_scale); + ClassDB::bind_method(D_METHOD("get_speed_scale"), &AnimatedSprite::get_speed_scale); + ClassDB::bind_method(D_METHOD("_res_changed"), &AnimatedSprite::_res_changed); ADD_SIGNAL(MethodInfo("frame_changed")); @@ -632,6 +657,7 @@ void AnimatedSprite::_bind_methods() { ADD_PROPERTYNZ(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_PROPERTYNZ(PropertyInfo(Variant::INT, "frame", PROPERTY_HINT_SPRITE_FRAME), "set_frame", "get_frame"); + ADD_PROPERTYNO(PropertyInfo(Variant::REAL, "speed_scale"), "set_speed_scale", "get_speed_scale"); ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "playing"), "_set_playing", "_is_playing"); ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "centered"), "set_centered", "is_centered"); ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset"); @@ -646,6 +672,7 @@ AnimatedSprite::AnimatedSprite() { vflip = false; frame = 0; + speed_scale = 1.0f; playing = false; animation = "default"; timeout = 0; diff --git a/scene/2d/animated_sprite.h b/scene/2d/animated_sprite.h index 8a696a40fc..7b91a1faef 100644 --- a/scene/2d/animated_sprite.h +++ b/scene/2d/animated_sprite.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef ANIMATED_SPRITE_H #define ANIMATED_SPRITE_H @@ -128,6 +129,7 @@ class AnimatedSprite : public Node2D { bool playing; StringName animation; int frame; + float speed_scale; bool centered; Point2 offset; @@ -149,9 +151,14 @@ protected: virtual void _validate_property(PropertyInfo &property) const; public: + virtual Dictionary _edit_get_state() const; + virtual void _edit_set_state(const Dictionary &p_state); + virtual void _edit_set_pivot(const Point2 &p_pivot); virtual Point2 _edit_get_pivot() const; virtual bool _edit_use_pivot() const; + virtual Rect2 _edit_get_rect() const; + virtual bool _edit_use_rect() const; void set_sprite_frames(const Ref<SpriteFrames> &p_frames); Ref<SpriteFrames> get_sprite_frames() const; @@ -166,6 +173,9 @@ public: void set_frame(int p_frame); int get_frame() const; + void set_speed_scale(float p_speed_scale); + float get_speed_scale() const; + void set_centered(bool p_center); bool is_centered() const; @@ -181,8 +191,6 @@ public: void set_modulate(const Color &p_color); Color get_modulate() const; - virtual Rect2 _edit_get_rect() const; - virtual String get_configuration_warning() const; AnimatedSprite(); }; diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp index 29a01d30e1..bb914b90fc 100644 --- a/scene/2d/area_2d.cpp +++ b/scene/2d/area_2d.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "area_2d.h" #include "scene/scene_string_names.h" #include "servers/audio_server.h" @@ -169,7 +170,7 @@ void Area2D::_body_inout(int p_status, const RID &p_body, int p_instance, int p_ E->get().in_tree = node && node->is_inside_tree(); if (node) { node->connect(SceneStringNames::get_singleton()->tree_entered, this, SceneStringNames::get_singleton()->_body_enter_tree, make_binds(objid)); - node->connect(SceneStringNames::get_singleton()->tree_exited, this, SceneStringNames::get_singleton()->_body_exit_tree, make_binds(objid)); + node->connect(SceneStringNames::get_singleton()->tree_exiting, this, SceneStringNames::get_singleton()->_body_exit_tree, make_binds(objid)); if (E->get().in_tree) { emit_signal(SceneStringNames::get_singleton()->body_entered, node); } @@ -196,7 +197,7 @@ void Area2D::_body_inout(int p_status, const RID &p_body, int p_instance, int p_ if (node) { node->disconnect(SceneStringNames::get_singleton()->tree_entered, this, SceneStringNames::get_singleton()->_body_enter_tree); - node->disconnect(SceneStringNames::get_singleton()->tree_exited, this, SceneStringNames::get_singleton()->_body_exit_tree); + node->disconnect(SceneStringNames::get_singleton()->tree_exiting, this, SceneStringNames::get_singleton()->_body_exit_tree); if (E->get().in_tree) emit_signal(SceneStringNames::get_singleton()->body_exited, obj); } @@ -270,7 +271,7 @@ void Area2D::_area_inout(int p_status, const RID &p_area, int p_instance, int p_ E->get().in_tree = node && node->is_inside_tree(); if (node) { node->connect(SceneStringNames::get_singleton()->tree_entered, this, SceneStringNames::get_singleton()->_area_enter_tree, make_binds(objid)); - node->connect(SceneStringNames::get_singleton()->tree_exited, this, SceneStringNames::get_singleton()->_area_exit_tree, make_binds(objid)); + node->connect(SceneStringNames::get_singleton()->tree_exiting, this, SceneStringNames::get_singleton()->_area_exit_tree, make_binds(objid)); if (E->get().in_tree) { emit_signal(SceneStringNames::get_singleton()->area_entered, node); } @@ -297,7 +298,7 @@ void Area2D::_area_inout(int p_status, const RID &p_area, int p_instance, int p_ if (node) { node->disconnect(SceneStringNames::get_singleton()->tree_entered, this, SceneStringNames::get_singleton()->_area_enter_tree); - node->disconnect(SceneStringNames::get_singleton()->tree_exited, this, SceneStringNames::get_singleton()->_area_exit_tree); + node->disconnect(SceneStringNames::get_singleton()->tree_exiting, this, SceneStringNames::get_singleton()->_area_exit_tree); if (E->get().in_tree) emit_signal(SceneStringNames::get_singleton()->area_exited, obj); } @@ -337,7 +338,7 @@ void Area2D::_clear_monitoring() { //ERR_CONTINUE(!node); node->disconnect(SceneStringNames::get_singleton()->tree_entered, this, SceneStringNames::get_singleton()->_body_enter_tree); - node->disconnect(SceneStringNames::get_singleton()->tree_exited, this, SceneStringNames::get_singleton()->_body_exit_tree); + node->disconnect(SceneStringNames::get_singleton()->tree_exiting, this, SceneStringNames::get_singleton()->_body_exit_tree); if (!E->get().in_tree) continue; @@ -367,7 +368,7 @@ void Area2D::_clear_monitoring() { //ERR_CONTINUE(!node); node->disconnect(SceneStringNames::get_singleton()->tree_entered, this, SceneStringNames::get_singleton()->_area_enter_tree); - node->disconnect(SceneStringNames::get_singleton()->tree_exited, this, SceneStringNames::get_singleton()->_area_exit_tree); + node->disconnect(SceneStringNames::get_singleton()->tree_exiting, this, SceneStringNames::get_singleton()->_area_exit_tree); if (!E->get().in_tree) continue; @@ -398,7 +399,7 @@ void Area2D::set_monitoring(bool p_enable) { if (p_enable == monitoring) return; if (locked) { - ERR_EXPLAIN("Function blocked during in/out signal. Use call_deferred(\"set_enable_monitoring\",true/false)"); + ERR_EXPLAIN("Function blocked during in/out signal. Use call_deferred(\"set_monitoring\",true/false)"); } ERR_FAIL_COND(locked); diff --git a/scene/2d/area_2d.h b/scene/2d/area_2d.h index 66217c7b08..0fda9b867d 100644 --- a/scene/2d/area_2d.h +++ b/scene/2d/area_2d.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef AREA_2D_H #define AREA_2D_H diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp index 937a026e34..f998f23d3b 100644 --- a/scene/2d/audio_stream_player_2d.cpp +++ b/scene/2d/audio_stream_player_2d.cpp @@ -1,3 +1,32 @@ +/*************************************************************************/ +/* audio_stream_player_2d.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 "audio_stream_player_2d.h" @@ -25,7 +54,7 @@ void AudioStreamPlayer2D::_mix_audio() { int buffer_size = mix_buffer.size(); //mix - stream_playback->mix(buffer, 1.0, buffer_size); + stream_playback->mix(buffer, pitch_scale, buffer_size); //write all outputs for (int i = 0; i < output_count; i++) { @@ -166,7 +195,7 @@ void AudioStreamPlayer2D::_notification(int p_what) { float dist = global_pos.distance_to(screen_in_global); //distance to screen center if (dist > max_distance) - continue; //cant hear this sound in this viewport + continue; //can't hear this sound in this viewport float multiplier = Math::pow(1.0f - dist / max_distance, attenuation); multiplier *= Math::db2linear(volume_db); //also apply player volume! @@ -197,14 +226,14 @@ void AudioStreamPlayer2D::_notification(int p_what) { setseek = setplay; active = true; setplay = -1; - //do not update, this makes it easier to animate (will shut off otherise) + //do not update, this makes it easier to animate (will shut off otherwise) //_change_notify("playing"); //update property in editor } //stop playing if no longer active if (!active) { set_physics_process_internal(false); - //do not update, this makes it easier to animate (will shut off otherise) + //do not update, this makes it easier to animate (will shut off otherwise) //_change_notify("playing"); //update property in editor emit_signal("finished"); } @@ -250,6 +279,13 @@ float AudioStreamPlayer2D::get_volume_db() const { return volume_db; } +void AudioStreamPlayer2D::set_pitch_scale(float p_pitch_scale) { + pitch_scale = p_pitch_scale; +} +float AudioStreamPlayer2D::get_pitch_scale() const { + return pitch_scale; +} + void AudioStreamPlayer2D::play(float p_from_pos) { if (stream_playback.is_valid()) { @@ -390,6 +426,9 @@ void AudioStreamPlayer2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_volume_db", "volume_db"), &AudioStreamPlayer2D::set_volume_db); ClassDB::bind_method(D_METHOD("get_volume_db"), &AudioStreamPlayer2D::get_volume_db); + ClassDB::bind_method(D_METHOD("set_pitch_scale", "pitch_scale"), &AudioStreamPlayer2D::set_pitch_scale); + ClassDB::bind_method(D_METHOD("get_pitch_scale"), &AudioStreamPlayer2D::get_pitch_scale); + ClassDB::bind_method(D_METHOD("play", "from_position"), &AudioStreamPlayer2D::play, DEFVAL(0.0)); ClassDB::bind_method(D_METHOD("seek", "to_position"), &AudioStreamPlayer2D::seek); ClassDB::bind_method(D_METHOD("stop"), &AudioStreamPlayer2D::stop); @@ -419,6 +458,7 @@ void AudioStreamPlayer2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "volume_db", PROPERTY_HINT_RANGE, "-80,24"), "set_volume_db", "get_volume_db"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "pitch_scale", PROPERTY_HINT_RANGE, "0.01,32,0.01"), "set_pitch_scale", "get_pitch_scale"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_playing", "is_playing"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "is_autoplay_enabled"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_distance", PROPERTY_HINT_RANGE, "1,65536,1"), "set_max_distance", "get_max_distance"); @@ -432,6 +472,7 @@ void AudioStreamPlayer2D::_bind_methods() { AudioStreamPlayer2D::AudioStreamPlayer2D() { volume_db = 0; + pitch_scale = 1.0; autoplay = false; setseek = -1; active = false; diff --git a/scene/2d/audio_stream_player_2d.h b/scene/2d/audio_stream_player_2d.h index 85104fce21..9ae8e3a518 100644 --- a/scene/2d/audio_stream_player_2d.h +++ b/scene/2d/audio_stream_player_2d.h @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* audio_stream_player_2d.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 AUDIO_STREAM_PLAYER_2D_H #define AUDIO_STREAM_PLAYER_2D_H @@ -40,6 +70,7 @@ private: volatile float setplay; float volume_db; + float pitch_scale; bool autoplay; StringName bus; @@ -68,6 +99,9 @@ public: void set_volume_db(float p_volume); float get_volume_db() const; + void set_pitch_scale(float p_pitch_scale); + float get_pitch_scale() const; + void play(float p_from_pos = 0.0); void seek(float p_seconds); void stop(); diff --git a/scene/2d/back_buffer_copy.cpp b/scene/2d/back_buffer_copy.cpp index 1d13db493c..caa1adebdb 100644 --- a/scene/2d/back_buffer_copy.cpp +++ b/scene/2d/back_buffer_copy.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "back_buffer_copy.h" void BackBufferCopy::_update_copy_mode() { @@ -54,6 +55,10 @@ Rect2 BackBufferCopy::_edit_get_rect() const { return rect; } +bool BackBufferCopy::_edit_use_rect() const { + return true; +} + void BackBufferCopy::set_rect(const Rect2 &p_rect) { rect = p_rect; diff --git a/scene/2d/back_buffer_copy.h b/scene/2d/back_buffer_copy.h index 01438ca7a1..752d56de2b 100644 --- a/scene/2d/back_buffer_copy.h +++ b/scene/2d/back_buffer_copy.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef BACKBUFFERCOPY_H #define BACKBUFFERCOPY_H @@ -53,6 +54,7 @@ protected: public: Rect2 _edit_get_rect() const; + virtual bool _edit_use_rect() const; void set_rect(const Rect2 &p_rect); Rect2 get_rect() const; diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp index 02e21913a5..d172da5bd9 100644 --- a/scene/2d/camera_2d.cpp +++ b/scene/2d/camera_2d.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "camera_2d.h" #include "core/math/math_funcs.h" #include "scene/scene_string_names.h" @@ -90,8 +91,8 @@ Transform2D Camera2D::get_camera_transform() { if (anchor_mode == ANCHOR_MODE_DRAG_CENTER) { if (h_drag_enabled && !Engine::get_singleton()->is_editor_hint()) { - camera_pos.x = MIN(camera_pos.x, (new_camera_pos.x + screen_size.x * 0.5 * drag_margin[MARGIN_RIGHT])); - camera_pos.x = MAX(camera_pos.x, (new_camera_pos.x - screen_size.x * 0.5 * drag_margin[MARGIN_LEFT])); + camera_pos.x = MIN(camera_pos.x, (new_camera_pos.x + screen_size.x * 0.5 * drag_margin[MARGIN_LEFT])); + camera_pos.x = MAX(camera_pos.x, (new_camera_pos.x - screen_size.x * 0.5 * drag_margin[MARGIN_RIGHT])); } else { if (h_ofs < 0) { @@ -103,8 +104,8 @@ Transform2D Camera2D::get_camera_transform() { if (v_drag_enabled && !Engine::get_singleton()->is_editor_hint()) { - camera_pos.y = MIN(camera_pos.y, (new_camera_pos.y + screen_size.y * 0.5 * drag_margin[MARGIN_BOTTOM])); - camera_pos.y = MAX(camera_pos.y, (new_camera_pos.y - screen_size.y * 0.5 * drag_margin[MARGIN_TOP])); + camera_pos.y = MIN(camera_pos.y, (new_camera_pos.y + screen_size.y * 0.5 * drag_margin[MARGIN_TOP])); + camera_pos.y = MAX(camera_pos.y, (new_camera_pos.y - screen_size.y * 0.5 * drag_margin[MARGIN_BOTTOM])); } else { @@ -142,7 +143,7 @@ Transform2D Camera2D::get_camera_transform() { if (smoothing_enabled && !Engine::get_singleton()->is_editor_hint()) { - float c = smoothing * get_physics_process_delta_time(); + float c = smoothing * get_process_delta_time(); smoothed_camera_pos = ((camera_pos - smoothed_camera_pos) * c) + smoothed_camera_pos; ret_camera_pos = smoothed_camera_pos; //camera_pos=camera_pos*(1.0-smoothing)+new_camera_pos*smoothing; @@ -216,14 +217,14 @@ void Camera2D::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_PHYSICS_PROCESS: { + case NOTIFICATION_INTERNAL_PROCESS: { _update_scroll(); } break; case NOTIFICATION_TRANSFORM_CHANGED: { - if (!is_physics_processing()) + if (!is_processing_internal()) _update_scroll(); } break; @@ -245,7 +246,7 @@ void Camera2D::_notification(int p_what) { add_to_group(canvas_group_name); if (Engine::get_singleton()->is_editor_hint()) { - set_physics_process(false); + set_process_internal(false); } _update_scroll(); @@ -502,9 +503,9 @@ void Camera2D::set_follow_smoothing(float p_speed) { smoothing = p_speed; if (smoothing > 0 && !(is_inside_tree() && Engine::get_singleton()->is_editor_hint())) - set_physics_process(true); + set_process_internal(true); else - set_physics_process(false); + set_process_internal(false); } float Camera2D::get_follow_smoothing() const { @@ -540,6 +541,7 @@ bool Camera2D::is_v_drag_enabled() const { void Camera2D::set_v_offset(float p_offset) { v_ofs = p_offset; + _update_scroll(); } float Camera2D::get_v_offset() const { @@ -550,6 +552,7 @@ float Camera2D::get_v_offset() const { void Camera2D::set_h_offset(float p_offset) { h_ofs = p_offset; + _update_scroll(); } float Camera2D::get_h_offset() const { @@ -712,6 +715,7 @@ void Camera2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rotating"), "set_rotating", "is_rotating"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "current"), "_set_current", "is_current"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "zoom"), "set_zoom", "get_zoom"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "custom_viewport", PROPERTY_HINT_RESOURCE_TYPE, "Viewport", 0), "set_custom_viewport", "get_custom_viewport"); ADD_GROUP("Limit", "limit_"); ADD_PROPERTYI(PropertyInfo(Variant::INT, "limit_left"), "set_limit", "get_limit", MARGIN_LEFT); @@ -728,6 +732,10 @@ void Camera2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smoothing_enabled"), "set_enable_follow_smoothing", "is_follow_smoothing_enabled"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "smoothing_speed"), "set_follow_smoothing", "get_follow_smoothing"); + ADD_GROUP("Offset", "offset_"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "offset_v", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_v_offset", "get_v_offset"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "offset_h", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_h_offset", "get_h_offset"); + ADD_GROUP("Drag Margin", "drag_margin_"); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "drag_margin_left", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_margin", "get_drag_margin", MARGIN_LEFT); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "drag_margin_top", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_margin", "get_drag_margin", MARGIN_TOP); diff --git a/scene/2d/camera_2d.h b/scene/2d/camera_2d.h index 743bf6abd6..99571500bf 100644 --- a/scene/2d/camera_2d.h +++ b/scene/2d/camera_2d.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef CAMERA_2D_H #define CAMERA_2D_H diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp index 4fb22ac04a..27bdeda4a8 100644 --- a/scene/2d/canvas_item.cpp +++ b/scene/2d/canvas_item.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "canvas_item.h" #include "core/method_bind_ext.gen.inc" #include "message_queue.h" @@ -93,6 +94,7 @@ void CanvasItemMaterial::_update_shader() { case BLEND_MODE_SUB: code += "blend_sub"; break; case BLEND_MODE_MUL: code += "blend_mul"; break; case BLEND_MODE_PREMULT_ALPHA: code += "blend_premul_alpha"; break; + case BLEND_MODE_DISABLED: code += "blend_disabled"; break; } switch (light_mode) { @@ -100,7 +102,7 @@ void CanvasItemMaterial::_update_shader() { case LIGHT_MODE_UNSHADED: code += ",unshaded"; break; case LIGHT_MODE_LIGHT_ONLY: code += ",light_only"; break; } - code += ";\n"; //thats it. + code += ";\n"; //that's it. ShaderData shader_data; shader_data.shader = VS::get_singleton()->shader_create(); @@ -244,6 +246,14 @@ CanvasItemMaterial::~CanvasItemMaterial() { /////////////////////////////////////////////////////////////////// +bool CanvasItem::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + if (_edit_use_rect()) { + return _edit_get_rect().has_point(p_point); + } else { + return p_point.length() < p_tolerance; + } +} + bool CanvasItem::is_visible_in_tree() const { if (!is_inside_tree()) @@ -262,7 +272,8 @@ bool CanvasItem::is_visible_in_tree() const { void CanvasItem::_propagate_visibility_changed(bool p_visible) { - notification(NOTIFICATION_VISIBILITY_CHANGED); + if (!first_draw) + notification(NOTIFICATION_VISIBILITY_CHANGED); if (p_visible) update(); //todo optimize @@ -411,7 +422,7 @@ void CanvasItem::_enter_canvas() { RID canvas; if (canvas_layer) - canvas = canvas_layer->get_world_2d()->get_canvas(); + canvas = canvas_layer->get_canvas(); else canvas = get_viewport()->find_world_2d()->get_canvas(); @@ -683,7 +694,7 @@ void CanvasItem::draw_texture(const Ref<Texture> &p_texture, const Point2 &p_pos ERR_FAIL_COND(p_texture.is_null()); - p_texture->draw(canvas_item, p_pos, p_modulate); + p_texture->draw(canvas_item, p_pos, p_modulate, false, p_normal_map); } void CanvasItem::draw_texture_rect(const Ref<Texture> &p_texture, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose, const Ref<Texture> &p_normal_map) { @@ -778,6 +789,22 @@ void CanvasItem::draw_colored_polygon(const Vector<Point2> &p_points, const Colo VisualServer::get_singleton()->canvas_item_add_polygon(canvas_item, p_points, colors, p_uvs, rid, rid_normal, p_antialiased); } +void CanvasItem::draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map) { + + ERR_FAIL_COND(p_mesh.is_null()); + RID texture_rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); + RID normal_map_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID(); + + VisualServer::get_singleton()->canvas_item_add_mesh(canvas_item, p_mesh->get_rid(), texture_rid, normal_map_rid); +} +void CanvasItem::draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map) { + + ERR_FAIL_COND(p_multimesh.is_null()); + RID texture_rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); + RID normal_map_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID(); + VisualServer::get_singleton()->canvas_item_add_multimesh(canvas_item, p_multimesh->get_rid(), texture_rid, normal_map_rid); +} + void CanvasItem::draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, const Color &p_modulate, int p_clip_w) { if (!drawing) { @@ -804,6 +831,12 @@ float CanvasItem::draw_char(const Ref<Font> &p_font, const Point2 &p_pos, const void CanvasItem::_notify_transform(CanvasItem *p_node) { + /* This check exists to avoid re-propagating the transform + * notification down the tree on dirty nodes. It provides + * optimization by avoiding redundancy (nodes are dirty, will get the + * notification anyway). + */ + if (/*p_node->xform_change.in_list() &&*/ p_node->global_invalid) { return; //nothing to do } @@ -837,7 +870,7 @@ RID CanvasItem::get_canvas() const { ERR_FAIL_COND_V(!is_inside_tree(), RID()); if (canvas_layer) - return canvas_layer->get_world_2d()->get_canvas(); + return canvas_layer->get_canvas(); else return get_viewport()->find_world_2d()->get_canvas(); } @@ -858,9 +891,7 @@ Ref<World2D> CanvasItem::get_world_2d() const { CanvasItem *tl = get_toplevel(); - if (tl->canvas_layer) { - return tl->canvas_layer->get_world_2d(); - } else if (tl->get_viewport()) { + if (tl->get_viewport()) { return tl->get_viewport()->find_world_2d(); } else { return Ref<World2D>(); @@ -959,7 +990,8 @@ void CanvasItem::_bind_methods() { ClassDB::bind_method(D_METHOD("_edit_set_position", "position"), &CanvasItem::_edit_set_position); ClassDB::bind_method(D_METHOD("_edit_get_position"), &CanvasItem::_edit_get_position); - ClassDB::bind_method(D_METHOD("_edit_use_position"), &CanvasItem::_edit_use_position); + ClassDB::bind_method(D_METHOD("_edit_set_scale", "scale"), &CanvasItem::_edit_set_scale); + ClassDB::bind_method(D_METHOD("_edit_get_scale"), &CanvasItem::_edit_get_scale); ClassDB::bind_method(D_METHOD("_edit_set_rect", "rect"), &CanvasItem::_edit_set_rect); ClassDB::bind_method(D_METHOD("_edit_get_rect"), &CanvasItem::_edit_get_rect); ClassDB::bind_method(D_METHOD("_edit_use_rect"), &CanvasItem::_edit_use_rect); @@ -1015,6 +1047,8 @@ void CanvasItem::_bind_methods() { ClassDB::bind_method(D_METHOD("draw_colored_polygon", "points", "color", "uvs", "texture", "normal_map", "antialiased"), &CanvasItem::draw_colored_polygon, DEFVAL(PoolVector2Array()), DEFVAL(Variant()), DEFVAL(Variant()), DEFVAL(false)); ClassDB::bind_method(D_METHOD("draw_string", "font", "position", "text", "modulate", "clip_w"), &CanvasItem::draw_string, DEFVAL(Color(1, 1, 1)), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("draw_char", "font", "position", "char", "next", "modulate"), &CanvasItem::draw_char, DEFVAL(Color(1, 1, 1))); + ClassDB::bind_method(D_METHOD("draw_mesh", "mesh", "texture", "normal_map"), &CanvasItem::draw_mesh, DEFVAL(Ref<Texture>())); + ClassDB::bind_method(D_METHOD("draw_multimesh", "mesh", "texture", "normal_map"), &CanvasItem::draw_mesh, DEFVAL(Ref<Texture>())); ClassDB::bind_method(D_METHOD("draw_set_transform", "position", "rotation", "scale"), &CanvasItem::draw_set_transform); ClassDB::bind_method(D_METHOD("draw_set_transform_matrix", "xform"), &CanvasItem::draw_set_transform_matrix); @@ -1058,9 +1092,9 @@ void CanvasItem::_bind_methods() { ADD_GROUP("Material", ""); ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,CanvasItemMaterial"), "set_material", "get_material"); ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "use_parent_material"), "set_use_parent_material", "get_use_parent_material"); - //exporting these two things doesn't really make much sense i think - //ADD_PROPERTY( PropertyInfo(Variant::BOOL,"transform/toplevel"), "set_as_toplevel","is_set_as_toplevel") ; - //ADD_PROPERTY(PropertyInfo(Variant::BOOL,"transform/notify"),"set_transform_notify","is_transform_notify_enabled"); + //exporting these things doesn't really make much sense i think + // ADD_PROPERTY(PropertyInfo(Variant::BOOL, "toplevel", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_as_toplevel", "is_set_as_toplevel"); + // ADD_PROPERTY(PropertyInfo(Variant::BOOL,"transform/notify"),"set_transform_notify","is_transform_notify_enabled"); ADD_SIGNAL(MethodInfo("draw")); ADD_SIGNAL(MethodInfo("visibility_changed")); @@ -1072,6 +1106,7 @@ void CanvasItem::_bind_methods() { BIND_ENUM_CONSTANT(BLEND_MODE_SUB); BIND_ENUM_CONSTANT(BLEND_MODE_MUL); BIND_ENUM_CONSTANT(BLEND_MODE_PREMULT_ALPHA); + BIND_ENUM_CONSTANT(BLEND_MODE_DISABLED); BIND_CONSTANT(NOTIFICATION_TRANSFORM_CHANGED); BIND_CONSTANT(NOTIFICATION_DRAW); diff --git a/scene/2d/canvas_item.h b/scene/2d/canvas_item.h index 1db9e4902a..10d5082dfc 100644 --- a/scene/2d/canvas_item.h +++ b/scene/2d/canvas_item.h @@ -27,12 +27,14 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef CANVAS_ITEM_H #define CANVAS_ITEM_H #include "scene/main/node.h" #include "scene/main/scene_tree.h" #include "scene/resources/material.h" +#include "scene/resources/multimesh.h" #include "scene/resources/shader.h" #include "scene/resources/texture.h" @@ -52,7 +54,8 @@ public: BLEND_MODE_ADD, BLEND_MODE_SUB, BLEND_MODE_MUL, - BLEND_MODE_PREMULT_ALPHA + BLEND_MODE_PREMULT_ALPHA, + BLEND_MODE_DISABLED }; enum LightMode { @@ -143,7 +146,8 @@ public: BLEND_MODE_ADD, BLEND_MODE_SUB, BLEND_MODE_MUL, - BLEND_MODE_PREMULT_ALPHA + BLEND_MODE_PREMULT_ALPHA, + BLEND_MODE_DISABLED }; private: @@ -218,30 +222,46 @@ public: /* EDITOR */ + // Save and restore a CanvasItem state virtual void _edit_set_state(const Dictionary &p_state){}; virtual Dictionary _edit_get_state() const { return Dictionary(); }; - // Used to move/select the node - virtual void _edit_set_position(const Point2 &p_position){}; - virtual Point2 _edit_get_position() const { return Point2(); }; - virtual bool _edit_use_position() const { return false; }; + // Used to move the node + virtual void _edit_set_position(const Point2 &p_position) = 0; + virtual Point2 _edit_get_position() const = 0; + + // Used to scale the node + virtual void _edit_set_scale(const Size2 &p_scale) = 0; + virtual Size2 _edit_get_scale() const = 0; - // Used to resize/move/select the node + // Used to resize/move the node virtual void _edit_set_rect(const Rect2 &p_rect){}; - virtual Rect2 _edit_get_rect() const { return Rect2(-32, -32, 64, 64); }; - virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { return true; } - Rect2 _edit_get_item_and_children_rect() const; + virtual Rect2 _edit_get_rect() const { return Rect2(0, 0, 0, 0); }; virtual bool _edit_use_rect() const { return false; }; + Rect2 _edit_get_item_and_children_rect() const; + + // used to select the node + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; + // Used to rotate the node - virtual void _edit_set_rotation(float p_rotation){}; - virtual float _edit_get_rotation() const { return 0.0; }; - virtual bool _edit_use_rotation() const { return false; }; + virtual void + _edit_set_rotation(float p_rotation){}; + virtual float _edit_get_rotation() const { + return 0.0; + }; + virtual bool _edit_use_rotation() const { + return false; + }; // Used to set a pivot virtual void _edit_set_pivot(const Point2 &p_pivot){}; - virtual Point2 _edit_get_pivot() const { return Point2(); }; - virtual bool _edit_use_pivot() const { return false; }; + virtual Point2 _edit_get_pivot() const { + return Point2(); + }; + virtual bool _edit_use_pivot() const { + return false; + }; virtual Size2 _edit_get_minimum_size() const; @@ -281,6 +301,9 @@ public: void draw_polygon(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), Ref<Texture> p_texture = Ref<Texture>(), const Ref<Texture> &p_normal_map = Ref<Texture>(), bool p_antialiased = false); void draw_colored_polygon(const Vector<Point2> &p_points, const Color &p_color, const Vector<Point2> &p_uvs = Vector<Point2>(), Ref<Texture> p_texture = Ref<Texture>(), const Ref<Texture> &p_normal_map = Ref<Texture>(), bool p_antialiased = false); + void draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map); + void draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map); + void draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, const Color &p_modulate = Color(1, 1, 1), int p_clip_w = -1); float draw_char(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, const String &p_next = "", const Color &p_modulate = Color(1, 1, 1)); @@ -303,7 +326,9 @@ public: virtual Transform2D get_global_transform_with_canvas() const; CanvasItem *get_toplevel() const; - _FORCE_INLINE_ RID get_canvas_item() const { return canvas_item; } + _FORCE_INLINE_ RID get_canvas_item() const { + return canvas_item; + } void set_block_transform_notify(bool p_enable); bool is_block_transform_notify_enabled() const; diff --git a/scene/2d/canvas_modulate.cpp b/scene/2d/canvas_modulate.cpp index 925c01f6b3..2f8568ccbf 100644 --- a/scene/2d/canvas_modulate.cpp +++ b/scene/2d/canvas_modulate.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "canvas_modulate.h" void CanvasModulate::_notification(int p_what) { diff --git a/scene/2d/canvas_modulate.h b/scene/2d/canvas_modulate.h index b9d7b14b0a..888ed95f6b 100644 --- a/scene/2d/canvas_modulate.h +++ b/scene/2d/canvas_modulate.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef CANVASMODULATE_H #define CANVASMODULATE_H diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp index b4f9f3fd9d..d05c818ae1 100644 --- a/scene/2d/collision_object_2d.cpp +++ b/scene/2d/collision_object_2d.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "collision_object_2d.h" #include "scene/scene_string_names.h" #include "servers/physics_2d_server.h" @@ -327,6 +328,20 @@ void CollisionObject2D::_update_pickable() { Physics2DServer::get_singleton()->body_set_pickable(rid, pickable); } +String CollisionObject2D::get_configuration_warning() const { + + String warning = Node2D::get_configuration_warning(); + + if (shapes.empty()) { + if (warning == String()) { + warning += "\n"; + } + warning += TTR("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."); + } + + return warning; +} + void CollisionObject2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_rid"), &CollisionObject2D::get_rid); diff --git a/scene/2d/collision_object_2d.h b/scene/2d/collision_object_2d.h index 62298b4036..6da63d1a0b 100644 --- a/scene/2d/collision_object_2d.h +++ b/scene/2d/collision_object_2d.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef COLLISION_OBJECT_2D_H #define COLLISION_OBJECT_2D_H @@ -106,6 +107,8 @@ public: void set_pickable(bool p_enabled); bool is_pickable() const; + String get_configuration_warning() const; + _FORCE_INLINE_ RID get_rid() const { return rid; } CollisionObject2D(); diff --git a/scene/2d/collision_polygon_2d.cpp b/scene/2d/collision_polygon_2d.cpp index e46bf3c77d..9d2a83fda7 100644 --- a/scene/2d/collision_polygon_2d.cpp +++ b/scene/2d/collision_polygon_2d.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "collision_polygon_2d.h" #include "collision_object_2d.h" @@ -114,6 +115,15 @@ Vector<Vector<Vector2> > CollisionPolygon2D::_decompose_in_convex() { return decomp; } +void CollisionPolygon2D::_update_in_shape_owner(bool p_xform_only) { + + parent->shape_owner_set_transform(owner_id, get_transform()); + if (p_xform_only) + return; + parent->shape_owner_set_disabled(owner_id, disabled); + parent->shape_owner_set_one_way_collision(owner_id, one_way_collision); +} + void CollisionPolygon2D::_notification(int p_what) { switch (p_what) { @@ -123,22 +133,27 @@ void CollisionPolygon2D::_notification(int p_what) { if (parent) { owner_id = parent->create_shape_owner(this); _build_polygon(); - parent->shape_owner_set_transform(owner_id, get_transform()); - parent->shape_owner_set_disabled(owner_id, disabled); - parent->shape_owner_set_one_way_collision(owner_id, one_way_collision); + _update_in_shape_owner(); } /*if (Engine::get_singleton()->is_editor_hint()) { //display above all else set_z_as_relative(false); - set_z(VS::CANVAS_ITEM_Z_MAX - 1); + set_z_index(VS::CANVAS_ITEM_Z_MAX - 1); }*/ } break; + case NOTIFICATION_ENTER_TREE: { + + if (parent) { + _update_in_shape_owner(); + } + + } break; case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { if (parent) { - parent->shape_owner_set_transform(owner_id, get_transform()); + _update_in_shape_owner(true); } } break; @@ -160,7 +175,8 @@ void CollisionPolygon2D::_notification(int p_what) { Vector2 p = polygon[i]; Vector2 n = polygon[(i + 1) % polygon.size()]; - draw_line(p, n, Color(0.9, 0.2, 0.0, 0.8), 3); + // draw line with width <= 1, so it does not scale with zoom and break pixel exact editing + draw_line(p, n, Color(0.9, 0.2, 0.0, 0.8), 1); } #define DEBUG_DECOMPOSE #if defined(TOOLS_ENABLED) && defined(DEBUG_DECOMPOSE) @@ -248,6 +264,10 @@ Rect2 CollisionPolygon2D::_edit_get_rect() const { return aabb; } +bool CollisionPolygon2D::_edit_use_rect() const { + return true; +} + bool CollisionPolygon2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { return Geometry::is_point_in_polygon(p_point, Variant(polygon)); diff --git a/scene/2d/collision_polygon_2d.h b/scene/2d/collision_polygon_2d.h index fa7e5e1046..412a923292 100644 --- a/scene/2d/collision_polygon_2d.h +++ b/scene/2d/collision_polygon_2d.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef COLLISION_POLYGON_2D_H #define COLLISION_POLYGON_2D_H @@ -58,6 +59,8 @@ protected: void _build_polygon(); + void _update_in_shape_owner(bool p_xform_only = false); + protected: void _notification(int p_what); static void _bind_methods(); @@ -70,6 +73,7 @@ public: Vector<Point2> get_polygon() const; virtual Rect2 _edit_get_rect() const; + virtual bool _edit_use_rect() const; virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; virtual String get_configuration_warning() const; diff --git a/scene/2d/collision_shape_2d.cpp b/scene/2d/collision_shape_2d.cpp index 135ede829d..83ef4df8f4 100644 --- a/scene/2d/collision_shape_2d.cpp +++ b/scene/2d/collision_shape_2d.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "collision_shape_2d.h" #include "collision_object_2d.h" @@ -44,6 +45,15 @@ void CollisionShape2D::_shape_changed() { update(); } +void CollisionShape2D::_update_in_shape_owner(bool p_xform_only) { + + parent->shape_owner_set_transform(owner_id, get_transform()); + if (p_xform_only) + return; + parent->shape_owner_set_disabled(owner_id, disabled); + parent->shape_owner_set_one_way_collision(owner_id, one_way_collision); +} + void CollisionShape2D::_notification(int p_what) { switch (p_what) { @@ -56,22 +66,27 @@ void CollisionShape2D::_notification(int p_what) { if (shape.is_valid()) { parent->shape_owner_add_shape(owner_id, shape); } - parent->shape_owner_set_transform(owner_id, get_transform()); - parent->shape_owner_set_disabled(owner_id, disabled); - parent->shape_owner_set_one_way_collision(owner_id, one_way_collision); + _update_in_shape_owner(); } /*if (Engine::get_singleton()->is_editor_hint()) { //display above all else set_z_as_relative(false); - set_z(VS::CANVAS_ITEM_Z_MAX - 1); + set_z_index(VS::CANVAS_ITEM_Z_MAX - 1); }*/ } break; + case NOTIFICATION_ENTER_TREE: { + + if (parent) { + _update_in_shape_owner(); + } + + } break; case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { if (parent) { - parent->shape_owner_set_transform(owner_id, get_transform()); + _update_in_shape_owner(true); } } break; @@ -158,11 +173,6 @@ Ref<Shape2D> CollisionShape2D::get_shape() const { return shape; } -Rect2 CollisionShape2D::_edit_get_rect() const { - - return rect; -} - bool CollisionShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { if (!shape.is_valid()) diff --git a/scene/2d/collision_shape_2d.h b/scene/2d/collision_shape_2d.h index 921066c5c8..ed2c09d53d 100644 --- a/scene/2d/collision_shape_2d.h +++ b/scene/2d/collision_shape_2d.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef COLLISION_SHAPE_2D_H #define COLLISION_SHAPE_2D_H @@ -46,12 +47,13 @@ class CollisionShape2D : public Node2D { bool disabled; bool one_way_collision; + void _update_in_shape_owner(bool p_xform_only = false); + protected: void _notification(int p_what); static void _bind_methods(); public: - virtual Rect2 _edit_get_rect() const; virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; void set_shape(const Ref<Shape2D> &p_shape); diff --git a/scene/2d/joints_2d.cpp b/scene/2d/joints_2d.cpp index eec483c29e..329382c034 100644 --- a/scene/2d/joints_2d.cpp +++ b/scene/2d/joints_2d.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "joints_2d.h" #include "engine.h" @@ -74,8 +75,7 @@ void Joint2D::_update_joint(bool p_only_free) { ba = body_a->get_rid(); bb = body_b->get_rid(); - if (exclude_from_collision) - Physics2DServer::get_singleton()->body_add_collision_exception(body_a->get_rid(), body_b->get_rid()); + Physics2DServer::get_singleton()->joint_disable_collisions_between_bodies(joint, exclude_from_collision); } void Joint2D::set_node_a(const NodePath &p_node_a) { diff --git a/scene/2d/joints_2d.h b/scene/2d/joints_2d.h index 9b6ba22d64..744994f0a7 100644 --- a/scene/2d/joints_2d.h +++ b/scene/2d/joints_2d.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef JOINTS_2D_H #define JOINTS_2D_H diff --git a/scene/2d/light_2d.cpp b/scene/2d/light_2d.cpp index a6a871be1d..9a44eb31bb 100644 --- a/scene/2d/light_2d.cpp +++ b/scene/2d/light_2d.cpp @@ -27,40 +27,46 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "light_2d.h" #include "engine.h" #include "servers/visual_server.h" -void Light2D::_edit_set_pivot(const Point2 &p_pivot) { +Dictionary Light2D::_edit_get_state() const { + Dictionary state = Node2D::_edit_get_state(); + state["offset"] = get_texture_offset(); + return state; +} - set_texture_offset(p_pivot); +void Light2D::_edit_set_state(const Dictionary &p_state) { + Node2D::_edit_set_state(p_state); + set_texture_offset(p_state["offset"]); } -Point2 Light2D::_edit_get_pivot() const { +void Light2D::_edit_set_pivot(const Point2 &p_pivot) { + set_position(get_transform().xform(p_pivot)); + set_texture_offset(get_texture_offset() - p_pivot); +} - return get_texture_offset(); +Point2 Light2D::_edit_get_pivot() const { + return Vector2(); } -bool Light2D::_edit_use_pivot() const { +bool Light2D::_edit_use_pivot() const { return true; } Rect2 Light2D::_edit_get_rect() const { - if (texture.is_null()) - return Rect2(0, 0, 1, 1); - - Size2i s; + return Node2D::_edit_get_rect(); - s = texture->get_size() * _scale; - Point2i ofs = texture_offset; - ofs -= s / 2; - - if (s == Size2(0, 0)) - s = Size2(1, 1); + Size2 s = texture->get_size() * _scale; + return Rect2(texture_offset - s / 2.0, s); +} - return Rect2(ofs, s); +bool Light2D::_edit_use_rect() const { + return true; } void Light2D::_update_light_visibility() { @@ -130,6 +136,7 @@ void Light2D::set_texture_offset(const Vector2 &p_offset) { texture_offset = p_offset; VS::get_singleton()->canvas_light_set_texture_offset(canvas_light, texture_offset); item_rect_changed(); + _change_notify("offset"); } Vector2 Light2D::get_texture_offset() const { diff --git a/scene/2d/light_2d.h b/scene/2d/light_2d.h index 9c55f72870..543805e329 100644 --- a/scene/2d/light_2d.h +++ b/scene/2d/light_2d.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef LIGHT_2D_H #define LIGHT_2D_H @@ -84,10 +85,14 @@ protected: static void _bind_methods(); public: + virtual Dictionary _edit_get_state() const; + virtual void _edit_set_state(const Dictionary &p_state); + virtual void _edit_set_pivot(const Point2 &p_pivot); virtual Point2 _edit_get_pivot() const; virtual bool _edit_use_pivot() const; virtual Rect2 _edit_get_rect() const; + virtual bool _edit_use_rect() const; void set_enabled(bool p_enabled); bool is_enabled() const; diff --git a/scene/2d/light_occluder_2d.cpp b/scene/2d/light_occluder_2d.cpp index b95d4f8f0a..c9e5d0f1bc 100644 --- a/scene/2d/light_occluder_2d.cpp +++ b/scene/2d/light_occluder_2d.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "light_occluder_2d.h" #include "engine.h" @@ -106,12 +107,12 @@ OccluderPolygon2D::~OccluderPolygon2D() { VS::get_singleton()->free(occ_polygon); } -#ifdef DEBUG_ENABLED void LightOccluder2D::_poly_changed() { +#ifdef DEBUG_ENABLED update(); -} #endif +} void LightOccluder2D::_notification(int p_what) { @@ -220,9 +221,7 @@ void LightOccluder2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_occluder_light_mask", "mask"), &LightOccluder2D::set_occluder_light_mask); ClassDB::bind_method(D_METHOD("get_occluder_light_mask"), &LightOccluder2D::get_occluder_light_mask); -#ifdef DEBUG_ENABLED ClassDB::bind_method("_poly_changed", &LightOccluder2D::_poly_changed); -#endif ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "occluder", PROPERTY_HINT_RESOURCE_TYPE, "OccluderPolygon2D"), "set_occluder_polygon", "get_occluder_polygon"); ADD_PROPERTY(PropertyInfo(Variant::INT, "light_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_occluder_light_mask", "get_occluder_light_mask"); diff --git a/scene/2d/light_occluder_2d.h b/scene/2d/light_occluder_2d.h index a2e81ef2b4..d59c9100b0 100644 --- a/scene/2d/light_occluder_2d.h +++ b/scene/2d/light_occluder_2d.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef LIGHTOCCLUDER2D_H #define LIGHTOCCLUDER2D_H @@ -77,9 +78,7 @@ class LightOccluder2D : public Node2D { int mask; Ref<OccluderPolygon2D> occluder_polygon; -#ifdef DEBUG_ENABLED void _poly_changed(); -#endif protected: void _notification(int p_what); diff --git a/scene/2d/line_2d.cpp b/scene/2d/line_2d.cpp index abc80216d4..3e61dd05f4 100644 --- a/scene/2d/line_2d.cpp +++ b/scene/2d/line_2d.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "line_2d.h" #include "line_builder.h" @@ -61,6 +62,10 @@ Rect2 Line2D::_edit_get_rect() const { return aabb; } +bool Line2D::_edit_use_rect() const { + return true; +} + bool Line2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { const real_t d = _width / 2 + p_tolerance; @@ -251,18 +256,22 @@ void Line2D::_draw() { lb.sharp_limit = _sharp_limit; lb.width = _width; - lb.build(); - RID texture_rid; - if (_texture.is_valid()) + if (_texture.is_valid()) { texture_rid = (**_texture).get_rid(); + lb.tile_aspect = _texture->get_size().aspect(); + } + + lb.build(); + VS::get_singleton()->canvas_item_add_triangle_array( get_canvas_item(), lb.indices, lb.vertices, lb.colors, - lb.uvs, + lb.uvs, Vector<int>(), Vector<float>(), + texture_rid); // DEBUG diff --git a/scene/2d/line_2d.h b/scene/2d/line_2d.h index 54a4683dc7..24c48982cd 100644 --- a/scene/2d/line_2d.h +++ b/scene/2d/line_2d.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef LINE2D_H #define LINE2D_H @@ -58,6 +59,7 @@ public: Line2D(); virtual Rect2 _edit_get_rect() const; + virtual bool _edit_use_rect() const; virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; void set_points(const PoolVector<Vector2> &p_points); diff --git a/scene/2d/line_builder.cpp b/scene/2d/line_builder.cpp index ae2f9371a3..845788bada 100644 --- a/scene/2d/line_builder.cpp +++ b/scene/2d/line_builder.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "line_builder.h" //---------------------------------------------------------------------------- @@ -100,6 +101,7 @@ LineBuilder::LineBuilder() { round_precision = 8; begin_cap_mode = Line2D::LINE_CAP_NONE; end_cap_mode = Line2D::LINE_CAP_NONE; + tile_aspect = 1.f; _interpolate_color = false; _last_index[0] = 0; @@ -110,6 +112,7 @@ void LineBuilder::clear_output() { vertices.clear(); colors.clear(); indices.clear(); + uvs.clear(); } void LineBuilder::build() { @@ -120,6 +123,8 @@ void LineBuilder::build() { return; } + ERR_FAIL_COND(tile_aspect <= 0.f); + const float hw = width / 2.f; const float hw_sq = hw * hw; const float sharp_limit_sq = sharp_limit * sharp_limit; @@ -163,7 +168,7 @@ void LineBuilder::build() { current_distance1 = current_distance0; } else if (begin_cap_mode == Line2D::LINE_CAP_ROUND) { if (texture_mode == Line2D::LINE_TEXTURE_TILE) { - uvx0 = 0.5f; + uvx0 = 0.5f / tile_aspect; } new_arc(pos0, pos_up0 - pos0, -Math_PI, color0, Rect2(0.f, 0.f, 1.f, 1.f)); total_distance += width; @@ -285,8 +290,8 @@ void LineBuilder::build() { color1 = gradient->get_color_at_offset(current_distance1 / total_distance); } if (texture_mode == Line2D::LINE_TEXTURE_TILE) { - uvx0 = current_distance0 / width; - uvx1 = current_distance1 / width; + uvx0 = current_distance0 / (width * tile_aspect); + uvx1 = current_distance1 / (width * tile_aspect); } strip_add_quad(pos_up1, pos_down1, color1, uvx1); @@ -346,7 +351,7 @@ void LineBuilder::build() { } if (intersection_result != SEGMENT_INTERSECT) - // In this case the joint is too fucked up to be re-used, + // In this case the joint is too corrputed to be re-used, // start again the strip with fallback points strip_begin(pos_up0, pos_down0, color1, uvx1); } @@ -372,7 +377,7 @@ void LineBuilder::build() { color1 = gradient->get_color(gradient->get_points_count() - 1); } if (texture_mode == Line2D::LINE_TEXTURE_TILE) { - uvx1 = current_distance1 / width; + uvx1 = current_distance1 / (width * tile_aspect); } strip_add_quad(pos_up1, pos_down1, color1, uvx1); diff --git a/scene/2d/line_builder.h b/scene/2d/line_builder.h index 0b19cd4674..b1c62f84e2 100644 --- a/scene/2d/line_builder.h +++ b/scene/2d/line_builder.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef LINE_BUILDER_H #define LINE_BUILDER_H @@ -49,6 +50,7 @@ public: Line2D::LineTextureMode texture_mode; float sharp_limit; int round_precision; + float tile_aspect; // w/h // TODO offset_joints option (offers alternative implementation of round joints) // TODO Move in a struct and reference it diff --git a/scene/2d/mesh_instance_2d.cpp b/scene/2d/mesh_instance_2d.cpp new file mode 100644 index 0000000000..adbb227d0c --- /dev/null +++ b/scene/2d/mesh_instance_2d.cpp @@ -0,0 +1,76 @@ +#include "mesh_instance_2d.h" + +void MeshInstance2D::_notification(int p_what) { + + if (p_what == NOTIFICATION_DRAW) { + if (mesh.is_valid()) { + draw_mesh(mesh, texture, normal_map); + } + } +} + +void MeshInstance2D::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_mesh", "mesh"), &MeshInstance2D::set_mesh); + ClassDB::bind_method(D_METHOD("get_mesh"), &MeshInstance2D::get_mesh); + + ClassDB::bind_method(D_METHOD("set_texture", "texture"), &MeshInstance2D::set_texture); + ClassDB::bind_method(D_METHOD("get_texture"), &MeshInstance2D::get_texture); + + ClassDB::bind_method(D_METHOD("set_normal_map", "normal_map"), &MeshInstance2D::set_normal_map); + ClassDB::bind_method(D_METHOD("get_normal_map"), &MeshInstance2D::get_normal_map); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh"); + ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture"); + ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "normal_map", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_normal_map", "get_normal_map"); +} + +void MeshInstance2D::set_mesh(const Ref<Mesh> &p_mesh) { + + mesh = p_mesh; + update(); +} + +Ref<Mesh> MeshInstance2D::get_mesh() const { + + return mesh; +} + +void MeshInstance2D::set_texture(const Ref<Texture> &p_texture) { + + if (p_texture == texture) + return; + texture = p_texture; + update(); + emit_signal("texture_changed"); + _change_notify("texture"); +} + +void MeshInstance2D::set_normal_map(const Ref<Texture> &p_texture) { + + normal_map = p_texture; + update(); +} + +Ref<Texture> MeshInstance2D::get_normal_map() const { + + return normal_map; +} + +Ref<Texture> MeshInstance2D::get_texture() const { + + return texture; +} + +Rect2 MeshInstance2D::_edit_get_rect() const { + + if (mesh.is_valid()) { + AABB aabb = mesh->get_aabb(); + return Rect2(aabb.position.x, aabb.position.y, aabb.size.x, aabb.size.y); + } + + return Node2D::_edit_get_rect(); +} + +MeshInstance2D::MeshInstance2D() { +} diff --git a/scene/2d/mesh_instance_2d.h b/scene/2d/mesh_instance_2d.h new file mode 100644 index 0000000000..d1d1ade0ae --- /dev/null +++ b/scene/2d/mesh_instance_2d.h @@ -0,0 +1,33 @@ +#ifndef MESH_INSTANCE_2D_H +#define MESH_INSTANCE_2D_H + +#include "scene/2d/node_2d.h" + +class MeshInstance2D : public Node2D { + GDCLASS(MeshInstance2D, Node2D) + + Ref<Mesh> mesh; + + Ref<Texture> texture; + Ref<Texture> normal_map; + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + void set_mesh(const Ref<Mesh> &p_mesh); + Ref<Mesh> get_mesh() const; + + void set_texture(const Ref<Texture> &p_texture); + Ref<Texture> get_texture() const; + + void set_normal_map(const Ref<Texture> &p_texture); + Ref<Texture> get_normal_map() const; + + virtual Rect2 _edit_get_rect() const; + + MeshInstance2D(); +}; + +#endif // MESH_INSTANCE_2D_H diff --git a/scene/2d/navigation2d.cpp b/scene/2d/navigation2d.cpp index 8e910563ca..4737e14111 100644 --- a/scene/2d/navigation2d.cpp +++ b/scene/2d/navigation2d.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "navigation2d.h" #define USE_ENTRY_POINT diff --git a/scene/2d/navigation2d.h b/scene/2d/navigation2d.h index 52c4967d3d..02c46d5de1 100644 --- a/scene/2d/navigation2d.h +++ b/scene/2d/navigation2d.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef NAVIGATION_2D_H #define NAVIGATION_2D_H diff --git a/scene/2d/navigation_polygon.cpp b/scene/2d/navigation_polygon.cpp index dde41dbf3f..6e27bf1c1d 100644 --- a/scene/2d/navigation_polygon.cpp +++ b/scene/2d/navigation_polygon.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "navigation_polygon.h" #include "core_string_names.h" @@ -311,9 +312,9 @@ void NavigationPolygon::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_outlines", "outlines"), &NavigationPolygon::_set_outlines); ClassDB::bind_method(D_METHOD("_get_outlines"), &NavigationPolygon::_get_outlines); - ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR3_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_vertices", "get_vertices"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_polygons", "_get_polygons"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "outlines", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_outlines", "_get_outlines"); + ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR3_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_vertices", "get_vertices"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_polygons", "_get_polygons"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "outlines", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_outlines", "_get_outlines"); } NavigationPolygon::NavigationPolygon() : diff --git a/scene/2d/navigation_polygon.h b/scene/2d/navigation_polygon.h index 7b5220f92d..d7684c2d94 100644 --- a/scene/2d/navigation_polygon.h +++ b/scene/2d/navigation_polygon.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef NAVIGATION_POLYGON_H #define NAVIGATION_POLYGON_H diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp index 2a305512af..3813bd96fe 100644 --- a/scene/2d/node_2d.cpp +++ b/scene/2d/node_2d.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "node_2d.h" #include "message_queue.h" @@ -45,10 +46,9 @@ Dictionary Node2D::_edit_get_state() const { } void Node2D::_edit_set_state(const Dictionary &p_state) { - Dictionary state = p_state; - pos = state["position"]; - angle = state["rotation"]; - _scale = state["scale"]; + pos = p_state["position"]; + angle = p_state["rotation"]; + _scale = p_state["scale"]; _update_transform(); _change_notify("rotation"); @@ -58,14 +58,39 @@ void Node2D::_edit_set_state(const Dictionary &p_state) { } void Node2D::_edit_set_position(const Point2 &p_position) { - pos = p_position; + set_position(p_position); } Point2 Node2D::_edit_get_position() const { return pos; } +void Node2D::_edit_set_scale(const Size2 &p_scale) { + set_scale(p_scale); +} + +Size2 Node2D::_edit_get_scale() const { + return _scale; +} + +void Node2D::_edit_set_rotation(float p_rotation) { + angle = p_rotation; + _update_transform(); + _change_notify("rotation"); + _change_notify("rotation_degrees"); +} + +float Node2D::_edit_get_rotation() const { + return angle; +} + +bool Node2D::_edit_use_rotation() const { + return true; +} + void Node2D::_edit_set_rect(const Rect2 &p_edit_rect) { + ERR_FAIL_COND(!_edit_use_rect()); + Rect2 r = _edit_get_rect(); Vector2 zero_offset; @@ -81,7 +106,7 @@ void Node2D::_edit_set_rect(const Rect2 &p_edit_rect) { if (r.size.y != 0) new_scale.y = p_edit_rect.size.y / r.size.y; - Point2 new_pos = p_edit_rect.position + p_edit_rect.size * zero_offset; //p_edit_rect.pos - r.pos; + Point2 new_pos = p_edit_rect.position + p_edit_rect.size * zero_offset; Transform2D postxf; postxf.set_rotation_and_scale(angle, _scale); @@ -95,25 +120,6 @@ void Node2D::_edit_set_rect(const Rect2 &p_edit_rect) { _change_notify("position"); } -bool Node2D::_edit_use_rect() const { - return true; -} - -void Node2D::_edit_set_rotation(float p_rotation) { - angle = p_rotation; - _update_transform(); - _change_notify("rotation"); - _change_notify("rotation_degrees"); -} - -float Node2D::_edit_get_rotation() const { - return angle; -} - -bool Node2D::_edit_use_rotation() const { - return true; -} - void Node2D::_update_xform_values() { pos = _mat.elements[2]; @@ -329,13 +335,13 @@ void Node2D::set_global_transform(const Transform2D &p_transform) { set_transform(p_transform); } -void Node2D::set_z(int p_z) { +void Node2D::set_z_index(int p_z) { ERR_FAIL_COND(p_z < VS::CANVAS_ITEM_Z_MIN); ERR_FAIL_COND(p_z > VS::CANVAS_ITEM_Z_MAX); - z = p_z; - VS::get_singleton()->canvas_item_set_z(get_canvas_item(), z); - _change_notify("z"); + z_index = p_z; + VS::get_singleton()->canvas_item_set_z_index(get_canvas_item(), z_index); + _change_notify("z_index"); } void Node2D::set_z_as_relative(bool p_enabled) { @@ -351,9 +357,9 @@ bool Node2D::is_z_relative() const { return z_relative; } -int Node2D::get_z() const { +int Node2D::get_z_index() const { - return z; + return z_index; } Transform2D Node2D::get_relative_transform_to_parent(const Node *p_parent) const { @@ -427,8 +433,8 @@ void Node2D::_bind_methods() { ClassDB::bind_method(D_METHOD("to_local", "global_point"), &Node2D::to_local); ClassDB::bind_method(D_METHOD("to_global", "local_point"), &Node2D::to_global); - ClassDB::bind_method(D_METHOD("set_z", "z"), &Node2D::set_z); - ClassDB::bind_method(D_METHOD("get_z"), &Node2D::get_z); + ClassDB::bind_method(D_METHOD("set_z_index", "z_index"), &Node2D::set_z_index); + ClassDB::bind_method(D_METHOD("get_z_index"), &Node2D::get_z_index); ClassDB::bind_method(D_METHOD("set_z_as_relative", "enable"), &Node2D::set_z_as_relative); ClassDB::bind_method(D_METHOD("is_z_relative"), &Node2D::is_z_relative); @@ -442,14 +448,14 @@ void Node2D::_bind_methods() { ADD_PROPERTYNO(PropertyInfo(Variant::VECTOR2, "scale"), "set_scale", "get_scale"); ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "transform", PROPERTY_HINT_NONE, "", 0), "set_transform", "get_transform"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "global_position", PROPERTY_HINT_NONE, "", 0), "set_global_position", "get_global_position"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "global_position", PROPERTY_HINT_NONE, "", 0), "set_global_position", "get_global_position"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "global_rotation", PROPERTY_HINT_NONE, "", 0), "set_global_rotation", "get_global_rotation"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "global_rotation_degrees", PROPERTY_HINT_NONE, "", 0), "set_global_rotation_degrees", "get_global_rotation_degrees"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "global_scale", PROPERTY_HINT_NONE, "", 0), "set_global_scale", "get_global_scale"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "global_scale", PROPERTY_HINT_NONE, "", 0), "set_global_scale", "get_global_scale"); ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "global_transform", PROPERTY_HINT_NONE, "", 0), "set_global_transform", "get_global_transform"); - ADD_GROUP("Z", ""); - ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "z", PROPERTY_HINT_RANGE, itos(VS::CANVAS_ITEM_Z_MIN) + "," + itos(VS::CANVAS_ITEM_Z_MAX) + ",1"), "set_z", "get_z"); + ADD_GROUP("Z Index", ""); + ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "z_index", PROPERTY_HINT_RANGE, itos(VS::CANVAS_ITEM_Z_MIN) + "," + itos(VS::CANVAS_ITEM_Z_MAX) + ",1"), "set_z_index", "get_z_index"); ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "z_as_relative"), "set_z_as_relative", "is_z_relative"); } @@ -458,6 +464,6 @@ Node2D::Node2D() { angle = 0; _scale = Vector2(1, 1); _xform_dirty = false; - z = 0; + z_index = 0; z_relative = true; } diff --git a/scene/2d/node_2d.h b/scene/2d/node_2d.h index 7d2e5aa00c..725686cdf8 100644 --- a/scene/2d/node_2d.h +++ b/scene/2d/node_2d.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef NODE2D_H #define NODE2D_H @@ -39,7 +40,7 @@ class Node2D : public CanvasItem { Point2 pos; float angle; Size2 _scale; - int z; + int z_index; bool z_relative; Transform2D _mat; @@ -61,12 +62,16 @@ public: virtual void _edit_set_position(const Point2 &p_position); virtual Point2 _edit_get_position() const; - virtual void _edit_set_rect(const Rect2 &p_edit_rect); - virtual bool _edit_use_rect() const; + + virtual void _edit_set_scale(const Size2 &p_scale); + virtual Size2 _edit_get_scale() const; + virtual void _edit_set_rotation(float p_rotation); virtual float _edit_get_rotation() const; virtual bool _edit_use_rotation() const; + virtual void _edit_set_rect(const Rect2 &p_edit_rect); + void set_position(const Point2 &p_pos); void set_rotation(float p_radians); void set_rotation_degrees(float p_degrees); @@ -96,8 +101,8 @@ public: void set_global_rotation_degrees(float p_degrees); void set_global_scale(const Size2 &p_scale); - void set_z(int p_z); - int get_z() const; + void set_z_index(int p_z); + int get_z_index() const; void look_at(const Vector2 &p_pos); float get_angle_to(const Vector2 &p_pos) const; diff --git a/scene/2d/parallax_background.cpp b/scene/2d/parallax_background.cpp index 46d0b7647f..027d64b813 100644 --- a/scene/2d/parallax_background.cpp +++ b/scene/2d/parallax_background.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "parallax_background.h" #include "parallax_layer.h" diff --git a/scene/2d/parallax_background.h b/scene/2d/parallax_background.h index a7b745f199..31d1c553a2 100644 --- a/scene/2d/parallax_background.h +++ b/scene/2d/parallax_background.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef PARALLAX_BACKGROUND_H #define PARALLAX_BACKGROUND_H diff --git a/scene/2d/parallax_layer.cpp b/scene/2d/parallax_layer.cpp index 7a9ea1931e..584c2f2c85 100644 --- a/scene/2d/parallax_layer.cpp +++ b/scene/2d/parallax_layer.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "parallax_layer.h" #include "engine.h" @@ -71,7 +72,7 @@ void ParallaxLayer::_update_mirroring() { ParallaxBackground *pb = Object::cast_to<ParallaxBackground>(get_parent()); if (pb) { - RID c = pb->get_world_2d()->get_canvas(); + RID c = pb->get_canvas(); RID ci = get_canvas_item(); Point2 mirrorScale = mirroring * get_scale(); VisualServer::get_singleton()->canvas_set_item_mirroring(c, ci, mirrorScale); diff --git a/scene/2d/parallax_layer.h b/scene/2d/parallax_layer.h index 937451c3e2..b2b98b0ef6 100644 --- a/scene/2d/parallax_layer.h +++ b/scene/2d/parallax_layer.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef PARALLAX_LAYER_H #define PARALLAX_LAYER_H diff --git a/scene/2d/particles_2d.cpp b/scene/2d/particles_2d.cpp index a760b8e599..7eaa5bb88c 100644 --- a/scene/2d/particles_2d.cpp +++ b/scene/2d/particles_2d.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "particles_2d.h" #include "engine.h" diff --git a/scene/2d/particles_2d.h b/scene/2d/particles_2d.h index e7a510d8bc..f367095581 100644 --- a/scene/2d/particles_2d.h +++ b/scene/2d/particles_2d.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef PARTICLES_FRAME_H #define PARTICLES_FRAME_H diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp index d2a307a9ed..7377591f7d 100644 --- a/scene/2d/path_2d.cpp +++ b/scene/2d/path_2d.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "path_2d.h" #include "engine.h" @@ -38,7 +39,7 @@ Rect2 Path2D::_edit_get_rect() const { - if (curve->get_point_count() == 0) + if (!curve.is_valid() || curve->get_point_count() == 0) return Rect2(0, 0, 0, 0); Rect2 aabb = Rect2(curve->get_point_position(0), Vector2(0, 0)); @@ -56,6 +57,10 @@ Rect2 Path2D::_edit_get_rect() const { return aabb; } +bool Path2D::_edit_use_rect() const { + return true; +} + bool Path2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { for (int i = 0; i < curve->get_point_count(); i++) { @@ -91,7 +96,7 @@ void Path2D::_notification(int p_what) { #else const float line_width = 2; #endif - const Color color = Color(0.5, 0.6, 1.0, 0.7); + const Color color = Color(1.0, 1.0, 1.0, 1.0); for (int i = 0; i < curve->get_point_count(); i++) { @@ -146,6 +151,7 @@ void Path2D::_bind_methods() { Path2D::Path2D() { set_curve(Ref<Curve2D>(memnew(Curve2D))); //create one by default + set_self_modulate(Color(0.5, 0.6, 1.0, 0.7)); } ///////////////////////////////////////////////////////////////////////////////// @@ -243,66 +249,16 @@ bool PathFollow2D::get_cubic_interpolation() const { return cubic; } -bool PathFollow2D::_set(const StringName &p_name, const Variant &p_value) { - - if (p_name == SceneStringNames::get_singleton()->offset) { - set_offset(p_value); - } else if (p_name == SceneStringNames::get_singleton()->unit_offset) { - set_unit_offset(p_value); - } else if (p_name == SceneStringNames::get_singleton()->rotate) { - set_rotate(p_value); - } else if (p_name == SceneStringNames::get_singleton()->v_offset) { - set_v_offset(p_value); - } else if (p_name == SceneStringNames::get_singleton()->h_offset) { - set_h_offset(p_value); - } else if (String(p_name) == "cubic_interp") { - set_cubic_interpolation(p_value); - } else if (String(p_name) == "loop") { - set_loop(p_value); - } else if (String(p_name) == "lookahead") { - set_lookahead(p_value); - } else - return false; +void PathFollow2D::_validate_property(PropertyInfo &property) const { - return true; -} + if (property.name == "offset") { -bool PathFollow2D::_get(const StringName &p_name, Variant &r_ret) const { - - if (p_name == SceneStringNames::get_singleton()->offset) { - r_ret = get_offset(); - } else if (p_name == SceneStringNames::get_singleton()->unit_offset) { - r_ret = get_unit_offset(); - } else if (p_name == SceneStringNames::get_singleton()->rotate) { - r_ret = is_rotating(); - } else if (p_name == SceneStringNames::get_singleton()->v_offset) { - r_ret = get_v_offset(); - } else if (p_name == SceneStringNames::get_singleton()->h_offset) { - r_ret = get_h_offset(); - } else if (String(p_name) == "cubic_interp") { - r_ret = cubic; - } else if (String(p_name) == "loop") { - r_ret = loop; - } else if (String(p_name) == "lookahead") { - r_ret = lookahead; - } else - return false; + float max = 10000; + if (path && path->get_curve().is_valid()) + max = path->get_curve()->get_baked_length(); - return true; -} -void PathFollow2D::_get_property_list(List<PropertyInfo> *p_list) const { - - float max = 10000; - if (path && path->get_curve().is_valid()) - max = path->get_curve()->get_baked_length(); - p_list->push_back(PropertyInfo(Variant::REAL, "offset", PROPERTY_HINT_RANGE, "0," + rtos(max) + ",0.01")); - p_list->push_back(PropertyInfo(Variant::REAL, "unit_offset", PROPERTY_HINT_RANGE, "0,1,0.0001", PROPERTY_USAGE_EDITOR)); - p_list->push_back(PropertyInfo(Variant::REAL, "h_offset")); - p_list->push_back(PropertyInfo(Variant::REAL, "v_offset")); - p_list->push_back(PropertyInfo(Variant::BOOL, "rotate")); - p_list->push_back(PropertyInfo(Variant::BOOL, "cubic_interp")); - p_list->push_back(PropertyInfo(Variant::BOOL, "loop")); - p_list->push_back(PropertyInfo(Variant::REAL, "lookahead", PROPERTY_HINT_RANGE, "0.001,1024.0,0.001")); + property.hint_string = "0," + rtos(max) + ",0.01"; + } } String PathFollow2D::get_configuration_warning() const { @@ -339,6 +295,18 @@ void PathFollow2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_loop", "loop"), &PathFollow2D::set_loop); ClassDB::bind_method(D_METHOD("has_loop"), &PathFollow2D::has_loop); + + 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::REAL, "offset", PROPERTY_HINT_RANGE, "0,10000,0.01"), "set_offset", "get_offset"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "unit_offset", PROPERTY_HINT_RANGE, "0,1,0.0001", PROPERTY_USAGE_EDITOR), "set_unit_offset", "get_unit_offset"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "h_offset"), "set_h_offset", "get_h_offset"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "v_offset"), "set_v_offset", "get_v_offset"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rotate"), "set_rotate", "is_rotating"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cubic_interp"), "set_cubic_interpolation", "get_cubic_interpolation"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "lookahead", PROPERTY_HINT_RANGE, "0.001,1024.0,0.001"), "set_lookahead", "get_lookahead"); } void PathFollow2D::set_offset(float p_offset) { diff --git a/scene/2d/path_2d.h b/scene/2d/path_2d.h index 7f53821c7e..64696442c3 100644 --- a/scene/2d/path_2d.h +++ b/scene/2d/path_2d.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef PATH_2D_H #define PATH_2D_H @@ -47,6 +48,7 @@ protected: public: virtual Rect2 _edit_get_rect() const; + virtual bool _edit_use_rect() const; virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; void set_curve(const Ref<Curve2D> &p_curve); @@ -73,9 +75,7 @@ private: void _update_transform(); 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; void _notification(int p_what); static void _bind_methods(); diff --git a/scene/2d/path_texture.cpp b/scene/2d/path_texture.cpp index 59e01f7cb6..d36a9fb65a 100644 --- a/scene/2d/path_texture.cpp +++ b/scene/2d/path_texture.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "path_texture.h" void PathTexture::set_begin_texture(const Ref<Texture> &p_texture) { diff --git a/scene/2d/path_texture.h b/scene/2d/path_texture.h index cfe00f3880..7a347c0653 100644 --- a/scene/2d/path_texture.h +++ b/scene/2d/path_texture.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef PATH_TEXTURE_H #define PATH_TEXTURE_H diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index ec6ff182db..feb11089d0 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -27,8 +27,10 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "physics_body_2d.h" +#include "core/method_bind_ext.gen.inc" #include "engine.h" #include "scene/scene_string_names.h" @@ -243,6 +245,7 @@ void RigidBody2D::_body_enter_tree(ObjectID p_id) { Node *node = Object::cast_to<Node>(obj); ERR_FAIL_COND(!node); + ERR_FAIL_COND(!contact_monitor); Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.find(p_id); ERR_FAIL_COND(!E); ERR_FAIL_COND(E->get().in_scene); @@ -265,6 +268,7 @@ 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); + ERR_FAIL_COND(!contact_monitor); Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.find(p_id); ERR_FAIL_COND(!E); ERR_FAIL_COND(!E->get().in_scene); @@ -290,6 +294,7 @@ void RigidBody2D::_body_inout(int p_status, ObjectID p_instance, int p_body_shap Object *obj = ObjectDB::get_instance(objid); Node *node = Object::cast_to<Node>(obj); + ERR_FAIL_COND(!contact_monitor); Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.find(objid); /*if (obj) { @@ -309,7 +314,7 @@ void RigidBody2D::_body_inout(int p_status, ObjectID p_instance, int p_body_shap E->get().in_scene = node && node->is_inside_tree(); if (node) { node->connect(SceneStringNames::get_singleton()->tree_entered, this, SceneStringNames::get_singleton()->_body_enter_tree, make_binds(objid)); - node->connect(SceneStringNames::get_singleton()->tree_exited, this, SceneStringNames::get_singleton()->_body_exit_tree, make_binds(objid)); + node->connect(SceneStringNames::get_singleton()->tree_exiting, this, SceneStringNames::get_singleton()->_body_exit_tree, make_binds(objid)); if (E->get().in_scene) { emit_signal(SceneStringNames::get_singleton()->body_entered, node); } @@ -338,7 +343,7 @@ void RigidBody2D::_body_inout(int p_status, ObjectID p_instance, int p_body_shap if (node) { node->disconnect(SceneStringNames::get_singleton()->tree_entered, this, SceneStringNames::get_singleton()->_body_enter_tree); - node->disconnect(SceneStringNames::get_singleton()->tree_exited, this, SceneStringNames::get_singleton()->_body_exit_tree); + node->disconnect(SceneStringNames::get_singleton()->tree_exiting, this, SceneStringNames::get_singleton()->_body_exit_tree); if (in_scene) emit_signal(SceneStringNames::get_singleton()->body_exited, obj); } @@ -358,19 +363,17 @@ struct _RigidBody2DInOut { int local_shape; }; -bool RigidBody2D::_test_motion(const Vector2 &p_motion, float p_margin, const Ref<Physics2DTestMotionResult> &p_result) { +bool RigidBody2D::_test_motion(const Vector2 &p_motion, bool p_infinite_inertia, float p_margin, const Ref<Physics2DTestMotionResult> &p_result) { Physics2DServer::MotionResult *r = NULL; if (p_result.is_valid()) r = p_result->get_result_ptr(); - return Physics2DServer::get_singleton()->body_test_motion(get_rid(), get_global_transform(), p_motion, p_margin, r); + return Physics2DServer::get_singleton()->body_test_motion(get_rid(), get_global_transform(), p_motion, p_infinite_inertia, p_margin, r); } void RigidBody2D::_direct_state_changed(Object *p_state) { -//eh.. fuck #ifdef DEBUG_ENABLED - state = Object::cast_to<Physics2DDirectBodyState>(p_state); #else state = (Physics2DDirectBodyState *)p_state; //trust it @@ -762,6 +765,14 @@ void RigidBody2D::set_contact_monitor(bool p_enabled) { for (Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.front(); E; E = E->next()) { //clean up mess + Object *obj = ObjectDB::get_instance(E->key()); + Node *node = Object::cast_to<Node>(obj); + + if (node) { + + node->disconnect(SceneStringNames::get_singleton()->tree_entered, this, SceneStringNames::get_singleton()->_body_enter_tree); + node->disconnect(SceneStringNames::get_singleton()->tree_exiting, this, SceneStringNames::get_singleton()->_body_exit_tree); + } } memdelete(contact_monitor); @@ -806,7 +817,7 @@ String RigidBody2D::get_configuration_warning() const { if (warning != String()) { warning += "\n"; } - warning += TTR("Size changes to RigidBody2D (in character or rigid modes) will be overriden by the physics engine when running.\nChange the size in children collision shapes instead."); + warning += TTR("Size changes to RigidBody2D (in character or rigid modes) will be overridden by the physics engine when running.\nChange the size in children collision shapes instead."); } return warning; @@ -876,7 +887,7 @@ void RigidBody2D::_bind_methods() { 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("test_motion", "motion", "margin", "result"), &RigidBody2D::_test_motion, DEFVAL(0.08), DEFVAL(Variant())); + ClassDB::bind_method(D_METHOD("test_motion", "motion", "infinite_inertia", "margin", "result"), &RigidBody2D::_test_motion, DEFVAL(true), DEFVAL(0.08), DEFVAL(Variant())); ClassDB::bind_method(D_METHOD("_direct_state_changed"), &RigidBody2D::_direct_state_changed); ClassDB::bind_method(D_METHOD("_body_enter_tree"), &RigidBody2D::_body_enter_tree); @@ -888,6 +899,7 @@ void RigidBody2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Rigid,Static,Character,Kinematic"), "set_mode", "get_mode"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "mass", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01"), "set_mass", "get_mass"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "inertia", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01", 0), "set_inertia", "get_inertia"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "weight", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01", PROPERTY_USAGE_EDITOR), "set_weight", "get_weight"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_friction", "get_friction"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce"); @@ -904,6 +916,9 @@ void RigidBody2D::_bind_methods() { ADD_GROUP("Angular", "angular_"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_velocity"), "set_angular_velocity", "get_angular_velocity"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_damp", PROPERTY_HINT_RANGE, "-1,128,0.01"), "set_angular_damp", "get_angular_damp"); + ADD_GROUP("Applied Forces", "applied_"); + ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "applied_force"), "set_applied_force", "get_applied_force"); + ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "applied_torque"), "set_applied_torque", "get_applied_torque"); ADD_SIGNAL(MethodInfo("body_shape_entered", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape"))); ADD_SIGNAL(MethodInfo("body_shape_exited", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape"))); @@ -956,11 +971,11 @@ RigidBody2D::~RigidBody2D() { ////////////////////////// -Ref<KinematicCollision2D> KinematicBody2D::_move(const Vector2 &p_motion) { +Ref<KinematicCollision2D> KinematicBody2D::_move(const Vector2 &p_motion, bool p_infinite_inertia) { Collision col; - if (move_and_collide(p_motion, col)) { + if (move_and_collide(p_motion, p_infinite_inertia, col)) { if (motion_cache.is_null()) { motion_cache.instance(); motion_cache->owner = this; @@ -974,11 +989,11 @@ Ref<KinematicCollision2D> KinematicBody2D::_move(const Vector2 &p_motion) { return Ref<KinematicCollision2D>(); } -bool KinematicBody2D::move_and_collide(const Vector2 &p_motion, Collision &r_collision) { +bool KinematicBody2D::move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, Collision &r_collision) { Transform2D gt = get_global_transform(); Physics2DServer::MotionResult result; - bool colliding = Physics2DServer::get_singleton()->body_test_motion(get_rid(), gt, p_motion, margin, &result); + bool colliding = Physics2DServer::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_infinite_inertia, margin, &result); if (colliding) { r_collision.collider_metadata = result.collider_metadata; @@ -998,7 +1013,7 @@ bool KinematicBody2D::move_and_collide(const Vector2 &p_motion, Collision &r_col return colliding; } -Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const Vector2 &p_floor_direction, float p_slope_stop_min_velocity, int p_max_slides, float p_floor_max_angle) { +Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const Vector2 &p_floor_direction, bool p_infinite_inertia, float p_slope_stop_min_velocity, int p_max_slides, float p_floor_max_angle) { Vector2 motion = (floor_velocity + p_linear_velocity) * get_physics_process_delta_time(); Vector2 lv = p_linear_velocity; @@ -1013,7 +1028,7 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const Collision collision; - bool collided = move_and_collide(motion, collision); + bool collided = move_and_collide(motion, p_infinite_inertia, collision); if (collided) { @@ -1080,11 +1095,11 @@ Vector2 KinematicBody2D::get_floor_velocity() const { return floor_velocity; } -bool KinematicBody2D::test_move(const Transform2D &p_from, const Vector2 &p_motion) { +bool KinematicBody2D::test_move(const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia) { ERR_FAIL_COND_V(!is_inside_tree(), false); - return Physics2DServer::get_singleton()->body_test_motion(get_rid(), p_from, p_motion, margin); + return Physics2DServer::get_singleton()->body_test_motion(get_rid(), p_from, p_motion, p_infinite_inertia, margin); } void KinematicBody2D::set_safe_margin(float p_margin) { @@ -1125,10 +1140,10 @@ Ref<KinematicCollision2D> KinematicBody2D::_get_slide_collision(int p_bounce) { void KinematicBody2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec"), &KinematicBody2D::_move); - ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity", "floor_normal", "slope_stop_min_velocity", "max_bounces", "floor_max_angle"), &KinematicBody2D::move_and_slide, DEFVAL(Vector2(0, 0)), DEFVAL(5), DEFVAL(4), DEFVAL(Math::deg2rad((float)45))); + ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "infinite_inertia"), &KinematicBody2D::_move, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity", "floor_normal", "infinite_inertia", "slope_stop_min_velocity", "max_bounces", "floor_max_angle"), &KinematicBody2D::move_and_slide, DEFVAL(Vector2(0, 0)), DEFVAL(true), DEFVAL(5), DEFVAL(4), DEFVAL(Math::deg2rad((float)45))); - ClassDB::bind_method(D_METHOD("test_move", "from", "rel_vec"), &KinematicBody2D::test_move); + ClassDB::bind_method(D_METHOD("test_move", "from", "rel_vec", "infinite_inertia"), &KinematicBody2D::test_move); ClassDB::bind_method(D_METHOD("is_on_floor"), &KinematicBody2D::is_on_floor); ClassDB::bind_method(D_METHOD("is_on_ceiling"), &KinematicBody2D::is_on_ceiling); diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h index a39eb09514..0fda3c5c05 100644 --- a/scene/2d/physics_body_2d.h +++ b/scene/2d/physics_body_2d.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef PHYSICS_BODY_2D_H #define PHYSICS_BODY_2D_H @@ -184,7 +185,7 @@ private: void _body_inout(int p_status, ObjectID p_instance, int p_body_shape, int p_local_shape); void _direct_state_changed(Object *p_state); - bool _test_motion(const Vector2 &p_motion, float p_margin = 0.08, const Ref<Physics2DTestMotionResult> &p_result = Ref<Physics2DTestMotionResult>()); + bool _test_motion(const Vector2 &p_motion, bool p_infinite_inertia = true, float p_margin = 0.08, const Ref<Physics2DTestMotionResult> &p_result = Ref<Physics2DTestMotionResult>()); protected: void _notification(int p_what); @@ -295,20 +296,20 @@ private: _FORCE_INLINE_ bool _ignores_mode(Physics2DServer::BodyMode) const; - Ref<KinematicCollision2D> _move(const Vector2 &p_motion); + Ref<KinematicCollision2D> _move(const Vector2 &p_motion, bool p_infinite_inertia = true); Ref<KinematicCollision2D> _get_slide_collision(int p_bounce); protected: static void _bind_methods(); public: - bool move_and_collide(const Vector2 &p_motion, Collision &r_collision); - bool test_move(const Transform2D &p_from, const Vector2 &p_motion); + bool move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, Collision &r_collision); + bool test_move(const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia); void set_safe_margin(float p_margin); float get_safe_margin() const; - Vector2 move_and_slide(const Vector2 &p_linear_velocity, const Vector2 &p_floor_direction = Vector2(0, 0), float p_slope_stop_min_velocity = 5, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45)); + Vector2 move_and_slide(const Vector2 &p_linear_velocity, const Vector2 &p_floor_direction = Vector2(0, 0), bool p_infinite_inertia = true, float p_slope_stop_min_velocity = 5, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45)); bool is_on_floor() const; bool is_on_wall() const; bool is_on_ceiling() const; diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp index f9951cdd08..4d6ebc81c3 100644 --- a/scene/2d/polygon_2d.cpp +++ b/scene/2d/polygon_2d.cpp @@ -27,11 +27,35 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "polygon_2d.h" #include "core/math/geometry.h" +#include "skeleton_2d.h" +Dictionary Polygon2D::_edit_get_state() const { + Dictionary state = Node2D::_edit_get_state(); + state["offset"] = offset; + return state; +} -Rect2 Polygon2D::_edit_get_rect() const { +void Polygon2D::_edit_set_state(const Dictionary &p_state) { + Node2D::_edit_set_state(p_state); + set_offset(p_state["offset"]); +} + +void Polygon2D::_edit_set_pivot(const Point2 &p_pivot) { + set_position(get_transform().xform(p_pivot)); + set_offset(get_offset() - p_pivot); +} +Point2 Polygon2D::_edit_get_pivot() const { + return Vector2(); +} + +bool Polygon2D::_edit_use_pivot() const { + return true; +} + +Rect2 Polygon2D::_edit_get_rect() const { if (rect_cache_dirty) { int l = polygon.size(); PoolVector<Vector2>::Read r = polygon.read(); @@ -49,23 +73,13 @@ Rect2 Polygon2D::_edit_get_rect() const { return item_rect; } -bool Polygon2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { - - return Geometry::is_point_in_polygon(p_point, Variant(polygon)); -} - -void Polygon2D::_edit_set_pivot(const Point2 &p_pivot) { - - set_offset(p_pivot); +bool Polygon2D::_edit_use_rect() const { + return true; } -Point2 Polygon2D::_edit_get_pivot() const { - - return get_offset(); -} -bool Polygon2D::_edit_use_pivot() const { +bool Polygon2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { - return true; + return Geometry::is_point_in_polygon(p_point - get_offset(), Variant(polygon)); } void Polygon2D::_notification(int p_what) { @@ -77,8 +91,20 @@ void Polygon2D::_notification(int p_what) { if (polygon.size() < 3) return; + Skeleton2D *skeleton_node = NULL; + if (has_node(skeleton)) { + skeleton_node = Object::cast_to<Skeleton2D>(get_node(skeleton)); + } + + if (skeleton_node) + VS::get_singleton()->canvas_item_attach_skeleton(get_canvas_item(), skeleton_node->get_skeleton()); + else + VS::get_singleton()->canvas_item_attach_skeleton(get_canvas_item(), RID()); + Vector<Vector2> points; Vector<Vector2> uvs; + Vector<int> bones; + Vector<float> weights; points.resize(polygon.size()); @@ -166,6 +192,70 @@ void Polygon2D::_notification(int p_what) { } } + if (!invert && bone_weights.size()) { + //a skeleton is set! fill indices and weights + int vc = points.size(); + bones.resize(vc * 4); + weights.resize(vc * 4); + + int *bonesw = bones.ptrw(); + float *weightsw = weights.ptrw(); + + for (int i = 0; i < vc * 4; i++) { + bonesw[i] = 0; + weightsw[i] = 0; + } + + for (int i = 0; i < bone_weights.size(); i++) { + if (bone_weights[i].weights.size() != points.size()) { + continue; //different number of vertices, sorry not using. + } + if (!skeleton_node->has_node(bone_weights[i].path)) { + continue; //node does not exist + } + Bone2D *bone = Object::cast_to<Bone2D>(skeleton_node->get_node(bone_weights[i].path)); + if (!bone) { + continue; + } + + int bone_index = bone->get_index_in_skeleton(); + PoolVector<float>::Read r = bone_weights[i].weights.read(); + for (int j = 0; j < vc; j++) { + if (r[j] == 0.0) + continue; //weight is unpainted, skip + //find an index with a weight + for (int k = 0; k < 4; k++) { + if (weightsw[j * 4 + k] < r[j]) { + //this is less than this weight, insert weight! + for (int l = 3; l > k; l--) { + weightsw[j * 4 + l] = weightsw[j * 4 + l - 1]; + bonesw[j * 4 + l] = bonesw[j * 4 + l - 1]; + } + weightsw[j * 4 + k] = r[j]; + bonesw[j * 4 + k] = bone_index; + break; + } + } + } + } + + //normalize the weights + for (int i = 0; i < vc; i++) { + float tw = 0; + for (int j = 0; j < 4; j++) { + tw += weightsw[i * 4 + j]; + } + if (tw == 0) + continue; //unpainted, do nothing + + //normalize + for (int j = 0; j < 4; j++) { + weightsw[i * 4 + j] /= tw; + // print_line("point " + itos(i) + " idx " + itos(j) + " index: " + itos(bonesw[i * 4 + j]) + " weight: " + rtos(weightsw[i * 4 + j])); + } + } + } + Vector<Color> colors; int color_len = vertex_colors.size(); colors.resize(len); @@ -182,7 +272,81 @@ void Polygon2D::_notification(int p_what) { // Vector<int> indices = Geometry::triangulate_polygon(points); // VS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), indices, points, colors, uvs, texture.is_valid() ? texture->get_rid() : RID()); - VS::get_singleton()->canvas_item_add_polygon(get_canvas_item(), points, colors, uvs, texture.is_valid() ? texture->get_rid() : RID(), RID(), antialiased); + if (invert || splits.size() == 0) { + Vector<int> indices = Geometry::triangulate_polygon(points); + VS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), indices, points, colors, uvs, bones, weights, texture.is_valid() ? texture->get_rid() : RID()); + } else { + //use splits + Vector<int> loop; + int sc = splits.size(); + PoolVector<int>::Read r = splits.read(); + int last = points.size(); + + Vector<Vector<int> > loops; + + for (int i = 0; i < last; i++) { + + int split; + int min_end = -1; + + do { + + loop.push_back(i); + + split = -1; + int end = -1; + + for (int j = 0; j < sc; j += 2) { + if (r[j + 1] >= last) + continue; //no longer valid + if (min_end != -1 && r[j + 1] >= min_end) + continue; + if (r[j] == i) { + if (split == -1 || r[j + 1] > end) { + split = r[j]; + end = r[j + 1]; + } + } + } + + if (split != -1) { + for (int j = end; j < last; j++) { + loop.push_back(j); + } + loops.push_back(loop); + last = end + 1; + loop.clear(); + min_end = end; //avoid this split from repeating + } + + } while (split != -1); + } + + if (loop.size()) { + loops.push_back(loop); + } + + Vector<int> indices; + + for (int i = 0; i < loops.size(); i++) { + Vector<int> loop = loops[i]; + Vector<Vector2> vertices; + vertices.resize(loop.size()); + for (int j = 0; j < vertices.size(); j++) { + vertices[j] = points[loop[j]]; + } + Vector<int> sub_indices = Geometry::triangulate_polygon(vertices); + int from = indices.size(); + indices.resize(from + sub_indices.size()); + for (int j = 0; j < sub_indices.size(); j++) { + indices[from + j] = loop[sub_indices[j]]; + } + } + + //print_line("loops: " + itos(loops.size()) + " indices: " + itos(indices.size())); + + VS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), indices, points, colors, uvs, bones, weights, texture.is_valid() ? texture->get_rid() : RID()); + } } break; } @@ -210,6 +374,18 @@ PoolVector<Vector2> Polygon2D::get_uv() const { return uv; } +void Polygon2D::set_splits(const PoolVector<int> &p_splits) { + + ERR_FAIL_COND(p_splits.size() & 1); //splits should be multiple of 2 + splits = p_splits; + update(); +} + +PoolVector<int> Polygon2D::get_splits() const { + + return splits; +} + void Polygon2D::set_color(const Color &p_color) { color = p_color; @@ -323,6 +499,7 @@ void Polygon2D::set_offset(const Vector2 &p_offset) { offset = p_offset; rect_cache_dirty = true; update(); + _change_notify("offset"); } Vector2 Polygon2D::get_offset() const { @@ -330,6 +507,74 @@ Vector2 Polygon2D::get_offset() const { return offset; } +void Polygon2D::add_bone(const NodePath &p_path, const PoolVector<float> &p_weights) { + + Bone bone; + bone.path = p_path; + bone.weights = p_weights; + bone_weights.push_back(bone); +} +int Polygon2D::get_bone_count() const { + return bone_weights.size(); +} +NodePath Polygon2D::get_bone_path(int p_index) const { + ERR_FAIL_INDEX_V(p_index, bone_weights.size(), NodePath()); + return bone_weights[p_index].path; +} +PoolVector<float> Polygon2D::get_bone_weights(int p_index) const { + + ERR_FAIL_INDEX_V(p_index, bone_weights.size(), PoolVector<float>()); + return bone_weights[p_index].weights; +} +void Polygon2D::erase_bone(int p_idx) { + + ERR_FAIL_INDEX(p_idx, bone_weights.size()); + bone_weights.remove(p_idx); +} + +void Polygon2D::clear_bones() { + bone_weights.clear(); +} + +void Polygon2D::set_bone_weights(int p_index, const PoolVector<float> &p_weights) { + ERR_FAIL_INDEX(p_index, bone_weights.size()); + bone_weights[p_index].weights = p_weights; + update(); +} +void Polygon2D::set_bone_path(int p_index, const NodePath &p_path) { + ERR_FAIL_INDEX(p_index, bone_weights.size()); + bone_weights[p_index].path = p_path; + update(); +} + +Array Polygon2D::_get_bones() const { + Array bones; + for (int i = 0; i < get_bone_count(); i++) { + bones.push_back(get_bone_path(i)); + bones.push_back(get_bone_weights(i)); + } + return bones; +} +void Polygon2D::_set_bones(const Array &p_bones) { + + ERR_FAIL_COND(p_bones.size() & 1); + clear_bones(); + for (int i = 0; i < p_bones.size(); i += 2) { + add_bone(p_bones[i], p_bones[i + 1]); + } +} + +void Polygon2D::set_skeleton(const NodePath &p_skeleton) { + if (skeleton == p_skeleton) + return; + skeleton = p_skeleton; + update(); +} + +NodePath Polygon2D::get_skeleton() const { + return skeleton; +} + void Polygon2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_polygon", "polygon"), &Polygon2D::set_polygon); @@ -341,6 +586,9 @@ void Polygon2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_color", "color"), &Polygon2D::set_color); ClassDB::bind_method(D_METHOD("get_color"), &Polygon2D::get_color); + ClassDB::bind_method(D_METHOD("set_splits", "splits"), &Polygon2D::set_splits); + ClassDB::bind_method(D_METHOD("get_splits"), &Polygon2D::get_splits); + ClassDB::bind_method(D_METHOD("set_vertex_colors", "vertex_colors"), &Polygon2D::set_vertex_colors); ClassDB::bind_method(D_METHOD("get_vertex_colors"), &Polygon2D::get_vertex_colors); @@ -371,8 +619,24 @@ void Polygon2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_offset", "offset"), &Polygon2D::set_offset); ClassDB::bind_method(D_METHOD("get_offset"), &Polygon2D::get_offset); + ClassDB::bind_method(D_METHOD("add_bone", "path", "weights"), &Polygon2D::add_bone); + ClassDB::bind_method(D_METHOD("get_bone_count"), &Polygon2D::get_bone_count); + ClassDB::bind_method(D_METHOD("get_bone_path", "index"), &Polygon2D::get_bone_path); + ClassDB::bind_method(D_METHOD("get_bone_weights", "index"), &Polygon2D::get_bone_weights); + ClassDB::bind_method(D_METHOD("erase_bone", "index"), &Polygon2D::erase_bone); + ClassDB::bind_method(D_METHOD("clear_bones"), &Polygon2D::clear_bones); + ClassDB::bind_method(D_METHOD("set_bone_path", "index", "path"), &Polygon2D::set_bone_path); + ClassDB::bind_method(D_METHOD("set_bone_weights", "index", "weights"), &Polygon2D::set_bone_weights); + + ClassDB::bind_method(D_METHOD("set_skeleton", "skeleton"), &Polygon2D::set_skeleton); + ClassDB::bind_method(D_METHOD("get_skeleton"), &Polygon2D::get_skeleton); + + ClassDB::bind_method(D_METHOD("_set_bones", "bones"), &Polygon2D::_set_bones); + ClassDB::bind_method(D_METHOD("_get_bones"), &Polygon2D::_get_bones); + ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "polygon"), "set_polygon", "get_polygon"); ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "uv"), "set_uv", "get_uv"); + ADD_PROPERTY(PropertyInfo(Variant::POOL_INT_ARRAY, "splits"), "set_splits", "get_splits"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color"); ADD_PROPERTY(PropertyInfo(Variant::POOL_COLOR_ARRAY, "vertex_colors"), "set_vertex_colors", "get_vertex_colors"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset"); @@ -382,11 +646,16 @@ void Polygon2D::_bind_methods() { ADD_GROUP("Texture", "texture_"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "texture_offset"), "set_texture_offset", "get_texture_offset"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "texture_scale"), "set_texture_scale", "get_texture_scale"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "texture_rotation", PROPERTY_HINT_RANGE, "-1440,1440,0.1"), "set_texture_rotation_degrees", "get_texture_rotation_degrees"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "texture_rotation_degrees", PROPERTY_HINT_RANGE, "-1440,1440,0.1"), "set_texture_rotation_degrees", "get_texture_rotation_degrees"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "texture_rotation", PROPERTY_HINT_NONE, "", 0), "set_texture_rotation", "get_texture_rotation"); + ADD_GROUP("Skeleton", ""); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "skeleton"), "set_skeleton", "get_skeleton"); ADD_GROUP("Invert", "invert_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "invert_enable"), "set_invert", "get_invert"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "invert_border", PROPERTY_HINT_RANGE, "0.1,16384,0.1"), "set_invert_border", "get_invert_border"); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bones", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_bones", "_get_bones"); } Polygon2D::Polygon2D() { diff --git a/scene/2d/polygon_2d.h b/scene/2d/polygon_2d.h index 5014cbc6e9..575f71d74a 100644 --- a/scene/2d/polygon_2d.h +++ b/scene/2d/polygon_2d.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef POLYGON_2D_H #define POLYGON_2D_H @@ -39,6 +40,15 @@ class Polygon2D : public Node2D { PoolVector<Vector2> polygon; PoolVector<Vector2> uv; PoolVector<Color> vertex_colors; + PoolVector<int> splits; + + struct Bone { + NodePath path; + PoolVector<float> weights; + }; + + Vector<Bone> bone_weights; + Color color; Ref<Texture> texture; Size2 tex_scale; @@ -53,17 +63,36 @@ class Polygon2D : public Node2D { mutable bool rect_cache_dirty; mutable Rect2 item_rect; + NodePath skeleton; + + Array _get_bones() const; + void _set_bones(const Array &p_bones); + protected: void _notification(int p_what); static void _bind_methods(); public: + virtual Dictionary _edit_get_state() const; + virtual void _edit_set_state(const Dictionary &p_state); + + virtual void _edit_set_pivot(const Point2 &p_pivot); + virtual Point2 _edit_get_pivot() const; + virtual bool _edit_use_pivot() const; + virtual Rect2 _edit_get_rect() const; + virtual bool _edit_use_rect() const; + + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; + void set_polygon(const PoolVector<Vector2> &p_polygon); PoolVector<Vector2> get_polygon() const; void set_uv(const PoolVector<Vector2> &p_uv); PoolVector<Vector2> get_uv() const; + void set_splits(const PoolVector<int> &p_uv); + PoolVector<int> get_splits() const; + void set_color(const Color &p_color); Color get_color() const; @@ -97,14 +126,17 @@ public: void set_offset(const Vector2 &p_offset); Vector2 get_offset() const; - //editor stuff - - virtual void _edit_set_pivot(const Point2 &p_pivot); - virtual Point2 _edit_get_pivot() const; - virtual bool _edit_use_pivot() const; - - virtual Rect2 _edit_get_rect() const; - virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; + void add_bone(const NodePath &p_path = NodePath(), const PoolVector<float> &p_weights = PoolVector<float>()); + int get_bone_count() const; + NodePath get_bone_path(int p_index) const; + PoolVector<float> get_bone_weights(int p_index) const; + void erase_bone(int p_idx); + void clear_bones(); + void set_bone_weights(int p_index, const PoolVector<float> &p_weights); + void set_bone_path(int p_index, const NodePath &p_path); + + void set_skeleton(const NodePath &p_skeleton); + NodePath get_skeleton() const; Polygon2D(); }; diff --git a/scene/2d/position_2d.cpp b/scene/2d/position_2d.cpp index bff7554ec3..64d23719e7 100644 --- a/scene/2d/position_2d.cpp +++ b/scene/2d/position_2d.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "position_2d.h" #include "engine.h" @@ -43,6 +44,10 @@ Rect2 Position2D::_edit_get_rect() const { return Rect2(Point2(-10, -10), Size2(20, 20)); } +bool Position2D::_edit_use_rect() const { + return false; +} + void Position2D::_notification(int p_what) { switch (p_what) { diff --git a/scene/2d/position_2d.h b/scene/2d/position_2d.h index e5862974fa..bff474cccd 100644 --- a/scene/2d/position_2d.h +++ b/scene/2d/position_2d.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef POSITION_2D_H #define POSITION_2D_H @@ -43,6 +44,7 @@ protected: public: virtual Rect2 _edit_get_rect() const; + virtual bool _edit_use_rect() const; Position2D(); }; diff --git a/scene/2d/ray_cast_2d.cpp b/scene/2d/ray_cast_2d.cpp index d7c152b108..255d2d38d5 100644 --- a/scene/2d/ray_cast_2d.cpp +++ b/scene/2d/ray_cast_2d.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "ray_cast_2d.h" #include "collision_object_2d.h" @@ -100,7 +101,7 @@ void RayCast2D::set_enabled(bool p_enabled) { enabled = p_enabled; if (is_inside_tree() && !Engine::get_singleton()->is_editor_hint()) - set_physics_process(p_enabled); + set_physics_process_internal(p_enabled); if (!p_enabled) collided = false; } @@ -140,9 +141,9 @@ void RayCast2D::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: { if (enabled && !Engine::get_singleton()->is_editor_hint()) - set_physics_process(true); + set_physics_process_internal(true); else - set_physics_process(false); + set_physics_process_internal(false); if (Object::cast_to<CollisionObject2D>(get_parent())) { if (exclude_parent_body) @@ -154,7 +155,7 @@ void RayCast2D::_notification(int p_what) { case NOTIFICATION_EXIT_TREE: { if (enabled) - set_physics_process(false); + set_physics_process_internal(false); } break; @@ -182,7 +183,7 @@ void RayCast2D::_notification(int p_what) { } break; - case NOTIFICATION_PHYSICS_PROCESS: { + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { if (!enabled) break; diff --git a/scene/2d/ray_cast_2d.h b/scene/2d/ray_cast_2d.h index a2e1d689fc..0850cdc7cc 100644 --- a/scene/2d/ray_cast_2d.h +++ b/scene/2d/ray_cast_2d.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef RAY_CAST_2D_H #define RAY_CAST_2D_H diff --git a/scene/2d/remote_transform_2d.cpp b/scene/2d/remote_transform_2d.cpp index c283d69f86..da764e032b 100644 --- a/scene/2d/remote_transform_2d.cpp +++ b/scene/2d/remote_transform_2d.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "remote_transform_2d.h" #include "scene/scene_string_names.h" diff --git a/scene/2d/remote_transform_2d.h b/scene/2d/remote_transform_2d.h index cfc38ebabb..3f395f820e 100644 --- a/scene/2d/remote_transform_2d.h +++ b/scene/2d/remote_transform_2d.h @@ -27,6 +27,10 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + +#ifndef REMOTE_TRANSFORM_2D_H +#define REMOTE_TRANSFORM_2D_H + #include "scene/2d/node_2d.h" class RemoteTransform2D : public Node2D { @@ -69,3 +73,5 @@ public: RemoteTransform2D(); }; + +#endif // REMOTE_TRANSFORM_2D_H diff --git a/scene/2d/screen_button.cpp b/scene/2d/screen_button.cpp index cf059ec55e..9480f18176 100644 --- a/scene/2d/screen_button.cpp +++ b/scene/2d/screen_button.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "screen_button.h" #include "input_map.h" #include "os/input.h" @@ -334,6 +335,10 @@ Rect2 TouchScreenButton::_edit_get_rect() const { return Rect2(Size2(), texture->get_size()); } +bool TouchScreenButton::_edit_use_rect() const { + return true; +} + void TouchScreenButton::set_visibility_mode(VisibilityMode p_mode) { visibility = p_mode; update(); diff --git a/scene/2d/screen_button.h b/scene/2d/screen_button.h index ad558bb9ec..b2fafcc93d 100644 --- a/scene/2d/screen_button.h +++ b/scene/2d/screen_button.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef SCREEN_BUTTON_H #define SCREEN_BUTTON_H @@ -103,6 +104,7 @@ public: bool is_pressed() const; Rect2 _edit_get_rect() const; + virtual bool _edit_use_rect() const; TouchScreenButton(); }; diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp new file mode 100644 index 0000000000..2363c791fa --- /dev/null +++ b/scene/2d/skeleton_2d.cpp @@ -0,0 +1,278 @@ +#include "skeleton_2d.h" + +void Bone2D::_notification(int p_what) { + + if (p_what == NOTIFICATION_ENTER_TREE) { + Node *parent = get_parent(); + parent_bone = Object::cast_to<Bone2D>(parent); + skeleton = NULL; + while (parent) { + skeleton = Object::cast_to<Skeleton2D>(parent); + if (skeleton) + break; + if (!Object::cast_to<Bone2D>(parent)) + break; //skeletons must be chained to Bone2Ds. + + parent = parent->get_parent(); + } + + if (skeleton) { + Skeleton2D::Bone bone; + bone.bone = this; + skeleton->bones.push_back(bone); + skeleton->_make_bone_setup_dirty(); + } + } + if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) { + if (skeleton) { + skeleton->_make_transform_dirty(); + } + } + if (p_what == NOTIFICATION_MOVED_IN_PARENT) { + if (skeleton) { + skeleton->_make_bone_setup_dirty(); + } + } + + if (p_what == NOTIFICATION_EXIT_TREE) { + if (skeleton) { + for (int i = 0; i < skeleton->bones.size(); i++) { + if (skeleton->bones[i].bone == this) { + skeleton->bones.remove(i); + break; + } + } + skeleton->_make_bone_setup_dirty(); + skeleton = NULL; + } + parent_bone = NULL; + } +} +void Bone2D::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_rest", "rest"), &Bone2D::set_rest); + ClassDB::bind_method(D_METHOD("get_rest"), &Bone2D::get_rest); + ClassDB::bind_method(D_METHOD("apply_rest"), &Bone2D::apply_rest); + ClassDB::bind_method(D_METHOD("get_skeleton_rest"), &Bone2D::get_skeleton_rest); + ClassDB::bind_method(D_METHOD("get_index_in_skeleton"), &Bone2D::get_index_in_skeleton); + + ClassDB::bind_method(D_METHOD("set_default_length", "default_length"), &Bone2D::set_default_length); + ClassDB::bind_method(D_METHOD("get_default_length"), &Bone2D::get_default_length); + + ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D,"rest"),"set_rest","get_rest"); + ADD_PROPERTY(PropertyInfo(Variant::REAL,"default_length",PROPERTY_HINT_RANGE,"1,1024,1"),"set_default_length","get_default_length"); +} + +void Bone2D::set_rest(const Transform2D &p_rest) { + rest = p_rest; + if (skeleton) + skeleton->_make_bone_setup_dirty(); + + update_configuration_warning(); +} + +Transform2D Bone2D::get_rest() const { + return rest; +} + +Transform2D Bone2D::get_skeleton_rest() const { + + if (parent_bone) { + return parent_bone->get_skeleton_rest() * rest; + } else { + return rest; + } +} + +void Bone2D::apply_rest() { + set_transform(rest); +} + +void Bone2D::set_default_length(float p_length) { + + default_length=p_length; + +} + +float Bone2D::get_default_length() const { + return default_length; +} + +int Bone2D::get_index_in_skeleton() const { + ERR_FAIL_COND_V(!skeleton,-1); + skeleton->_update_bone_setup(); + return skeleton_index; +} +String Bone2D::get_configuration_warning() const { + + String warning = Node2D::get_configuration_warning(); + if (!skeleton) { + if (warning!=String()) { + warning+="\n"; + } + if (parent_bone) { + warning+=TTR("This Bone2D chain should end at a Skeleton2D node."); + } else { + warning+=TTR("A Bone2D only works with a Skeleton2D or another Bone2D as parent node."); + } + } + + if (rest==Transform2D(0,0,0,0,0,0)) { + if (warning!=String()) { + warning+="\n"; + } + warning+=TTR("This bone lacks a proper REST pose. Go to the Skeleton2D node and set one."); + + } + + return warning; +} + +Bone2D::Bone2D() { + skeleton = NULL; + parent_bone = NULL; + skeleton_index=-1; + default_length=16; + set_notify_local_transform(true); + //this is a clever hack so the bone knows no rest has been set yet, allowing to show an error. + for(int i=0;i<3;i++) { + rest[i]=Vector2(0,0); + } +} + +////////////////////////////////////// + +void Skeleton2D::_make_bone_setup_dirty() { + + if (bone_setup_dirty) + return; + bone_setup_dirty = true; + if (is_inside_tree()) { + call_deferred("_update_bone_setup"); + } +} + +void Skeleton2D::_update_bone_setup() { + + if (!bone_setup_dirty) + return; + + bone_setup_dirty = false; + VS::get_singleton()->skeleton_allocate(skeleton, bones.size(), true); + + bones.sort(); //sorty so they are always in the same order/index + + for (int i = 0; i < bones.size(); i++) { + bones[i].rest_inverse = bones[i].bone->get_skeleton_rest().affine_inverse(); //bind pose + bones[i].bone->skeleton_index=i; + Bone2D *parent_bone = Object::cast_to<Bone2D>(bones[i].bone->get_parent()); + if (parent_bone) { + bones[i].parent_index=parent_bone->skeleton_index; + } else { + bones[i].parent_index=-1; + } + } + + transform_dirty = true; + _update_transform(); +} + +void Skeleton2D::_make_transform_dirty() { + + if (transform_dirty) + return; + transform_dirty = true; + if (is_inside_tree()) { + call_deferred("_update_transform"); + } +} + +void Skeleton2D::_update_transform() { + + if (bone_setup_dirty) { + _update_bone_setup(); + return; //above will update transform anyway + } + if (!transform_dirty) + return; + + transform_dirty = false; + + for (int i = 0; i < bones.size(); i++) { + + ERR_CONTINUE(bones[i].parent_index>=i); + if (bones[i].parent_index>=0) { + bones[i].accum_transform = bones[bones[i].parent_index].accum_transform * bones[i].bone->get_transform(); + } else { + bones[i].accum_transform = bones[i].bone->get_transform(); + } + } + + for (int i = 0; i < bones.size(); i++) { + + Transform2D final_xform = bones[i].accum_transform * bones[i].rest_inverse; + VS::get_singleton()->skeleton_bone_set_transform_2d(skeleton, i, final_xform); + } +} + +int Skeleton2D::get_bone_count() const { + + ERR_FAIL_COND_V(!is_inside_tree(), 0); + + if (bone_setup_dirty) { + const_cast<Skeleton2D *>(this)->_update_bone_setup(); + } + + return bones.size(); +} + +Bone2D *Skeleton2D::get_bone(int p_idx) { + + ERR_FAIL_COND_V(!is_inside_tree(), NULL); + ERR_FAIL_INDEX_V(p_idx, bones.size(), NULL); + + return bones[p_idx].bone; +} + +void Skeleton2D::_notification(int p_what) { + + if (p_what == NOTIFICATION_READY) { + + if (bone_setup_dirty) + _update_bone_setup(); + if (transform_dirty) + _update_transform(); + + request_ready(); + } + + if (p_what == NOTIFICATION_TRANSFORM_CHANGED) { + VS::get_singleton()->skeleton_set_base_transform_2d(skeleton,get_global_transform()); + } +} + +RID Skeleton2D::get_skeleton() const { + return skeleton; +} +void Skeleton2D::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_update_bone_setup"), &Skeleton2D::_update_bone_setup); + ClassDB::bind_method(D_METHOD("_update_transform"), &Skeleton2D::_update_transform); + + ClassDB::bind_method(D_METHOD("get_bone_count"), &Skeleton2D::get_bone_count); + ClassDB::bind_method(D_METHOD("get_bone"), &Skeleton2D::get_bone); + + ClassDB::bind_method(D_METHOD("get_skeleton"), &Skeleton2D::get_skeleton); +} + +Skeleton2D::Skeleton2D() { + bone_setup_dirty = true; + transform_dirty = true; + + skeleton = VS::get_singleton()->skeleton_create(); +} + +Skeleton2D::~Skeleton2D() { + + VS::get_singleton()->free(skeleton); +} diff --git a/scene/2d/skeleton_2d.h b/scene/2d/skeleton_2d.h new file mode 100644 index 0000000000..cd270dac85 --- /dev/null +++ b/scene/2d/skeleton_2d.h @@ -0,0 +1,79 @@ +#ifndef SKELETON_2D_H +#define SKELETON_2D_H + +#include "scene/2d/node_2d.h" + +class Skeleton2D; + +class Bone2D : public Node2D { + GDCLASS(Bone2D, Node2D) + + Bone2D *parent_bone; + Skeleton2D *skeleton; + Transform2D rest; + float default_length; + +friend class Skeleton2D; + int skeleton_index; + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + void set_rest(const Transform2D &p_rest); + Transform2D get_rest() const; + void apply_rest(); + Transform2D get_skeleton_rest() const; + + String get_configuration_warning() const; + + void set_default_length(float p_length); + float get_default_length() const; + + int get_index_in_skeleton() const; + + Bone2D(); +}; + +class Skeleton2D : public Node2D { + GDCLASS(Skeleton2D, Node2D); + + friend class Bone2D; + + struct Bone { + bool operator<(const Bone &p_bone) const { + return p_bone.bone->is_greater_than(bone); + } + Bone2D *bone; + int parent_index; + Transform2D accum_transform; + Transform2D rest_inverse; + }; + + Vector<Bone> bones; + + bool bone_setup_dirty; + void _make_bone_setup_dirty(); + void _update_bone_setup(); + + bool transform_dirty; + void _make_transform_dirty(); + void _update_transform(); + + RID skeleton; + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + int get_bone_count() const; + Bone2D *get_bone(int p_idx); + + RID get_skeleton() const; + Skeleton2D(); + ~Skeleton2D(); +}; + +#endif // SKELETON_2D_H diff --git a/scene/2d/sprite.cpp b/scene/2d/sprite.cpp index 68df95c042..64d0771fab 100644 --- a/scene/2d/sprite.cpp +++ b/scene/2d/sprite.cpp @@ -27,53 +27,72 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "sprite.h" #include "core/core_string_names.h" #include "os/os.h" #include "scene/main/viewport.h" #include "scene/scene_string_names.h" -void Sprite::_edit_set_pivot(const Point2 &p_pivot) { +Dictionary Sprite::_edit_get_state() const { + Dictionary state = Node2D::_edit_get_state(); + state["offset"] = offset; + return state; +} - set_offset(p_pivot); +void Sprite::_edit_set_state(const Dictionary &p_state) { + Node2D::_edit_set_state(p_state); + set_offset(p_state["offset"]); } -Point2 Sprite::_edit_get_pivot() const { +void Sprite::_edit_set_pivot(const Point2 &p_pivot) { + set_offset(get_offset() - p_pivot); + set_position(get_transform().xform(p_pivot)); +} - return get_offset(); +Point2 Sprite::_edit_get_pivot() const { + return Vector2(); } + bool Sprite::_edit_use_pivot() const { + return true; +} + +Rect2 Sprite::_edit_get_rect() const { + return get_rect(); +} +bool Sprite::_edit_use_rect() const { return true; } void Sprite::_get_rects(Rect2 &r_src_rect, Rect2 &r_dst_rect, bool &r_filter_clip) const { - Size2 s; - r_filter_clip = false; + Rect2 base_rect; if (region) { - - s = region_rect.size; - r_src_rect = region_rect; r_filter_clip = region_filter_clip; + base_rect = region_rect; } else { - s = Size2(texture->get_size()); - s = s / Size2(hframes, vframes); - - r_src_rect.size = s; - r_src_rect.position.x += float(frame % hframes) * s.x; - r_src_rect.position.y += float(frame / hframes) * s.y; + r_filter_clip = false; + base_rect = Rect2(0, 0, texture->get_width(), texture->get_height()); } - Point2 ofs = offset; + Size2 frame_size = base_rect.size / Size2(hframes, vframes); + Point2 frame_offset = Point2(frame % hframes, frame / hframes); + frame_offset *= frame_size; + + r_src_rect.size = frame_size; + r_src_rect.position = base_rect.position + frame_offset; + + Point2 dest_offset = offset; if (centered) - ofs -= s / 2; + dest_offset -= frame_size / 2; if (Engine::get_singleton()->get_use_pixel_snap()) { - ofs = ofs.floor(); + dest_offset = dest_offset.floor(); } - r_dst_rect = Rect2(ofs, s); + r_dst_rect = Rect2(dest_offset, frame_size); if (hflip) r_dst_rect.size.x = -r_dst_rect.size.x; @@ -110,7 +129,15 @@ void Sprite::set_texture(const Ref<Texture> &p_texture) { if (p_texture == texture) return; + + if (texture.is_valid()) + texture->remove_change_receptor(this); + texture = p_texture; + + if (texture.is_valid()) + texture->add_change_receptor(this); + update(); emit_signal("texture_changed"); item_rect_changed(); @@ -270,15 +297,65 @@ bool Sprite::_edit_is_selected_on_click(const Point2 &p_point, double p_toleranc Rect2 src_rect, dst_rect; bool filter_clip; _get_rects(src_rect, dst_rect, filter_clip); + dst_rect.size = dst_rect.size.abs(); if (!dst_rect.has_point(p_point)) return false; - Vector2 q = ((p_point - dst_rect.position) / dst_rect.size) * src_rect.size + src_rect.position; + Vector2 q = (p_point - dst_rect.position) / dst_rect.size; + if (hflip) + q.x = 1.0f - q.x; + if (vflip) + q.y = 1.0f - q.y; + q = q * src_rect.size + src_rect.position; + + Ref<Image> image; + Ref<AtlasTexture> atlasTexture = texture; + if (atlasTexture.is_null()) { + image = texture->get_data(); + } else { + ERR_FAIL_COND_V(atlasTexture->get_atlas().is_null(), false); + + image = atlasTexture->get_atlas()->get_data(); + + Rect2 region = atlasTexture->get_region(); + Rect2 margin = atlasTexture->get_margin(); + + q -= margin.position; + + if ((q.x > region.size.width) || (q.y > region.size.height)) { + return false; + } + + q += region.position; + } - Ref<Image> image = texture->get_data(); ERR_FAIL_COND_V(image.is_null(), false); + if (image->is_compressed()) { + return dst_rect.has_point(p_point); + } + bool is_repeat = texture->get_flags() & Texture::FLAG_REPEAT; + bool is_mirrored_repeat = texture->get_flags() & Texture::FLAG_MIRRORED_REPEAT; + if (is_repeat) { + int mirror_x = 0; + int mirror_y = 0; + if (is_mirrored_repeat) { + mirror_x = (int)(q.x / texture->get_size().width); + mirror_y = (int)(q.y / texture->get_size().height); + } + q.x = Math::fmod(q.x, texture->get_size().width); + q.y = Math::fmod(q.y, texture->get_size().height); + if (mirror_x % 2 == 1) { + q.x = texture->get_size().width - q.x - 1; + } + if (mirror_y % 2 == 1) { + q.y = texture->get_size().height - q.y - 1; + } + } else { + q.x = MIN(q.x, texture->get_size().width - 1); + q.y = MIN(q.y, texture->get_size().height - 1); + } image->lock(); const Color c = image->get_pixel((int)q.x, (int)q.y); image->unlock(); @@ -286,7 +363,7 @@ bool Sprite::_edit_is_selected_on_click(const Point2 &p_point, double p_toleranc return c.a > 0.01; } -Rect2 Sprite::_edit_get_rect() const { +Rect2 Sprite::get_rect() const { if (texture.is_null()) return Rect2(0, 0, 1, 1); @@ -298,13 +375,13 @@ Rect2 Sprite::_edit_get_rect() const { Size2i s; if (region) { - s = region_rect.size; } else { s = texture->get_size(); - s = s / Point2(hframes, vframes); } + s = s / Point2(hframes, vframes); + Point2 ofs = offset; if (centered) ofs -= s / 2; @@ -325,6 +402,15 @@ void Sprite::_validate_property(PropertyInfo &property) const { } } +void Sprite::_changed_callback(Object *p_changed, const char *p_prop) { + + // Changes to the texture need to trigger an update to make + // the editor redraw the sprite with the updated texture. + if (texture.is_valid() && texture.ptr() == p_changed) { + update(); + } +} + void Sprite::_bind_methods() { ClassDB::bind_method(D_METHOD("set_texture", "texture"), &Sprite::set_texture); @@ -363,6 +449,8 @@ void Sprite::_bind_methods() { ClassDB::bind_method(D_METHOD("set_hframes", "hframes"), &Sprite::set_hframes); ClassDB::bind_method(D_METHOD("get_hframes"), &Sprite::get_hframes); + ClassDB::bind_method(D_METHOD("get_rect"), &Sprite::get_rect); + ADD_SIGNAL(MethodInfo("frame_changed")); ADD_SIGNAL(MethodInfo("texture_changed")); @@ -397,3 +485,8 @@ Sprite::Sprite() { vframes = 1; hframes = 1; } + +Sprite::~Sprite() { + if (texture.is_valid()) + texture->remove_change_receptor(this); +} diff --git a/scene/2d/sprite.h b/scene/2d/sprite.h index 7d9b2cf457..609ad8bb34 100644 --- a/scene/2d/sprite.h +++ b/scene/2d/sprite.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef SPRITE_H #define SPRITE_H @@ -63,12 +64,19 @@ protected: virtual void _validate_property(PropertyInfo &property) const; + virtual void _changed_callback(Object *p_changed, const char *p_prop); + public: + virtual Dictionary _edit_get_state() const; + virtual void _edit_set_state(const Dictionary &p_state); + virtual void _edit_set_pivot(const Point2 &p_pivot); virtual Point2 _edit_get_pivot() const; virtual bool _edit_use_pivot() const; virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; + virtual Rect2 _edit_get_rect() const; + virtual bool _edit_use_rect() const; void set_texture(const Ref<Texture> &p_texture); Ref<Texture> get_texture() const; @@ -106,7 +114,10 @@ public: void set_hframes(int p_amount); int get_hframes() const; + Rect2 get_rect() const; + Sprite(); + ~Sprite(); }; #endif // SPRITE_H diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 73fce4be75..60766862cc 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "tile_map.h" #include "io/marshalls.h" @@ -141,16 +142,20 @@ void TileMap::_update_quadrant_transform() { void TileMap::set_tileset(const Ref<TileSet> &p_tileset) { - if (tile_set.is_valid()) + if (tile_set.is_valid()) { tile_set->disconnect("changed", this, "_recreate_quadrants"); + tile_set->remove_change_receptor(this); + } _clear_quadrants(); tile_set = p_tileset; - if (tile_set.is_valid()) + if (tile_set.is_valid()) { tile_set->connect("changed", this, "_recreate_quadrants"); - else + tile_set->add_change_receptor(this); + } else { clear(); + } _recreate_quadrants(); emit_signal("settings_changed"); @@ -260,12 +265,18 @@ void TileMap::_update_dirty_quadrants() { SceneTree *st = SceneTree::get_singleton(); Color debug_collision_color; + Color debug_navigation_color; bool debug_shapes = st && st->is_debugging_collisions_hint(); if (debug_shapes) { debug_collision_color = st->get_debug_collisions_color(); } + bool debug_navigation = st && st->is_debugging_navigation_hint(); + if (debug_navigation) { + debug_navigation_color = st->get_debug_navigation_color(); + } + while (dirty_quadrant_list.first()) { Quadrant &q = *dirty_quadrant_list.first()->self(); @@ -293,6 +304,7 @@ void TileMap::_update_dirty_quadrants() { } q.occluder_instances.clear(); Ref<ShaderMaterial> prev_material; + int prev_z_index; RID prev_canvas_item; RID prev_debug_canvas_item; @@ -313,11 +325,12 @@ void TileMap::_update_dirty_quadrants() { continue; Ref<ShaderMaterial> mat = tile_set->tile_get_material(c.id); + int z_index = tile_set->tile_get_z_index(c.id); RID canvas_item; RID debug_canvas_item; - if (prev_canvas_item == RID() || prev_material != mat) { + if (prev_canvas_item == RID() || prev_material != mat || prev_z_index != z_index) { canvas_item = vs->canvas_item_create(); if (mat.is_valid()) @@ -328,6 +341,7 @@ void TileMap::_update_dirty_quadrants() { xform.set_origin(q.pos); vs->canvas_item_set_transform(canvas_item, xform); vs->canvas_item_set_light_mask(canvas_item, get_light_mask()); + vs->canvas_item_set_z_index(canvas_item, z_index); q.canvas_items.push_back(canvas_item); @@ -336,13 +350,14 @@ void TileMap::_update_dirty_quadrants() { debug_canvas_item = vs->canvas_item_create(); vs->canvas_item_set_parent(debug_canvas_item, canvas_item); vs->canvas_item_set_z_as_relative_to_parent(debug_canvas_item, false); - vs->canvas_item_set_z(debug_canvas_item, VS::CANVAS_ITEM_Z_MAX - 1); + vs->canvas_item_set_z_index(debug_canvas_item, VS::CANVAS_ITEM_Z_MAX - 1); q.canvas_items.push_back(debug_canvas_item); prev_debug_canvas_item = debug_canvas_item; } prev_canvas_item = canvas_item; prev_material = mat; + prev_z_index = z_index; } else { canvas_item = prev_canvas_item; @@ -352,7 +367,7 @@ void TileMap::_update_dirty_quadrants() { } Rect2 r = tile_set->tile_get_region(c.id); - if (tile_set->tile_get_is_autotile(c.id)) { + if (tile_set->tile_get_tile_mode(c.id) == TileSet::AUTO_TILE) { int spacing = tile_set->autotile_get_spacing(c.id); r.size = tile_set->autotile_get_size(c.id); r.position += (r.size + Vector2(spacing, spacing)) * Vector2(c.autotile_coord_x, c.autotile_coord_y); @@ -446,7 +461,7 @@ void TileMap::_update_dirty_quadrants() { for (int i = 0; i < shapes.size(); i++) { Ref<Shape2D> shape = shapes[i].shape; if (shape.is_valid()) { - if (!tile_set->tile_get_is_autotile(c.id) || (shapes[i].autotile_coord.x == c.autotile_coord_x && shapes[i].autotile_coord.y == c.autotile_coord_y)) { + if (tile_set->tile_get_tile_mode(c.id) == TileSet::SINGLE_TILE || (shapes[i].autotile_coord.x == c.autotile_coord_x && shapes[i].autotile_coord.y == c.autotile_coord_y)) { Transform2D xform; xform.set_origin(offset.floor()); @@ -473,7 +488,7 @@ void TileMap::_update_dirty_quadrants() { if (navigation) { Ref<NavigationPolygon> navpoly; Vector2 npoly_ofs; - if (tile_set->tile_get_is_autotile(c.id)) { + if (tile_set->tile_get_tile_mode(c.id) == TileSet::AUTO_TILE) { navpoly = tile_set->autotile_get_navigation_polygon(c.id, Vector2(c.autotile_coord_x, c.autotile_coord_y)); npoly_ofs = Vector2(); } else { @@ -492,11 +507,60 @@ void TileMap::_update_dirty_quadrants() { np.id = pid; np.xform = xform; q.navpoly_ids[E->key()] = np; + + if (debug_navigation) { + RID debug_navigation_item = vs->canvas_item_create(); + vs->canvas_item_set_parent(debug_navigation_item, canvas_item); + vs->canvas_item_set_z_as_relative_to_parent(debug_navigation_item, false); + vs->canvas_item_set_z_index(debug_navigation_item, VS::CANVAS_ITEM_Z_MAX - 2); // Display one below collision debug + + if (debug_navigation_item.is_valid()) { + PoolVector<Vector2> navigation_polygon_vertices = navpoly->get_vertices(); + int vsize = navigation_polygon_vertices.size(); + + if (vsize > 2) { + Vector<Color> colors; + Vector<Vector2> vertices; + vertices.resize(vsize); + colors.resize(vsize); + { + PoolVector<Vector2>::Read vr = navigation_polygon_vertices.read(); + for (int i = 0; i < vsize; i++) { + vertices[i] = vr[i]; + colors[i] = debug_navigation_color; + } + } + + Vector<int> indices; + + for (int i = 0; i < navpoly->get_polygon_count(); i++) { + Vector<int> polygon = navpoly->get_polygon(i); + + for (int j = 2; j < polygon.size(); j++) { + + int kofs[3] = { 0, j - 1, j }; + for (int k = 0; k < 3; k++) { + + int idx = polygon[kofs[k]]; + ERR_FAIL_INDEX(idx, vsize); + indices.push_back(idx); + } + } + } + Transform2D navxform; + navxform.set_origin(offset.floor()); + _fix_cell_transform(navxform, c, npoly_ofs + center_ofs, s); + + vs->canvas_item_set_transform(debug_navigation_item, navxform); + vs->canvas_item_add_triangle_array(debug_navigation_item, indices, vertices, colors); + } + } + } } } Ref<OccluderPolygon2D> occluder; - if (tile_set->tile_get_is_autotile(c.id)) { + if (tile_set->tile_get_tile_mode(c.id) == TileSet::AUTO_TILE) { occluder = tile_set->autotile_get_light_occluder(c.id, Vector2(c.autotile_coord_x, c.autotile_coord_y)); } else { occluder = tile_set->tile_get_light_occluder(c.id); @@ -765,7 +829,7 @@ void TileMap::update_cell_bitmask(int p_x, int p_y) { Map<PosKey, Cell>::Element *E = tile_map.find(p); if (E != NULL) { int id = get_cell(p_x, p_y); - if (tile_set->tile_get_is_autotile(id)) { + if (tile_set->tile_get_tile_mode(id) == TileSet::AUTO_TILE) { uint16_t mask = 0; if (tile_set->autotile_get_bitmask_mode(id) == TileSet::BITMASK_2X2) { if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y - 1)) && tile_set->is_tile_bound(id, get_cell(p_x, p_y - 1)) && tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y))) { @@ -781,13 +845,13 @@ void TileMap::update_cell_bitmask(int p_x, int p_y) { mask |= TileSet::BIND_BOTTOMRIGHT; } } else if (tile_set->autotile_get_bitmask_mode(id) == TileSet::BITMASK_3X3) { - if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y - 1)) && tile_set->is_tile_bound(id, get_cell(p_x, p_y - 1)) && tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y))) { + if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y - 1))) { mask |= TileSet::BIND_TOPLEFT; } if (tile_set->is_tile_bound(id, get_cell(p_x, p_y - 1))) { mask |= TileSet::BIND_TOP; } - if (tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y - 1)) && tile_set->is_tile_bound(id, get_cell(p_x, p_y - 1)) && tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y))) { + if (tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y - 1))) { mask |= TileSet::BIND_TOPRIGHT; } if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y))) { @@ -797,13 +861,13 @@ void TileMap::update_cell_bitmask(int p_x, int p_y) { if (tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y))) { mask |= TileSet::BIND_RIGHT; } - if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y + 1)) && tile_set->is_tile_bound(id, get_cell(p_x, p_y + 1)) && tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y))) { + if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y + 1))) { mask |= TileSet::BIND_BOTTOMLEFT; } if (tile_set->is_tile_bound(id, get_cell(p_x, p_y + 1))) { mask |= TileSet::BIND_BOTTOM; } - if (tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y + 1)) && tile_set->is_tile_bound(id, get_cell(p_x, p_y + 1)) && tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y))) { + if (tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y + 1))) { mask |= TileSet::BIND_BOTTOMRIGHT; } } @@ -829,6 +893,16 @@ void TileMap::update_dirty_bitmask() { } } +void TileMap::fix_invalid_tiles() { + + for (Map<PosKey, Cell>::Element *E = tile_map.front(); E; E = E->next()) { + + if (!tile_set->has_tile(get_cell(E->key().x, E->key().y))) { + set_cell(E->key().x, E->key().y, INVALID_CELL); + } + } +} + int TileMap::get_cell(int p_x, int p_y) const { PosKey pk(p_x, p_y); @@ -998,8 +1072,8 @@ void TileMap::_set_tile_data(const PoolVector<int> &p_data) { bool flip_v = v & (1 << 30); bool transpose = v & (1 << 31); v &= (1 << 29) - 1; - int16_t coord_x; - int16_t coord_y; + int16_t coord_x = 0; + int16_t coord_y = 0; if (format == FORMAT_2) { coord_x = decode_uint16(&local[8]); coord_y = decode_uint16(&local[10]); @@ -1049,6 +1123,10 @@ Rect2 TileMap::_edit_get_rect() const { return rect_cache; } +bool TileMap::_edit_use_rect() const { + return true; +} + void TileMap::set_collision_layer(uint32_t p_layer) { collision_layer = p_layer; @@ -1311,10 +1389,10 @@ bool TileMap::_get(const StringName &p_name, Variant &r_ret) const { void TileMap::_get_property_list(List<PropertyInfo> *p_list) const { - PropertyInfo p(Variant::INT, "format", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); + PropertyInfo p(Variant::INT, "format", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL); p_list->push_back(p); - p = PropertyInfo(Variant::OBJECT, "tile_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); + p = PropertyInfo(Variant::OBJECT, "tile_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL); p_list->push_back(p); } @@ -1514,6 +1592,7 @@ void TileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("is_cell_y_flipped", "x", "y"), &TileMap::is_cell_y_flipped); ClassDB::bind_method(D_METHOD("is_cell_transposed", "x", "y"), &TileMap::is_cell_transposed); + ClassDB::bind_method(D_METHOD("fix_invalid_tiles"), &TileMap::fix_invalid_tiles); ClassDB::bind_method(D_METHOD("clear"), &TileMap::clear); ClassDB::bind_method(D_METHOD("get_used_cells"), &TileMap::get_used_cells); @@ -1572,6 +1651,12 @@ void TileMap::_bind_methods() { BIND_ENUM_CONSTANT(TILE_ORIGIN_BOTTOM_LEFT); } +void TileMap::_changed_callback(Object *p_changed, const char *p_prop) { + if (tile_set.is_valid() && tile_set.ptr() == p_changed) { + emit_signal("settings_changed"); + } +} + TileMap::TileMap() { rect_cache_dirty = true; @@ -1600,5 +1685,8 @@ TileMap::TileMap() { TileMap::~TileMap() { + if (tile_set.is_valid()) + tile_set->remove_change_receptor(this); + clear(); } diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index 21258f27c9..07947004b3 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef TILE_MAP_H #define TILE_MAP_H @@ -216,6 +217,8 @@ protected: void _notification(int p_what); static void _bind_methods(); + virtual void _changed_callback(Object *p_changed, const char *p_prop); + public: enum { INVALID_CELL = -1 @@ -242,6 +245,7 @@ public: int get_cellv(const Vector2 &p_pos) const; Rect2 _edit_get_rect() const; + virtual bool _edit_use_rect() const; void make_bitmask_area_dirty(const Vector2 &p_pos); void update_bitmask_area(const Vector2 &p_pos); @@ -307,6 +311,7 @@ public: void set_clip_uv(bool p_enable); bool get_clip_uv() const; + void fix_invalid_tiles(); void clear(); TileMap(); diff --git a/scene/2d/visibility_notifier_2d.cpp b/scene/2d/visibility_notifier_2d.cpp index cc6ee1a426..ddca97e60a 100644 --- a/scene/2d/visibility_notifier_2d.cpp +++ b/scene/2d/visibility_notifier_2d.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "visibility_notifier_2d.h" #include "engine.h" @@ -88,6 +89,10 @@ Rect2 VisibilityNotifier2D::_edit_get_rect() const { return rect; } +bool VisibilityNotifier2D::_edit_use_rect() const { + return true; +} + Rect2 VisibilityNotifier2D::get_rect() const { return rect; @@ -218,7 +223,7 @@ void VisibilityEnabler2D::_find_nodes(Node *p_node) { if (add) { - p_node->connect(SceneStringNames::get_singleton()->tree_exited, this, "_node_removed", varray(p_node), CONNECT_ONESHOT); + p_node->connect(SceneStringNames::get_singleton()->tree_exiting, this, "_node_removed", varray(p_node), CONNECT_ONESHOT); nodes[p_node] = meta; _change_node_state(p_node, false); } @@ -261,7 +266,7 @@ void VisibilityEnabler2D::_notification(int p_what) { if (!visible) _change_node_state(E->key(), true); - E->key()->disconnect(SceneStringNames::get_singleton()->tree_exited, this, "_node_removed"); + E->key()->disconnect(SceneStringNames::get_singleton()->tree_exiting, this, "_node_removed"); } nodes.clear(); diff --git a/scene/2d/visibility_notifier_2d.h b/scene/2d/visibility_notifier_2d.h index b2fbb39dfc..c4e12dfa22 100644 --- a/scene/2d/visibility_notifier_2d.h +++ b/scene/2d/visibility_notifier_2d.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef VISIBILITY_NOTIFIER_2D_H #define VISIBILITY_NOTIFIER_2D_H @@ -55,6 +56,7 @@ protected: public: virtual Rect2 _edit_get_rect() const; + virtual bool _edit_use_rect() const; void set_rect(const Rect2 &p_rect); Rect2 get_rect() const; diff --git a/scene/2d/y_sort.cpp b/scene/2d/y_sort.cpp index 85c068b212..d3997c13c3 100644 --- a/scene/2d/y_sort.cpp +++ b/scene/2d/y_sort.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "y_sort.h" void YSort::set_sort_enabled(bool p_enabled) { diff --git a/scene/2d/y_sort.h b/scene/2d/y_sort.h index 63796f8719..5ff12aa25a 100644 --- a/scene/2d/y_sort.h +++ b/scene/2d/y_sort.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef Y_SORT_H #define Y_SORT_H diff --git a/scene/3d/area.cpp b/scene/3d/area.cpp index ad8bf7141a..21f471039f 100644 --- a/scene/3d/area.cpp +++ b/scene/3d/area.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "area.h" #include "scene/scene_string_names.h" #include "servers/audio_server.h" @@ -168,7 +169,7 @@ void Area::_body_inout(int p_status, const RID &p_body, int p_instance, int p_bo E->get().in_tree = node && node->is_inside_tree(); if (node) { node->connect(SceneStringNames::get_singleton()->tree_entered, this, SceneStringNames::get_singleton()->_body_enter_tree, make_binds(objid)); - node->connect(SceneStringNames::get_singleton()->tree_exited, this, SceneStringNames::get_singleton()->_body_exit_tree, make_binds(objid)); + node->connect(SceneStringNames::get_singleton()->tree_exiting, this, SceneStringNames::get_singleton()->_body_exit_tree, make_binds(objid)); if (E->get().in_tree) { emit_signal(SceneStringNames::get_singleton()->body_entered, node); } @@ -195,7 +196,7 @@ void Area::_body_inout(int p_status, const RID &p_body, int p_instance, int p_bo if (node) { node->disconnect(SceneStringNames::get_singleton()->tree_entered, this, SceneStringNames::get_singleton()->_body_enter_tree); - node->disconnect(SceneStringNames::get_singleton()->tree_exited, this, SceneStringNames::get_singleton()->_body_exit_tree); + node->disconnect(SceneStringNames::get_singleton()->tree_exiting, this, SceneStringNames::get_singleton()->_body_exit_tree); if (E->get().in_tree) emit_signal(SceneStringNames::get_singleton()->body_exited, obj); } @@ -245,7 +246,7 @@ void Area::_clear_monitoring() { emit_signal(SceneStringNames::get_singleton()->body_exited, obj); node->disconnect(SceneStringNames::get_singleton()->tree_entered, this, SceneStringNames::get_singleton()->_body_enter_tree); - node->disconnect(SceneStringNames::get_singleton()->tree_exited, this, SceneStringNames::get_singleton()->_body_exit_tree); + node->disconnect(SceneStringNames::get_singleton()->tree_exiting, this, SceneStringNames::get_singleton()->_body_exit_tree); } } @@ -275,7 +276,7 @@ void Area::_clear_monitoring() { emit_signal(SceneStringNames::get_singleton()->area_exited, obj); node->disconnect(SceneStringNames::get_singleton()->tree_entered, this, SceneStringNames::get_singleton()->_area_enter_tree); - node->disconnect(SceneStringNames::get_singleton()->tree_exited, this, SceneStringNames::get_singleton()->_area_exit_tree); + node->disconnect(SceneStringNames::get_singleton()->tree_exiting, this, SceneStringNames::get_singleton()->_area_exit_tree); } } } @@ -365,7 +366,7 @@ void Area::_area_inout(int p_status, const RID &p_area, int p_instance, int p_ar E->get().in_tree = node && node->is_inside_tree(); if (node) { node->connect(SceneStringNames::get_singleton()->tree_entered, this, SceneStringNames::get_singleton()->_area_enter_tree, make_binds(objid)); - node->connect(SceneStringNames::get_singleton()->tree_exited, this, SceneStringNames::get_singleton()->_area_exit_tree, make_binds(objid)); + node->connect(SceneStringNames::get_singleton()->tree_exiting, this, SceneStringNames::get_singleton()->_area_exit_tree, make_binds(objid)); if (E->get().in_tree) { emit_signal(SceneStringNames::get_singleton()->area_entered, node); } @@ -392,7 +393,7 @@ void Area::_area_inout(int p_status, const RID &p_area, int p_instance, int p_ar if (node) { node->disconnect(SceneStringNames::get_singleton()->tree_entered, this, SceneStringNames::get_singleton()->_area_enter_tree); - node->disconnect(SceneStringNames::get_singleton()->tree_exited, this, SceneStringNames::get_singleton()->_area_exit_tree); + node->disconnect(SceneStringNames::get_singleton()->tree_exiting, this, SceneStringNames::get_singleton()->_area_exit_tree); if (E->get().in_tree) { emit_signal(SceneStringNames::get_singleton()->area_exited, obj); } diff --git a/scene/3d/area.h b/scene/3d/area.h index fa01a783f9..e49b7e493b 100644 --- a/scene/3d/area.h +++ b/scene/3d/area.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef AREA_H #define AREA_H diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp index 6c102e4027..c2a50ec7bb 100644 --- a/scene/3d/audio_stream_player_3d.cpp +++ b/scene/3d/audio_stream_player_3d.cpp @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* audio_stream_player_3d.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 "audio_stream_player_3d.h" #include "engine.h" #include "scene/3d/area.h" @@ -27,18 +57,18 @@ void AudioStreamPlayer3D::_mix_audio() { //mix if (output_count > 0 || out_of_range_mode == OUT_OF_RANGE_MIX) { - float pitch_scale = 0.0; + float output_pitch_scale = 0.0; if (output_count) { //used for doppler, not realistic but good enough for (int i = 0; i < output_count; i++) { - pitch_scale += outputs[i].pitch_scale; + output_pitch_scale += outputs[i].pitch_scale; } - pitch_scale /= float(output_count); + output_pitch_scale /= float(output_count); } else { - pitch_scale = 1.0; + output_pitch_scale = 1.0; } - stream_playback->mix(buffer, pitch_scale, buffer_size); + stream_playback->mix(buffer, pitch_scale * output_pitch_scale, buffer_size); } //write all outputs @@ -290,7 +320,7 @@ void AudioStreamPlayer3D::_notification(int p_what) { total_max = MAX(total_max, cam_area_pos.length()); } if (total_max > max_distance) { - continue; //cant hear this sound in this camera + continue; //can't hear this sound in this camera } } @@ -506,14 +536,14 @@ void AudioStreamPlayer3D::_notification(int p_what) { setseek = setplay; active = true; setplay = -1; - //do not update, this makes it easier to animate (will shut off otherise) + //do not update, this makes it easier to animate (will shut off otherwise) ///_change_notify("playing"); //update property in editor } //stop playing if no longer active if (!active) { set_physics_process_internal(false); - //do not update, this makes it easier to animate (will shut off otherise) + //do not update, this makes it easier to animate (will shut off otherwise) //_change_notify("playing"); //update property in editor emit_signal("finished"); } @@ -577,6 +607,13 @@ float AudioStreamPlayer3D::get_max_db() const { return max_db; } +void AudioStreamPlayer3D::set_pitch_scale(float p_pitch_scale) { + pitch_scale = p_pitch_scale; +} +float AudioStreamPlayer3D::get_pitch_scale() const { + return pitch_scale; +} + void AudioStreamPlayer3D::play(float p_from_pos) { if (stream_playback.is_valid()) { @@ -802,6 +839,9 @@ void AudioStreamPlayer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_max_db", "max_db"), &AudioStreamPlayer3D::set_max_db); ClassDB::bind_method(D_METHOD("get_max_db"), &AudioStreamPlayer3D::get_max_db); + ClassDB::bind_method(D_METHOD("set_pitch_scale", "pitch_scale"), &AudioStreamPlayer3D::set_pitch_scale); + ClassDB::bind_method(D_METHOD("get_pitch_scale"), &AudioStreamPlayer3D::get_pitch_scale); + ClassDB::bind_method(D_METHOD("play", "from_position"), &AudioStreamPlayer3D::play, DEFVAL(0.0)); ClassDB::bind_method(D_METHOD("seek", "to_position"), &AudioStreamPlayer3D::seek); ClassDB::bind_method(D_METHOD("stop"), &AudioStreamPlayer3D::stop); @@ -855,6 +895,7 @@ void AudioStreamPlayer3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::REAL, "unit_db", PROPERTY_HINT_RANGE, "-80,80"), "set_unit_db", "get_unit_db"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "unit_size", PROPERTY_HINT_RANGE, "0.1,100,0.1"), "set_unit_size", "get_unit_size"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_db", PROPERTY_HINT_RANGE, "-24,6"), "set_max_db", "get_max_db"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "pitch_scale", PROPERTY_HINT_RANGE, "0.01,32,0.01"), "set_pitch_scale", "get_pitch_scale"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_playing", "is_playing"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "is_autoplay_enabled"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_distance", PROPERTY_HINT_RANGE, "0,65536,1"), "set_max_distance", "get_max_distance"); @@ -891,6 +932,7 @@ AudioStreamPlayer3D::AudioStreamPlayer3D() { unit_size = 1; attenuation_model = ATTENUATION_INVERSE_DISTANCE; max_db = 3; + pitch_scale = 1.0; autoplay = false; setseek = -1; active = false; diff --git a/scene/3d/audio_stream_player_3d.h b/scene/3d/audio_stream_player_3d.h index 5982d7a3ac..1fcb83cf21 100644 --- a/scene/3d/audio_stream_player_3d.h +++ b/scene/3d/audio_stream_player_3d.h @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* audio_stream_player_3d.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 AUDIO_STREAM_PLAYER_3D_H #define AUDIO_STREAM_PLAYER_3D_H @@ -76,6 +106,7 @@ private: float unit_db; float unit_size; float max_db; + float pitch_scale; bool autoplay; StringName bus; @@ -123,6 +154,9 @@ public: void set_max_db(float p_boost); float get_max_db() const; + void set_pitch_scale(float p_pitch_scale); + float get_pitch_scale() const; + void play(float p_from_pos = 0.0); void seek(float p_seconds); void stop(); diff --git a/scene/3d/baked_lightmap.cpp b/scene/3d/baked_lightmap.cpp index 8c282a31b8..204aaef7ec 100644 --- a/scene/3d/baked_lightmap.cpp +++ b/scene/3d/baked_lightmap.cpp @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* baked_lightmap.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 "baked_lightmap.h" #include "io/resource_saver.h" #include "os/dir_access.h" @@ -140,11 +170,11 @@ void BakedLightmapData::_bind_methods() { ClassDB::bind_method(D_METHOD("clear_users"), &BakedLightmapData::clear_users); ADD_PROPERTY(PropertyInfo(Variant::AABB, "bounds", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_bounds", "get_bounds"); - ADD_PROPERTY(PropertyInfo(Variant::POOL_BYTE_ARRAY, "octree", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_octree", "get_octree"); ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "cell_space_transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_cell_space_transform", "get_cell_space_transform"); ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_subdiv", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_cell_subdiv", "get_cell_subdiv"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "energy", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_energy", "get_energy"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "user_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_user_data", "_get_user_data"); + ADD_PROPERTY(PropertyInfo(Variant::POOL_BYTE_ARRAY, "octree", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_octree", "get_octree"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "user_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_user_data", "_get_user_data"); } BakedLightmapData::BakedLightmapData() { @@ -286,7 +316,7 @@ bool BakedLightmap::_bake_time(void *ud, float p_secs, float p_progress) { int mins_left = p_secs / 60; int secs_left = Math::fmod(p_secs, 60.0f); int percent = p_progress * 100; - bool abort = bake_step_function(btd->pass + percent, btd->text + " " + itos(percent) + "% (Time Left: " + itos(mins_left) + ":" + itos(secs_left) + "s)"); + bool abort = bake_step_function(btd->pass + percent, btd->text + " " + vformat(RTR("%d%%"), percent) + " " + vformat(RTR("(Time Left: %d:%02d s)"), mins_left, secs_left)); btd->last_step = time; if (abort) return true; @@ -754,7 +784,7 @@ void BakedLightmap::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::REAL, "capture_cell_size", PROPERTY_HINT_RANGE, "0.01,64,0.01"), "set_capture_cell_size", "get_capture_cell_size"); ADD_GROUP("Data", ""); ADD_PROPERTY(PropertyInfo(Variant::STRING, "image_path", PROPERTY_HINT_DIR), "set_image_path", "get_image_path"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "light_data", PROPERTY_HINT_RESOURCE_TYPE, "BakedIndirectLightData"), "set_light_data", "get_light_data"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "light_data", PROPERTY_HINT_RESOURCE_TYPE, "BakedLightmapData"), "set_light_data", "get_light_data"); BIND_ENUM_CONSTANT(BAKE_QUALITY_LOW); BIND_ENUM_CONSTANT(BAKE_QUALITY_MEDIUM); diff --git a/scene/3d/baked_lightmap.h b/scene/3d/baked_lightmap.h index 9b53e41d73..60b62f74eb 100644 --- a/scene/3d/baked_lightmap.h +++ b/scene/3d/baked_lightmap.h @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* baked_lightmap.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 BAKED_INDIRECT_LIGHT_H #define BAKED_INDIRECT_LIGHT_H diff --git a/scene/3d/bone_attachment.cpp b/scene/3d/bone_attachment.cpp index 9e3b15461b..a875b65c22 100644 --- a/scene/3d/bone_attachment.cpp +++ b/scene/3d/bone_attachment.cpp @@ -27,45 +27,30 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "bone_attachment.h" - -bool BoneAttachment::_get(const StringName &p_name, Variant &r_ret) const { - if (String(p_name) == "bone_name") { - - r_ret = get_bone_name(); - return true; - } +#include "bone_attachment.h" - return false; -} -bool BoneAttachment::_set(const StringName &p_name, const Variant &p_value) { +void BoneAttachment::_validate_property(PropertyInfo &property) const { - if (String(p_name) == "bone_name") { + if (property.name == "bone_name") { + Skeleton *parent = Object::cast_to<Skeleton>(get_parent()); - set_bone_name(p_value); - return true; - } + if (parent) { - return false; -} -void BoneAttachment::_get_property_list(List<PropertyInfo> *p_list) const { - - Skeleton *parent = Object::cast_to<Skeleton>(get_parent()); + String names; + for (int i = 0; i < parent->get_bone_count(); i++) { + if (i > 0) + names += ","; + names += parent->get_bone_name(i); + } - if (parent) { + property.hint = PROPERTY_HINT_ENUM; + property.hint_string = names; + } else { - String names; - for (int i = 0; i < parent->get_bone_count(); i++) { - if (i > 0) - names += ","; - names += parent->get_bone_name(i); + property.hint = PROPERTY_HINT_NONE; + property.hint_string = ""; } - - p_list->push_back(PropertyInfo(Variant::STRING, "bone_name", PROPERTY_HINT_ENUM, names)); - } else { - - p_list->push_back(PropertyInfo(Variant::STRING, "bone_name")); } } @@ -137,4 +122,6 @@ BoneAttachment::BoneAttachment() { void BoneAttachment::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bone_name", "bone_name"), &BoneAttachment::set_bone_name); ClassDB::bind_method(D_METHOD("get_bone_name"), &BoneAttachment::get_bone_name); + + ADD_PROPERTY(PropertyInfo(Variant::STRING, "bone_name"), "set_bone_name", "get_bone_name"); } diff --git a/scene/3d/bone_attachment.h b/scene/3d/bone_attachment.h index 57bc71a51f..81a225015e 100644 --- a/scene/3d/bone_attachment.h +++ b/scene/3d/bone_attachment.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef BONE_ATTACHMENT_H #define BONE_ATTACHMENT_H @@ -43,9 +44,7 @@ class BoneAttachment : public Spatial { void _check_unbind(); protected: - bool _get(const StringName &p_name, Variant &r_ret) const; - bool _set(const StringName &p_name, const Variant &p_value); - void _get_property_list(List<PropertyInfo> *p_list) const; + virtual void _validate_property(PropertyInfo &property) const; void _notification(int p_what); static void _bind_methods(); diff --git a/scene/3d/camera.cpp b/scene/3d/camera.cpp index 840783c399..9de189c158 100644 --- a/scene/3d/camera.cpp +++ b/scene/3d/camera.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "camera.h" #include "camera_matrix.h" @@ -200,7 +201,7 @@ void Camera::make_current() { //get_scene()->call_group(SceneMainLoop::GROUP_CALL_REALTIME,camera_group,"_camera_make_current",this); } -void Camera::clear_current() { +void Camera::clear_current(bool p_enable_next) { current = false; if (!is_inside_tree()) @@ -208,7 +209,10 @@ void Camera::clear_current() { if (get_viewport()->get_camera() == this) { get_viewport()->_camera_set(NULL); - get_viewport()->_camera_make_next_current(this); + + if (p_enable_next) { + get_viewport()->_camera_make_next_current(this); + } } } @@ -438,7 +442,7 @@ void Camera::_bind_methods() { ClassDB::bind_method(D_METHOD("set_perspective", "fov", "z_near", "z_far"), &Camera::set_perspective); ClassDB::bind_method(D_METHOD("set_orthogonal", "size", "z_near", "z_far"), &Camera::set_orthogonal); ClassDB::bind_method(D_METHOD("make_current"), &Camera::make_current); - ClassDB::bind_method(D_METHOD("clear_current"), &Camera::clear_current); + ClassDB::bind_method(D_METHOD("clear_current", "enable_next"), &Camera::clear_current, DEFVAL(true)); ClassDB::bind_method(D_METHOD("set_current"), &Camera::set_current); ClassDB::bind_method(D_METHOD("is_current"), &Camera::is_current); ClassDB::bind_method(D_METHOD("get_camera_transform"), &Camera::get_camera_transform); diff --git a/scene/3d/camera.h b/scene/3d/camera.h index c24286ecc5..1b506e0c4f 100644 --- a/scene/3d/camera.h +++ b/scene/3d/camera.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef CAMERA_H #define CAMERA_H @@ -96,7 +97,7 @@ protected: void _update_camera_mode(); void _notification(int p_what); - virtual void _validate_property(PropertyInfo &property) const; + virtual void _validate_property(PropertyInfo &p_property) const; static void _bind_methods(); @@ -112,7 +113,7 @@ public: void set_projection(Camera::Projection p_mode); void make_current(); - void clear_current(); + void clear_current(bool p_enable_next = true); void set_current(bool p_current); bool is_current() const; @@ -131,9 +132,9 @@ public: virtual Transform get_camera_transform() const; - Vector3 project_ray_normal(const Point2 &p_pos) const; + virtual Vector3 project_ray_normal(const Point2 &p_pos) const; virtual Vector3 project_ray_origin(const Point2 &p_pos) const; - Vector3 project_local_ray_normal(const Point2 &p_pos) const; + virtual Vector3 project_local_ray_normal(const Point2 &p_pos) const; virtual Point2 unproject_position(const Vector3 &p_pos) const; bool is_position_behind(const Vector3 &p_pos) const; virtual Vector3 project_position(const Point2 &p_point) const; diff --git a/scene/3d/collision_object.cpp b/scene/3d/collision_object.cpp index e591b17aa6..1d5d1b2afe 100644 --- a/scene/3d/collision_object.cpp +++ b/scene/3d/collision_object.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "collision_object.h" #include "scene/scene_string_names.h" @@ -364,6 +365,20 @@ bool CollisionObject::get_capture_input_on_drag() const { return capture_input_on_drag; } +String CollisionObject::get_configuration_warning() const { + + String warning = Spatial::get_configuration_warning(); + + if (shapes.empty()) { + if (warning == String()) { + warning += "\n"; + } + warning += TTR("This node has no shape, so it can't collide or interact with other objects.\nConsider adding a CollisionShape or CollisionPolygon as a child to define its shape."); + } + + return warning; +} + CollisionObject::CollisionObject() { capture_input_on_drag = false; diff --git a/scene/3d/collision_object.h b/scene/3d/collision_object.h index c1fed559c5..f31d65e411 100644 --- a/scene/3d/collision_object.h +++ b/scene/3d/collision_object.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef COLLISION_OBJECT_H #define COLLISION_OBJECT_H @@ -108,6 +109,8 @@ public: _FORCE_INLINE_ RID get_rid() const { return rid; } + virtual String get_configuration_warning() const; + CollisionObject(); ~CollisionObject(); }; diff --git a/scene/3d/collision_polygon.cpp b/scene/3d/collision_polygon.cpp index 7d894a0544..379dd21c39 100644 --- a/scene/3d/collision_polygon.cpp +++ b/scene/3d/collision_polygon.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "collision_polygon.h" #include "collision_object.h" @@ -72,6 +73,14 @@ void CollisionPolygon::_build_polygon() { } } +void CollisionPolygon::_update_in_shape_owner(bool p_xform_only) { + + parent->shape_owner_set_transform(owner_id, get_transform()); + if (p_xform_only) + return; + parent->shape_owner_set_disabled(owner_id, disabled); +} + void CollisionPolygon::_notification(int p_what) { switch (p_what) { @@ -81,14 +90,20 @@ void CollisionPolygon::_notification(int p_what) { if (parent) { owner_id = parent->create_shape_owner(this); _build_polygon(); - parent->shape_owner_set_transform(owner_id, get_transform()); - parent->shape_owner_set_disabled(owner_id, disabled); + _update_in_shape_owner(); + } + } break; + case NOTIFICATION_ENTER_TREE: { + + if (parent) { + _update_in_shape_owner(); } + } break; case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { if (parent) { - parent->shape_owner_set_transform(owner_id, get_transform()); + _update_in_shape_owner(true); } } break; @@ -158,6 +173,9 @@ String CollisionPolygon::get_configuration_warning() const { return String(); } +bool CollisionPolygon::_is_editable_3d_polygon() const { + return true; +} void CollisionPolygon::_bind_methods() { ClassDB::bind_method(D_METHOD("set_depth", "depth"), &CollisionPolygon::set_depth); @@ -169,6 +187,8 @@ void CollisionPolygon::_bind_methods() { ClassDB::bind_method(D_METHOD("set_disabled", "disabled"), &CollisionPolygon::set_disabled); ClassDB::bind_method(D_METHOD("is_disabled"), &CollisionPolygon::is_disabled); + ClassDB::bind_method(D_METHOD("_is_editable_3d_polygon"), &CollisionPolygon::_is_editable_3d_polygon); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "depth"), "set_depth", "get_depth"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled"); ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "polygon"), "set_polygon", "get_polygon"); diff --git a/scene/3d/collision_polygon.h b/scene/3d/collision_polygon.h index b979057da1..f1f137c9c5 100644 --- a/scene/3d/collision_polygon.h +++ b/scene/3d/collision_polygon.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef COLLISION_POLYGON_H #define COLLISION_POLYGON_H @@ -50,6 +51,10 @@ protected: void _build_polygon(); + void _update_in_shape_owner(bool p_xform_only = false); + + bool _is_editable_3d_polygon() const; + protected: void _notification(int p_what); static void _bind_methods(); diff --git a/scene/3d/collision_shape.cpp b/scene/3d/collision_shape.cpp index b5f3a09c09..943f4158f7 100644 --- a/scene/3d/collision_shape.cpp +++ b/scene/3d/collision_shape.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "collision_shape.h" #include "scene/resources/box_shape.h" #include "scene/resources/capsule_shape.h" @@ -63,6 +64,14 @@ void CollisionShape::make_convex_from_brothers() { } } +void CollisionShape::_update_in_shape_owner(bool p_xform_only) { + + parent->shape_owner_set_transform(owner_id, get_transform()); + if (p_xform_only) + return; + parent->shape_owner_set_disabled(owner_id, disabled); +} + void CollisionShape::_notification(int p_what) { switch (p_what) { @@ -74,19 +83,20 @@ void CollisionShape::_notification(int p_what) { if (shape.is_valid()) { parent->shape_owner_add_shape(owner_id, shape); } - parent->shape_owner_set_transform(owner_id, get_transform()); - parent->shape_owner_set_disabled(owner_id, disabled); + _update_in_shape_owner(); } } break; case NOTIFICATION_ENTER_TREE: { + if (parent) { + _update_in_shape_owner(); + } if (get_tree()->is_debugging_collisions_hint()) { _create_debug_shape(); } - } break; case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { if (parent) { - parent->shape_owner_set_transform(owner_id, get_transform()); + _update_in_shape_owner(true); } } break; case NOTIFICATION_UNPARENTED: { diff --git a/scene/3d/collision_shape.h b/scene/3d/collision_shape.h index 4728d3d5c1..c9c91a5824 100644 --- a/scene/3d/collision_shape.h +++ b/scene/3d/collision_shape.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef COLLISION_SHAPE_H #define COLLISION_SHAPE_H @@ -50,6 +51,8 @@ class CollisionShape : public Spatial { void _create_debug_shape(); + void _update_in_shape_owner(bool p_xform_only = false); + protected: void _notification(int p_what); static void _bind_methods(); diff --git a/scene/3d/gi_probe.cpp b/scene/3d/gi_probe.cpp index 21e8a6e17a..4ad2eb60ee 100644 --- a/scene/3d/gi_probe.cpp +++ b/scene/3d/gi_probe.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "gi_probe.h" #include "mesh_instance.h" @@ -534,7 +535,7 @@ void GIProbe::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::REAL, "normal_bias", PROPERTY_HINT_RANGE, "0,4,0.001"), "set_normal_bias", "get_normal_bias"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "interior"), "set_interior", "is_interior"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "compress"), "set_compress", "is_compressed"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "data", PROPERTY_HINT_RESOURCE_TYPE, "GIProbeData"), "set_probe_data", "get_probe_data"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "data", PROPERTY_HINT_RESOURCE_TYPE, "GIProbeData", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE), "set_probe_data", "get_probe_data"); BIND_ENUM_CONSTANT(SUBDIV_64); BIND_ENUM_CONSTANT(SUBDIV_128); diff --git a/scene/3d/gi_probe.h b/scene/3d/gi_probe.h index af3c854f1f..af4b646590 100644 --- a/scene/3d/gi_probe.h +++ b/scene/3d/gi_probe.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef GIPROBE_H #define GIPROBE_H diff --git a/scene/3d/immediate_geometry.cpp b/scene/3d/immediate_geometry.cpp index 8d8c1b65fc..14a0b0505d 100644 --- a/scene/3d/immediate_geometry.cpp +++ b/scene/3d/immediate_geometry.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "immediate_geometry.h" void ImmediateGeometry::begin(Mesh::PrimitiveType p_primitive, const Ref<Texture> &p_texture) { diff --git a/scene/3d/immediate_geometry.h b/scene/3d/immediate_geometry.h index abcabf2c34..f7201b4bba 100644 --- a/scene/3d/immediate_geometry.h +++ b/scene/3d/immediate_geometry.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef IMMEDIATE_GEOMETRY_H #define IMMEDIATE_GEOMETRY_H diff --git a/scene/3d/interpolated_camera.cpp b/scene/3d/interpolated_camera.cpp index 03f82e0e44..ffa283f634 100644 --- a/scene/3d/interpolated_camera.cpp +++ b/scene/3d/interpolated_camera.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "interpolated_camera.h" #include "engine.h" @@ -37,10 +38,10 @@ void InterpolatedCamera::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: { if (Engine::get_singleton()->is_editor_hint() && enabled) - set_physics_process(false); + set_process_internal(false); } break; - case NOTIFICATION_PROCESS: { + case NOTIFICATION_INTERNAL_PROCESS: { if (!enabled) break; @@ -110,9 +111,9 @@ void InterpolatedCamera::set_interpolation_enabled(bool p_enable) { if (p_enable) { if (is_inside_tree() && Engine::get_singleton()->is_editor_hint()) return; - set_process(true); + set_process_internal(true); } else - set_process(false); + set_process_internal(false); } bool InterpolatedCamera::is_interpolation_enabled() const { diff --git a/scene/3d/interpolated_camera.h b/scene/3d/interpolated_camera.h index d74b13c214..b025112295 100644 --- a/scene/3d/interpolated_camera.h +++ b/scene/3d/interpolated_camera.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef INTERPOLATED_CAMERA_H #define INTERPOLATED_CAMERA_H diff --git a/scene/3d/light.cpp b/scene/3d/light.cpp index 04c1f9e3e8..240bd631a1 100644 --- a/scene/3d/light.cpp +++ b/scene/3d/light.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "light.h" #include "engine.h" diff --git a/scene/3d/light.h b/scene/3d/light.h index 2639e47217..b35b397ced 100644 --- a/scene/3d/light.h +++ b/scene/3d/light.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef LIGHT_H #define LIGHT_H diff --git a/scene/3d/listener.cpp b/scene/3d/listener.cpp index 752a2059f5..439c1f8c45 100644 --- a/scene/3d/listener.cpp +++ b/scene/3d/listener.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "listener.h" #include "scene/resources/mesh.h" diff --git a/scene/3d/listener.h b/scene/3d/listener.h index db8dca8ce2..8047971ebd 100644 --- a/scene/3d/listener.h +++ b/scene/3d/listener.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef LISTENER_H #define LISTENER_H diff --git a/scene/3d/mesh_instance.cpp b/scene/3d/mesh_instance.cpp index cffcbd9a41..80bae911d4 100644 --- a/scene/3d/mesh_instance.cpp +++ b/scene/3d/mesh_instance.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "mesh_instance.h" #include "collision_shape.h" diff --git a/scene/3d/mesh_instance.h b/scene/3d/mesh_instance.h index 74041a46bc..5d359cd4d5 100644 --- a/scene/3d/mesh_instance.h +++ b/scene/3d/mesh_instance.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef MESH_INSTANCE_H #define MESH_INSTANCE_H diff --git a/scene/3d/multimesh_instance.cpp b/scene/3d/multimesh_instance.cpp index a6a8d434fa..4cbd1df64a 100644 --- a/scene/3d/multimesh_instance.cpp +++ b/scene/3d/multimesh_instance.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "multimesh_instance.h" void MultiMeshInstance::_bind_methods() { diff --git a/scene/3d/multimesh_instance.h b/scene/3d/multimesh_instance.h index 312748b6f0..1cd077a0a5 100644 --- a/scene/3d/multimesh_instance.h +++ b/scene/3d/multimesh_instance.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef MULTIMESH_INSTANCE_H #define MULTIMESH_INSTANCE_H diff --git a/scene/3d/navigation.cpp b/scene/3d/navigation.cpp index 5d41fe576b..77bf703706 100644 --- a/scene/3d/navigation.cpp +++ b/scene/3d/navigation.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "navigation.h" void Navigation::_navmesh_link(int p_id) { @@ -34,6 +35,7 @@ void Navigation::_navmesh_link(int p_id) { ERR_FAIL_COND(!navmesh_map.has(p_id)); NavMesh &nm = navmesh_map[p_id]; ERR_FAIL_COND(nm.linked); + ERR_FAIL_COND(nm.navmesh.is_null()); PoolVector<Vector3> vertices = nm.navmesh->get_vertices(); int len = vertices.size(); diff --git a/scene/3d/navigation.h b/scene/3d/navigation.h index a5a4deebcd..5a501039c8 100644 --- a/scene/3d/navigation.h +++ b/scene/3d/navigation.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef NAVIGATION_H #define NAVIGATION_H diff --git a/scene/3d/navigation_mesh.cpp b/scene/3d/navigation_mesh.cpp index 3e548b8e8f..073e56fdb4 100644 --- a/scene/3d/navigation_mesh.cpp +++ b/scene/3d/navigation_mesh.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "navigation_mesh.h" #include "mesh_instance.h" #include "navigation.h" @@ -404,8 +405,8 @@ void NavigationMesh::_bind_methods() { BIND_CONSTANT(SAMPLE_PARTITION_MONOTONE); BIND_CONSTANT(SAMPLE_PARTITION_LAYERS); - ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR3_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_vertices", "get_vertices"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_polygons", "_get_polygons"); + ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR3_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_vertices", "get_vertices"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_polygons", "_get_polygons"); ADD_PROPERTY(PropertyInfo(Variant::INT, "sample_partition_type/sample_partition_type", PROPERTY_HINT_ENUM, "Watershed,Monotone,Layers"), "set_sample_partition_type", "get_sample_partition_type"); diff --git a/scene/3d/navigation_mesh.h b/scene/3d/navigation_mesh.h index 0a81e1c67b..753ad76edc 100644 --- a/scene/3d/navigation_mesh.h +++ b/scene/3d/navigation_mesh.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef NAVIGATION_MESH_H #define NAVIGATION_MESH_H diff --git a/scene/3d/particles.cpp b/scene/3d/particles.cpp index fda4647141..a39ac5a8f5 100644 --- a/scene/3d/particles.cpp +++ b/scene/3d/particles.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "particles.h" #include "scene/resources/surface_tool.h" #include "servers/visual_server.h" @@ -848,9 +849,9 @@ void ParticlesMaterial::_update_shader() { code += " vec4(1.250, -1.050, -0.203, 0.0),\n"; code += " vec4(0.000, 0.000, 0.000, 0.0)) * hue_rot_s;\n"; if (color_ramp.is_valid()) { - code += " COLOR = textureLod(color_ramp,vec2(CUSTOM.y,0.0),0.0) * hue_rot_mat;\n"; + code += " COLOR = hue_rot_mat * textureLod(color_ramp,vec2(CUSTOM.y,0.0),0.0);\n"; } else { - code += " COLOR = color_value * hue_rot_mat;\n"; + code += " COLOR = hue_rot_mat * color_value;\n"; } if (emission_color_texture.is_valid() && emission_shape >= EMISSION_SHAPE_POINTS) { code += " COLOR*= texelFetch(emission_texture_color,emission_tex_ofs,0);\n"; @@ -1027,8 +1028,6 @@ void ParticlesMaterial::set_param(Parameter p_param, float p_value) { case PARAM_ANIM_OFFSET: { VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset, p_value); } break; - case PARAM_MAX: { - }; } } float ParticlesMaterial::get_param(Parameter p_param) const { @@ -1081,8 +1080,6 @@ void ParticlesMaterial::set_param_randomness(Parameter p_param, float p_value) { case PARAM_ANIM_OFFSET: { VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset_random, p_value); } break; - case PARAM_MAX: { - }; } } float ParticlesMaterial::get_param_randomness(Parameter p_param) const { @@ -1159,8 +1156,6 @@ void ParticlesMaterial::set_param_texture(Parameter p_param, const Ref<Texture> case PARAM_ANIM_OFFSET: { VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset_texture, p_texture); } break; - case PARAM_MAX: { - }; } _queue_shader_change(); @@ -1232,28 +1227,19 @@ void ParticlesMaterial::set_emission_box_extents(Vector3 p_extents) { void ParticlesMaterial::set_emission_point_texture(const Ref<Texture> &p_points) { emission_point_texture = p_points; - RID texture; - if (p_points.is_valid()) - texture = p_points->get_rid(); - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_points, texture); + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_points, p_points); } void ParticlesMaterial::set_emission_normal_texture(const Ref<Texture> &p_normals) { emission_normal_texture = p_normals; - RID texture; - if (p_normals.is_valid()) - texture = p_normals->get_rid(); - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_normal, texture); + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_normal, p_normals); } void ParticlesMaterial::set_emission_color_texture(const Ref<Texture> &p_colors) { emission_color_texture = p_colors; - RID texture; - if (p_colors.is_valid()) - texture = p_colors->get_rid(); - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_color, texture); + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_color, p_colors); _queue_shader_change(); } @@ -1315,10 +1301,7 @@ void ParticlesMaterial::set_trail_size_modifier(const Ref<CurveTexture> &p_trail curve->ensure_default_setup(); } - RID texture; - if (p_trail_size_modifier.is_valid()) - texture = p_trail_size_modifier->get_rid(); - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->trail_size_modifier, texture); + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->trail_size_modifier, curve); _queue_shader_change(); } @@ -1330,10 +1313,7 @@ Ref<CurveTexture> ParticlesMaterial::get_trail_size_modifier() const { void ParticlesMaterial::set_trail_color_modifier(const Ref<GradientTexture> &p_trail_color_modifier) { trail_color_modifier = p_trail_color_modifier; - RID texture; - if (p_trail_color_modifier.is_valid()) - texture = p_trail_color_modifier->get_rid(); - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->trail_color_modifier, texture); + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->trail_color_modifier, p_trail_color_modifier); _queue_shader_change(); } @@ -1596,4 +1576,21 @@ ParticlesMaterial::ParticlesMaterial() : } ParticlesMaterial::~ParticlesMaterial() { + + if (material_mutex) + material_mutex->lock(); + + if (shader_map.has(current_key)) { + shader_map[current_key].users--; + if (shader_map[current_key].users == 0) { + //deallocate shader, as it's no longer in use + VS::get_singleton()->free(shader_map[current_key].shader); + shader_map.erase(current_key); + } + + VS::get_singleton()->material_set_shader(_get_material(), RID()); + } + + if (material_mutex) + material_mutex->unlock(); } diff --git a/scene/3d/particles.h b/scene/3d/particles.h index ebd2b6ce48..17e21c6cee 100644 --- a/scene/3d/particles.h +++ b/scene/3d/particles.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef VISUALINSTANCEPARTICLES_H #define VISUALINSTANCEPARTICLES_H diff --git a/scene/3d/path.cpp b/scene/3d/path.cpp index e0520fdb7c..57d79c960f 100644 --- a/scene/3d/path.cpp +++ b/scene/3d/path.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "path.h" #include "engine.h" @@ -39,6 +40,9 @@ void Path::_curve_changed() { if (is_inside_tree() && Engine::get_singleton()->is_editor_hint()) update_gizmo(); + if (is_inside_tree()) { + emit_signal("curve_changed"); + } } void Path::set_curve(const Ref<Curve3D> &p_curve) { @@ -67,6 +71,8 @@ void Path::_bind_methods() { ClassDB::bind_method(D_METHOD("_curve_changed"), &Path::_curve_changed); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve3D"), "set_curve", "get_curve"); + + ADD_SIGNAL(MethodInfo("curve_changed")); } Path::Path() { @@ -189,61 +195,16 @@ bool PathFollow::get_cubic_interpolation() const { return cubic; } -bool PathFollow::_set(const StringName &p_name, const Variant &p_value) { - - if (p_name == SceneStringNames::get_singleton()->offset) { - set_offset(p_value); - } else if (p_name == SceneStringNames::get_singleton()->unit_offset) { - set_unit_offset(p_value); - } else if (p_name == SceneStringNames::get_singleton()->rotation_mode) { - set_rotation_mode(RotationMode(p_value.operator int())); - } else if (p_name == SceneStringNames::get_singleton()->v_offset) { - set_v_offset(p_value); - } else if (p_name == SceneStringNames::get_singleton()->h_offset) { - set_h_offset(p_value); - } else if (String(p_name) == "cubic_interp") { - set_cubic_interpolation(p_value); - } else if (String(p_name) == "loop") { - set_loop(p_value); - } else - return false; - - return true; -} +void PathFollow::_validate_property(PropertyInfo &property) const { -bool PathFollow::_get(const StringName &p_name, Variant &r_ret) const { - - if (p_name == SceneStringNames::get_singleton()->offset) { - r_ret = get_offset(); - } else if (p_name == SceneStringNames::get_singleton()->unit_offset) { - r_ret = get_unit_offset(); - } else if (p_name == SceneStringNames::get_singleton()->rotation_mode) { - r_ret = get_rotation_mode(); - } else if (p_name == SceneStringNames::get_singleton()->v_offset) { - r_ret = get_v_offset(); - } else if (p_name == SceneStringNames::get_singleton()->h_offset) { - r_ret = get_h_offset(); - } else if (String(p_name) == "cubic_interp") { - r_ret = cubic; - } else if (String(p_name) == "loop") { - r_ret = loop; - } else - return false; - - return true; -} -void PathFollow::_get_property_list(List<PropertyInfo> *p_list) const { - - float max = 10000; - if (path && path->get_curve().is_valid()) - max = path->get_curve()->get_baked_length(); - p_list->push_back(PropertyInfo(Variant::REAL, "offset", PROPERTY_HINT_RANGE, "0," + rtos(max) + ",0.01")); - p_list->push_back(PropertyInfo(Variant::REAL, "unit_offset", PROPERTY_HINT_RANGE, "0,1,0.0001", PROPERTY_USAGE_EDITOR)); - p_list->push_back(PropertyInfo(Variant::REAL, "h_offset")); - p_list->push_back(PropertyInfo(Variant::REAL, "v_offset")); - p_list->push_back(PropertyInfo(Variant::INT, "rotation_mode", PROPERTY_HINT_ENUM, "None,Y,XY,XYZ")); - p_list->push_back(PropertyInfo(Variant::BOOL, "cubic_interp")); - p_list->push_back(PropertyInfo(Variant::BOOL, "loop")); + if (property.name == "offset") { + + float max = 10000; + if (path && path->get_curve().is_valid()) + max = path->get_curve()->get_baked_length(); + + property.hint_string = "0," + rtos(max) + ",0.01"; + } } void PathFollow::_bind_methods() { @@ -269,6 +230,14 @@ void PathFollow::_bind_methods() { ClassDB::bind_method(D_METHOD("set_loop", "loop"), &PathFollow::set_loop); ClassDB::bind_method(D_METHOD("has_loop"), &PathFollow::has_loop); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "offset", PROPERTY_HINT_RANGE, "0,10000,0.01"), "set_offset", "get_offset"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "unit_offset", PROPERTY_HINT_RANGE, "0,1,0.0001", PROPERTY_USAGE_EDITOR), "set_unit_offset", "get_unit_offset"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "h_offset"), "set_h_offset", "get_h_offset"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "v_offset"), "set_v_offset", "get_v_offset"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "rotation_mode", PROPERTY_HINT_ENUM, "None,Y,XY,XYZ"), "set_rotation_mode", "get_rotation_mode"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cubic_interp"), "set_cubic_interpolation", "get_cubic_interpolation"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop"); + BIND_ENUM_CONSTANT(ROTATION_NONE); BIND_ENUM_CONSTANT(ROTATION_Y); BIND_ENUM_CONSTANT(ROTATION_XY); diff --git a/scene/3d/path.h b/scene/3d/path.h index 7c3e0f35cc..2ed686ac3c 100644 --- a/scene/3d/path.h +++ b/scene/3d/path.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef PATH_H #define PATH_H @@ -78,9 +79,7 @@ private: void _update_transform(); 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; void _notification(int p_what); static void _bind_methods(); diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp index 4c4e4bf1e6..4528799985 100644 --- a/scene/3d/physics_body.cpp +++ b/scene/3d/physics_body.cpp @@ -27,12 +27,17 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "physics_body.h" #include "engine.h" #include "method_bind_ext.gen.inc" #include "scene/scene_string_names.h" +#ifdef TOOLS_ENABLED +#include "editor/plugins/spatial_editor_plugin.h" +#endif + void PhysicsBody::_notification(int p_what) { /* @@ -257,6 +262,7 @@ void RigidBody::_body_enter_tree(ObjectID p_id) { Node *node = Object::cast_to<Node>(obj); ERR_FAIL_COND(!node); + ERR_FAIL_COND(!contact_monitor); Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.find(p_id); ERR_FAIL_COND(!E); ERR_FAIL_COND(E->get().in_tree); @@ -280,6 +286,7 @@ void RigidBody::_body_exit_tree(ObjectID p_id) { Object *obj = ObjectDB::get_instance(p_id); Node *node = Object::cast_to<Node>(obj); ERR_FAIL_COND(!node); + ERR_FAIL_COND(!contact_monitor); Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.find(p_id); ERR_FAIL_COND(!E); ERR_FAIL_COND(!E->get().in_tree); @@ -305,6 +312,7 @@ void RigidBody::_body_inout(int p_status, ObjectID p_instance, int p_body_shape, Object *obj = ObjectDB::get_instance(objid); Node *node = Object::cast_to<Node>(obj); + ERR_FAIL_COND(!contact_monitor); Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.find(objid); ERR_FAIL_COND(!body_in && !E); @@ -317,7 +325,7 @@ void RigidBody::_body_inout(int p_status, ObjectID p_instance, int p_body_shape, E->get().in_tree = node && node->is_inside_tree(); if (node) { node->connect(SceneStringNames::get_singleton()->tree_entered, this, SceneStringNames::get_singleton()->_body_enter_tree, make_binds(objid)); - node->connect(SceneStringNames::get_singleton()->tree_exited, this, SceneStringNames::get_singleton()->_body_exit_tree, make_binds(objid)); + node->connect(SceneStringNames::get_singleton()->tree_exiting, this, SceneStringNames::get_singleton()->_body_exit_tree, make_binds(objid)); if (E->get().in_tree) { emit_signal(SceneStringNames::get_singleton()->body_entered, node); } @@ -344,7 +352,7 @@ void RigidBody::_body_inout(int p_status, ObjectID p_instance, int p_body_shape, if (node) { node->disconnect(SceneStringNames::get_singleton()->tree_entered, this, SceneStringNames::get_singleton()->_body_enter_tree); - node->disconnect(SceneStringNames::get_singleton()->tree_exited, this, SceneStringNames::get_singleton()->_body_exit_tree); + node->disconnect(SceneStringNames::get_singleton()->tree_exiting, this, SceneStringNames::get_singleton()->_body_exit_tree); if (in_tree) emit_signal(SceneStringNames::get_singleton()->body_exited, obj); } @@ -366,9 +374,7 @@ struct _RigidBodyInOut { void RigidBody::_direct_state_changed(Object *p_state) { -//eh.. fuck #ifdef DEBUG_ENABLED - state = Object::cast_to<PhysicsDirectBodyState>(p_state); #else state = (PhysicsDirectBodyState *)p_state; //trust it @@ -692,6 +698,10 @@ void RigidBody::apply_impulse(const Vector3 &p_pos, const Vector3 &p_impulse) { PhysicsServer::get_singleton()->body_apply_impulse(get_rid(), p_pos, p_impulse); } +void RigidBody::apply_torque_impulse(const Vector3 &p_impulse) { + PhysicsServer::get_singleton()->body_apply_torque_impulse(get_rid(), p_impulse); +} + void RigidBody::set_use_continuous_collision_detection(bool p_enable) { ccd = p_enable; @@ -718,6 +728,14 @@ void RigidBody::set_contact_monitor(bool p_enabled) { for (Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.front(); E; E = E->next()) { //clean up mess + Object *obj = ObjectDB::get_instance(E->key()); + Node *node = Object::cast_to<Node>(obj); + + if (node) { + + node->disconnect(SceneStringNames::get_singleton()->tree_entered, this, SceneStringNames::get_singleton()->_body_enter_tree); + node->disconnect(SceneStringNames::get_singleton()->tree_exiting, this, SceneStringNames::get_singleton()->_body_exit_tree); + } } memdelete(contact_monitor); @@ -767,11 +785,11 @@ String RigidBody::get_configuration_warning() const { String warning = CollisionObject::get_configuration_warning(); - if ((get_mode() == MODE_RIGID || get_mode() == MODE_CHARACTER) && (ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(0).length() - 1.0) > 0.05)) { + if ((get_mode() == MODE_RIGID || get_mode() == MODE_CHARACTER) && (ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(2).length() - 1.0) > 0.05)) { if (warning != String()) { warning += "\n"; } - warning += TTR("Size changes to RigidBody (in character or rigid modes) will be overriden by the physics engine when running.\nChange the size in children collision shapes instead."); + warning += TTR("Size changes to RigidBody (in character or rigid modes) will be overridden by the physics engine when running.\nChange the size in children collision shapes instead."); } return warning; @@ -823,6 +841,7 @@ void RigidBody::_bind_methods() { ClassDB::bind_method(D_METHOD("set_axis_velocity", "axis_velocity"), &RigidBody::set_axis_velocity); ClassDB::bind_method(D_METHOD("apply_impulse", "position", "impulse"), &RigidBody::apply_impulse); + ClassDB::bind_method(D_METHOD("apply_torque_impulse", "impulse"), &RigidBody::apply_torque_impulse); ClassDB::bind_method(D_METHOD("set_sleeping", "sleeping"), &RigidBody::set_sleeping); ClassDB::bind_method(D_METHOD("is_sleeping"), &RigidBody::is_sleeping); @@ -913,10 +932,10 @@ RigidBody::~RigidBody() { ////////////////////////////////////////////////////// ////////////////////////// -Ref<KinematicCollision> KinematicBody::_move(const Vector3 &p_motion) { +Ref<KinematicCollision> KinematicBody::_move(const Vector3 &p_motion, bool p_infinite_inertia) { Collision col; - if (move_and_collide(p_motion, col)) { + if (move_and_collide(p_motion, p_infinite_inertia, col)) { if (motion_cache.is_null()) { motion_cache.instance(); motion_cache->owner = this; @@ -930,11 +949,11 @@ Ref<KinematicCollision> KinematicBody::_move(const Vector3 &p_motion) { return Ref<KinematicCollision>(); } -bool KinematicBody::move_and_collide(const Vector3 &p_motion, Collision &r_collision) { +bool KinematicBody::move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, Collision &r_collision) { Transform gt = get_global_transform(); PhysicsServer::MotionResult result; - bool colliding = PhysicsServer::get_singleton()->body_test_motion(get_rid(), gt, p_motion, &result); + bool colliding = PhysicsServer::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_infinite_inertia, &result); if (colliding) { r_collision.collider_metadata = result.collider_metadata; @@ -960,7 +979,7 @@ bool KinematicBody::move_and_collide(const Vector3 &p_motion, Collision &r_colli return colliding; } -Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Vector3 &p_floor_direction, float p_slope_stop_min_velocity, int p_max_slides, float p_floor_max_angle) { +Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Vector3 &p_floor_direction, bool p_infinite_inertia, float p_slope_stop_min_velocity, int p_max_slides, float p_floor_max_angle) { Vector3 lv = p_linear_velocity; @@ -982,7 +1001,7 @@ Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Ve Collision collision; - bool collided = move_and_collide(motion, collision); + bool collided = move_and_collide(motion, p_infinite_inertia, collision); if (collided) { @@ -1055,11 +1074,11 @@ Vector3 KinematicBody::get_floor_velocity() const { return floor_velocity; } -bool KinematicBody::test_move(const Transform &p_from, const Vector3 &p_motion) { +bool KinematicBody::test_move(const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia) { ERR_FAIL_COND_V(!is_inside_tree(), false); - return PhysicsServer::get_singleton()->body_test_motion(get_rid(), p_from, p_motion); + return PhysicsServer::get_singleton()->body_test_motion(get_rid(), p_from, p_motion, p_infinite_inertia); } void KinematicBody::set_axis_lock(PhysicsServer::BodyAxis p_axis, bool p_lock) { @@ -1108,10 +1127,10 @@ Ref<KinematicCollision> KinematicBody::_get_slide_collision(int p_bounce) { void KinematicBody::_bind_methods() { - ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec"), &KinematicBody::_move); - ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity", "floor_normal", "slope_stop_min_velocity", "max_slides", "floor_max_angle"), &KinematicBody::move_and_slide, DEFVAL(Vector3(0, 0, 0)), DEFVAL(0.05), DEFVAL(4), DEFVAL(Math::deg2rad((float)45))); + ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "infinite_inertia"), &KinematicBody::_move, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity", "floor_normal", "infinite_inertia", "slope_stop_min_velocity", "max_slides", "floor_max_angle"), &KinematicBody::move_and_slide, DEFVAL(Vector3(0, 0, 0)), DEFVAL(true), DEFVAL(0.05), DEFVAL(4), DEFVAL(Math::deg2rad((float)45))); - ClassDB::bind_method(D_METHOD("test_move", "from", "rel_vec"), &KinematicBody::test_move); + ClassDB::bind_method(D_METHOD("test_move", "from", "rel_vec", "infinite_inertia"), &KinematicBody::test_move); ClassDB::bind_method(D_METHOD("is_on_floor"), &KinematicBody::is_on_floor); ClassDB::bind_method(D_METHOD("is_on_ceiling"), &KinematicBody::is_on_ceiling); @@ -1251,3 +1270,1108 @@ KinematicCollision::KinematicCollision() { collision.local_shape = 0; owner = NULL; } + +/////////////////////////////////////// + +bool PhysicalBone::JointData::_set(const StringName &p_name, const Variant &p_value, RID j) { + return false; +} + +bool PhysicalBone::JointData::_get(const StringName &p_name, Variant &r_ret) const { + return false; +} + +void PhysicalBone::JointData::_get_property_list(List<PropertyInfo> *p_list) const { +} + +bool PhysicalBone::PinJointData::_set(const StringName &p_name, const Variant &p_value, RID j) { + if (JointData::_set(p_name, p_value, j)) { + return true; + } + + if ("joint_constraints/bias" == p_name) { + bias = p_value; + if (j.is_valid()) + PhysicsServer::get_singleton()->pin_joint_set_param(j, PhysicsServer::PIN_JOINT_BIAS, bias); + + } else if ("joint_constraints/damping" == p_name) { + damping = p_value; + if (j.is_valid()) + PhysicsServer::get_singleton()->pin_joint_set_param(j, PhysicsServer::PIN_JOINT_DAMPING, damping); + + } else if ("joint_constraints/impulse_clamp" == p_name) { + impulse_clamp = p_value; + if (j.is_valid()) + PhysicsServer::get_singleton()->pin_joint_set_param(j, PhysicsServer::PIN_JOINT_IMPULSE_CLAMP, impulse_clamp); + + } else { + return false; + } + + return true; +} + +bool PhysicalBone::PinJointData::_get(const StringName &p_name, Variant &r_ret) const { + if (JointData::_get(p_name, r_ret)) { + return true; + } + + if ("joint_constraints/bias" == p_name) { + r_ret = bias; + } else if ("joint_constraints/damping" == p_name) { + r_ret = damping; + } else if ("joint_constraints/impulse_clamp" == p_name) { + r_ret = impulse_clamp; + } else { + return false; + } + + return true; +} + +void PhysicalBone::PinJointData::_get_property_list(List<PropertyInfo> *p_list) const { + JointData::_get_property_list(p_list); + + p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/bias", PROPERTY_HINT_RANGE, "0.01,0.99,0.01")); + p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/damping", PROPERTY_HINT_RANGE, "0.01,8.0,0.01")); + p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/impulse_clamp", PROPERTY_HINT_RANGE, "0.0,64.0,0.01")); +} + +bool PhysicalBone::ConeJointData::_set(const StringName &p_name, const Variant &p_value, RID j) { + if (JointData::_set(p_name, p_value, j)) { + return true; + } + + if ("joint_constraints/swing_span" == p_name) { + swing_span = Math::deg2rad(real_t(p_value)); + if (j.is_valid()) + PhysicsServer::get_singleton()->cone_twist_joint_set_param(j, PhysicsServer::CONE_TWIST_JOINT_SWING_SPAN, swing_span); + + } else if ("joint_constraints/twist_span" == p_name) { + twist_span = Math::deg2rad(real_t(p_value)); + if (j.is_valid()) + PhysicsServer::get_singleton()->cone_twist_joint_set_param(j, PhysicsServer::CONE_TWIST_JOINT_TWIST_SPAN, twist_span); + + } else if ("joint_constraints/bias" == p_name) { + bias = p_value; + if (j.is_valid()) + PhysicsServer::get_singleton()->cone_twist_joint_set_param(j, PhysicsServer::CONE_TWIST_JOINT_BIAS, bias); + + } else if ("joint_constraints/softness" == p_name) { + softness = p_value; + if (j.is_valid()) + PhysicsServer::get_singleton()->cone_twist_joint_set_param(j, PhysicsServer::CONE_TWIST_JOINT_SOFTNESS, softness); + + } else if ("joint_constraints/relaxation" == p_name) { + relaxation = p_value; + if (j.is_valid()) + PhysicsServer::get_singleton()->cone_twist_joint_set_param(j, PhysicsServer::CONE_TWIST_JOINT_RELAXATION, relaxation); + + } else { + return false; + } + + return true; +} + +bool PhysicalBone::ConeJointData::_get(const StringName &p_name, Variant &r_ret) const { + if (JointData::_get(p_name, r_ret)) { + return true; + } + + if ("joint_constraints/swing_span" == p_name) { + r_ret = Math::rad2deg(swing_span); + } else if ("joint_constraints/twist_span" == p_name) { + r_ret = Math::rad2deg(twist_span); + } else if ("joint_constraints/bias" == p_name) { + r_ret = bias; + } else if ("joint_constraints/softness" == p_name) { + r_ret = softness; + } else if ("joint_constraints/relaxation" == p_name) { + r_ret = relaxation; + } else { + return false; + } + + return true; +} + +void PhysicalBone::ConeJointData::_get_property_list(List<PropertyInfo> *p_list) const { + JointData::_get_property_list(p_list); + + p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/swing_span", PROPERTY_HINT_RANGE, "-180,180,0.01")); + p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/twist_span", PROPERTY_HINT_RANGE, "-40000,40000,0.1")); + p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/bias", PROPERTY_HINT_RANGE, "0.01,16.0,0.01")); + p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/softness", PROPERTY_HINT_RANGE, "0.01,16.0,0.01")); + p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/relaxation", PROPERTY_HINT_RANGE, "0.01,16.0,0.01")); +} + +bool PhysicalBone::HingeJointData::_set(const StringName &p_name, const Variant &p_value, RID j) { + if (JointData::_set(p_name, p_value, j)) { + return true; + } + + if ("joint_constraints/angular_limit_enabled" == p_name) { + angular_limit_enabled = p_value; + if (j.is_valid()) + PhysicsServer::get_singleton()->hinge_joint_set_flag(j, PhysicsServer::HINGE_JOINT_FLAG_USE_LIMIT, angular_limit_enabled); + + } else if ("joint_constraints/angular_limit_upper" == p_name) { + angular_limit_upper = Math::deg2rad(real_t(p_value)); + if (j.is_valid()) + PhysicsServer::get_singleton()->hinge_joint_set_param(j, PhysicsServer::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)); + if (j.is_valid()) + PhysicsServer::get_singleton()->hinge_joint_set_param(j, PhysicsServer::HINGE_JOINT_LIMIT_LOWER, angular_limit_lower); + + } else if ("joint_constraints/angular_limit_bias" == p_name) { + angular_limit_bias = p_value; + if (j.is_valid()) + PhysicsServer::get_singleton()->hinge_joint_set_param(j, PhysicsServer::HINGE_JOINT_LIMIT_BIAS, angular_limit_bias); + + } else if ("joint_constraints/angular_limit_softness" == p_name) { + angular_limit_softness = p_value; + if (j.is_valid()) + PhysicsServer::get_singleton()->hinge_joint_set_param(j, PhysicsServer::HINGE_JOINT_LIMIT_SOFTNESS, angular_limit_softness); + + } else if ("joint_constraints/angular_limit_relaxation" == p_name) { + angular_limit_relaxation = p_value; + if (j.is_valid()) + PhysicsServer::get_singleton()->hinge_joint_set_param(j, PhysicsServer::HINGE_JOINT_LIMIT_RELAXATION, angular_limit_relaxation); + + } else { + return false; + } + + return true; +} + +bool PhysicalBone::HingeJointData::_get(const StringName &p_name, Variant &r_ret) const { + if (JointData::_get(p_name, r_ret)) { + return true; + } + + 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); + } else if ("joint_constraints/angular_limit_lower" == p_name) { + r_ret = Math::rad2deg(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) { + r_ret = angular_limit_softness; + } else if ("joint_constraints/angular_limit_relaxation" == p_name) { + r_ret = angular_limit_relaxation; + } else { + return false; + } + + return true; +} + +void PhysicalBone::HingeJointData::_get_property_list(List<PropertyInfo> *p_list) const { + JointData::_get_property_list(p_list); + + p_list->push_back(PropertyInfo(Variant::BOOL, "joint_constraints/angular_limit_enabled")); + p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_upper", PROPERTY_HINT_RANGE, "-180,180,0.01")); + p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_lower", PROPERTY_HINT_RANGE, "-180,180,0.01")); + p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_bias", PROPERTY_HINT_RANGE, "0.01,0.99,0.01")); + p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_softness", PROPERTY_HINT_RANGE, "0.01,16,0.01")); + p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_relaxation", PROPERTY_HINT_RANGE, "0.01,16,0.01")); +} + +bool PhysicalBone::SliderJointData::_set(const StringName &p_name, const Variant &p_value, RID j) { + if (JointData::_set(p_name, p_value, j)) { + return true; + } + + if ("joint_constraints/linear_limit_upper" == p_name) { + linear_limit_upper = p_value; + if (j.is_valid()) + PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_UPPER, linear_limit_upper); + + } else if ("joint_constraints/linear_limit_lower" == p_name) { + linear_limit_lower = p_value; + if (j.is_valid()) + PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_LOWER, linear_limit_lower); + + } else if ("joint_constraints/linear_limit_softness" == p_name) { + linear_limit_softness = p_value; + if (j.is_valid()) + PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_SOFTNESS, linear_limit_softness); + + } else if ("joint_constraints/linear_limit_restitution" == p_name) { + linear_limit_restitution = p_value; + if (j.is_valid()) + PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_RESTITUTION, linear_limit_restitution); + + } else if ("joint_constraints/linear_limit_damping" == p_name) { + linear_limit_damping = p_value; + if (j.is_valid()) + PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_DAMPING, linear_limit_restitution); + + } else if ("joint_constraints/angular_limit_upper" == p_name) { + angular_limit_upper = Math::deg2rad(real_t(p_value)); + if (j.is_valid()) + PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::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)); + if (j.is_valid()) + PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_LOWER, angular_limit_lower); + + } else if ("joint_constraints/angular_limit_softness" == p_name) { + angular_limit_softness = p_value; + if (j.is_valid()) + PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_SOFTNESS, angular_limit_softness); + + } else if ("joint_constraints/angular_limit_restitution" == p_name) { + angular_limit_restitution = p_value; + if (j.is_valid()) + PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_SOFTNESS, angular_limit_softness); + + } else if ("joint_constraints/angular_limit_damping" == p_name) { + angular_limit_damping = p_value; + if (j.is_valid()) + PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_DAMPING, angular_limit_damping); + + } else { + return false; + } + + return true; +} + +bool PhysicalBone::SliderJointData::_get(const StringName &p_name, Variant &r_ret) const { + if (JointData::_get(p_name, r_ret)) { + return true; + } + + if ("joint_constraints/linear_limit_upper" == p_name) { + r_ret = linear_limit_upper; + } else if ("joint_constraints/linear_limit_lower" == p_name) { + r_ret = linear_limit_lower; + } else if ("joint_constraints/linear_limit_softness" == p_name) { + r_ret = linear_limit_softness; + } else if ("joint_constraints/linear_limit_restitution" == p_name) { + r_ret = linear_limit_restitution; + } 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); + } else if ("joint_constraints/angular_limit_lower" == p_name) { + r_ret = Math::rad2deg(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) { + r_ret = angular_limit_restitution; + } else if ("joint_constraints/angular_limit_damping" == p_name) { + r_ret = angular_limit_damping; + } else { + return false; + } + + return true; +} + +void PhysicalBone::SliderJointData::_get_property_list(List<PropertyInfo> *p_list) const { + JointData::_get_property_list(p_list); + + p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/linear_limit_upper")); + p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/linear_limit_lower")); + p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/linear_limit_softness", PROPERTY_HINT_RANGE, "0.01,16.0,0.01")); + p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/linear_limit_restitution", PROPERTY_HINT_RANGE, "0.01,16.0,0.01")); + p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/linear_limit_damping", PROPERTY_HINT_RANGE, "0,16.0,0.01")); + + p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_upper", PROPERTY_HINT_RANGE, "-180,180,0.01")); + p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_lower", PROPERTY_HINT_RANGE, "-180,180,0.01")); + p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_softness", PROPERTY_HINT_RANGE, "0.01,16.0,0.01")); + p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_restitution", PROPERTY_HINT_RANGE, "0.01,16.0,0.01")); + p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_damping", PROPERTY_HINT_RANGE, "0,16.0,0.01")); +} + +bool PhysicalBone::SixDOFJointData::_set(const StringName &p_name, const Variant &p_value, RID j) { + if (JointData::_set(p_name, p_value, j)) { + return true; + } + + String path = p_name; + + Vector3::Axis axis; + { + const String axis_s = path.get_slicec('/', 1); + if ("x" == axis_s) { + axis = Vector3::AXIS_X; + } else if ("y" == axis_s) { + axis = Vector3::AXIS_Y; + } else if ("z" == axis_s) { + axis = Vector3::AXIS_Z; + } else { + return false; + } + } + + String var_name = path.get_slicec('/', 2); + + if ("linear_limit_enabled" == var_name) { + axis_data[axis].linear_limit_enabled = p_value; + if (j.is_valid()) + PhysicsServer::get_singleton()->generic_6dof_joint_set_flag(j, axis, PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT, axis_data[axis].linear_limit_enabled); + + } else if ("linear_limit_upper" == var_name) { + axis_data[axis].linear_limit_upper = p_value; + if (j.is_valid()) + PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_LINEAR_UPPER_LIMIT, axis_data[axis].linear_limit_upper); + + } else if ("linear_limit_lower" == var_name) { + axis_data[axis].linear_limit_lower = p_value; + if (j.is_valid()) + PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_LINEAR_LOWER_LIMIT, axis_data[axis].linear_limit_lower); + + } else if ("linear_limit_softness" == var_name) { + axis_data[axis].linear_limit_softness = p_value; + if (j.is_valid()) + PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_LINEAR_LIMIT_SOFTNESS, axis_data[axis].linear_limit_softness); + + } else if ("linear_restitution" == var_name) { + axis_data[axis].linear_restitution = p_value; + if (j.is_valid()) + PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_LINEAR_RESTITUTION, axis_data[axis].linear_restitution); + + } else if ("linear_damping" == var_name) { + axis_data[axis].linear_damping = p_value; + if (j.is_valid()) + PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_LINEAR_DAMPING, axis_data[axis].linear_damping); + + } else if ("angular_limit_enabled" == var_name) { + axis_data[axis].angular_limit_enabled = p_value; + if (j.is_valid()) + PhysicsServer::get_singleton()->generic_6dof_joint_set_flag(j, axis, PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT, axis_data[axis].angular_limit_enabled); + + } else if ("angular_limit_upper" == var_name) { + axis_data[axis].angular_limit_upper = Math::deg2rad(real_t(p_value)); + if (j.is_valid()) + PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::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)); + if (j.is_valid()) + PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_ANGULAR_LOWER_LIMIT, axis_data[axis].angular_limit_lower); + + } else if ("angular_limit_softness" == var_name) { + axis_data[axis].angular_limit_softness = p_value; + if (j.is_valid()) + PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_ANGULAR_LIMIT_SOFTNESS, axis_data[axis].angular_limit_softness); + + } else if ("angular_restitution" == var_name) { + axis_data[axis].angular_restitution = p_value; + if (j.is_valid()) + PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_ANGULAR_RESTITUTION, axis_data[axis].angular_restitution); + + } else if ("angular_damping" == var_name) { + axis_data[axis].angular_damping = p_value; + if (j.is_valid()) + PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_ANGULAR_DAMPING, axis_data[axis].angular_damping); + + } else if ("erp" == var_name) { + axis_data[axis].erp = p_value; + if (j.is_valid()) + PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_ANGULAR_ERP, axis_data[axis].erp); + + } else { + return false; + } + + return true; +} + +bool PhysicalBone::SixDOFJointData::_get(const StringName &p_name, Variant &r_ret) const { + if (JointData::_get(p_name, r_ret)) { + return true; + } + + String path = p_name; + + int axis; + { + const String axis_s = path.get_slicec('/', 1); + if ("x" == axis_s) { + axis = 0; + } else if ("y" == axis_s) { + axis = 1; + } else if ("z" == axis_s) { + axis = 2; + } else { + return false; + } + } + + String var_name = path.get_slicec('/', 2); + + if ("linear_limit_enabled" == var_name) { + r_ret = axis_data[axis].linear_limit_enabled; + } else if ("linear_limit_upper" == var_name) { + r_ret = axis_data[axis].linear_limit_upper; + } else if ("linear_limit_lower" == var_name) { + r_ret = axis_data[axis].linear_limit_lower; + } else if ("linear_limit_softness" == var_name) { + r_ret = axis_data[axis].linear_limit_softness; + } else if ("linear_restitution" == var_name) { + r_ret = axis_data[axis].linear_restitution; + } else if ("linear_damping" == var_name) { + r_ret = axis_data[axis].linear_damping; + } 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); + } else if ("angular_limit_lower" == var_name) { + r_ret = Math::rad2deg(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) { + r_ret = axis_data[axis].angular_restitution; + } else if ("angular_damping" == var_name) { + r_ret = axis_data[axis].angular_damping; + } else if ("erp" == var_name) { + r_ret = axis_data[axis].erp; + } else { + return false; + } + + return true; +} + +void PhysicalBone::SixDOFJointData::_get_property_list(List<PropertyInfo> *p_list) const { + const StringName axis_names[] = { "x", "y", "z" }; + for (int i = 0; i < 3; ++i) { + p_list->push_back(PropertyInfo(Variant::BOOL, "joint_constraints/" + axis_names[i] + "/linear_limit_enabled")); + p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/linear_limit_upper")); + p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/linear_limit_lower")); + p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/linear_limit_softness", PROPERTY_HINT_RANGE, "0.01,16,0.01")); + p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/linear_restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01")); + p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/linear_damping", PROPERTY_HINT_RANGE, "0.01,16,0.01")); + p_list->push_back(PropertyInfo(Variant::BOOL, "joint_constraints/" + axis_names[i] + "/angular_limit_enabled")); + p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/angular_limit_upper", PROPERTY_HINT_RANGE, "-180,180,0.01")); + p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/angular_limit_lower", PROPERTY_HINT_RANGE, "-180,180,0.01")); + p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/angular_limit_softness", PROPERTY_HINT_RANGE, "0.01,16,0.01")); + p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/angular_restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01")); + p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/angular_damping", PROPERTY_HINT_RANGE, "0.01,16,0.01")); + p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/erp")); + } +} + +bool PhysicalBone::_set(const StringName &p_name, const Variant &p_value) { + if (p_name == "bone_name") { + set_bone_name(p_value); + return true; + } + + if (joint_data) { + if (joint_data->_set(p_name, p_value)) { +#ifdef TOOLS_ENABLED + if (get_gizmo().is_valid()) + get_gizmo()->redraw(); +#endif + return true; + } + } + + return false; +} + +bool PhysicalBone::_get(const StringName &p_name, Variant &r_ret) const { + if (p_name == "bone_name") { + r_ret = get_bone_name(); + return true; + } + + if (joint_data) { + return joint_data->_get(p_name, r_ret); + } + + return false; +} + +void PhysicalBone::_get_property_list(List<PropertyInfo> *p_list) const { + + Skeleton *parent = find_skeleton_parent(get_parent()); + + if (parent) { + + String names; + for (int i = 0; i < parent->get_bone_count(); i++) { + if (i > 0) + names += ","; + names += parent->get_bone_name(i); + } + + p_list->push_back(PropertyInfo(Variant::STRING, "bone_name", PROPERTY_HINT_ENUM, names)); + } else { + + p_list->push_back(PropertyInfo(Variant::STRING, "bone_name")); + } + + if (joint_data) { + joint_data->_get_property_list(p_list); + } +} + +void PhysicalBone::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: + parent_skeleton = find_skeleton_parent(get_parent()); + update_bone_id(); + reset_to_rest_position(); + break; + case NOTIFICATION_EXIT_TREE: + if (parent_skeleton) { + if (-1 != bone_id) { + parent_skeleton->unbind_physical_bone_from_bone(bone_id); + } + } + parent_skeleton = NULL; + update_bone_id(); + break; + case NOTIFICATION_TRANSFORM_CHANGED: + if (Engine::get_singleton()->is_editor_hint()) { + + update_offset(); + } + break; + } +} + +void PhysicalBone::_direct_state_changed(Object *p_state) { + + if (!simulate_physics) { + return; + } + + /// Update bone transform + + PhysicsDirectBodyState *state; + +#ifdef DEBUG_ENABLED + state = Object::cast_to<PhysicsDirectBodyState>(p_state); +#else + state = (PhysicsDirectBodyState *)p_state; //trust it +#endif + + Transform global_transform(state->get_transform()); + + set_ignore_transform_notification(true); + set_global_transform(global_transform); + set_ignore_transform_notification(false); + + // Update skeleton + if (parent_skeleton) { + if (-1 != bone_id) { + parent_skeleton->set_bone_global_pose(bone_id, parent_skeleton->get_global_transform().affine_inverse() * (global_transform * body_offset_inverse)); + } + } +} + +void PhysicalBone::_bind_methods() { + ClassDB::bind_method(D_METHOD("_direct_state_changed"), &PhysicalBone::_direct_state_changed); + + ClassDB::bind_method(D_METHOD("set_joint_type", "joint_type"), &PhysicalBone::set_joint_type); + ClassDB::bind_method(D_METHOD("get_joint_type"), &PhysicalBone::get_joint_type); + + ClassDB::bind_method(D_METHOD("set_joint_offset", "offset"), &PhysicalBone::set_joint_offset); + ClassDB::bind_method(D_METHOD("get_joint_offset"), &PhysicalBone::get_joint_offset); + + ClassDB::bind_method(D_METHOD("set_body_offset", "offset"), &PhysicalBone::set_body_offset); + ClassDB::bind_method(D_METHOD("get_body_offset"), &PhysicalBone::get_body_offset); + + ClassDB::bind_method(D_METHOD("set_static_body", "simulate"), &PhysicalBone::set_static_body); + ClassDB::bind_method(D_METHOD("is_static_body"), &PhysicalBone::is_static_body); + + ClassDB::bind_method(D_METHOD("set_simulate_physics", "simulate"), &PhysicalBone::set_simulate_physics); + ClassDB::bind_method(D_METHOD("get_simulate_physics"), &PhysicalBone::get_simulate_physics); + + ClassDB::bind_method(D_METHOD("is_simulating_physics"), &PhysicalBone::is_simulating_physics); + + ClassDB::bind_method(D_METHOD("set_mass", "mass"), &PhysicalBone::set_mass); + ClassDB::bind_method(D_METHOD("get_mass"), &PhysicalBone::get_mass); + + ClassDB::bind_method(D_METHOD("set_weight", "weight"), &PhysicalBone::set_weight); + ClassDB::bind_method(D_METHOD("get_weight"), &PhysicalBone::get_weight); + + ClassDB::bind_method(D_METHOD("set_friction", "friction"), &PhysicalBone::set_friction); + ClassDB::bind_method(D_METHOD("get_friction"), &PhysicalBone::get_friction); + + ClassDB::bind_method(D_METHOD("set_bounce", "bounce"), &PhysicalBone::set_bounce); + ClassDB::bind_method(D_METHOD("get_bounce"), &PhysicalBone::get_bounce); + + ClassDB::bind_method(D_METHOD("set_gravity_scale", "gravity_scale"), &PhysicalBone::set_gravity_scale); + ClassDB::bind_method(D_METHOD("get_gravity_scale"), &PhysicalBone::get_gravity_scale); + + 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::TRANSFORM, "joint_offset"), "set_joint_offset", "get_joint_offset"); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "simulate_physics"), "set_simulate_physics", "get_simulate_physics"); + ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "body_offset"), "set_body_offset", "get_body_offset"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "static_body"), "set_static_body", "is_static_body"); + + ADD_PROPERTY(PropertyInfo(Variant::REAL, "mass", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01"), "set_mass", "get_mass"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "weight", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01"), "set_weight", "get_weight"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_friction", "get_friction"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "gravity_scale", PROPERTY_HINT_RANGE, "-10,10,0.01"), "set_gravity_scale", "get_gravity_scale"); +} + +Skeleton *PhysicalBone::find_skeleton_parent(Node *p_parent) { + if (!p_parent) { + return NULL; + } + Skeleton *s = Object::cast_to<Skeleton>(p_parent); + return s ? s : find_skeleton_parent(p_parent->get_parent()); +} + +void PhysicalBone::_fix_joint_offset() { + // Clamp joint origin to bone origin + if (parent_skeleton) { + joint_offset.origin = body_offset.affine_inverse().origin; + } +} + +void PhysicalBone::_reload_joint() { + + if (joint.is_valid()) { + PhysicsServer::get_singleton()->free(joint); + joint = RID(); + } + + if (!parent_skeleton) { + return; + } + + PhysicalBone *body_a = parent_skeleton->get_physical_bone_parent(bone_id); + if (!body_a) { + return; + } + + Transform joint_transf = get_global_transform() * joint_offset; + Transform local_a = body_a->get_global_transform().affine_inverse() * joint_transf; + local_a.orthonormalize(); + + switch (get_joint_type()) { + case JOINT_TYPE_PIN: { + + joint = PhysicsServer::get_singleton()->joint_create_pin(body_a->get_rid(), local_a.origin, get_rid(), joint_offset.origin); + const PinJointData *pjd(static_cast<const PinJointData *>(joint_data)); + PhysicsServer::get_singleton()->pin_joint_set_param(joint, PhysicsServer::PIN_JOINT_BIAS, pjd->bias); + PhysicsServer::get_singleton()->pin_joint_set_param(joint, PhysicsServer::PIN_JOINT_DAMPING, pjd->damping); + PhysicsServer::get_singleton()->pin_joint_set_param(joint, PhysicsServer::PIN_JOINT_IMPULSE_CLAMP, pjd->impulse_clamp); + + } break; + case JOINT_TYPE_CONE: { + + joint = PhysicsServer::get_singleton()->joint_create_cone_twist(body_a->get_rid(), local_a, get_rid(), joint_offset); + const ConeJointData *cjd(static_cast<const ConeJointData *>(joint_data)); + PhysicsServer::get_singleton()->cone_twist_joint_set_param(joint, PhysicsServer::CONE_TWIST_JOINT_SWING_SPAN, cjd->swing_span); + PhysicsServer::get_singleton()->cone_twist_joint_set_param(joint, PhysicsServer::CONE_TWIST_JOINT_TWIST_SPAN, cjd->twist_span); + PhysicsServer::get_singleton()->cone_twist_joint_set_param(joint, PhysicsServer::CONE_TWIST_JOINT_BIAS, cjd->bias); + PhysicsServer::get_singleton()->cone_twist_joint_set_param(joint, PhysicsServer::CONE_TWIST_JOINT_SOFTNESS, cjd->softness); + PhysicsServer::get_singleton()->cone_twist_joint_set_param(joint, PhysicsServer::CONE_TWIST_JOINT_RELAXATION, cjd->relaxation); + + } break; + case JOINT_TYPE_HINGE: { + + joint = PhysicsServer::get_singleton()->joint_create_hinge(body_a->get_rid(), local_a, get_rid(), joint_offset); + const HingeJointData *hjd(static_cast<const HingeJointData *>(joint_data)); + PhysicsServer::get_singleton()->hinge_joint_set_flag(joint, PhysicsServer::HINGE_JOINT_FLAG_USE_LIMIT, hjd->angular_limit_enabled); + PhysicsServer::get_singleton()->hinge_joint_set_param(joint, PhysicsServer::HINGE_JOINT_LIMIT_UPPER, hjd->angular_limit_upper); + PhysicsServer::get_singleton()->hinge_joint_set_param(joint, PhysicsServer::HINGE_JOINT_LIMIT_LOWER, hjd->angular_limit_lower); + PhysicsServer::get_singleton()->hinge_joint_set_param(joint, PhysicsServer::HINGE_JOINT_LIMIT_BIAS, hjd->angular_limit_bias); + PhysicsServer::get_singleton()->hinge_joint_set_param(joint, PhysicsServer::HINGE_JOINT_LIMIT_SOFTNESS, hjd->angular_limit_softness); + PhysicsServer::get_singleton()->hinge_joint_set_param(joint, PhysicsServer::HINGE_JOINT_LIMIT_RELAXATION, hjd->angular_limit_relaxation); + + } break; + case JOINT_TYPE_SLIDER: { + + joint = PhysicsServer::get_singleton()->joint_create_slider(body_a->get_rid(), local_a, get_rid(), joint_offset); + const SliderJointData *sjd(static_cast<const SliderJointData *>(joint_data)); + PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_UPPER, sjd->linear_limit_upper); + PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_LOWER, sjd->linear_limit_lower); + PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_SOFTNESS, sjd->linear_limit_softness); + PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_RESTITUTION, sjd->linear_limit_restitution); + PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_DAMPING, sjd->linear_limit_restitution); + PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_UPPER, sjd->angular_limit_upper); + PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_LOWER, sjd->angular_limit_lower); + PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_SOFTNESS, sjd->angular_limit_softness); + PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_SOFTNESS, sjd->angular_limit_softness); + PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_DAMPING, sjd->angular_limit_damping); + + } break; + case JOINT_TYPE_6DOF: { + + joint = PhysicsServer::get_singleton()->joint_create_generic_6dof(body_a->get_rid(), local_a, get_rid(), joint_offset); + const SixDOFJointData *g6dofjd(static_cast<const SixDOFJointData *>(joint_data)); + for (int axis = 0; axis < 3; ++axis) { + PhysicsServer::get_singleton()->generic_6dof_joint_set_flag(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT, g6dofjd->axis_data[axis].linear_limit_enabled); + PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_LINEAR_UPPER_LIMIT, g6dofjd->axis_data[axis].linear_limit_upper); + PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_LINEAR_LOWER_LIMIT, g6dofjd->axis_data[axis].linear_limit_lower); + PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_LINEAR_LIMIT_SOFTNESS, g6dofjd->axis_data[axis].linear_limit_softness); + PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_LINEAR_RESTITUTION, g6dofjd->axis_data[axis].linear_restitution); + PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_LINEAR_DAMPING, g6dofjd->axis_data[axis].linear_damping); + PhysicsServer::get_singleton()->generic_6dof_joint_set_flag(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT, g6dofjd->axis_data[axis].angular_limit_enabled); + PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_ANGULAR_UPPER_LIMIT, g6dofjd->axis_data[axis].angular_limit_upper); + PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_ANGULAR_LOWER_LIMIT, g6dofjd->axis_data[axis].angular_limit_lower); + PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_ANGULAR_LIMIT_SOFTNESS, g6dofjd->axis_data[axis].angular_limit_softness); + PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_ANGULAR_RESTITUTION, g6dofjd->axis_data[axis].angular_restitution); + PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_ANGULAR_DAMPING, g6dofjd->axis_data[axis].angular_damping); + PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_ANGULAR_ERP, g6dofjd->axis_data[axis].erp); + } + + } break; + } +} + +void PhysicalBone::_on_bone_parent_changed() { + _reload_joint(); +} + +void PhysicalBone::_set_gizmo_move_joint(bool p_move_joint) { +#ifdef TOOLS_ENABLED + gizmo_move_joint = p_move_joint; + SpatialEditor::get_singleton()->update_transform_gizmo(); +#endif +} + +#ifdef TOOLS_ENABLED +Transform PhysicalBone::get_global_gizmo_transform() const { + return gizmo_move_joint ? get_global_transform() * joint_offset : get_global_transform(); +} + +Transform PhysicalBone::get_local_gizmo_transform() const { + return gizmo_move_joint ? get_transform() * joint_offset : get_transform(); +} +#endif + +const PhysicalBone::JointData *PhysicalBone::get_joint_data() const { + return joint_data; +} + +Skeleton *PhysicalBone::find_skeleton_parent() { + return find_skeleton_parent(this); +} + +void PhysicalBone::set_joint_type(JointType p_joint_type) { + + if (p_joint_type == get_joint_type()) + return; + + memdelete(joint_data); + joint_data = NULL; + switch (p_joint_type) { + case JOINT_TYPE_PIN: + joint_data = memnew(PinJointData); + break; + case JOINT_TYPE_CONE: + joint_data = memnew(ConeJointData); + break; + case JOINT_TYPE_HINGE: + joint_data = memnew(HingeJointData); + break; + case JOINT_TYPE_SLIDER: + joint_data = memnew(SliderJointData); + break; + case JOINT_TYPE_6DOF: + joint_data = memnew(SixDOFJointData); + break; + } + + _reload_joint(); + +#ifdef TOOLS_ENABLED + _change_notify(); + if (get_gizmo().is_valid()) + get_gizmo()->redraw(); +#endif +} + +PhysicalBone::JointType PhysicalBone::get_joint_type() const { + return joint_data ? joint_data->get_joint_type() : JOINT_TYPE_NONE; +} + +void PhysicalBone::set_joint_offset(const Transform &p_offset) { + joint_offset = p_offset; + + _fix_joint_offset(); + + set_ignore_transform_notification(true); + reset_to_rest_position(); + set_ignore_transform_notification(false); + +#ifdef TOOLS_ENABLED + if (get_gizmo().is_valid()) + get_gizmo()->redraw(); +#endif +} + +const Transform &PhysicalBone::get_body_offset() const { + return body_offset; +} + +void PhysicalBone::set_body_offset(const Transform &p_offset) { + body_offset = p_offset; + body_offset_inverse = body_offset.affine_inverse(); + + _fix_joint_offset(); + + set_ignore_transform_notification(true); + reset_to_rest_position(); + set_ignore_transform_notification(false); + +#ifdef TOOLS_ENABLED + if (get_gizmo().is_valid()) + get_gizmo()->redraw(); +#endif +} + +const Transform &PhysicalBone::get_joint_offset() const { + return joint_offset; +} + +void PhysicalBone::set_static_body(bool p_static) { + + static_body = p_static; + + set_as_toplevel(!static_body); + + _reset_physics_simulation_state(); +} + +bool PhysicalBone::is_static_body() { + return static_body; +} + +void PhysicalBone::set_simulate_physics(bool p_simulate) { + if (simulate_physics == p_simulate) { + return; + } + + simulate_physics = p_simulate; + _reset_physics_simulation_state(); +} + +bool PhysicalBone::get_simulate_physics() { + return simulate_physics; +} + +bool PhysicalBone::is_simulating_physics() { + return _internal_simulate_physics && !_internal_static_body; +} + +void PhysicalBone::set_bone_name(const String &p_name) { + + bone_name = p_name; + bone_id = -1; + + update_bone_id(); + reset_to_rest_position(); +} + +const String &PhysicalBone::get_bone_name() const { + + return bone_name; +} + +void PhysicalBone::set_mass(real_t p_mass) { + + ERR_FAIL_COND(p_mass <= 0); + mass = p_mass; + PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_MASS, mass); +} + +real_t PhysicalBone::get_mass() const { + + return mass; +} + +void PhysicalBone::set_weight(real_t p_weight) { + + set_mass(p_weight / real_t(GLOBAL_DEF("physics/3d/default_gravity", 9.8))); +} + +real_t PhysicalBone::get_weight() const { + + return mass * real_t(GLOBAL_DEF("physics/3d/default_gravity", 9.8)); +} + +void PhysicalBone::set_friction(real_t p_friction) { + + ERR_FAIL_COND(p_friction < 0 || p_friction > 1); + + friction = p_friction; + PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, friction); +} + +real_t PhysicalBone::get_friction() const { + + return friction; +} + +void PhysicalBone::set_bounce(real_t p_bounce) { + + ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1); + + bounce = p_bounce; + PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, bounce); +} +real_t PhysicalBone::get_bounce() const { + + return bounce; +} + +void PhysicalBone::set_gravity_scale(real_t p_gravity_scale) { + + gravity_scale = p_gravity_scale; + PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_GRAVITY_SCALE, gravity_scale); +} + +real_t PhysicalBone::get_gravity_scale() const { + + return gravity_scale; +} + +PhysicalBone::PhysicalBone() : + PhysicsBody(PhysicsServer::BODY_MODE_STATIC), +#ifdef TOOLS_ENABLED + gizmo_move_joint(false), +#endif + joint_data(NULL), + static_body(false), + simulate_physics(false), + _internal_static_body(!static_body), + _internal_simulate_physics(simulate_physics), + bone_id(-1), + parent_skeleton(NULL), + bone_name(""), + bounce(0), + mass(1), + friction(1), + gravity_scale(1) { + + set_static_body(static_body); + _reset_physics_simulation_state(); +} + +PhysicalBone::~PhysicalBone() { + memdelete(joint_data); +} + +void PhysicalBone::update_bone_id() { + if (!parent_skeleton) { + return; + } + + const int new_bone_id = parent_skeleton->find_bone(bone_name); + + if (new_bone_id != bone_id) { + if (-1 != bone_id) { + // Assert the unbind from old node + parent_skeleton->unbind_physical_bone_from_bone(bone_id); + parent_skeleton->unbind_child_node_from_bone(bone_id, this); + } + + bone_id = new_bone_id; + + parent_skeleton->bind_physical_bone_to_bone(bone_id, this); + + _fix_joint_offset(); + _internal_static_body = !static_body; // Force staticness reset + _reset_staticness_state(); + } +} + +void PhysicalBone::update_offset() { +#ifdef TOOLS_ENABLED + if (parent_skeleton) { + + Transform bone_transform(parent_skeleton->get_global_transform()); + if (-1 != bone_id) + bone_transform *= parent_skeleton->get_bone_global_pose(bone_id); + + if (gizmo_move_joint) { + bone_transform *= body_offset; + set_joint_offset(bone_transform.affine_inverse() * get_global_transform()); + } else { + set_body_offset(bone_transform.affine_inverse() * get_global_transform()); + } + } +#endif +} + +void PhysicalBone::reset_to_rest_position() { + if (parent_skeleton) { + if (-1 == bone_id) { + set_global_transform(parent_skeleton->get_global_transform() * body_offset); + } else { + set_global_transform(parent_skeleton->get_global_transform() * parent_skeleton->get_bone_global_pose(bone_id) * body_offset); + } + } +} + +void PhysicalBone::_reset_physics_simulation_state() { + if (simulate_physics && !static_body) { + _start_physics_simulation(); + } else { + _stop_physics_simulation(); + } + + _reset_staticness_state(); +} + +void PhysicalBone::_reset_staticness_state() { + + if (parent_skeleton && -1 != bone_id) { + if (static_body && simulate_physics) { // With this check I'm sure the position of this body is updated only when it's necessary + + if (_internal_static_body) { + return; + } + + parent_skeleton->bind_child_node_to_bone(bone_id, this); + _internal_static_body = true; + } else { + + if (!_internal_static_body) { + return; + } + + parent_skeleton->unbind_child_node_from_bone(bone_id, this); + _internal_static_body = false; + } + } +} + +void PhysicalBone::_start_physics_simulation() { + if (_internal_simulate_physics || !parent_skeleton) { + return; + } + reset_to_rest_position(); + PhysicsServer::get_singleton()->body_set_mode(get_rid(), PhysicsServer::BODY_MODE_RIGID); + PhysicsServer::get_singleton()->body_set_collision_layer(get_rid(), get_collision_layer()); + PhysicsServer::get_singleton()->body_set_collision_mask(get_rid(), get_collision_mask()); + PhysicsServer::get_singleton()->body_set_force_integration_callback(get_rid(), this, "_direct_state_changed"); + parent_skeleton->set_bone_ignore_animation(bone_id, true); + _internal_simulate_physics = true; +} + +void PhysicalBone::_stop_physics_simulation() { + if (!_internal_simulate_physics || !parent_skeleton) { + return; + } + PhysicsServer::get_singleton()->body_set_mode(get_rid(), PhysicsServer::BODY_MODE_STATIC); + PhysicsServer::get_singleton()->body_set_collision_layer(get_rid(), 0); + PhysicsServer::get_singleton()->body_set_collision_mask(get_rid(), 0); + PhysicsServer::get_singleton()->body_set_force_integration_callback(get_rid(), NULL, ""); + parent_skeleton->set_bone_ignore_animation(bone_id, false); + _internal_simulate_physics = false; +} diff --git a/scene/3d/physics_body.h b/scene/3d/physics_body.h index f64733e707..17d2769c79 100644 --- a/scene/3d/physics_body.h +++ b/scene/3d/physics_body.h @@ -27,11 +27,13 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef PHYSICS_BODY__H #define PHYSICS_BODY__H #include "scene/3d/collision_object.h" #include "servers/physics_server.h" +#include "skeleton.h" #include "vset.h" class PhysicsBody : public CollisionObject { @@ -114,7 +116,7 @@ public: MODE_KINEMATIC, }; -private: +protected: bool can_sleep; PhysicsDirectBodyState *state; Mode mode; @@ -177,9 +179,8 @@ private: void _body_exit_tree(ObjectID p_id); void _body_inout(int p_status, ObjectID p_instance, int p_body_shape, int p_local_shape); - void _direct_state_changed(Object *p_state); + virtual void _direct_state_changed(Object *p_state); -protected: void _notification(int p_what); static void _bind_methods(); @@ -242,6 +243,7 @@ public: Array get_colliding_bodies() const; void apply_impulse(const Vector3 &p_pos, const Vector3 &p_impulse); + void apply_torque_impulse(const Vector3 &p_impulse); virtual String get_configuration_warning() const; @@ -285,15 +287,15 @@ private: _FORCE_INLINE_ bool _ignores_mode(PhysicsServer::BodyMode) const; - Ref<KinematicCollision> _move(const Vector3 &p_motion); + Ref<KinematicCollision> _move(const Vector3 &p_motion, bool p_infinite_inertia = true); Ref<KinematicCollision> _get_slide_collision(int p_bounce); protected: static void _bind_methods(); public: - bool move_and_collide(const Vector3 &p_motion, Collision &r_collision); - bool test_move(const Transform &p_from, const Vector3 &p_motion); + bool move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, Collision &r_collision); + bool test_move(const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia); void set_axis_lock(PhysicsServer::BodyAxis p_axis, bool p_lock); bool get_axis_lock(PhysicsServer::BodyAxis p_axis) const; @@ -301,7 +303,7 @@ public: void set_safe_margin(float p_margin); float get_safe_margin() const; - Vector3 move_and_slide(const Vector3 &p_linear_velocity, const Vector3 &p_floor_direction = Vector3(0, 0, 0), float p_slope_stop_min_velocity = 0.05, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45)); + Vector3 move_and_slide(const Vector3 &p_linear_velocity, const Vector3 &p_floor_direction = Vector3(0, 0, 0), bool p_infinite_inertia = true, float p_slope_stop_min_velocity = 0.05, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45)); bool is_on_floor() const; bool is_on_wall() const; bool is_on_ceiling() const; @@ -341,4 +343,267 @@ public: KinematicCollision(); }; +class PhysicalBone : public PhysicsBody { + + GDCLASS(PhysicalBone, PhysicsBody); + +public: + enum JointType { + JOINT_TYPE_NONE, + JOINT_TYPE_PIN, + JOINT_TYPE_CONE, + JOINT_TYPE_HINGE, + JOINT_TYPE_SLIDER, + JOINT_TYPE_6DOF + }; + + struct JointData { + virtual JointType get_joint_type() { return JOINT_TYPE_NONE; } + + /// "j" is used to set the parameter inside the PhysicsServer + virtual bool _set(const StringName &p_name, const Variant &p_value, RID j = RID()); + virtual bool _get(const StringName &p_name, Variant &r_ret) const; + virtual void _get_property_list(List<PropertyInfo> *p_list) const; + }; + + struct PinJointData : public JointData { + virtual JointType get_joint_type() { return JOINT_TYPE_PIN; } + + virtual bool _set(const StringName &p_name, const Variant &p_value, RID j = RID()); + virtual bool _get(const StringName &p_name, Variant &r_ret) const; + virtual void _get_property_list(List<PropertyInfo> *p_list) const; + + real_t bias; + real_t damping; + real_t impulse_clamp; + + PinJointData() : + bias(0.3), + damping(1.), + impulse_clamp(0) {} + }; + + struct ConeJointData : public JointData { + virtual JointType get_joint_type() { return JOINT_TYPE_CONE; } + + virtual bool _set(const StringName &p_name, const Variant &p_value, RID j = RID()); + virtual bool _get(const StringName &p_name, Variant &r_ret) const; + virtual void _get_property_list(List<PropertyInfo> *p_list) const; + + real_t swing_span; + real_t twist_span; + real_t bias; + real_t softness; + real_t relaxation; + + ConeJointData() : + swing_span(Math_PI * 0.25), + twist_span(Math_PI), + bias(0.3), + softness(0.8), + relaxation(1.) {} + }; + + struct HingeJointData : public JointData { + virtual JointType get_joint_type() { return JOINT_TYPE_HINGE; } + + virtual bool _set(const StringName &p_name, const Variant &p_value, RID j = RID()); + virtual bool _get(const StringName &p_name, Variant &r_ret) const; + virtual void _get_property_list(List<PropertyInfo> *p_list) const; + + bool angular_limit_enabled; + real_t angular_limit_upper; + real_t angular_limit_lower; + real_t angular_limit_bias; + real_t angular_limit_softness; + real_t angular_limit_relaxation; + + HingeJointData() : + angular_limit_enabled(false), + angular_limit_upper(Math_PI * 0.5), + angular_limit_lower(-Math_PI * 0.5), + angular_limit_bias(0.3), + angular_limit_softness(0.9), + angular_limit_relaxation(1.) {} + }; + + struct SliderJointData : public JointData { + virtual JointType get_joint_type() { return JOINT_TYPE_SLIDER; } + + virtual bool _set(const StringName &p_name, const Variant &p_value, RID j = RID()); + virtual bool _get(const StringName &p_name, Variant &r_ret) const; + virtual void _get_property_list(List<PropertyInfo> *p_list) const; + + real_t linear_limit_upper; + real_t linear_limit_lower; + real_t linear_limit_softness; + real_t linear_limit_restitution; + real_t linear_limit_damping; + real_t angular_limit_upper; + real_t angular_limit_lower; + real_t angular_limit_softness; + real_t angular_limit_restitution; + real_t angular_limit_damping; + + SliderJointData() : + linear_limit_upper(1.), + linear_limit_lower(-1.), + linear_limit_softness(1.), + linear_limit_restitution(0.7), + linear_limit_damping(1.), + angular_limit_upper(0), + angular_limit_lower(0), + angular_limit_softness(1.), + angular_limit_restitution(0.7), + angular_limit_damping(1.) {} + }; + + struct SixDOFJointData : public JointData { + struct SixDOFAxisData { + bool linear_limit_enabled; + real_t linear_limit_upper; + real_t linear_limit_lower; + real_t linear_limit_softness; + real_t linear_restitution; + real_t linear_damping; + bool angular_limit_enabled; + real_t angular_limit_upper; + real_t angular_limit_lower; + real_t angular_limit_softness; + real_t angular_restitution; + real_t angular_damping; + real_t erp; + + SixDOFAxisData() : + linear_limit_enabled(true), + linear_limit_upper(0), + linear_limit_lower(0), + linear_limit_softness(0.7), + linear_restitution(0.5), + linear_damping(1.), + angular_limit_enabled(true), + angular_limit_upper(0), + angular_limit_lower(0), + angular_limit_softness(0.5), + angular_restitution(0), + angular_damping(1.), + erp(0.5) {} + }; + + virtual JointType get_joint_type() { return JOINT_TYPE_6DOF; } + + virtual bool _set(const StringName &p_name, const Variant &p_value, RID j = RID()); + virtual bool _get(const StringName &p_name, Variant &r_ret) const; + virtual void _get_property_list(List<PropertyInfo> *p_list) const; + + SixDOFAxisData axis_data[3]; + + SixDOFJointData() {} + }; + +private: +#ifdef TOOLS_ENABLED + // if false gizmo move body + bool gizmo_move_joint; +#endif + + JointData *joint_data; + Transform joint_offset; + RID joint; + + Skeleton *parent_skeleton; + Transform body_offset; + Transform body_offset_inverse; + bool static_body; + bool _internal_static_body; + bool simulate_physics; + bool _internal_simulate_physics; + int bone_id; + + String bone_name; + real_t bounce; + real_t mass; + real_t friction; + real_t gravity_scale; + +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 _notification(int p_what); + void _direct_state_changed(Object *p_state); + + static void _bind_methods(); + +private: + static Skeleton *find_skeleton_parent(Node *p_parent); + void _fix_joint_offset(); + void _reload_joint(); + +public: + void _on_bone_parent_changed(); + void _set_gizmo_move_joint(bool p_move_joint); + +public: +#ifdef TOOLS_ENABLED + virtual Transform get_global_gizmo_transform() const; + virtual Transform get_local_gizmo_transform() const; +#endif + + const JointData *get_joint_data() const; + Skeleton *find_skeleton_parent(); + + int get_bone_id() const { return bone_id; } + + void set_joint_type(JointType p_joint_type); + JointType get_joint_type() const; + + void set_joint_offset(const Transform &p_offset); + const Transform &get_joint_offset() const; + + void set_body_offset(const Transform &p_offset); + const Transform &get_body_offset() const; + + void set_static_body(bool p_static); + bool is_static_body(); + + void set_simulate_physics(bool p_simulate); + bool get_simulate_physics(); + bool is_simulating_physics(); + + void set_bone_name(const String &p_name); + const String &get_bone_name() const; + + void set_mass(real_t p_mass); + real_t get_mass() const; + + void set_weight(real_t p_weight); + real_t get_weight() const; + + void set_friction(real_t p_friction); + real_t get_friction() const; + + void set_bounce(real_t p_bounce); + real_t get_bounce() const; + + void set_gravity_scale(real_t p_gravity_scale); + real_t get_gravity_scale() const; + + PhysicalBone(); + ~PhysicalBone(); + +private: + void update_bone_id(); + void update_offset(); + void reset_to_rest_position(); + + void _reset_physics_simulation_state(); + void _reset_staticness_state(); + + void _start_physics_simulation(); + void _stop_physics_simulation(); +}; + +VARIANT_ENUM_CAST(PhysicalBone::JointType); + #endif // PHYSICS_BODY__H diff --git a/scene/3d/physics_joint.cpp b/scene/3d/physics_joint.cpp index c36e684477..b2d10006f7 100644 --- a/scene/3d/physics_joint.cpp +++ b/scene/3d/physics_joint.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "physics_joint.h" void Joint::_update_joint(bool p_only_free) { @@ -70,8 +71,7 @@ void Joint::_update_joint(bool p_only_free) { ba = body_a->get_rid(); bb = body_b->get_rid(); - if (exclude_from_collision) - PhysicsServer::get_singleton()->body_add_collision_exception(body_a->get_rid(), body_b->get_rid()); + PhysicsServer::get_singleton()->joint_disable_collisions_between_bodies(joint, exclude_from_collision); } void Joint::set_node_a(const NodePath &p_node_a) { @@ -716,6 +716,9 @@ void Generic6DOFJoint::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_limit_x/softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_x", "get_param_x", PARAM_LINEAR_LIMIT_SOFTNESS); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "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::REAL, "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::REAL, "linear_motor_x/target_velocity"), "set_param_x", "get_param_x", PARAM_LINEAR_MOTOR_TARGET_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_motor_x/force_limit"), "set_param_x", "get_param_x", PARAM_LINEAR_MOTOR_FORCE_LIMIT); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_limit_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_ANGULAR_LIMIT); ADD_PROPERTY(PropertyInfo(Variant::REAL, "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::REAL, "angular_limit_x/lower_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_lo_limit_x", "_get_angular_lo_limit_x"); @@ -734,6 +737,9 @@ void Generic6DOFJoint::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_limit_y/softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_LINEAR_LIMIT_SOFTNESS); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "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::REAL, "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::REAL, "linear_motor_y/target_velocity"), "set_param_y", "get_param_y", PARAM_LINEAR_MOTOR_TARGET_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_motor_y/force_limit"), "set_param_y", "get_param_y", PARAM_LINEAR_MOTOR_FORCE_LIMIT); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_limit_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_ANGULAR_LIMIT); ADD_PROPERTY(PropertyInfo(Variant::REAL, "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::REAL, "angular_limit_y/lower_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_lo_limit_y", "_get_angular_lo_limit_y"); @@ -752,6 +758,9 @@ void Generic6DOFJoint::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_limit_z/softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_z", "get_param_z", PARAM_LINEAR_LIMIT_SOFTNESS); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "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::REAL, "linear_limit_z/damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_z", "get_param_z", PARAM_LINEAR_DAMPING); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_motor_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_LINEAR_MOTOR); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_motor_z/target_velocity"), "set_param_z", "get_param_z", PARAM_LINEAR_MOTOR_TARGET_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_motor_z/force_limit"), "set_param_z", "get_param_z", PARAM_LINEAR_MOTOR_FORCE_LIMIT); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_limit_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_ANGULAR_LIMIT); ADD_PROPERTY(PropertyInfo(Variant::REAL, "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::REAL, "angular_limit_z/lower_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_lo_limit_z", "_get_angular_lo_limit_z"); @@ -769,6 +778,8 @@ void Generic6DOFJoint::_bind_methods() { BIND_ENUM_CONSTANT(PARAM_LINEAR_LIMIT_SOFTNESS); BIND_ENUM_CONSTANT(PARAM_LINEAR_RESTITUTION); BIND_ENUM_CONSTANT(PARAM_LINEAR_DAMPING); + BIND_ENUM_CONSTANT(PARAM_LINEAR_MOTOR_TARGET_VELOCITY); + BIND_ENUM_CONSTANT(PARAM_LINEAR_MOTOR_FORCE_LIMIT); BIND_ENUM_CONSTANT(PARAM_ANGULAR_LOWER_LIMIT); BIND_ENUM_CONSTANT(PARAM_ANGULAR_UPPER_LIMIT); BIND_ENUM_CONSTANT(PARAM_ANGULAR_LIMIT_SOFTNESS); @@ -783,6 +794,7 @@ void Generic6DOFJoint::_bind_methods() { BIND_ENUM_CONSTANT(FLAG_ENABLE_LINEAR_LIMIT); BIND_ENUM_CONSTANT(FLAG_ENABLE_ANGULAR_LIMIT); BIND_ENUM_CONSTANT(FLAG_ENABLE_MOTOR); + BIND_ENUM_CONSTANT(FLAG_ENABLE_LINEAR_MOTOR); BIND_ENUM_CONSTANT(FLAG_MAX); } @@ -912,6 +924,8 @@ Generic6DOFJoint::Generic6DOFJoint() { set_param_x(PARAM_LINEAR_LIMIT_SOFTNESS, 0.7); set_param_x(PARAM_LINEAR_RESTITUTION, 0.5); set_param_x(PARAM_LINEAR_DAMPING, 1.0); + set_param_x(PARAM_LINEAR_MOTOR_TARGET_VELOCITY, 0); + set_param_x(PARAM_LINEAR_MOTOR_FORCE_LIMIT, 0); set_param_x(PARAM_ANGULAR_LOWER_LIMIT, 0); set_param_x(PARAM_ANGULAR_UPPER_LIMIT, 0); set_param_x(PARAM_ANGULAR_LIMIT_SOFTNESS, 0.5f); @@ -925,12 +939,15 @@ Generic6DOFJoint::Generic6DOFJoint() { set_flag_x(FLAG_ENABLE_ANGULAR_LIMIT, true); set_flag_x(FLAG_ENABLE_LINEAR_LIMIT, true); set_flag_x(FLAG_ENABLE_MOTOR, false); + set_flag_x(FLAG_ENABLE_LINEAR_MOTOR, false); set_param_y(PARAM_LINEAR_LOWER_LIMIT, 0); set_param_y(PARAM_LINEAR_UPPER_LIMIT, 0); set_param_y(PARAM_LINEAR_LIMIT_SOFTNESS, 0.7); set_param_y(PARAM_LINEAR_RESTITUTION, 0.5); set_param_y(PARAM_LINEAR_DAMPING, 1.0); + set_param_y(PARAM_LINEAR_MOTOR_TARGET_VELOCITY, 0); + set_param_y(PARAM_LINEAR_MOTOR_FORCE_LIMIT, 0); set_param_y(PARAM_ANGULAR_LOWER_LIMIT, 0); set_param_y(PARAM_ANGULAR_UPPER_LIMIT, 0); set_param_y(PARAM_ANGULAR_LIMIT_SOFTNESS, 0.5f); @@ -944,12 +961,15 @@ Generic6DOFJoint::Generic6DOFJoint() { set_flag_y(FLAG_ENABLE_ANGULAR_LIMIT, true); set_flag_y(FLAG_ENABLE_LINEAR_LIMIT, true); set_flag_y(FLAG_ENABLE_MOTOR, false); + set_flag_y(FLAG_ENABLE_LINEAR_MOTOR, false); set_param_z(PARAM_LINEAR_LOWER_LIMIT, 0); set_param_z(PARAM_LINEAR_UPPER_LIMIT, 0); set_param_z(PARAM_LINEAR_LIMIT_SOFTNESS, 0.7); set_param_z(PARAM_LINEAR_RESTITUTION, 0.5); set_param_z(PARAM_LINEAR_DAMPING, 1.0); + set_param_z(PARAM_LINEAR_MOTOR_TARGET_VELOCITY, 0); + set_param_z(PARAM_LINEAR_MOTOR_FORCE_LIMIT, 0); set_param_z(PARAM_ANGULAR_LOWER_LIMIT, 0); set_param_z(PARAM_ANGULAR_UPPER_LIMIT, 0); set_param_z(PARAM_ANGULAR_LIMIT_SOFTNESS, 0.5f); @@ -963,4 +983,5 @@ Generic6DOFJoint::Generic6DOFJoint() { set_flag_z(FLAG_ENABLE_ANGULAR_LIMIT, true); set_flag_z(FLAG_ENABLE_LINEAR_LIMIT, true); set_flag_z(FLAG_ENABLE_MOTOR, false); + set_flag_z(FLAG_ENABLE_LINEAR_MOTOR, false); } diff --git a/scene/3d/physics_joint.h b/scene/3d/physics_joint.h index 1520c40cdd..37870d6f30 100644 --- a/scene/3d/physics_joint.h +++ b/scene/3d/physics_joint.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef PHYSICS_JOINT_H #define PHYSICS_JOINT_H @@ -248,6 +249,8 @@ public: PARAM_LINEAR_LIMIT_SOFTNESS = PhysicsServer::G6DOF_JOINT_LINEAR_LIMIT_SOFTNESS, PARAM_LINEAR_RESTITUTION = PhysicsServer::G6DOF_JOINT_LINEAR_RESTITUTION, PARAM_LINEAR_DAMPING = PhysicsServer::G6DOF_JOINT_LINEAR_DAMPING, + PARAM_LINEAR_MOTOR_TARGET_VELOCITY = PhysicsServer::G6DOF_JOINT_LINEAR_MOTOR_TARGET_VELOCITY, + PARAM_LINEAR_MOTOR_FORCE_LIMIT = PhysicsServer::G6DOF_JOINT_LINEAR_MOTOR_FORCE_LIMIT, PARAM_ANGULAR_LOWER_LIMIT = PhysicsServer::G6DOF_JOINT_ANGULAR_LOWER_LIMIT, PARAM_ANGULAR_UPPER_LIMIT = PhysicsServer::G6DOF_JOINT_ANGULAR_UPPER_LIMIT, PARAM_ANGULAR_LIMIT_SOFTNESS = PhysicsServer::G6DOF_JOINT_ANGULAR_LIMIT_SOFTNESS, @@ -264,6 +267,7 @@ public: FLAG_ENABLE_LINEAR_LIMIT = PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT, FLAG_ENABLE_ANGULAR_LIMIT = PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT, FLAG_ENABLE_MOTOR = PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_MOTOR, + FLAG_ENABLE_LINEAR_MOTOR = PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_MOTOR, FLAG_MAX = PhysicsServer::G6DOF_JOINT_FLAG_MAX }; diff --git a/scene/3d/portal.cpp b/scene/3d/portal.cpp index 35ef6fa2f1..d16d9ed7c5 100644 --- a/scene/3d/portal.cpp +++ b/scene/3d/portal.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "portal.h" #include "project_settings.h" #include "scene/resources/surface_tool.h" diff --git a/scene/3d/portal.h b/scene/3d/portal.h index 545dd61650..cb3f208072 100644 --- a/scene/3d/portal.h +++ b/scene/3d/portal.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef PORTAL_H #define PORTAL_H diff --git a/scene/3d/position_3d.cpp b/scene/3d/position_3d.cpp index 955c54a008..3ae04f491f 100644 --- a/scene/3d/position_3d.cpp +++ b/scene/3d/position_3d.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "position_3d.h" #include "scene/resources/mesh.h" diff --git a/scene/3d/position_3d.h b/scene/3d/position_3d.h index 59512ea108..2bc69eff9b 100644 --- a/scene/3d/position_3d.h +++ b/scene/3d/position_3d.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef POSITION_3D_H #define POSITION_3D_H diff --git a/scene/3d/proximity_group.cpp b/scene/3d/proximity_group.cpp index 7daf43aefd..101d9ed70c 100644 --- a/scene/3d/proximity_group.cpp +++ b/scene/3d/proximity_group.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "proximity_group.h" #include "math_funcs.h" @@ -111,11 +112,6 @@ void ProximityGroup::_new_group(StringName p_name) { groups[p_name] = group_version; }; -void ProximityGroup::set_group_name(String p_group_name) { - - group_name = p_group_name; -}; - void ProximityGroup::_notification(int p_what) { switch (p_what) { @@ -152,9 +148,24 @@ void ProximityGroup::_proximity_group_broadcast(String p_name, Variant p_params) }; }; -void ProximityGroup::set_dispatch_mode(int p_mode) { +void ProximityGroup::set_group_name(const String &p_group_name) { + + group_name = p_group_name; +}; + +String ProximityGroup::get_group_name() const { + + return group_name; +}; + +void ProximityGroup::set_dispatch_mode(DispatchMode p_mode) { + + dispatch_mode = p_mode; +}; + +ProximityGroup::DispatchMode ProximityGroup::get_dispatch_mode() const { - dispatch_mode = (DispatchMode)p_mode; + return dispatch_mode; }; void ProximityGroup::set_grid_radius(const Vector3 &p_radius) { @@ -170,15 +181,22 @@ Vector3 ProximityGroup::get_grid_radius() const { void ProximityGroup::_bind_methods() { ClassDB::bind_method(D_METHOD("set_group_name", "name"), &ProximityGroup::set_group_name); - ClassDB::bind_method(D_METHOD("broadcast", "name", "parameters"), &ProximityGroup::broadcast); + ClassDB::bind_method(D_METHOD("get_group_name"), &ProximityGroup::get_group_name); ClassDB::bind_method(D_METHOD("set_dispatch_mode", "mode"), &ProximityGroup::set_dispatch_mode); - ClassDB::bind_method(D_METHOD("_proximity_group_broadcast", "name", "params"), &ProximityGroup::_proximity_group_broadcast); + ClassDB::bind_method(D_METHOD("get_dispatch_mode"), &ProximityGroup::get_dispatch_mode); ClassDB::bind_method(D_METHOD("set_grid_radius", "radius"), &ProximityGroup::set_grid_radius); ClassDB::bind_method(D_METHOD("get_grid_radius"), &ProximityGroup::get_grid_radius); + ClassDB::bind_method(D_METHOD("broadcast", "name", "parameters"), &ProximityGroup::broadcast); + ClassDB::bind_method(D_METHOD("_proximity_group_broadcast", "name", "params"), &ProximityGroup::_proximity_group_broadcast); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "group_name"), "set_group_name", "get_group_name"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "dispatch_mode", PROPERTY_HINT_ENUM, "Proxy,Signal"), "set_dispatch_mode", "get_dispatch_mode"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "grid_radius"), "set_grid_radius", "get_grid_radius"); - ADD_SIGNAL(MethodInfo("broadcast", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::ARRAY, "parameters"))); + ADD_SIGNAL(MethodInfo("broadcast", PropertyInfo(Variant::STRING, "group_name"), PropertyInfo(Variant::ARRAY, "parameters"))); + + BIND_ENUM_CONSTANT(MODE_PROXY); + BIND_ENUM_CONSTANT(MODE_SIGNAL); }; ProximityGroup::ProximityGroup() { diff --git a/scene/3d/proximity_group.h b/scene/3d/proximity_group.h index 7c61c0f949..448f30bf80 100644 --- a/scene/3d/proximity_group.h +++ b/scene/3d/proximity_group.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef PROXIMITY_GROUP_H #define PROXIMITY_GROUP_H @@ -66,15 +67,21 @@ public: static void _bind_methods(); public: - void set_group_name(String p_group_name); - void broadcast(String p_name, Variant p_params); - void set_dispatch_mode(int p_mode); + void set_group_name(const String &p_group_name); + String get_group_name() const; + + void set_dispatch_mode(DispatchMode p_mode); + DispatchMode get_dispatch_mode() const; void set_grid_radius(const Vector3 &p_radius); Vector3 get_grid_radius() const; + void broadcast(String p_name, Variant p_params); + ProximityGroup(); ~ProximityGroup(); }; +VARIANT_ENUM_CAST(ProximityGroup::DispatchMode); + #endif diff --git a/scene/3d/ray_cast.cpp b/scene/3d/ray_cast.cpp index 962af4b244..7f83e2c3ea 100644 --- a/scene/3d/ray_cast.cpp +++ b/scene/3d/ray_cast.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "ray_cast.h" #include "collision_object.h" @@ -102,7 +103,7 @@ void RayCast::set_enabled(bool p_enabled) { enabled = p_enabled; if (is_inside_tree() && !Engine::get_singleton()->is_editor_hint()) - set_physics_process(p_enabled); + set_physics_process_internal(p_enabled); if (!p_enabled) collided = false; @@ -149,12 +150,12 @@ void RayCast::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: { if (enabled && !Engine::get_singleton()->is_editor_hint()) { - set_physics_process(true); + set_physics_process_internal(true); if (get_tree()->is_debugging_collisions_hint()) _update_debug_shape(); } else - set_physics_process(false); + set_physics_process_internal(false); if (Object::cast_to<CollisionObject>(get_parent())) { if (exclude_parent_body) @@ -167,14 +168,14 @@ void RayCast::_notification(int p_what) { case NOTIFICATION_EXIT_TREE: { if (enabled) { - set_physics_process(false); + set_physics_process_internal(false); } if (debug_shape) _clear_debug_shape(); } break; - case NOTIFICATION_PHYSICS_PROCESS: { + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { if (!enabled) break; @@ -216,6 +217,8 @@ void RayCast::_update_raycast_state() { against_shape = rr.shape; } else { collided = false; + against = 0; + against_shape = 0; } } diff --git a/scene/3d/ray_cast.h b/scene/3d/ray_cast.h index c44fd907e5..20cea80700 100644 --- a/scene/3d/ray_cast.h +++ b/scene/3d/ray_cast.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef RAY_CAST_H #define RAY_CAST_H diff --git a/scene/3d/reflection_probe.cpp b/scene/3d/reflection_probe.cpp index b8fa50357a..2178da02b5 100644 --- a/scene/3d/reflection_probe.cpp +++ b/scene/3d/reflection_probe.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "reflection_probe.h" void ReflectionProbe::set_intensity(float p_intensity) { @@ -194,7 +195,7 @@ void ReflectionProbe::_validate_property(PropertyInfo &property) const { if (property.name == "interior/ambient_color" || property.name == "interior/ambient_energy" || property.name == "interior/ambient_contrib") { if (!interior) { - property.usage = PROPERTY_USAGE_NOEDITOR; + property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL; } } } diff --git a/scene/3d/reflection_probe.h b/scene/3d/reflection_probe.h index 742e4121f7..13ae1c81f6 100644 --- a/scene/3d/reflection_probe.h +++ b/scene/3d/reflection_probe.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef REFLECTIONPROBE_H #define REFLECTIONPROBE_H diff --git a/scene/3d/remote_transform.h b/scene/3d/remote_transform.h index 5fc9b071b0..6b788a2d75 100644 --- a/scene/3d/remote_transform.h +++ b/scene/3d/remote_transform.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef REMOTETRANSFORM_H #define REMOTETRANSFORM_H diff --git a/scene/3d/room_instance.cpp b/scene/3d/room_instance.cpp index 94ad07c617..0d83a9942e 100644 --- a/scene/3d/room_instance.cpp +++ b/scene/3d/room_instance.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "room_instance.h" #include "servers/visual_server.h" diff --git a/scene/3d/room_instance.h b/scene/3d/room_instance.h index dc066c73c3..ff388e606c 100644 --- a/scene/3d/room_instance.h +++ b/scene/3d/room_instance.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef ROOM_INSTANCE_H #define ROOM_INSTANCE_H diff --git a/scene/3d/scenario_fx.cpp b/scene/3d/scenario_fx.cpp index cefe3e46f3..d5bff676cb 100644 --- a/scene/3d/scenario_fx.cpp +++ b/scene/3d/scenario_fx.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "scenario_fx.h" #include "scene/main/viewport.h" @@ -78,7 +79,11 @@ Ref<Environment> WorldEnvironment::get_environment() const { String WorldEnvironment::get_configuration_warning() const { - if (/*!is_visible_in_tree() ||*/ !is_inside_tree() || !environment.is_valid()) + if (!environment.is_valid()) { + return TTR("WorldEnvironment needs an Environment resource."); + } + + if (/*!is_visible_in_tree() ||*/ !is_inside_tree()) return String(); List<Node *> nodes; @@ -88,6 +93,10 @@ String WorldEnvironment::get_configuration_warning() const { return TTR("Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."); } + if (environment.is_valid() && get_viewport() && !get_viewport()->get_camera() && environment->get_background() != Environment::BG_CANVAS) { + return TTR("This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set this environment's Background Mode to Canvas (for 2D scenes)."); + } + return String(); } diff --git a/scene/3d/scenario_fx.h b/scene/3d/scenario_fx.h index 3a8a927f88..7a8e2b548f 100644 --- a/scene/3d/scenario_fx.h +++ b/scene/3d/scenario_fx.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef SCENARIO_FX_H #define SCENARIO_FX_H diff --git a/scene/3d/skeleton.cpp b/scene/3d/skeleton.cpp index ef60a2151d..a7eb54c85d 100644 --- a/scene/3d/skeleton.cpp +++ b/scene/3d/skeleton.cpp @@ -27,11 +27,13 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "skeleton.h" #include "message_queue.h" #include "core/project_settings.h" +#include "scene/3d/physics_body.h" #include "scene/resources/surface_tool.h" bool Skeleton::_set(const StringName &p_path, const Variant &p_value) { @@ -60,7 +62,7 @@ bool Skeleton::_set(const StringName &p_path, const Variant &p_value) { set_bone_enabled(which, p_value); else if (what == "pose") set_bone_pose(which, p_value); - else if (what == "bound_childs") { + else if (what == "bound_children") { Array children = p_value; if (is_inside_tree()) { @@ -104,7 +106,7 @@ bool Skeleton::_get(const StringName &p_path, Variant &r_ret) const { r_ret = is_bone_enabled(which); else if (what == "pose") r_ret = get_bone_pose(which); - else if (what == "bound_childs") { + else if (what == "bound_children") { Array children; for (const List<uint32_t>::Element *E = bones[which].nodes_bound.front(); E; E = E->next()) { @@ -133,7 +135,7 @@ void Skeleton::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::TRANSFORM, prep + "rest")); p_list->push_back(PropertyInfo(Variant::BOOL, prep + "enabled")); p_list->push_back(PropertyInfo(Variant::TRANSFORM, prep + "pose", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); - p_list->push_back(PropertyInfo(Variant::ARRAY, prep + "bound_childs")); + p_list->push_back(PropertyInfo(Variant::ARRAY, prep + "bound_children")); } } @@ -160,14 +162,14 @@ void Skeleton::_notification(int p_what) { //if moved, just update transforms VisualServer *vs = VisualServer::get_singleton(); - Bone *bonesptr = &bones[0]; + const Bone *bonesptr = bones.ptr(); int len = bones.size(); Transform global_transform = get_global_transform(); Transform global_transform_inverse = global_transform.affine_inverse(); for (int i = 0; i < len; i++) { - Bone &b = bonesptr[i]; + const Bone &b = bonesptr[i]; vs->skeleton_bone_set_transform(skeleton, i, global_transform * (b.transform_final * global_transform_inverse)); } } break; @@ -376,6 +378,17 @@ void Skeleton::unparent_bone_and_rest(int p_bone) { _make_dirty(); } +void Skeleton::set_bone_ignore_animation(int p_bone, bool p_ignore) { + ERR_FAIL_INDEX(p_bone, bones.size()); + bones[p_bone].ignore_animation = p_ignore; +} + +bool Skeleton::is_bone_ignore_animation(int p_bone) const { + + ERR_FAIL_INDEX_V(p_bone, bones.size(), false); + return bones[p_bone].ignore_animation; +} + void Skeleton::set_bone_disable_rest(int p_bone, bool p_disable) { ERR_FAIL_INDEX(p_bone, bones.size()); @@ -521,6 +534,103 @@ void Skeleton::localize_rests() { } } +void _notify_physical_bones_simulation(bool start, Node *p_node) { + + for (int i = p_node->get_child_count() - 1; 0 <= i; --i) { + _notify_physical_bones_simulation(start, p_node->get_child(i)); + } + + PhysicalBone *pb = Object::cast_to<PhysicalBone>(p_node); + if (pb) { + pb->set_simulate_physics(start); + } +} + +void Skeleton::bind_physical_bone_to_bone(int p_bone, PhysicalBone *p_physical_bone) { + ERR_FAIL_INDEX(p_bone, bones.size()); + ERR_FAIL_COND(bones[p_bone].physical_bone); + ERR_FAIL_COND(!p_physical_bone); + bones[p_bone].physical_bone = p_physical_bone; + + _rebuild_physical_bones_cache(); +} + +void Skeleton::unbind_physical_bone_from_bone(int p_bone) { + ERR_FAIL_INDEX(p_bone, bones.size()); + bones[p_bone].physical_bone = NULL; + + _rebuild_physical_bones_cache(); +} + +PhysicalBone *Skeleton::get_physical_bone(int p_bone) { + ERR_FAIL_INDEX_V(p_bone, bones.size(), NULL); + + return bones[p_bone].physical_bone; +} + +PhysicalBone *Skeleton::get_physical_bone_parent(int p_bone) { + ERR_FAIL_INDEX_V(p_bone, bones.size(), NULL); + + if (bones[p_bone].cache_parent_physical_bone) { + return bones[p_bone].cache_parent_physical_bone; + } + + return _get_physical_bone_parent(p_bone); +} + +PhysicalBone *Skeleton::_get_physical_bone_parent(int p_bone) { + ERR_FAIL_INDEX_V(p_bone, bones.size(), NULL); + + const int parent_bone = bones[p_bone].parent; + if (0 > parent_bone) { + return NULL; + } + + PhysicalBone *pb = bones[parent_bone].physical_bone; + if (pb) { + return pb; + } else { + return get_physical_bone_parent(parent_bone); + } +} + +void Skeleton::_rebuild_physical_bones_cache() { + const int b_size = bones.size(); + for (int i = 0; i < b_size; ++i) { + bones[i].cache_parent_physical_bone = _get_physical_bone_parent(i); + if (bones[i].physical_bone) + bones[i].physical_bone->_on_bone_parent_changed(); + } +} + +void Skeleton::physical_bones_simulation(bool start) { + _notify_physical_bones_simulation(start, this); +} + +void _physical_bones_add_remove_collision_exception(bool p_add, Node *p_node, RID p_exception) { + + for (int i = p_node->get_child_count() - 1; 0 <= i; --i) { + _physical_bones_add_remove_collision_exception(p_add, p_node->get_child(i), p_exception); + } + + CollisionObject *co = Object::cast_to<CollisionObject>(p_node); + if (co) { + if (p_add) { + PhysicsServer::get_singleton()->body_add_collision_exception(co->get_rid(), p_exception); + } else { + PhysicsServer::get_singleton()->body_remove_collision_exception(co->get_rid(), p_exception); + } + } +} + +void Skeleton::physical_bones_add_collision_exception(RID p_exception) { + _physical_bones_add_remove_collision_exception(true, this, p_exception); +} + +void Skeleton::physical_bones_remove_collision_exception(RID p_exception) { + _physical_bones_add_remove_collision_exception(false, this, p_exception); +} + void Skeleton::_bind_methods() { ClassDB::bind_method(D_METHOD("add_bone", "name"), &Skeleton::add_bone); @@ -557,6 +667,10 @@ void Skeleton::_bind_methods() { ClassDB::bind_method(D_METHOD("get_bone_transform", "bone_idx"), &Skeleton::get_bone_transform); + ClassDB::bind_method(D_METHOD("physical_bones_simulation", "start"), &Skeleton::physical_bones_simulation); + ClassDB::bind_method(D_METHOD("physical_bones_add_collision_exception", "exception"), &Skeleton::physical_bones_add_collision_exception); + ClassDB::bind_method(D_METHOD("physical_bones_remove_collision_exception", "exception"), &Skeleton::physical_bones_remove_collision_exception); + BIND_CONSTANT(NOTIFICATION_UPDATE_SKELETON); } diff --git a/scene/3d/skeleton.h b/scene/3d/skeleton.h index 1b999d91c3..f0e71c8b4f 100644 --- a/scene/3d/skeleton.h +++ b/scene/3d/skeleton.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef SKELETON_H #define SKELETON_H @@ -36,6 +37,8 @@ /** @author Juan Linietsky <reduzio@gmail.com> */ + +class PhysicalBone; class Skeleton : public Spatial { GDCLASS(Skeleton, Spatial); @@ -47,6 +50,8 @@ class Skeleton : public Spatial { bool enabled; int parent; + bool ignore_animation; + bool disable_rest; Transform rest; Transform rest_global_inverse; @@ -59,13 +64,19 @@ class Skeleton : public Spatial { Transform transform_final; + PhysicalBone *physical_bone; + PhysicalBone *cache_parent_physical_bone; + List<uint32_t> nodes_bound; Bone() { parent = -1; enabled = true; + ignore_animation = false; custom_pose_enable = false; disable_rest = false; + physical_bone = NULL; + cache_parent_physical_bone = NULL; } }; @@ -82,12 +93,12 @@ class Skeleton : public Spatial { Array _get_bound_child_nodes_to_bone(int p_bone) const { Array bound; - List<Node *> childs; - get_bound_child_nodes_to_bone(p_bone, &childs); + List<Node *> children; + get_bound_child_nodes_to_bone(p_bone, &children); - for (int i = 0; i < childs.size(); i++) { + for (int i = 0; i < children.size(); i++) { - bound.push_back(childs[i]); + bound.push_back(children[i]); } return bound; } @@ -117,6 +128,9 @@ public: void unparent_bone_and_rest(int p_bone); + void set_bone_ignore_animation(int p_bone, bool p_ignore); + bool is_bone_ignore_animation(int p_bone) const; + void set_bone_disable_rest(int p_bone, bool p_disable); bool is_bone_rest_disabled(int p_bone) const; @@ -148,6 +162,25 @@ public: void localize_rests(); // used for loaders and tools + // Physical bone API + + void bind_physical_bone_to_bone(int p_bone, PhysicalBone *p_physical_bone); + void unbind_physical_bone_from_bone(int p_bone); + + PhysicalBone *get_physical_bone(int p_bone); + PhysicalBone *get_physical_bone_parent(int p_bone); + +private: + /// This is a slow API os it's cached + PhysicalBone *_get_physical_bone_parent(int p_bone); + void _rebuild_physical_bones_cache(); + +public: + void physical_bones_simulation(bool start); + void physical_bones_add_collision_exception(RID p_exception); + void physical_bones_remove_collision_exception(RID p_exception); + +public: Skeleton(); ~Skeleton(); }; diff --git a/scene/3d/spatial.cpp b/scene/3d/spatial.cpp index a55c4a2cc5..748aa8aad4 100644 --- a/scene/3d/spatial.cpp +++ b/scene/3d/spatial.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "spatial.h" #include "engine.h" @@ -84,9 +85,7 @@ void Spatial::_notify_dirty() { } void Spatial::_update_local_transform() const { - data.local_transform.basis = Basis(); - data.local_transform.basis.scale(data.scale); - data.local_transform.basis.rotate(data.rotation); + data.local_transform.basis.set_euler_scale(data.rotation, data.scale); data.dirty &= ~DIRTY_LOCAL; } @@ -187,7 +186,9 @@ void Spatial::_notification(int p_what) { if (data.gizmo.is_valid()) { data.gizmo->create(); if (data.gizmo->can_draw()) { - data.gizmo->redraw(); + if (is_visible_in_tree()) { + data.gizmo->redraw(); + } } data.gizmo->transform(); } @@ -285,6 +286,16 @@ Transform Spatial::get_global_transform() const { return data.global_transform; } +#ifdef TOOLS_ENABLED +Transform Spatial::get_global_gizmo_transform() const { + return get_global_transform(); +} + +Transform Spatial::get_local_gizmo_transform() const { + return get_transform(); +} +#endif + Spatial *Spatial::get_parent_spatial() const { return data.parent; @@ -408,7 +419,9 @@ void Spatial::set_gizmo(const Ref<SpatialGizmo> &p_gizmo) { data.gizmo->create(); if (data.gizmo->can_draw()) { - data.gizmo->redraw(); + if (is_visible_in_tree()) { + data.gizmo->redraw(); + } } data.gizmo->transform(); } @@ -427,10 +440,9 @@ Ref<SpatialGizmo> Spatial::get_gizmo() const { #endif } -#ifdef TOOLS_ENABLED - void Spatial::_update_gizmo() { +#ifdef TOOLS_ENABLED if (!is_inside_world()) return; data.gizmo_dirty = false; @@ -442,8 +454,10 @@ void Spatial::_update_gizmo() { data.gizmo->clear(); } } +#endif } +#ifdef TOOLS_ENABLED void Spatial::set_disable_gizmo(bool p_enabled) { data.gizmo_disabled = p_enabled; @@ -723,9 +737,7 @@ void Spatial::_bind_methods() { ClassDB::bind_method(D_METHOD("is_set_as_toplevel"), &Spatial::is_set_as_toplevel); ClassDB::bind_method(D_METHOD("get_world"), &Spatial::get_world); -#ifdef TOOLS_ENABLED ClassDB::bind_method(D_METHOD("_update_gizmo"), &Spatial::_update_gizmo); -#endif ClassDB::bind_method(D_METHOD("update_gizmo"), &Spatial::update_gizmo); ClassDB::bind_method(D_METHOD("set_gizmo", "gizmo"), &Spatial::set_gizmo); @@ -787,7 +799,7 @@ void Spatial::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_scale", "get_scale"); ADD_GROUP("Visibility", ""); ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible"); - //ADD_PROPERTY( PropertyInfo(Variant::TRANSFORM,"transform/local"), "set_transform", "get_transform") ; + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gizmo", PROPERTY_HINT_RESOURCE_TYPE, "SpatialGizmo", 0), "set_gizmo", "get_gizmo"); ADD_SIGNAL(MethodInfo("visibility_changed")); } diff --git a/scene/3d/spatial.h b/scene/3d/spatial.h index 1c2e73d5ba..a43bed3e4a 100644 --- a/scene/3d/spatial.h +++ b/scene/3d/spatial.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef SPATIAL_H #define SPATIAL_H @@ -99,10 +100,8 @@ class Spatial : public Node { #endif } data; -#ifdef TOOLS_ENABLED void _update_gizmo(); -#endif void _notify_dirty(); void _propagate_transform_changed(Spatial *p_origin); @@ -146,6 +145,11 @@ public: Transform get_transform() const; Transform get_global_transform() const; +#ifdef TOOLS_ENABLED + virtual Transform get_global_gizmo_transform() const; + virtual Transform get_local_gizmo_transform() const; +#endif + void set_as_toplevel(bool p_enabled); bool is_set_as_toplevel() const; diff --git a/scene/3d/spatial_velocity_tracker.cpp b/scene/3d/spatial_velocity_tracker.cpp index 1c7423e645..c547e76e30 100644 --- a/scene/3d/spatial_velocity_tracker.cpp +++ b/scene/3d/spatial_velocity_tracker.cpp @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* spatial_velocity_tracker.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 "spatial_velocity_tracker.h" #include "engine.h" @@ -95,6 +125,8 @@ void SpatialVelocityTracker::_bind_methods() { ClassDB::bind_method(D_METHOD("update_position", "position"), &SpatialVelocityTracker::update_position); ClassDB::bind_method(D_METHOD("get_tracked_linear_velocity"), &SpatialVelocityTracker::get_tracked_linear_velocity); ClassDB::bind_method(D_METHOD("reset", "position"), &SpatialVelocityTracker::reset); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "track_physics_step"), "set_track_physics_step", "is_tracking_physics_step"); } SpatialVelocityTracker::SpatialVelocityTracker() { diff --git a/scene/3d/spatial_velocity_tracker.h b/scene/3d/spatial_velocity_tracker.h index c4371ff1f7..a8278a4fb5 100644 --- a/scene/3d/spatial_velocity_tracker.h +++ b/scene/3d/spatial_velocity_tracker.h @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* spatial_velocity_tracker.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 SPATIAL_VELOCITY_TRACKER_H #define SPATIAL_VELOCITY_TRACKER_H diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index 6bd673c7a2..232855c978 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "sprite_3d.h" #include "core_string_names.h" #include "scene/scene_string_names.h" diff --git a/scene/3d/sprite_3d.h b/scene/3d/sprite_3d.h index 0ec8f4d668..23e1d96b4b 100644 --- a/scene/3d/sprite_3d.h +++ b/scene/3d/sprite_3d.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef SPRITE_3D_H #define SPRITE_3D_H diff --git a/scene/3d/vehicle_body.cpp b/scene/3d/vehicle_body.cpp index d9918c0b62..b72665aa2b 100644 --- a/scene/3d/vehicle_body.cpp +++ b/scene/3d/vehicle_body.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "vehicle_body.h" #define ROLLING_INFLUENCE_FIX @@ -374,7 +375,7 @@ void VehicleBody::_update_wheel(int p_idx, PhysicsDirectBodyState *s) { Basis steeringMat(up, steering); - Basis rotatingMat(right, -wheel.m_rotation); + Basis rotatingMat(right, wheel.m_rotation); /* if (p_idx==1) @@ -523,7 +524,7 @@ void VehicleBody::_update_suspension(PhysicsDirectBodyState *s) { //bilateral constraint between two dynamic objects void VehicleBody::_resolve_single_bilateral(PhysicsDirectBodyState *s, const Vector3 &pos1, - PhysicsBody *body2, const Vector3 &pos2, const Vector3 &normal, real_t &impulse) { + PhysicsBody *body2, const Vector3 &pos2, const Vector3 &normal, real_t &impulse, real_t p_rollInfluence) { real_t normalLenSqr = normal.length_squared(); //ERR_FAIL_COND( normalLenSqr < real_t(1.1)); @@ -571,7 +572,7 @@ void VehicleBody::_resolve_single_bilateral(PhysicsDirectBodyState *s, const Vec b2invmass); // FIXME: rel_vel assignment here is overwritten by the following assignment. - // What seemes to be intented in the next next assignment is: rel_vel = normal.dot(rel_vel); + // What seemes to be intended in the next next assignment is: rel_vel = normal.dot(rel_vel); // Investigate why. real_t rel_vel = jac.getRelativeVelocity( s->get_linear_velocity(), @@ -581,8 +582,12 @@ void VehicleBody::_resolve_single_bilateral(PhysicsDirectBodyState *s, const Vec rel_vel = normal.dot(vel); - //TODO: move this into proper structure - real_t contactDamping = real_t(0.4); + // !BAS! We had this set to 0.4, in bullet its 0.2 + // real_t contactDamping = real_t(0.2); + + // !BAS! But seeing we apply this frame by frame, makes more sense to me to make this time based + // keeping in mind our anti roll factor + real_t contactDamping = s->get_step() / p_rollInfluence; #define ONLY_USE_LINEAR_MASS #ifdef ONLY_USE_LINEAR_MASS real_t massTerm = real_t(1.) / ((1.0 / mass) + b2invmass); @@ -703,7 +708,7 @@ void VehicleBody::_update_friction(PhysicsDirectBodyState *s) { _resolve_single_bilateral(s, wheelInfo.m_raycastInfo.m_contactPointWS, wheelInfo.m_raycastInfo.m_groundObject, wheelInfo.m_raycastInfo.m_contactPointWS, - m_axle[i], m_sideImpulse[i]); + m_axle[i], m_sideImpulse[i], wheelInfo.m_rollInfluence); m_sideImpulse[i] *= sideFrictionStiffness2; } @@ -815,26 +820,24 @@ void VehicleBody::_update_friction(PhysicsDirectBodyState *s) { void VehicleBody::_direct_state_changed(Object *p_state) { - PhysicsDirectBodyState *s = Object::cast_to<PhysicsDirectBodyState>(p_state); + RigidBody::_direct_state_changed(p_state); - set_ignore_transform_notification(true); - set_global_transform(s->get_transform()); - set_ignore_transform_notification(false); + state = Object::cast_to<PhysicsDirectBodyState>(p_state); - float step = s->get_step(); + float step = state->get_step(); for (int i = 0; i < wheels.size(); i++) { - _update_wheel(i, s); + _update_wheel(i, state); } for (int i = 0; i < wheels.size(); i++) { - _ray_cast(i, s); - wheels[i]->set_transform(s->get_transform().inverse() * wheels[i]->m_worldTransform); + _ray_cast(i, state); + wheels[i]->set_transform(state->get_transform().inverse() * wheels[i]->m_worldTransform); } - _update_suspension(s); + _update_suspension(state); for (int i = 0; i < wheels.size(); i++) { @@ -847,21 +850,21 @@ void VehicleBody::_direct_state_changed(Object *p_state) { suspensionForce = wheel.m_maxSuspensionForce; } Vector3 impulse = wheel.m_raycastInfo.m_contactNormalWS * suspensionForce * step; - Vector3 relpos = wheel.m_raycastInfo.m_contactPointWS - s->get_transform().origin; + Vector3 relpos = wheel.m_raycastInfo.m_contactPointWS - state->get_transform().origin; - s->apply_impulse(relpos, impulse); + state->apply_impulse(relpos, impulse); //getRigidBody()->applyImpulse(impulse, relpos); } - _update_friction(s); + _update_friction(state); for (int i = 0; i < wheels.size(); i++) { VehicleWheel &wheel = *wheels[i]; - Vector3 relpos = wheel.m_raycastInfo.m_hardPointWS - s->get_transform().origin; - Vector3 vel = s->get_linear_velocity() + (s->get_angular_velocity()).cross(relpos); // * mPos); + Vector3 relpos = wheel.m_raycastInfo.m_hardPointWS - state->get_transform().origin; + Vector3 vel = state->get_linear_velocity() + (state->get_angular_velocity()).cross(relpos); // * mPos); if (wheel.m_raycastInfo.m_isInContact) { - const Transform &chassisWorldTransform = s->get_transform(); + const Transform &chassisWorldTransform = state->get_transform(); Vector3 fwd( chassisWorldTransform.basis[0][Vector3::AXIS_Z], @@ -882,29 +885,8 @@ void VehicleBody::_direct_state_changed(Object *p_state) { wheel.m_deltaRotation *= real_t(0.99); //damping of rotation when not in contact } - linear_velocity = s->get_linear_velocity(); -} - -void VehicleBody::set_mass(real_t p_mass) { - - mass = p_mass; - PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_MASS, mass); -} - -real_t VehicleBody::get_mass() const { - - return mass; -} - -void VehicleBody::set_friction(real_t p_friction) { - friction = p_friction; - PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, friction); -} - -real_t VehicleBody::get_friction() const { - - return friction; + state = NULL; } void VehicleBody::set_engine_force(float p_engine_force) { @@ -935,18 +917,8 @@ float VehicleBody::get_steering() const { return m_steeringValue; } -Vector3 VehicleBody::get_linear_velocity() const { - return linear_velocity; -} - void VehicleBody::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_mass", "mass"), &VehicleBody::set_mass); - ClassDB::bind_method(D_METHOD("get_mass"), &VehicleBody::get_mass); - - ClassDB::bind_method(D_METHOD("set_friction", "friction"), &VehicleBody::set_friction); - ClassDB::bind_method(D_METHOD("get_friction"), &VehicleBody::get_friction); - ClassDB::bind_method(D_METHOD("set_engine_force", "engine_force"), &VehicleBody::set_engine_force); ClassDB::bind_method(D_METHOD("get_engine_force"), &VehicleBody::get_engine_force); @@ -956,21 +928,14 @@ void VehicleBody::_bind_methods() { ClassDB::bind_method(D_METHOD("set_steering", "steering"), &VehicleBody::set_steering); ClassDB::bind_method(D_METHOD("get_steering"), &VehicleBody::get_steering); - ClassDB::bind_method(D_METHOD("get_linear_velocity"), &VehicleBody::get_linear_velocity); - - ClassDB::bind_method(D_METHOD("_direct_state_changed"), &VehicleBody::_direct_state_changed); - ADD_GROUP("Motion", ""); ADD_PROPERTY(PropertyInfo(Variant::REAL, "engine_force", PROPERTY_HINT_RANGE, "0.00,1024.0,0.01"), "set_engine_force", "get_engine_force"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "brake", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_brake", "get_brake"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "steering", PROPERTY_HINT_RANGE, "-180,180.0,0.01"), "set_steering", "get_steering"); - ADD_GROUP("Mass", ""); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "mass", PROPERTY_HINT_RANGE, "0.01,65536,0.01"), "set_mass", "get_mass"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0.01,1,0.01"), "set_friction", "get_friction"); } VehicleBody::VehicleBody() : - PhysicsBody(PhysicsServer::BODY_MODE_RIGID) { + RigidBody() { m_pitchControl = 0; m_currentVehicleSpeedKmHour = real_t(0.); @@ -981,10 +946,11 @@ VehicleBody::VehicleBody() : friction = 1; + state = NULL; ccd = false; exclude.insert(get_rid()); - PhysicsServer::get_singleton()->body_set_force_integration_callback(get_rid(), this, "_direct_state_changed"); + //PhysicsServer::get_singleton()->body_set_force_integration_callback(get_rid(), this, "_direct_state_changed"); set_mass(40); } diff --git a/scene/3d/vehicle_body.h b/scene/3d/vehicle_body.h index b4420e95e7..1ac3693cc4 100644 --- a/scene/3d/vehicle_body.h +++ b/scene/3d/vehicle_body.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef VEHICLE_BODY_H #define VEHICLE_BODY_H @@ -138,20 +139,13 @@ public: VehicleWheel(); }; -class VehicleBody : public PhysicsBody { - - GDCLASS(VehicleBody, PhysicsBody); +class VehicleBody : public RigidBody { - real_t mass; - real_t friction; + GDCLASS(VehicleBody, RigidBody); float engine_force; float brake; - Vector3 linear_velocity; - Vector3 angular_velocity; - bool ccd; - real_t m_pitchControl; real_t m_steeringValue; real_t m_currentVehicleSpeedKmHour; @@ -174,7 +168,7 @@ class VehicleBody : public PhysicsBody { btVehicleWheelContactPoint(PhysicsDirectBodyState *s, PhysicsBody *body1, const Vector3 &frictionPosWorld, const Vector3 &frictionDirectionWorld, real_t maxImpulse); }; - void _resolve_single_bilateral(PhysicsDirectBodyState *s, const Vector3 &pos1, PhysicsBody *body2, const Vector3 &pos2, const Vector3 &normal, real_t &impulse); + void _resolve_single_bilateral(PhysicsDirectBodyState *s, const Vector3 &pos1, PhysicsBody *body2, const Vector3 &pos2, const Vector3 &normal, real_t &impulse, real_t p_rollInfluence); real_t _calc_rolling_friction(btVehicleWheelContactPoint &contactPoint); void _update_friction(PhysicsDirectBodyState *s); @@ -191,12 +185,6 @@ class VehicleBody : public PhysicsBody { void _direct_state_changed(Object *p_state); public: - void set_mass(real_t p_mass); - real_t get_mass() const; - - void set_friction(real_t p_friction); - real_t get_friction() const; - void set_engine_force(float p_engine_force); float get_engine_force() const; @@ -206,8 +194,6 @@ public: void set_steering(float p_steering); float get_steering() const; - Vector3 get_linear_velocity() const; - VehicleBody(); }; diff --git a/scene/3d/visibility_notifier.cpp b/scene/3d/visibility_notifier.cpp index e6c660bad7..9d6e4941f3 100644 --- a/scene/3d/visibility_notifier.cpp +++ b/scene/3d/visibility_notifier.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "visibility_notifier.h" #include "engine.h" @@ -169,7 +170,7 @@ void VisibilityEnabler::_find_nodes(Node *p_node) { if (add) { - p_node->connect(SceneStringNames::get_singleton()->tree_exited, this, "_node_removed", varray(p_node), CONNECT_ONESHOT); + p_node->connect(SceneStringNames::get_singleton()->tree_exiting, this, "_node_removed", varray(p_node), CONNECT_ONESHOT); nodes[p_node] = meta; _change_node_state(p_node, false); } @@ -207,7 +208,7 @@ void VisibilityEnabler::_notification(int p_what) { if (!visible) _change_node_state(E->key(), true); - E->key()->disconnect(SceneStringNames::get_singleton()->tree_exited, this, "_node_removed"); + E->key()->disconnect(SceneStringNames::get_singleton()->tree_exiting, this, "_node_removed"); } nodes.clear(); @@ -239,7 +240,7 @@ void VisibilityEnabler::_node_removed(Node *p_node) { if (!visible) _change_node_state(p_node, true); - p_node->disconnect(SceneStringNames::get_singleton()->tree_exited, this, "_node_removed"); + p_node->disconnect(SceneStringNames::get_singleton()->tree_exiting, this, "_node_removed"); nodes.erase(p_node); } diff --git a/scene/3d/visibility_notifier.h b/scene/3d/visibility_notifier.h index ae17939e12..b1985f4a0c 100644 --- a/scene/3d/visibility_notifier.h +++ b/scene/3d/visibility_notifier.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef VISIBILITY_NOTIFIER_H #define VISIBILITY_NOTIFIER_H diff --git a/scene/3d/visual_instance.cpp b/scene/3d/visual_instance.cpp index 7497f35d29..00541a7d8a 100644 --- a/scene/3d/visual_instance.cpp +++ b/scene/3d/visual_instance.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "visual_instance.h" #include "scene/scene_string_names.h" diff --git a/scene/3d/visual_instance.h b/scene/3d/visual_instance.h index 34f035c9f8..8458a343b2 100644 --- a/scene/3d/visual_instance.h +++ b/scene/3d/visual_instance.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef VISUAL_INSTANCE_H #define VISUAL_INSTANCE_H diff --git a/scene/3d/voxel_light_baker.cpp b/scene/3d/voxel_light_baker.cpp index e1717a56f3..13700e0bd3 100644 --- a/scene/3d/voxel_light_baker.cpp +++ b/scene/3d/voxel_light_baker.cpp @@ -461,16 +461,16 @@ void VoxelLightBaker::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p } } - if (bake_cells[p_idx].childs[i] == CHILD_EMPTY) { + if (bake_cells[p_idx].children[i] == CHILD_EMPTY) { //sub cell must be created uint32_t child_idx = bake_cells.size(); - bake_cells[p_idx].childs[i] = child_idx; + bake_cells[p_idx].children[i] = child_idx; bake_cells.resize(bake_cells.size() + 1); bake_cells[child_idx].level = p_level + 1; } - _plot_face(bake_cells[p_idx].childs[i], p_level + 1, nx, ny, nz, p_vtx, p_normal, p_uv, p_material, aabb); + _plot_face(bake_cells[p_idx].children[i], p_level + 1, nx, ny, nz, p_vtx, p_normal, p_uv, p_material, aabb); } } } @@ -700,7 +700,7 @@ void VoxelLightBaker::_init_light_plot(int p_idx, int p_level, int p_x, int p_y, int half = (1 << (cell_subdiv - 1)) >> (p_level + 1); for (int i = 0; i < 8; i++) { - uint32_t child = bake_cells[p_idx].childs[i]; + uint32_t child = bake_cells[p_idx].children[i]; if (child == CHILD_EMPTY) continue; @@ -809,7 +809,7 @@ uint32_t VoxelLightBaker::_find_cell_at_pos(const Cell *cells, int x, int y, int ofs_z += half; } - cell = bc->childs[child]; + cell = bc->children[child]; if (cell == CHILD_EMPTY) return CHILD_EMPTY; @@ -1257,7 +1257,7 @@ void VoxelLightBaker::_fixup_plot(int p_idx, int p_level) { for (int i = 0; i < 8; i++) { - uint32_t child = bake_cells[p_idx].childs[i]; + uint32_t child = bake_cells[p_idx].children[i]; if (child == CHILD_EMPTY) continue; @@ -1483,7 +1483,7 @@ void VoxelLightBaker::_sample_baked_octree_filtered_and_anisotropic(const Vector ofs_z += half; } - cell = bc->childs[child]; + cell = bc->children[child]; if (cell == CHILD_EMPTY) break; @@ -1766,7 +1766,7 @@ Vector3 VoxelLightBaker::_compute_ray_trace_at_pos(const Vector3 &p_pos, const V ofs_z += half; } - cell = bc->childs[child]; + cell = bc->children[child]; if (unlikely(cell == CHILD_EMPTY)) break; @@ -1928,7 +1928,7 @@ Error VoxelLightBaker::make_lightmap(const Transform &p_xform, Ref<Mesh> &p_mesh for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { if (lightmap_ptr[i * width + j].normal == Vector3()) - continue; //empty, dont write over it anyway + continue; //empty, don't write over it anyway float gauss_sum = gauss_kernel[0]; Vector3 accum = lightmap_ptr[i * width + j].pos * gauss_kernel[0]; for (int k = 1; k < 4; k++) { @@ -2191,7 +2191,7 @@ PoolVector<int> VoxelLightBaker::create_gi_probe_data() { for (int i = 0; i < bake_cells.size(); i++) { for (int j = 0; j < 8; j++) { - w32[ofs++] = bake_cells[i].childs[j]; + w32[ofs++] = bake_cells[i].children[j]; } { //albedo @@ -2275,7 +2275,7 @@ void VoxelLightBaker::_debug_mesh(int p_idx, int p_level, const AABB &p_aabb, Re for (int i = 0; i < 8; i++) { - uint32_t child = bake_cells[p_idx].childs[i]; + uint32_t child = bake_cells[p_idx].children[i]; if (child == CHILD_EMPTY || child >= max_original_cells) continue; @@ -2290,7 +2290,7 @@ void VoxelLightBaker::_debug_mesh(int p_idx, int p_level, const AABB &p_aabb, Re if (i & 4) aabb.position.z += aabb.size.z; - _debug_mesh(bake_cells[p_idx].childs[i], p_level + 1, aabb, p_multimesh, idx, p_mode); + _debug_mesh(bake_cells[p_idx].children[i], p_level + 1, aabb, p_multimesh, idx, p_mode); } } } @@ -2338,9 +2338,9 @@ Ref<MultiMesh> VoxelLightBaker::create_debug_multimesh(DebugMode p_mode) { for (int k = 0; k < 3; k++) { if (i < 3) - face_points[j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1); + face_points[j][(i + k) % 3] = v[k]; else - face_points[3 - j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1); + face_points[3 - j][(i + k) % 3] = -v[k]; } } @@ -2423,7 +2423,7 @@ PoolVector<uint8_t> VoxelLightBaker::create_capture_octree(int p_subdiv) { } for (int j = 0; j < 8; j++) { - uint32_t child = bake_cells[demap[i]].childs[j]; + uint32_t child = bake_cells[demap[i]].children[j]; octree[i].children[j] = child == CHILD_EMPTY ? CHILD_EMPTY : remap[child]; } } diff --git a/scene/3d/voxel_light_baker.h b/scene/3d/voxel_light_baker.h index d270a26a2a..6a1f1253a3 100644 --- a/scene/3d/voxel_light_baker.h +++ b/scene/3d/voxel_light_baker.h @@ -60,7 +60,7 @@ private: struct Cell { - uint32_t childs[8]; + uint32_t children[8]; float albedo[3]; //albedo in RGB24 float emission[3]; //accumulated light in 16:16 fixed point (needs to be integer for moving lights fast) float normal[3]; @@ -70,7 +70,7 @@ private: Cell() { for (int i = 0; i < 8; i++) { - childs[i] = CHILD_EMPTY; + children[i] = CHILD_EMPTY; } for (int i = 0; i < 3; i++) { diff --git a/scene/animation/animation_cache.cpp b/scene/animation/animation_cache.cpp index 76043436df..949a0be3bc 100644 --- a/scene/animation/animation_cache.cpp +++ b/scene/animation/animation_cache.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "animation_cache.h" void AnimationCache::_node_exit_tree(Node *p_node) { @@ -55,7 +56,7 @@ void AnimationCache::_clear_cache() { while (connected_nodes.size()) { - connected_nodes.front()->get()->disconnect("tree_exited", this, "_node_exit_tree"); + connected_nodes.front()->get()->disconnect("tree_exiting", this, "_node_exit_tree"); connected_nodes.erase(connected_nodes.front()); } path_cache.clear(); @@ -180,7 +181,7 @@ void AnimationCache::_update_cache() { if (!connected_nodes.has(path.node)) { connected_nodes.insert(path.node); - path.node->connect("tree_exited", this, "_node_exit_tree", Node::make_binds(path.node), CONNECT_ONESHOT); + path.node->connect("tree_exiting", this, "_node_exit_tree", Node::make_binds(path.node), CONNECT_ONESHOT); } } diff --git a/scene/animation/animation_cache.h b/scene/animation/animation_cache.h index 63c1f243ef..cf28236376 100644 --- a/scene/animation/animation_cache.h +++ b/scene/animation/animation_cache.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef ANIMATION_CACHE_H #define ANIMATION_CACHE_H diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index bac95c6cca..c0d1e62e07 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "animation_player.h" #include "engine.h" @@ -48,24 +49,15 @@ bool AnimationPlayer::_set(const StringName &p_name, const Variant &p_value) { String name = p_name; - if (p_name == SceneStringNames::get_singleton()->playback_speed || p_name == SceneStringNames::get_singleton()->speed) { //bw compatibility - set_speed_scale(p_value); - - } else if (p_name == SceneStringNames::get_singleton()->playback_active) { - set_active(p_value); - } else if (name.begins_with("playback/play")) { + if (name.begins_with("playback/play")) { // bw compatibility - String which = p_value; + set_current_animation(p_value); - if (which == "[stop]") - stop(); - else - play(which); } else if (name.begins_with("anims/")) { String which = name.get_slicec('/', 1); - add_animation(which, p_value); + } else if (name.begins_with("next/")) { String which = name.get_slicec('/', 1); @@ -86,9 +78,6 @@ bool AnimationPlayer::_set(const StringName &p_name, const Variant &p_value) { set_blend_time(from, to, time); } - } else if (p_name == SceneStringNames::get_singleton()->autoplay) { - autoplay = p_value; - } else return false; @@ -99,24 +88,15 @@ bool AnimationPlayer::_get(const StringName &p_name, Variant &r_ret) const { String name = p_name; - if (name == "playback/speed") { //bw compatibility + if (name == "playback/play") { // bw compatibility - r_ret = speed_scale; - } else if (name == "playback/active") { - - r_ret = is_active(); - } else if (name == "playback/play") { - - if (is_active() && is_playing()) - r_ret = playback.assigned; - else - r_ret = "[stop]"; + r_ret = get_current_animation(); } else if (name.begins_with("anims/")) { String which = name.get_slicec('/', 1); - r_ret = get_animation(which).get_ref_ptr(); + } else if (name.begins_with("next/")) { String which = name.get_slicec('/', 1); @@ -140,27 +120,43 @@ bool AnimationPlayer::_get(const StringName &p_name, Variant &r_ret) const { } r_ret = array; - } else if (name == "autoplay") { - r_ret = autoplay; - } else return false; return true; } -void AnimationPlayer::_get_property_list(List<PropertyInfo> *p_list) const { +void AnimationPlayer::_validate_property(PropertyInfo &property) const { - List<String> names; + if (property.name == "current_animation") { + List<String> names; + + for (Map<StringName, AnimationData>::Element *E = animation_set.front(); E; E = E->next()) { + names.push_back(E->key()); + } + names.sort(); + names.push_front("[stop]"); + String hint; + for (List<String>::Element *E = names.front(); E; E = E->next()) { + + if (E != names.front()) + hint += ","; + hint += E->get(); + } + + property.hint_string = hint; + } +} + +void AnimationPlayer::_get_property_list(List<PropertyInfo> *p_list) const { List<PropertyInfo> anim_names; for (Map<StringName, AnimationData>::Element *E = animation_set.front(); E; E = E->next()) { - anim_names.push_back(PropertyInfo(Variant::OBJECT, "anims/" + String(E->key()), PROPERTY_HINT_RESOURCE_TYPE, "Animation", PROPERTY_USAGE_NOEDITOR)); + anim_names.push_back(PropertyInfo(Variant::OBJECT, "anims/" + String(E->key()), PROPERTY_HINT_RESOURCE_TYPE, "Animation", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); if (E->get().next != StringName()) - anim_names.push_back(PropertyInfo(Variant::STRING, "next/" + String(E->key()), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); - names.push_back(E->key()); + anim_names.push_back(PropertyInfo(Variant::STRING, "next/" + String(E->key()), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); } anim_names.sort(); @@ -169,24 +165,7 @@ void AnimationPlayer::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(E->get()); } - { - names.sort(); - names.push_front("[stop]"); - String hint; - for (List<String>::Element *E = names.front(); E; E = E->next()) { - - if (E != names.front()) - hint += ","; - hint += E->get(); - } - - p_list->push_back(PropertyInfo(Variant::STRING, "playback/play", PROPERTY_HINT_ENUM, hint, PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ANIMATE_AS_TRIGGER)); - p_list->push_back(PropertyInfo(Variant::BOOL, "playback/active", PROPERTY_HINT_NONE, "")); - p_list->push_back(PropertyInfo(Variant::REAL, "playback/speed", PROPERTY_HINT_RANGE, "-64,64,0.01")); - } - - p_list->push_back(PropertyInfo(Variant::ARRAY, "blend_times", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::STRING, "autoplay", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::ARRAY, "blend_times", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); } void AnimationPlayer::advance(float p_time) { @@ -203,8 +182,8 @@ void AnimationPlayer::_notification(int p_what) { if (!processing) { //make sure that a previous process state was not saved //only process if "processing" is set - set_physics_process(false); - set_process(false); + set_physics_process_internal(false); + set_process_internal(false); } //_set_process(false); clear_caches(); @@ -267,16 +246,17 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim) { if (a->track_get_path(i).get_subname_count() == 1 && Object::cast_to<Skeleton>(child)) { - bone_idx = Object::cast_to<Skeleton>(child)->find_bone(a->track_get_path(i).get_subname(0)); - if (bone_idx == -1) { + Skeleton *sk = Object::cast_to<Skeleton>(child); + bone_idx = sk->find_bone(a->track_get_path(i).get_subname(0)); + if (bone_idx == -1 || sk->is_bone_ignore_animation(bone_idx)) { continue; } } { - if (!child->is_connected("tree_exited", this, "_node_removed")) - child->connect("tree_exited", this, "_node_removed", make_binds(child), CONNECT_ONESHOT); + if (!child->is_connected("tree_exiting", this, "_node_removed")) + child->connect("tree_exiting", this, "_node_removed", make_binds(child), CONNECT_ONESHOT); } TrackNodeCacheKey key; @@ -600,20 +580,16 @@ void AnimationPlayer::_animation_process2(float p_delta) { } void AnimationPlayer::_animation_update_transforms() { + { + Transform t; + for (int i = 0; i < cache_update_size; i++) { - for (int i = 0; i < cache_update_size; i++) { - - TrackNodeCache *nc = cache_update[i]; - - ERR_CONTINUE(nc->accum_pass != accum_pass); + TrackNodeCache *nc = cache_update[i]; - if (nc->spatial) { + ERR_CONTINUE(nc->accum_pass != accum_pass); - Transform t; t.origin = nc->loc_accum; - t.basis = nc->rot_accum; - t.basis.scale(nc->scale_accum); - + t.basis.set_quat_scale(nc->rot_accum, nc->scale_accum); if (nc->skeleton && nc->bone_idx >= 0) { nc->skeleton->set_bone_pose(nc->bone_idx, t); @@ -989,10 +965,27 @@ bool AnimationPlayer::is_playing() const { }; return true; - */ + */ } + void AnimationPlayer::set_current_animation(const String &p_anim) { + if (p_anim == "[stop]" || p_anim == "") { + stop(); + } else if (!is_playing() || playback.assigned != p_anim) { + play(p_anim); + } else { + // Same animation, do not replay from start + } +} + +String AnimationPlayer::get_current_animation() const { + + return (is_playing() ? playback.assigned : ""); +} + +void AnimationPlayer::set_assigned_animation(const String &p_anim) { + if (is_playing()) { play(p_anim); } else { @@ -1003,9 +996,9 @@ void AnimationPlayer::set_current_animation(const String &p_anim) { } } -String AnimationPlayer::get_current_animation() const { +String AnimationPlayer::get_assigned_animation() const { - return (playback.assigned); + return playback.assigned; } void AnimationPlayer::stop(bool p_reset) { @@ -1014,6 +1007,7 @@ void AnimationPlayer::stop(bool p_reset) { c.blend.clear(); if (p_reset) { c.current.from = NULL; + c.current.speed_scale = 1; } _set_process(false); queued.clear(); @@ -1028,12 +1022,21 @@ float AnimationPlayer::get_speed_scale() const { return speed_scale; } +float AnimationPlayer::get_playing_speed() const { + + if (!playing) { + return 0; + } + return speed_scale * playback.current.speed_scale; +} void AnimationPlayer::seek(float p_time, bool p_update) { if (!playback.current.from) { - if (playback.assigned) - set_current_animation(playback.assigned); + if (playback.assigned) { + ERR_FAIL_COND(!animation_set.has(playback.assigned)); + playback.current.from = &animation_set[playback.assigned]; + } ERR_FAIL_COND(!playback.current.from); } @@ -1046,8 +1049,10 @@ void AnimationPlayer::seek(float p_time, bool p_update) { void AnimationPlayer::seek_delta(float p_time, float p_delta) { if (!playback.current.from) { - if (playback.assigned) - set_current_animation(playback.assigned); + if (playback.assigned) { + ERR_FAIL_COND(!animation_set.has(playback.assigned)); + playback.current.from = &animation_set[playback.assigned]; + } ERR_FAIL_COND(!playback.current.from); } @@ -1202,7 +1207,7 @@ NodePath AnimationPlayer::get_root() const { void AnimationPlayer::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { String pf = p_function; - if (p_function == "play" || p_function == "remove_animation" || p_function == "has_animation" || p_function == "queue") { + if (p_function == "play" || p_function == "play_backwards" || p_function == "remove_animation" || p_function == "has_animation" || p_function == "queue") { List<StringName> al; get_animation_list(&al); for (List<StringName>::Element *E = al.front(); E; E = E->next()) { @@ -1305,6 +1310,8 @@ void AnimationPlayer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_current_animation", "anim"), &AnimationPlayer::set_current_animation); ClassDB::bind_method(D_METHOD("get_current_animation"), &AnimationPlayer::get_current_animation); + ClassDB::bind_method(D_METHOD("set_assigned_animation", "anim"), &AnimationPlayer::set_assigned_animation); + ClassDB::bind_method(D_METHOD("get_assigned_animation"), &AnimationPlayer::get_assigned_animation); ClassDB::bind_method(D_METHOD("queue", "name"), &AnimationPlayer::queue); ClassDB::bind_method(D_METHOD("clear_queue"), &AnimationPlayer::clear_queue); @@ -1313,6 +1320,7 @@ void AnimationPlayer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_speed_scale", "speed"), &AnimationPlayer::set_speed_scale); ClassDB::bind_method(D_METHOD("get_speed_scale"), &AnimationPlayer::get_speed_scale); + ClassDB::bind_method(D_METHOD("get_playing_speed"), &AnimationPlayer::get_playing_speed); ClassDB::bind_method(D_METHOD("set_autoplay", "name"), &AnimationPlayer::set_autoplay); ClassDB::bind_method(D_METHOD("get_autoplay"), &AnimationPlayer::get_autoplay); @@ -1333,14 +1341,22 @@ void AnimationPlayer::_bind_methods() { ClassDB::bind_method(D_METHOD("seek", "seconds", "update"), &AnimationPlayer::seek, DEFVAL(false)); ClassDB::bind_method(D_METHOD("advance", "delta"), &AnimationPlayer::advance); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_node"), "set_root", "get_root"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "current_animation", PROPERTY_HINT_ENUM, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ANIMATE_AS_TRIGGER), "set_current_animation", "get_current_animation"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "assigned_animation", PROPERTY_HINT_NONE, "", 0), "set_assigned_animation", "get_assigned_animation"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "autoplay", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_autoplay", "get_autoplay"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "current_animation_length", PROPERTY_HINT_NONE, "", 0), "", "get_current_animation_length"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "current_animation_position", PROPERTY_HINT_NONE, "", 0), "", "get_current_animation_position"); + ADD_GROUP("Playback Options", "playback_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "playback_process_mode", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_animation_process_mode", "get_animation_process_mode"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "playback_default_blend_time", PROPERTY_HINT_RANGE, "0,4096,0.01"), "set_default_blend_time", "get_default_blend_time"); - ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_node"), "set_root", "get_root"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playback_active", PROPERTY_HINT_NONE, "", 0), "set_active", "is_active"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "playback_speed", PROPERTY_HINT_RANGE, "-64,64,0.01"), "set_speed_scale", "get_speed_scale"); - ADD_SIGNAL(MethodInfo("animation_finished", PropertyInfo(Variant::STRING, "name"))); + ADD_SIGNAL(MethodInfo("animation_finished", PropertyInfo(Variant::STRING, "anim_name"))); ADD_SIGNAL(MethodInfo("animation_changed", PropertyInfo(Variant::STRING, "old_name"), PropertyInfo(Variant::STRING, "new_name"))); - ADD_SIGNAL(MethodInfo("animation_started", PropertyInfo(Variant::STRING, "name"))); + ADD_SIGNAL(MethodInfo("animation_started", PropertyInfo(Variant::STRING, "anim_name"))); BIND_ENUM_CONSTANT(ANIMATION_PROCESS_PHYSICS); BIND_ENUM_CONSTANT(ANIMATION_PROCESS_IDLE); diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h index 40a7252528..af2022ddac 100644 --- a/scene/animation/animation_player.h +++ b/scene/animation/animation_player.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef ANIMATION_PLAYER_H #define ANIMATION_PLAYER_H @@ -250,6 +251,7 @@ 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; void _get_property_list(List<PropertyInfo> *p_list) const; void _notification(int p_what); @@ -282,6 +284,8 @@ public: bool is_playing() const; String get_current_animation() const; void set_current_animation(const String &p_anim); + String get_assigned_animation() const; + void set_assigned_animation(const String &p_anim); void stop_all(); void set_active(bool p_active); bool is_active() const; @@ -289,6 +293,7 @@ public: void set_speed_scale(float p_speed); float get_speed_scale() const; + float get_playing_speed() const; void set_autoplay(const String &p_name); String get_autoplay() const; diff --git a/scene/animation/animation_tree_player.cpp b/scene/animation/animation_tree_player.cpp index 5777562f93..ce5b372d72 100644 --- a/scene/animation/animation_tree_player.cpp +++ b/scene/animation/animation_tree_player.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "animation_tree_player.h" #include "animation_player.h" @@ -812,7 +813,11 @@ void AnimationTreePlayer::_process_animation(float p_delta) { t.value = t.object->get_indexed(t.subpath); t.value.zero(); - t.skip = false; + if (t.skeleton) { + t.skip = t.skeleton->is_bone_ignore_animation(t.bone_idx); + } else { + t.skip = false; + } } /* STEP 2 PROCESS ANIMATIONS */ @@ -894,13 +899,12 @@ void AnimationTreePlayer::_process_animation(float p_delta) { } Transform xform; - xform.basis = t.rot; xform.origin = t.loc; t.scale.x += 1.0; t.scale.y += 1.0; t.scale.z += 1.0; - xform.basis.scale(t.scale); + xform.basis.set_quat_scale(t.rot, t.scale); if (t.bone_idx >= 0) { if (t.skeleton) diff --git a/scene/animation/animation_tree_player.h b/scene/animation/animation_tree_player.h index c6062b97e4..09d6f6fcb4 100644 --- a/scene/animation/animation_tree_player.h +++ b/scene/animation/animation_tree_player.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef ANIMATION_TREE_PLAYER_H #define ANIMATION_TREE_PLAYER_H diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp index 1711fbffee..49013b160a 100644 --- a/scene/animation/tween.cpp +++ b/scene/animation/tween.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "tween.h" #include "method_bind_ext.gen.inc" @@ -218,11 +219,13 @@ void Tween::_bind_methods() { ClassDB::bind_method(D_METHOD("targeting_property", "object", "property", "initial", "initial_val", "final_val", "duration", "trans_type", "ease_type", "delay"), &Tween::targeting_property, DEFVAL(0)); ClassDB::bind_method(D_METHOD("targeting_method", "object", "method", "initial", "initial_method", "final_val", "duration", "trans_type", "ease_type", "delay"), &Tween::targeting_method, DEFVAL(0)); - ADD_SIGNAL(MethodInfo("tween_started", PropertyInfo(Variant::OBJECT, "object"), PropertyInfo(Variant::STRING, "key"))); - ADD_SIGNAL(MethodInfo("tween_step", PropertyInfo(Variant::OBJECT, "object"), PropertyInfo(Variant::STRING, "key"), PropertyInfo(Variant::REAL, "elapsed"), PropertyInfo(Variant::OBJECT, "value"))); - ADD_SIGNAL(MethodInfo("tween_completed", PropertyInfo(Variant::OBJECT, "object"), PropertyInfo(Variant::STRING, "key"))); + ADD_SIGNAL(MethodInfo("tween_started", PropertyInfo(Variant::OBJECT, "object"), PropertyInfo(Variant::NODE_PATH, "key"))); + ADD_SIGNAL(MethodInfo("tween_step", PropertyInfo(Variant::OBJECT, "object"), PropertyInfo(Variant::NODE_PATH, "key"), PropertyInfo(Variant::REAL, "elapsed"), PropertyInfo(Variant::OBJECT, "value"))); + ADD_SIGNAL(MethodInfo("tween_completed", PropertyInfo(Variant::OBJECT, "object"), PropertyInfo(Variant::NODE_PATH, "key"))); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "repeat"), "set_repeat", "is_repeat"); ADD_PROPERTY(PropertyInfo(Variant::INT, "playback_process_mode", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_tween_process_mode", "get_tween_process_mode"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "playback_speed", PROPERTY_HINT_RANGE, "-64,64,0.01"), "set_speed_scale", "get_speed_scale"); BIND_ENUM_CONSTANT(TWEEN_PROCESS_PHYSICS); BIND_ENUM_CONSTANT(TWEEN_PROCESS_IDLE); diff --git a/scene/animation/tween.h b/scene/animation/tween.h index f052c415ae..757d80e90a 100644 --- a/scene/animation/tween.h +++ b/scene/animation/tween.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef TWEEN_H #define TWEEN_H diff --git a/scene/animation/tween_interpolaters.cpp b/scene/animation/tween_interpolaters.cpp index 72c603473a..11f2b0c17f 100644 --- a/scene/animation/tween_interpolaters.cpp +++ b/scene/animation/tween_interpolaters.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "tween.h" const real_t pi = 3.1415926535898; diff --git a/scene/audio/audio_player.cpp b/scene/audio/audio_player.cpp index 2c90fd5511..408c00334a 100644 --- a/scene/audio/audio_player.cpp +++ b/scene/audio/audio_player.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "audio_player.h" #include "engine.h" @@ -44,7 +45,7 @@ void AudioStreamPlayer::_mix_internal(bool p_fadeout) { } //mix - stream_playback->mix(buffer, 1.0, buffer_size); + stream_playback->mix(buffer, pitch_scale, buffer_size); //multiply volume interpolating to avoid clicks if this changes float target_volume = p_fadeout ? -80.0 : volume_db; @@ -125,8 +126,8 @@ void AudioStreamPlayer::_notification(int p_what) { if (!active || (setseek < 0 && !stream_playback->is_playing())) { active = false; - emit_signal("finished"); set_process_internal(false); + emit_signal("finished"); } } @@ -176,6 +177,13 @@ float AudioStreamPlayer::get_volume_db() const { return volume_db; } +void AudioStreamPlayer::set_pitch_scale(float p_pitch_scale) { + pitch_scale = p_pitch_scale; +} +float AudioStreamPlayer::get_pitch_scale() const { + return pitch_scale; +} + void AudioStreamPlayer::play(float p_from_pos) { if (stream_playback.is_valid()) { @@ -296,6 +304,9 @@ void AudioStreamPlayer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_volume_db", "volume_db"), &AudioStreamPlayer::set_volume_db); ClassDB::bind_method(D_METHOD("get_volume_db"), &AudioStreamPlayer::get_volume_db); + ClassDB::bind_method(D_METHOD("set_pitch_scale", "pitch_scale"), &AudioStreamPlayer::set_pitch_scale); + ClassDB::bind_method(D_METHOD("get_pitch_scale"), &AudioStreamPlayer::get_pitch_scale); + ClassDB::bind_method(D_METHOD("play", "from_position"), &AudioStreamPlayer::play, DEFVAL(0.0)); ClassDB::bind_method(D_METHOD("seek", "to_position"), &AudioStreamPlayer::seek); ClassDB::bind_method(D_METHOD("stop"), &AudioStreamPlayer::stop); @@ -319,6 +330,7 @@ void AudioStreamPlayer::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "volume_db", PROPERTY_HINT_RANGE, "-80,24"), "set_volume_db", "get_volume_db"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "pitch_scale", PROPERTY_HINT_RANGE, "0.01,32,0.01"), "set_pitch_scale", "get_pitch_scale"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_playing", "is_playing"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "is_autoplay_enabled"); ADD_PROPERTY(PropertyInfo(Variant::INT, "mix_target", PROPERTY_HINT_ENUM, "Stereo,Surround,Center"), "set_mix_target", "get_mix_target"); @@ -334,6 +346,7 @@ void AudioStreamPlayer::_bind_methods() { AudioStreamPlayer::AudioStreamPlayer() { mix_volume_db = 0; + pitch_scale = 1.0; volume_db = 0; autoplay = false; setseek = -1; diff --git a/scene/audio/audio_player.h b/scene/audio/audio_player.h index 8d7a635a8b..21189aea6d 100644 --- a/scene/audio/audio_player.h +++ b/scene/audio/audio_player.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef AUDIOPLAYER_H #define AUDIOPLAYER_H @@ -53,6 +54,7 @@ private: volatile bool active; float mix_volume_db; + float pitch_scale; float volume_db; bool autoplay; StringName bus; @@ -80,6 +82,9 @@ public: void set_volume_db(float p_volume); float get_volume_db() const; + void set_pitch_scale(float p_pitch_scale); + float get_pitch_scale() const; + void play(float p_from_pos = 0.0); void seek(float p_seconds); void stop(); diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index 41ff000ac1..acdbd9de08 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "base_button.h" #include "os/keyboard.h" @@ -59,7 +60,7 @@ void BaseButton::_gui_input(Ref<InputEvent> p_event) { Ref<InputEventMouseButton> b = p_event; if (b.is_valid()) { - if (status.disabled || b->get_button_index() != 1) + if (status.disabled || ((1 << (b->get_button_index() - 1)) & button_mask) == 0) return; if (status.pressing_button) @@ -210,6 +211,11 @@ void BaseButton::_gui_input(Ref<InputEvent> p_event) { if (!toggle_mode) { //mouse press attempt pressed(); + if (get_script_instance()) { + Variant::CallError ce; + get_script_instance()->call(SceneStringNames::get_singleton()->_pressed, NULL, 0, ce); + } + emit_signal("pressed"); } else { @@ -246,7 +252,7 @@ void BaseButton::_notification(int p_what) { status.hovering = false; update(); } - if (p_what == NOTIFICATION_DRAG_BEGIN) { + if (p_what == NOTIFICATION_DRAG_BEGIN || p_what == NOTIFICATION_SCROLL_BEGIN) { if (status.press_attempt) { status.press_attempt = false; @@ -306,14 +312,12 @@ void BaseButton::toggled(bool p_pressed) { } void BaseButton::set_disabled(bool p_disabled) { + if (status.disabled == p_disabled) + return; status.disabled = p_disabled; update(); _change_notify("disabled"); - if (p_disabled) - set_focus_mode(FOCUS_NONE); - else - set_focus_mode(enabled_focus_mode); } bool BaseButton::is_disabled() const { @@ -404,6 +408,16 @@ BaseButton::ActionMode BaseButton::get_action_mode() const { return action_mode; } +void BaseButton::set_button_mask(int p_mask) { + + button_mask = p_mask; +} + +int BaseButton::get_button_mask() const { + + return button_mask; +} + void BaseButton::set_enabled_focus_mode(FocusMode p_mode) { enabled_focus_mode = p_mode; @@ -492,6 +506,8 @@ void BaseButton::_bind_methods() { ClassDB::bind_method(D_METHOD("is_disabled"), &BaseButton::is_disabled); ClassDB::bind_method(D_METHOD("set_action_mode", "mode"), &BaseButton::set_action_mode); ClassDB::bind_method(D_METHOD("get_action_mode"), &BaseButton::get_action_mode); + ClassDB::bind_method(D_METHOD("set_button_mask", "mask"), &BaseButton::set_button_mask); + ClassDB::bind_method(D_METHOD("get_button_mask"), &BaseButton::get_button_mask); ClassDB::bind_method(D_METHOD("get_draw_mode"), &BaseButton::get_draw_mode); ClassDB::bind_method(D_METHOD("set_enabled_focus_mode", "mode"), &BaseButton::set_enabled_focus_mode); ClassDB::bind_method(D_METHOD("get_enabled_focus_mode"), &BaseButton::get_enabled_focus_mode); @@ -503,16 +519,17 @@ void BaseButton::_bind_methods() { ClassDB::bind_method(D_METHOD("get_button_group"), &BaseButton::get_button_group); BIND_VMETHOD(MethodInfo("_pressed")); - BIND_VMETHOD(MethodInfo("_toggled", PropertyInfo(Variant::BOOL, "pressed"))); + BIND_VMETHOD(MethodInfo("_toggled", PropertyInfo(Variant::BOOL, "button_pressed"))); ADD_SIGNAL(MethodInfo("pressed")); ADD_SIGNAL(MethodInfo("button_up")); ADD_SIGNAL(MethodInfo("button_down")); - ADD_SIGNAL(MethodInfo("toggled", PropertyInfo(Variant::BOOL, "pressed"))); + ADD_SIGNAL(MethodInfo("toggled", PropertyInfo(Variant::BOOL, "button_pressed"))); ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "toggle_mode"), "set_toggle_mode", "is_toggle_mode"); ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "pressed"), "set_pressed", "is_pressed"); ADD_PROPERTYNO(PropertyInfo(Variant::INT, "action_mode", PROPERTY_HINT_ENUM, "Button Press,Button Release"), "set_action_mode", "get_action_mode"); + ADD_PROPERTYNO(PropertyInfo(Variant::INT, "button_mask", PROPERTY_HINT_FLAGS, "Mouse Left, Mouse Right, Mouse Middle"), "set_button_mask", "get_button_mask"); ADD_PROPERTY(PropertyInfo(Variant::INT, "enabled_focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_enabled_focus_mode", "get_enabled_focus_mode"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shortcut", PROPERTY_HINT_RESOURCE_TYPE, "ShortCut"), "set_shortcut", "get_shortcut"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "group", PROPERTY_HINT_RESOURCE_TYPE, "ButtonGroup"), "set_button_group", "get_button_group"); @@ -538,15 +555,16 @@ BaseButton::BaseButton() { set_focus_mode(FOCUS_ALL); enabled_focus_mode = FOCUS_ALL; action_mode = ACTION_MODE_BUTTON_RELEASE; + button_mask = BUTTON_MASK_LEFT; +} + +BaseButton::~BaseButton() { if (button_group.is_valid()) { button_group->buttons.erase(this); } } -BaseButton::~BaseButton() { -} - void ButtonGroup::get_buttons(List<BaseButton *> *r_buttons) { for (Set<BaseButton *>::Element *E = buttons.front(); E; E = E->next()) { diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h index 00c9fe959c..79638bbcce 100644 --- a/scene/gui/base_button.h +++ b/scene/gui/base_button.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef BASE_BUTTON_H #define BASE_BUTTON_H @@ -48,6 +49,7 @@ public: }; private: + int button_mask; bool toggle_mode; FocusMode enabled_focus_mode; Ref<ShortCut> shortcut; @@ -89,8 +91,8 @@ public: /* Signals */ - bool is_pressed() const; ///< return wether button is pressed (toggled in) - bool is_pressing() const; ///< return wether button is pressed (toggled in) + bool is_pressed() const; ///< return whether button is pressed (toggled in) + bool is_pressing() const; ///< return whether button is pressed (toggled in) bool is_hovered() const; void set_pressed(bool p_pressed); ///only works in toggle mode @@ -103,6 +105,9 @@ public: void set_action_mode(ActionMode p_mode); ActionMode get_action_mode() const; + void set_button_mask(int p_mask); + int get_button_mask() const; + void set_enabled_focus_mode(FocusMode p_mode); FocusMode get_enabled_focus_mode() const; diff --git a/scene/gui/box_container.cpp b/scene/gui/box_container.cpp index b7f1dbaa70..12b9fe7c03 100644 --- a/scene/gui/box_container.cpp +++ b/scene/gui/box_container.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "box_container.h" #include "label.h" #include "margin_container.h" diff --git a/scene/gui/box_container.h b/scene/gui/box_container.h index b22b3e28e3..abc228f804 100644 --- a/scene/gui/box_container.h +++ b/scene/gui/box_container.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef BOX_CONTAINER_H #define BOX_CONTAINER_H diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp index d5715d63b6..03b25a138f 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "button.h" #include "print_string.h" #include "servers/visual_server.h" diff --git a/scene/gui/button.h b/scene/gui/button.h index b3645fff7a..0b41b14f02 100644 --- a/scene/gui/button.h +++ b/scene/gui/button.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef BUTTON_H #define BUTTON_H diff --git a/scene/gui/center_container.cpp b/scene/gui/center_container.cpp index bf9ca69be4..cf71f89830 100644 --- a/scene/gui/center_container.cpp +++ b/scene/gui/center_container.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "center_container.h" Size2 CenterContainer::get_minimum_size() const { diff --git a/scene/gui/center_container.h b/scene/gui/center_container.h index 97912b11c7..519e1493ec 100644 --- a/scene/gui/center_container.h +++ b/scene/gui/center_container.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef CENTER_CONTAINER_H #define CENTER_CONTAINER_H diff --git a/scene/gui/check_box.cpp b/scene/gui/check_box.cpp index 73db240468..0790f87ea7 100644 --- a/scene/gui/check_box.cpp +++ b/scene/gui/check_box.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* check_button.cpp */ +/* check_box.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "check_box.h" #include "servers/visual_server.h" diff --git a/scene/gui/check_box.h b/scene/gui/check_box.h index e7358c1fb2..8375b46ca0 100644 --- a/scene/gui/check_box.h +++ b/scene/gui/check_box.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef CHECK_BOX_H #define CHECK_BOX_H diff --git a/scene/gui/check_button.cpp b/scene/gui/check_button.cpp index dbafcd4353..f9ed0ecdbb 100644 --- a/scene/gui/check_button.cpp +++ b/scene/gui/check_button.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "check_button.h" #include "print_string.h" diff --git a/scene/gui/check_button.h b/scene/gui/check_button.h index 8cf927de9b..a11749e3f6 100644 --- a/scene/gui/check_button.h +++ b/scene/gui/check_button.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef CHECK_BUTTON_H #define CHECK_BUTTON_H diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index 95298f5a18..6f34f3e49f 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "color_picker.h" #include "os/input.h" @@ -76,8 +77,7 @@ void ColorPicker::_notification(int p_what) { void ColorPicker::set_focus_on_line_edit() { - c_text->grab_focus(); - c_text->select(); + c_text->call_deferred("grab_focus"); } void ColorPicker::_update_controls() { @@ -158,14 +158,16 @@ void ColorPicker::_update_color() { updating = true; for (int i = 0; i < 4; i++) { - scroll[i]->set_max(255); scroll[i]->set_step(0.01); if (raw_mode_enabled) { + scroll[i]->set_max(100); if (i == 3) scroll[i]->set_max(1); scroll[i]->set_value(color.components[i]); } else { - scroll[i]->set_value(color.components[i] * 255); + const int byte_value = color.components[i] * 255; + scroll[i]->set_max(next_power_of_2(MAX(255, byte_value)) - 1); + scroll[i]->set_value(byte_value); } } @@ -209,6 +211,7 @@ Color ColorPicker::get_pick_color() const { } void ColorPicker::add_preset(const Color &p_color) { + if (presets.find(p_color)) { presets.move_to_back(presets.find(p_color)); } else { @@ -240,6 +243,7 @@ bool ColorPicker::is_raw_mode() const { } void ColorPicker::_update_text_value() { + bool visible = true; if (text_is_constructor) { String t = "Color(" + String::num(color.r) + "," + String::num(color.g) + "," + String::num(color.b); if (edit_alpha && color.a < 1) @@ -248,8 +252,13 @@ void ColorPicker::_update_text_value() { t += ")"; c_text->set_text(t); } else { - c_text->set_text(color.to_html(edit_alpha && color.a < 1)); + if (color.r > 1 || color.g > 1 || color.b > 1 || color.r < 0 || color.g < 0 || color.b < 0) { + visible = false; + } else { + c_text->set_text(color.to_html(edit_alpha && color.a < 1)); + } } + c_text->set_visible(visible); } void ColorPicker::_sample_draw() { @@ -460,6 +469,31 @@ void ColorPicker::_screen_pick_pressed() { screen->show_modal(); } +void ColorPicker::_focus_enter() { + if (c_text->has_focus()) { + c_text->select_all(); + return; + } + for (int i = 0; i < 4; i++) { + if (values[i]->get_line_edit()->has_focus()) { + values[i]->get_line_edit()->select_all(); + break; + } + } +} + +void ColorPicker::_focus_exit() { + for (int i = 0; i < 4; i++) { + values[i]->get_line_edit()->select(0, 0); + } + c_text->select(0, 0); +} + +void ColorPicker::_html_focus_exit() { + _html_entered(c_text->get_text()); + _focus_exit(); +} + void ColorPicker::_bind_methods() { ClassDB::bind_method(D_METHOD("set_pick_color", "color"), &ColorPicker::set_pick_color); @@ -481,6 +515,13 @@ void ColorPicker::_bind_methods() { ClassDB::bind_method(D_METHOD("_w_input"), &ColorPicker::_w_input); ClassDB::bind_method(D_METHOD("_preset_input"), &ColorPicker::_preset_input); ClassDB::bind_method(D_METHOD("_screen_input"), &ColorPicker::_screen_input); + ClassDB::bind_method(D_METHOD("_focus_enter"), &ColorPicker::_focus_enter); + ClassDB::bind_method(D_METHOD("_focus_exit"), &ColorPicker::_focus_exit); + ClassDB::bind_method(D_METHOD("_html_focus_exit"), &ColorPicker::_html_focus_exit); + + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_pick_color", "get_pick_color"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "edit_alpha"), "set_edit_alpha", "is_editing_alpha"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "raw_mode"), "set_raw_mode", "is_raw_mode"); ADD_SIGNAL(MethodInfo("color_changed", PropertyInfo(Variant::COLOR, "color"))); } @@ -553,11 +594,14 @@ ColorPicker::ColorPicker() : scroll[i] = memnew(HSlider); scroll[i]->set_v_size_flags(SIZE_SHRINK_CENTER); + scroll[i]->set_focus_mode(FOCUS_NONE); hbc->add_child(scroll[i]); values[i] = memnew(SpinBox); scroll[i]->share(values[i]); hbc->add_child(values[i]); + values[i]->get_line_edit()->connect("focus_entered", this, "_focus_enter"); + values[i]->get_line_edit()->connect("focus_exited", this, "_focus_exit"); scroll[i]->set_min(0); scroll[i]->set_page(0); @@ -583,6 +627,9 @@ ColorPicker::ColorPicker() : c_text = memnew(LineEdit); hhb->add_child(c_text); c_text->connect("text_entered", this, "_html_entered"); + c_text->connect("focus_entered", this, "_focus_enter"); + c_text->connect("focus_exited", this, "_html_focus_exit"); + text_type->set_text("#"); c_text->set_h_size_flags(SIZE_EXPAND_FILL); @@ -613,6 +660,11 @@ void ColorPickerButton::_color_changed(const Color &p_color) { emit_signal("color_changed", p_color); } +void ColorPickerButton::_modal_closed() { + + emit_signal("popup_closed"); +} + void ColorPickerButton::pressed() { popup->set_position(get_global_position() - picker->get_combined_minimum_size()); @@ -675,8 +727,10 @@ void ColorPickerButton::_bind_methods() { ClassDB::bind_method(D_METHOD("set_edit_alpha", "show"), &ColorPickerButton::set_edit_alpha); ClassDB::bind_method(D_METHOD("is_editing_alpha"), &ColorPickerButton::is_editing_alpha); ClassDB::bind_method(D_METHOD("_color_changed"), &ColorPickerButton::_color_changed); + ClassDB::bind_method(D_METHOD("_modal_closed"), &ColorPickerButton::_modal_closed); ADD_SIGNAL(MethodInfo("color_changed", PropertyInfo(Variant::COLOR, "color"))); + ADD_SIGNAL(MethodInfo("popup_closed")); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_pick_color", "get_pick_color"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "edit_alpha"), "set_edit_alpha", "is_editing_alpha"); } @@ -688,5 +742,7 @@ ColorPickerButton::ColorPickerButton() { popup->add_child(picker); picker->connect("color_changed", this, "_color_changed"); + popup->connect("modal_closed", this, "_modal_closed"); + add_child(popup); } diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h index 1f6a8fb1bb..7d1a554ada 100644 --- a/scene/gui/color_picker.h +++ b/scene/gui/color_picker.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef COLOR_PICKER_H #define COLOR_PICKER_H @@ -87,6 +88,9 @@ private: void _screen_input(const Ref<InputEvent> &p_event); void _add_preset_pressed(); void _screen_pick_pressed(); + void _focus_enter(); + void _focus_exit(); + void _html_focus_exit(); protected: void _notification(int); @@ -116,6 +120,8 @@ class ColorPickerButton : public Button { ColorPicker *picker; void _color_changed(const Color &p_color); + void _modal_closed(); + virtual void pressed(); protected: diff --git a/scene/gui/color_rect.cpp b/scene/gui/color_rect.cpp index d212ba9416..463f3911dc 100644 --- a/scene/gui/color_rect.cpp +++ b/scene/gui/color_rect.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "color_rect.h" void ColorRect::set_frame_color(const Color &p_color) { diff --git a/scene/gui/color_rect.h b/scene/gui/color_rect.h index 747ae72997..a841008f76 100644 --- a/scene/gui/color_rect.h +++ b/scene/gui/color_rect.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef COLOR_RECT_H #define COLOR_RECT_H diff --git a/scene/gui/container.cpp b/scene/gui/container.cpp index 20616d5dd8..7cb0ad5707 100644 --- a/scene/gui/container.cpp +++ b/scene/gui/container.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "container.h" #include "message_queue.h" #include "scene/scene_string_names.h" diff --git a/scene/gui/container.h b/scene/gui/container.h index 2b9910dcf5..c472162f58 100644 --- a/scene/gui/container.h +++ b/scene/gui/container.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef CONTAINER_H #define CONTAINER_H diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 6072653c54..b7c1d35fd7 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "control.h" #include "project_settings.h" #include "scene/main/canvas_layer.h" @@ -48,31 +49,41 @@ Dictionary Control::_edit_get_state() const { Dictionary s; - s["rect"] = get_rect(); s["rotation"] = get_rotation(); s["scale"] = get_scale(); + s["pivot"] = get_pivot_offset(); Array anchors; anchors.push_back(get_anchor(MARGIN_LEFT)); anchors.push_back(get_anchor(MARGIN_TOP)); anchors.push_back(get_anchor(MARGIN_RIGHT)); anchors.push_back(get_anchor(MARGIN_BOTTOM)); s["anchors"] = anchors; + Array margins; + margins.push_back(get_margin(MARGIN_LEFT)); + margins.push_back(get_margin(MARGIN_TOP)); + margins.push_back(get_margin(MARGIN_RIGHT)); + margins.push_back(get_margin(MARGIN_BOTTOM)); + s["margins"] = margins; return s; } void Control::_edit_set_state(const Dictionary &p_state) { Dictionary state = p_state; - Rect2 rect = state["rect"]; - set_position(rect.position); - set_size(rect.size); set_rotation(state["rotation"]); set_scale(state["scale"]); + set_pivot_offset(state["pivot"]); Array anchors = state["anchors"]; - set_anchor(MARGIN_LEFT, anchors[0]); - set_anchor(MARGIN_TOP, anchors[1]); - set_anchor(MARGIN_RIGHT, anchors[2]); - set_anchor(MARGIN_BOTTOM, anchors[3]); + data.anchor[MARGIN_LEFT] = anchors[0]; + data.anchor[MARGIN_TOP] = anchors[1]; + data.anchor[MARGIN_RIGHT] = anchors[2]; + data.anchor[MARGIN_BOTTOM] = anchors[3]; + Array margins = state["margins"]; + data.margin[MARGIN_LEFT] = margins[0]; + data.margin[MARGIN_TOP] = margins[1]; + data.margin[MARGIN_RIGHT] = margins[2]; + data.margin[MARGIN_BOTTOM] = margins[3]; + _size_changed(); } void Control::_edit_set_position(const Point2 &p_position) { @@ -83,20 +94,17 @@ Point2 Control::_edit_get_position() const { return get_position(); }; -void Control::_edit_set_rect(const Rect2 &p_edit_rect) { - - Transform2D xform = _get_internal_transform(); - - Vector2 new_pos = xform.basis_xform(p_edit_rect.position); - - Vector2 pos = get_position() + new_pos; +void Control::_edit_set_scale(const Size2 &p_scale) { + set_scale(p_scale); +} - Rect2 new_rect = get_rect(); - new_rect.position = pos.snapped(Vector2(1, 1)); - new_rect.size = p_edit_rect.size.snapped(Vector2(1, 1)); +Size2 Control::_edit_get_scale() const { + return data.scale; +} - set_position(new_rect.position); - set_size(new_rect.size); +void Control::_edit_set_rect(const Rect2 &p_edit_rect) { + set_position((get_position() + get_transform().basis_xform(p_edit_rect.position)).snapped(Vector2(1, 1))); + set_size(p_edit_rect.size.snapped(Vector2(1, 1))); } Rect2 Control::_edit_get_rect() const { @@ -120,6 +128,9 @@ bool Control::_edit_use_rotation() const { } void Control::_edit_set_pivot(const Point2 &p_pivot) { + Vector2 delta_pivot = p_pivot - get_pivot_offset(); + Vector2 move = Vector2((cos(data.rotation) - 1.0) * delta_pivot.x - sin(data.rotation) * delta_pivot.y, sin(data.rotation) * delta_pivot.x + (cos(data.rotation) - 1.0) * delta_pivot.y); + set_position(get_position() + move); set_pivot_offset(p_pivot); } @@ -860,6 +871,8 @@ Ref<StyleBox> Control::get_stylebox(const StringName &p_name, const StringName & class_name = ClassDB::get_parent_class_nocheck(class_name); } + class_name = type; + Control *parent = Object::cast_to<Control>(theme_owner->get_parent()); if (parent) @@ -868,8 +881,6 @@ Ref<StyleBox> Control::get_stylebox(const StringName &p_name, const StringName & theme_owner = NULL; } - class_name = type; - while (class_name != StringName()) { if (Theme::get_default()->has_stylebox(p_name, class_name)) return Theme::get_default()->get_stylebox(p_name, class_name); @@ -1278,25 +1289,28 @@ void Control::_size_changed() { Size2 minimum_size = get_combined_minimum_size(); - if (data.h_grow == GROW_DIRECTION_BEGIN) { - if (minimum_size.width > new_size_cache.width) { - new_pos_cache.x = new_pos_cache.x + new_size_cache.width - minimum_size.width; - new_size_cache.width = minimum_size.width; + 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); } - } else { - new_size_cache.width = MAX(minimum_size.width, new_size_cache.width); + + new_size_cache.width = minimum_size.width; } - if (data.v_grow == GROW_DIRECTION_BEGIN) { - if (minimum_size.height > new_size_cache.height) { - new_pos_cache.y = new_pos_cache.y + new_size_cache.height - minimum_size.height; - new_size_cache.height = minimum_size.height; + 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); } - } else { - new_size_cache.height = MAX(minimum_size.height, new_size_cache.height); + + new_size_cache.height = minimum_size.height; } - if (get_viewport()->is_snap_controls_to_pixels_enabled()) { + // We use a little workaround to avoid flickering when moving the pivot with _edit_set_pivot() + if (Math::abs(Math::sin(data.rotation * 4.0f)) < 0.00001f && get_viewport()->is_snap_controls_to_pixels_enabled()) { new_size_cache = new_size_cache.floor(); new_pos_cache = new_pos_cache.floor(); } @@ -1324,7 +1338,7 @@ float Control::_get_parent_range(int p_idx) const { if (!is_inside_tree()) { - return 1.0; + return 0; } if (data.parent_canvas_item) { @@ -1333,7 +1347,7 @@ float Control::_get_parent_range(int p_idx) const { return get_viewport()->get_visible_rect().size[p_idx & 1]; } - return 1.0; + return 0; } float Control::_get_range(int p_idx) const { @@ -1356,24 +1370,31 @@ float Control::_a2s(float p_val, float p_anchor, float p_range) const { } void Control::set_anchor(Margin p_margin, float p_anchor, bool p_keep_margin, bool p_push_opposite_anchor) { - bool pushed = false; + float parent_range = _get_parent_range((p_margin == MARGIN_LEFT || p_margin == MARGIN_RIGHT) ? 0 : 1); + float previous_margin_pos = data.margin[p_margin] + data.anchor[p_margin] * parent_range; + float previous_opposite_margin_pos = data.margin[(p_margin + 2) % 4] + data.anchor[(p_margin + 2) % 4] * parent_range; + data.anchor[p_margin] = CLAMP(p_anchor, 0.0, 1.0); if (((p_margin == MARGIN_LEFT || p_margin == MARGIN_TOP) && data.anchor[p_margin] > data.anchor[(p_margin + 2) % 4]) || ((p_margin == MARGIN_RIGHT || p_margin == MARGIN_BOTTOM) && data.anchor[p_margin] < data.anchor[(p_margin + 2) % 4])) { if (p_push_opposite_anchor) { data.anchor[(p_margin + 2) % 4] = data.anchor[p_margin]; - pushed = true; } else { data.anchor[p_margin] = data.anchor[(p_margin + 2) % 4]; } } - if (is_inside_tree()) { - if (p_keep_margin) { - _size_changed(); + if (!p_keep_margin) { + data.margin[p_margin] = _s2a(previous_margin_pos, data.anchor[p_margin], parent_range); + if (p_push_opposite_anchor) { + data.margin[(p_margin + 2) % 4] = _s2a(previous_opposite_margin_pos, data.anchor[(p_margin + 2) % 4], parent_range); } } + if (is_inside_tree()) { + _size_changed(); + } + update(); _change_notify(); } @@ -2004,7 +2025,7 @@ Control *Control::find_prev_valid_focus() const { if (from->is_set_as_toplevel() || !Object::cast_to<Control>(from->get_parent())) { - //find last of the childs + //find last of the children prev_child = _prev_control(from); @@ -2154,6 +2175,7 @@ void Control::set_theme(const Ref<Theme> &p_theme) { data.theme = p_theme; if (!p_theme.is_null()) { + data.theme_owner = this; _propagate_theme_changed(this, this); } else { @@ -2737,7 +2759,7 @@ void Control::_bind_methods() { ClassDB::bind_method(D_METHOD("add_icon_override", "name", "texture"), &Control::add_icon_override); ClassDB::bind_method(D_METHOD("add_shader_override", "name", "shader"), &Control::add_shader_override); - ClassDB::bind_method(D_METHOD("add_style_override", "name", "stylebox"), &Control::add_style_override); + ClassDB::bind_method(D_METHOD("add_stylebox_override", "name", "stylebox"), &Control::add_style_override); ClassDB::bind_method(D_METHOD("add_font_override", "name", "font"), &Control::add_font_override); ClassDB::bind_method(D_METHOD("add_color_override", "name", "color"), &Control::add_color_override); ClassDB::bind_method(D_METHOD("add_constant_override", "name", "constant"), &Control::add_constant_override); @@ -2749,6 +2771,7 @@ void Control::_bind_methods() { ClassDB::bind_method(D_METHOD("get_constant", "name", "type"), &Control::get_constant, DEFVAL("")); ClassDB::bind_method(D_METHOD("has_icon_override", "name"), &Control::has_icon_override); + ClassDB::bind_method(D_METHOD("has_shader_override", "name"), &Control::has_shader_override); ClassDB::bind_method(D_METHOD("has_stylebox_override", "name"), &Control::has_stylebox_override); ClassDB::bind_method(D_METHOD("has_font_override", "name"), &Control::has_font_override); ClassDB::bind_method(D_METHOD("has_color_override", "name"), &Control::has_color_override); @@ -2825,17 +2848,18 @@ void Control::_bind_methods() { ADD_PROPERTYINZ(PropertyInfo(Variant::INT, "margin_bottom", PROPERTY_HINT_RANGE, "-4096,4096"), "set_margin", "get_margin", MARGIN_BOTTOM); ADD_GROUP("Grow Direction", "grow_"); - ADD_PROPERTYNO(PropertyInfo(Variant::INT, "grow_horizontal", PROPERTY_HINT_ENUM, "Begin,End"), "set_h_grow_direction", "get_h_grow_direction"); - ADD_PROPERTYNO(PropertyInfo(Variant::INT, "grow_vertical", PROPERTY_HINT_ENUM, "Begin,End"), "set_v_grow_direction", "get_v_grow_direction"); + ADD_PROPERTYNO(PropertyInfo(Variant::INT, "grow_horizontal", PROPERTY_HINT_ENUM, "Begin,End,Both"), "set_h_grow_direction", "get_h_grow_direction"); + ADD_PROPERTYNO(PropertyInfo(Variant::INT, "grow_vertical", PROPERTY_HINT_ENUM, "Begin,End,Both"), "set_v_grow_direction", "get_v_grow_direction"); ADD_GROUP("Rect", "rect_"); ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "rect_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_position", "get_position"); + ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "rect_global_position", PROPERTY_HINT_NONE, "", 0), "set_global_position", "get_global_position"); ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "rect_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_size", "get_size"); ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "rect_min_size"), "set_custom_minimum_size", "get_custom_minimum_size"); ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "rect_rotation", PROPERTY_HINT_RANGE, "-1080,1080,0.01"), "set_rotation_degrees", "get_rotation_degrees"); ADD_PROPERTYNO(PropertyInfo(Variant::VECTOR2, "rect_scale"), "set_scale", "get_scale"); ADD_PROPERTYNO(PropertyInfo(Variant::VECTOR2, "rect_pivot_offset"), "set_pivot_offset", "get_pivot_offset"); - ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "rect_clip_content"), "set_clip_contents", "is_clipping_contents"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rect_clip_content"), "set_clip_contents", "is_clipping_contents"); ADD_GROUP("Hint", "hint_"); ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "hint_tooltip", PROPERTY_HINT_MULTILINE_TEXT), "set_tooltip", "_get_tooltip"); @@ -2847,9 +2871,11 @@ void Control::_bind_methods() { ADD_PROPERTYINZ(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_bottom"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_BOTTOM); ADD_PROPERTYNZ(PropertyInfo(Variant::NODE_PATH, "focus_next"), "set_focus_next", "get_focus_next"); ADD_PROPERTYNZ(PropertyInfo(Variant::NODE_PATH, "focus_previous"), "set_focus_previous", "get_focus_previous"); + ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_focus_mode", "get_focus_mode"); ADD_GROUP("Mouse", "mouse_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "mouse_filter", PROPERTY_HINT_ENUM, "Stop,Pass,Ignore"), "set_mouse_filter", "get_mouse_filter"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "mouse_default_cursor_shape", PROPERTY_HINT_ENUM, "Arrow,Ibeam,Pointing hand,Cross,Wait,Busy,Drag,Can drop,Forbidden,Vertical resize,Horizontal resize,Secondary diagonal resize,Main diagonal resize,Move,Vertial split,Horizontal split,Help"), "set_default_cursor_shape", "get_default_cursor_shape"); ADD_GROUP("Size Flags", "size_flags_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "size_flags_horizontal", PROPERTY_HINT_FLAGS, "Fill,Expand,Shrink Center,Shrink End"), "set_h_size_flags", "get_h_size_flags"); @@ -2870,6 +2896,8 @@ void Control::_bind_methods() { BIND_CONSTANT(NOTIFICATION_FOCUS_EXIT); BIND_CONSTANT(NOTIFICATION_THEME_CHANGED); BIND_CONSTANT(NOTIFICATION_MODAL_CLOSE); + BIND_CONSTANT(NOTIFICATION_SCROLL_BEGIN); + BIND_CONSTANT(NOTIFICATION_SCROLL_END); BIND_ENUM_CONSTANT(CURSOR_ARROW); BIND_ENUM_CONSTANT(CURSOR_IBEAM); @@ -2923,6 +2951,7 @@ void Control::_bind_methods() { BIND_ENUM_CONSTANT(GROW_DIRECTION_BEGIN); BIND_ENUM_CONSTANT(GROW_DIRECTION_END); + BIND_ENUM_CONSTANT(GROW_DIRECTION_BOTH); BIND_ENUM_CONSTANT(ANCHOR_BEGIN); BIND_ENUM_CONSTANT(ANCHOR_END); diff --git a/scene/gui/control.h b/scene/gui/control.h index 8da40e7d3f..b5453e60f5 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef CONTROL_H #define CONTROL_H @@ -59,7 +60,8 @@ public: enum GrowDirection { GROW_DIRECTION_BEGIN, - GROW_DIRECTION_END + GROW_DIRECTION_END, + GROW_DIRECTION_BOTH }; enum FocusMode { @@ -270,6 +272,8 @@ public: NOTIFICATION_FOCUS_EXIT = 44, NOTIFICATION_THEME_CHANGED = 45, NOTIFICATION_MODAL_CLOSE = 46, + NOTIFICATION_SCROLL_BEGIN = 47, + NOTIFICATION_SCROLL_END = 48, }; @@ -279,6 +283,9 @@ public: virtual void _edit_set_position(const Point2 &p_position); virtual Point2 _edit_get_position() const; + virtual void _edit_set_scale(const Size2 &p_scale); + virtual Size2 _edit_get_scale() const; + virtual void _edit_set_rect(const Rect2 &p_edit_rect); virtual Rect2 _edit_get_rect() const; virtual bool _edit_use_rect() const; @@ -316,11 +323,11 @@ public: /* POSITIONING */ - void set_anchors_preset(LayoutPreset p_preset, bool p_keep_margin = false); + void set_anchors_preset(LayoutPreset p_preset, bool p_keep_margin = true); void set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode = PRESET_MODE_MINSIZE, int p_margin = 0); void set_anchors_and_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode = PRESET_MODE_MINSIZE, int p_margin = 0); - void set_anchor(Margin p_margin, float p_anchor, bool p_keep_margin = false, bool p_push_opposite_anchor = true); + void set_anchor(Margin p_margin, float p_anchor, bool p_keep_margin = true, bool p_push_opposite_anchor = true); float get_anchor(Margin p_margin) const; void set_margin(Margin p_margin, float p_value); diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp index a2cf0c951f..d9737fa21a 100644 --- a/scene/gui/dialogs.cpp +++ b/scene/gui/dialogs.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "dialogs.h" #include "line_edit.h" #include "print_string.h" diff --git a/scene/gui/dialogs.h b/scene/gui/dialogs.h index c4cde8ef22..feb080dd06 100644 --- a/scene/gui/dialogs.h +++ b/scene/gui/dialogs.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef DIALOGS_H #define DIALOGS_H @@ -107,7 +108,7 @@ class AcceptDialog : public WindowDialog { HBoxContainer *hbc; Label *label; Button *ok; - //Button *cancel; no more cancel (there is X on tht titlebar) + //Button *cancel; no more cancel (there is X on that titlebar) bool hide_on_ok; void _custom_action(const String &p_action); diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index 7bf06bc8c2..4bd92d888d 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "file_dialog.h" #include "os/keyboard.h" #include "print_string.h" @@ -209,7 +210,7 @@ void FileDialog::_action_pressed() { bool valid = false; if (filter->get_selected() == filter->get_item_count() - 1) { - valid = true; //match none + valid = true; // match none } else if (filters.size() > 1 && filter->get_selected() == 0) { // match all filters for (int i = 0; i < filters.size(); i++) { @@ -286,7 +287,7 @@ bool FileDialog::_is_open_should_be_disabled() { TreeItem *ti = tree->get_selected(); // We have something that we can't select? if (!ti) - return true; + return mode != MODE_OPEN_DIR; // In "Open folder" mode, having nothing selected picks the current folder. Dictionary d = ti->get_metadata(0); @@ -318,17 +319,15 @@ void FileDialog::deselect_items() { case MODE_OPEN_FILE: case MODE_OPEN_FILES: - get_ok()->set_text(TTR("Open")); - get_ok()->set_disabled(false); + get_ok()->set_text(RTR("Open")); break; - case MODE_OPEN_DIR: - get_ok()->set_text(TTR("Select Current Folder")); - get_ok()->set_disabled(false); + get_ok()->set_text(RTR("Select Current Folder")); break; } } } + void FileDialog::_tree_selected() { TreeItem *ti = tree->get_selected(); @@ -340,13 +339,13 @@ void FileDialog::_tree_selected() { file->set_text(d["name"]); } else if (mode == MODE_OPEN_DIR) { - get_ok()->set_text(TTR("Select this Folder")); + get_ok()->set_text(RTR("Select this Folder")); } get_ok()->set_disabled(_is_open_should_be_disabled()); } -void FileDialog::_tree_dc_selected() { +void FileDialog::_tree_item_activated() { TreeItem *ti = tree->get_selected(); if (!ti) @@ -755,7 +754,7 @@ void FileDialog::_bind_methods() { ClassDB::bind_method(D_METHOD("_unhandled_input"), &FileDialog::_unhandled_input); ClassDB::bind_method(D_METHOD("_tree_selected"), &FileDialog::_tree_selected); - ClassDB::bind_method(D_METHOD("_tree_db_selected"), &FileDialog::_tree_dc_selected); + ClassDB::bind_method(D_METHOD("_tree_item_activated"), &FileDialog::_tree_item_activated); ClassDB::bind_method(D_METHOD("_dir_entered"), &FileDialog::_dir_entered); ClassDB::bind_method(D_METHOD("_file_entered"), &FileDialog::_file_entered); ClassDB::bind_method(D_METHOD("_action_pressed"), &FileDialog::_action_pressed); @@ -792,6 +791,15 @@ void FileDialog::_bind_methods() { ClassDB::bind_method(D_METHOD("invalidate"), &FileDialog::invalidate); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "mode_overrides_title"), "set_mode_overrides_title", "is_mode_overriding_title"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Open one,Open many,Open folder,Open any,Save"), "set_mode", "get_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "access", PROPERTY_HINT_ENUM, "Resources,User data,File system"), "set_access", "get_access"); + ADD_PROPERTY(PropertyInfo(Variant::POOL_STRING_ARRAY, "filters"), "set_filters", "get_filters"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_hidden_files"), "set_show_hidden_files", "is_showing_hidden_files"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "current_dir"), "set_current_dir", "get_current_dir"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "current_file"), "set_current_file", "get_current_file"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "current_path"), "set_current_path", "get_current_path"); + ADD_SIGNAL(MethodInfo("file_selected", PropertyInfo(Variant::STRING, "path"))); ADD_SIGNAL(MethodInfo("files_selected", PropertyInfo(Variant::POOL_STRING_ARRAY, "paths"))); ADD_SIGNAL(MethodInfo("dir_selected", PropertyInfo(Variant::STRING, "dir"))); @@ -805,12 +813,6 @@ void FileDialog::_bind_methods() { BIND_ENUM_CONSTANT(ACCESS_RESOURCES); BIND_ENUM_CONSTANT(ACCESS_USERDATA); BIND_ENUM_CONSTANT(ACCESS_FILESYSTEM); - - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "mode_overrides_title"), "set_mode_overrides_title", "is_mode_overriding_title"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Open one,Open many,Open folder,Open any,Save"), "set_mode", "get_mode"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "access", PROPERTY_HINT_ENUM, "Resources,User data,File system"), "set_access", "get_access"); - ADD_PROPERTY(PropertyInfo(Variant::POOL_STRING_ARRAY, "filters"), "set_filters", "get_filters"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_hidden_files"), "set_show_hidden_files", "is_showing_hidden_files"); } void FileDialog::set_show_hidden_files(bool p_show) { @@ -841,7 +843,7 @@ FileDialog::FileDialog() { HBoxContainer *hbc = memnew(HBoxContainer); dir_up = memnew(ToolButton); - dir_up->set_tooltip(TTR("Go to parent folder")); + dir_up->set_tooltip(RTR("Go to parent folder")); hbc->add_child(dir_up); dir_up->connect("pressed", this, "_go_up"); @@ -877,7 +879,7 @@ FileDialog::FileDialog() { filter = memnew(OptionButton); filter->set_stretch_ratio(3); filter->set_h_size_flags(SIZE_EXPAND_FILL); - filter->set_clip_text(true); //too many extensions overflow it + filter->set_clip_text(true); // too many extensions overflows it hbc->add_child(filter); vbc->add_child(hbc); @@ -886,9 +888,8 @@ FileDialog::FileDialog() { _update_drives(); connect("confirmed", this, "_action_pressed"); - //cancel->connect("pressed", this,"_cancel_pressed"); tree->connect("cell_selected", this, "_tree_selected", varray(), CONNECT_DEFERRED); - tree->connect("item_activated", this, "_tree_db_selected", varray()); + tree->connect("item_activated", this, "_tree_item_activated", varray()); tree->connect("nothing_selected", this, "deselect_items"); dir->connect("text_entered", this, "_dir_entered"); file->connect("text_entered", this, "_file_entered"); @@ -918,7 +919,6 @@ FileDialog::FileDialog() { exterr->set_text(RTR("Must use a valid extension.")); add_child(exterr); - //update_file_list(); update_filters(); update_dir(); diff --git a/scene/gui/file_dialog.h b/scene/gui/file_dialog.h index 04f004bf47..ad483d5dab 100644 --- a/scene/gui/file_dialog.h +++ b/scene/gui/file_dialog.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef FILE_DIALOG_H #define FILE_DIALOG_H @@ -106,7 +107,7 @@ private: void _tree_selected(); void _select_drive(int p_idx); - void _tree_dc_selected(); + void _tree_item_activated(); void _dir_entered(String p_dir); void _file_entered(const String &p_file); void _action_pressed(); diff --git a/scene/gui/gradient_edit.cpp b/scene/gui/gradient_edit.cpp index 10cbac7d40..9fc8e98a7f 100644 --- a/scene/gui/gradient_edit.cpp +++ b/scene/gui/gradient_edit.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* color_ramp_edit.cpp */ +/* gradient_edit.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -27,6 +27,7 @@ /* 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 "os/keyboard.h" @@ -366,6 +367,13 @@ void GradientEdit::_notification(int p_what) { draw_line(Vector2(-1, -1), Vector2(-1, h + 1), Color(1, 1, 1, 0.6)); } } + + if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { + + if (!is_visible()) { + grabbing = false; + } + } } void GradientEdit::_draw_checker(int x, int y, int w, int h) { diff --git a/scene/gui/gradient_edit.h b/scene/gui/gradient_edit.h index 5471b0a769..e7834ea0de 100644 --- a/scene/gui/gradient_edit.h +++ b/scene/gui/gradient_edit.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* color_ramp_edit.h */ +/* gradient_edit.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -27,8 +27,9 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SCENE_GUI_COLOR_RAMP_EDIT_H_ -#define SCENE_GUI_COLOR_RAMP_EDIT_H_ + +#ifndef GRADIENT_EDIT_H +#define GRADIENT_EDIT_H #include "scene/gui/color_picker.h" #include "scene/gui/popup.h" @@ -72,9 +73,4 @@ public: virtual ~GradientEdit(); }; -/*class ColorRampEditPanel : public Panel -{ - GDCLASS(ColorRampEditPanel, Panel ); -};*/ - -#endif /* SCENE_GUI_COLOR_RAMP_EDIT_H_ */ +#endif // GRADIENT_EDIT_H diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index d0ab77ecf8..38ce91a4df 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "graph_edit.h" #include "os/input.h" @@ -1146,9 +1147,18 @@ void GraphEdit::_bind_methods() { 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("get_connection_list"), &GraphEdit::_get_connection_list); + ClassDB::bind_method(D_METHOD("clear_connections"), &GraphEdit::clear_connections); ClassDB::bind_method(D_METHOD("get_scroll_ofs"), &GraphEdit::get_scroll_ofs); ClassDB::bind_method(D_METHOD("set_scroll_ofs", "ofs"), &GraphEdit::set_scroll_ofs); + ClassDB::bind_method(D_METHOD("add_valid_right_disconnect_type", "type"), &GraphEdit::add_valid_right_disconnect_type); + ClassDB::bind_method(D_METHOD("remove_valid_right_disconnect_type", "type"), &GraphEdit::remove_valid_right_disconnect_type); + ClassDB::bind_method(D_METHOD("add_valid_left_disconnect_type", "type"), &GraphEdit::add_valid_left_disconnect_type); + ClassDB::bind_method(D_METHOD("remove_valid_left_disconnect_type", "type"), &GraphEdit::remove_valid_left_disconnect_type); + 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("set_zoom", "p_zoom"), &GraphEdit::set_zoom); ClassDB::bind_method(D_METHOD("get_zoom"), &GraphEdit::get_zoom); @@ -1179,6 +1189,12 @@ void GraphEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("set_selected", "node"), &GraphEdit::set_selected); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "right_disconnects"), "set_right_disconnects", "is_right_disconnects_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scroll_offset"), "set_scroll_ofs", "get_scroll_ofs"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "snap_distance"), "set_snap", "get_snap"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_snap"), "set_use_snap", "is_using_snap"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "zoom"), "set_zoom", "get_zoom"); + ADD_SIGNAL(MethodInfo("connection_request", PropertyInfo(Variant::STRING, "from"), PropertyInfo(Variant::INT, "from_slot"), PropertyInfo(Variant::STRING, "to"), PropertyInfo(Variant::INT, "to_slot"))); ADD_SIGNAL(MethodInfo("disconnection_request", PropertyInfo(Variant::STRING, "from"), PropertyInfo(Variant::INT, "from_slot"), PropertyInfo(Variant::STRING, "to"), PropertyInfo(Variant::INT, "to_slot"))); ADD_SIGNAL(MethodInfo("popup_request", PropertyInfo(Variant::VECTOR2, "p_position"))); diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h index d307765bc0..3bfde44854 100644 --- a/scene/gui/graph_edit.h +++ b/scene/gui/graph_edit.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* graph_edit.cpp */ +/* graph_edit.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef GRAPH_EDIT_H #define GRAPH_EDIT_H diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp index 12aa79bf65..24857d49fa 100644 --- a/scene/gui/graph_node.cpp +++ b/scene/gui/graph_node.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "graph_node.h" #include "method_bind_ext.gen.inc" @@ -704,8 +705,12 @@ void GraphNode::_bind_methods() { ClassDB::bind_method(D_METHOD("get_overlay"), &GraphNode::get_overlay); ADD_PROPERTY(PropertyInfo(Variant::STRING, "title"), "set_title", "get_title"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_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, "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"); ADD_SIGNAL(MethodInfo("offset_changed")); ADD_SIGNAL(MethodInfo("dragged", PropertyInfo(Variant::VECTOR2, "from"), PropertyInfo(Variant::VECTOR2, "to"))); diff --git a/scene/gui/graph_node.h b/scene/gui/graph_node.h index 13ef533134..20d25a69b0 100644 --- a/scene/gui/graph_node.h +++ b/scene/gui/graph_node.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef GRAPH_NODE_H #define GRAPH_NODE_H diff --git a/scene/gui/grid_container.cpp b/scene/gui/grid_container.cpp index a0d9b5f5ff..278e4123d7 100644 --- a/scene/gui/grid_container.cpp +++ b/scene/gui/grid_container.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "grid_container.h" void GridContainer::_notification(int p_what) { @@ -35,114 +36,131 @@ void GridContainer::_notification(int p_what) { case NOTIFICATION_SORT_CHILDREN: { - Map<int, int> col_minw; - Map<int, int> row_minh; - Set<int> col_expanded; - Set<int> row_expanded; + int valid_controls_index; + + Map<int, int> col_minw; // max of min_width of all controls in each col (indexed by col) + Map<int, int> row_minh; // max of min_height of all controls in each row (indexed by row) + Set<int> col_expanded; // columns which have the SIZE_EXPAND flag set + Set<int> row_expanded; // rows which have the SIZE_EXPAND flag set int hsep = get_constant("hseparation"); int vsep = get_constant("vseparation"); + int max_col = MIN(get_child_count(), columns); + int max_row = get_child_count() / columns; - int idx = 0; - int max_row = 0; - int max_col = 0; - - Size2 size = get_size(); - + // Compute the per-column/per-row data + valid_controls_index = 0; for (int i = 0; i < get_child_count(); i++) { - Control *c = Object::cast_to<Control>(get_child(i)); if (!c || !c->is_visible_in_tree()) continue; - int row = idx / columns; - int col = idx % columns; + int row = valid_controls_index / columns; + int col = valid_controls_index % columns; + valid_controls_index++; Size2i ms = c->get_combined_minimum_size(); if (col_minw.has(col)) col_minw[col] = MAX(col_minw[col], ms.width); else col_minw[col] = ms.width; - if (row_minh.has(row)) row_minh[row] = MAX(row_minh[row], ms.height); else row_minh[row] = ms.height; - //print_line("store row "+itos(row)+" mw "+itos(ms.height)); - - if (c->get_h_size_flags() & SIZE_EXPAND) + if (c->get_h_size_flags() & SIZE_EXPAND) { col_expanded.insert(col); - if (c->get_v_size_flags() & SIZE_EXPAND) + } + if (c->get_v_size_flags() & SIZE_EXPAND) { row_expanded.insert(row); - - max_col = MAX(col, max_col); - max_row = MAX(row, max_row); - idx++; + } } - Size2 ms; - int expand_rows = 0; - int expand_cols = 0; - + // Evaluate the remaining space for expanded columns/rows + Size2 remaining_space = get_size(); for (Map<int, int>::Element *E = col_minw.front(); E; E = E->next()) { - ms.width += E->get(); - if (col_expanded.has(E->key())) - expand_cols++; + if (!col_expanded.has(E->key())) + remaining_space.width -= E->get(); } for (Map<int, int>::Element *E = row_minh.front(); E; E = E->next()) { - ms.height += E->get(); - if (row_expanded.has(E->key())) - expand_rows++; + if (!row_expanded.has(E->key())) + remaining_space.height -= E->get(); + } + remaining_space.height -= vsep * MAX(max_row - 1, 0); + remaining_space.width -= hsep * MAX(max_col - 1, 0); + + bool can_fit = false; + while (!can_fit && col_expanded.size() > 0) { + // Check if all minwidth constraints are ok if we use the remaining space + can_fit = true; + int max_index = col_expanded.front()->get(); + for (Set<int>::Element *E = col_expanded.front(); E; E = E->next()) { + if (col_minw[E->get()] > col_minw[max_index]) { + max_index = E->get(); + } + if (can_fit && (remaining_space.width / col_expanded.size()) < col_minw[E->get()]) { + can_fit = false; + } + } + + // If not, the column with maximum minwidth is not expanded + if (!can_fit) { + col_expanded.erase(max_index); + remaining_space.width -= col_minw[max_index]; + } } - ms.height += vsep * max_row; - ms.width += hsep * max_col; + can_fit = false; + while (!can_fit && row_expanded.size() > 0) { + // Check if all minwidth constraints are ok if we use the remaining space + can_fit = true; + int max_index = row_expanded.front()->get(); + for (Set<int>::Element *E = row_expanded.front(); E; E = E->next()) { + if (row_minh[E->get()] > row_minh[max_index]) { + max_index = E->get(); + } + if (can_fit && (remaining_space.height / row_expanded.size()) < row_minh[E->get()]) { + can_fit = false; + } + } + + // If not, the row with maximum minwidth is not expanded + if (!can_fit) { + row_expanded.erase(max_index); + remaining_space.height -= row_minh[max_index]; + } + } - int row_expand = expand_rows ? (size.y - ms.y) / expand_rows : 0; - int col_expand = expand_cols ? (size.x - ms.x) / expand_cols : 0; + // Finally, fit the nodes + int col_expand = col_expanded.size() > 0 ? remaining_space.width / col_expanded.size() : 0; + int row_expand = row_expanded.size() > 0 ? remaining_space.height / row_expanded.size() : 0; int col_ofs = 0; int row_ofs = 0; - idx = 0; + valid_controls_index = 0; for (int i = 0; i < get_child_count(); i++) { - Control *c = Object::cast_to<Control>(get_child(i)); if (!c || !c->is_visible_in_tree()) continue; - int row = idx / columns; - int col = idx % columns; + int row = valid_controls_index / columns; + int col = valid_controls_index % columns; + valid_controls_index++; if (col == 0) { col_ofs = 0; - if (row > 0 && row_minh.has(row - 1)) - row_ofs += row_minh[row - 1] + vsep + (row_expanded.has(row - 1) ? row_expand : 0); + if (row > 0) + row_ofs += ((row_expanded.has(row - 1)) ? row_expand : row_minh[row - 1]) + vsep; } - Size2 s; - if (col_minw.has(col)) - s.width = col_minw[col]; - if (row_minh.has(row)) - s.height = row_minh[row]; - - if (row_expanded.has(row)) - s.height += row_expand; - if (col_expanded.has(col)) - s.width += col_expand; - Point2 p(col_ofs, row_ofs); + Size2 s((col_expanded.has(col)) ? col_expand : col_minw[col], (row_expanded.has(row)) ? row_expand : row_minh[row]); - //print_line("col: "+itos(col)+" row: "+itos(row)+" col_ofs: "+itos(col_ofs)+" row_ofs: "+itos(row_ofs)); fit_child_in_rect(c, Rect2(p, s)); - //print_line("col: "+itos(col)+" row: "+itos(row)+" rect: "+Rect2(p,s)); - - if (col_minw.has(col)) { - col_ofs += col_minw[col] + hsep + (col_expanded.has(col) ? col_expand : 0); - } - idx++; + col_ofs += s.width + hsep; } } break; @@ -166,6 +184,8 @@ void GridContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_columns", "columns"), &GridContainer::set_columns); ClassDB::bind_method(D_METHOD("get_columns"), &GridContainer::get_columns); + ClassDB::bind_method(D_METHOD("get_child_control_at_cell", "row", "column"), + &GridContainer::get_child_control_at_cell); ADD_PROPERTY(PropertyInfo(Variant::INT, "columns", PROPERTY_HINT_RANGE, "1,1024,1"), "set_columns", "get_columns"); } @@ -178,17 +198,19 @@ Size2 GridContainer::get_minimum_size() const { int hsep = get_constant("hseparation"); int vsep = get_constant("vseparation"); - int idx = 0; int max_row = 0; int max_col = 0; + int valid_controls_index = 0; for (int i = 0; i < get_child_count(); i++) { Control *c = Object::cast_to<Control>(get_child(i)); if (!c || !c->is_visible_in_tree()) continue; - int row = idx / columns; - int col = idx % columns; + int row = valid_controls_index / columns; + int col = valid_controls_index % columns; + valid_controls_index++; + Size2i ms = c->get_combined_minimum_size(); if (col_minw.has(col)) col_minw[col] = MAX(col_minw[col], ms.width); @@ -201,7 +223,6 @@ Size2 GridContainer::get_minimum_size() const { row_minh[row] = ms.height; max_col = MAX(col, max_col); max_row = MAX(row, max_row); - idx++; } Size2 ms; @@ -220,6 +241,21 @@ Size2 GridContainer::get_minimum_size() const { return ms; } +Control *GridContainer::get_child_control_at_cell(int row, int column) { + Control *c; + int grid_index = row * columns + column; + for (int i = 0; i < get_child_count(); i++) { + c = Object::cast_to<Control>(get_child(i)); + if (!c || !c->is_visible_in_tree()) + continue; + + if (grid_index == i) { + break; + } + } + return c; +} + GridContainer::GridContainer() { set_mouse_filter(MOUSE_FILTER_PASS); diff --git a/scene/gui/grid_container.h b/scene/gui/grid_container.h index c52f8230b0..7e3470dc89 100644 --- a/scene/gui/grid_container.h +++ b/scene/gui/grid_container.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef GRID_CONTAINER_H #define GRID_CONTAINER_H @@ -46,6 +47,7 @@ public: void set_columns(int p_columns); int get_columns() const; virtual Size2 get_minimum_size() const; + Control *get_child_control_at_cell(int row, int column); GridContainer(); }; diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index 5a4a0b2106..57b9a9a11b 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "item_list.h" #include "os/os.h" #include "project_settings.h" @@ -36,6 +37,7 @@ void ItemList::add_item(const String &p_item, const Ref<Texture> &p_texture, boo Item item; item.icon = p_texture; item.icon_region = Rect2i(); + item.icon_modulate = Color(1, 1, 1, 1); item.text = p_item; item.selectable = p_selectable; item.selected = false; @@ -53,6 +55,7 @@ void ItemList::add_icon_item(const Ref<Texture> &p_item, bool p_selectable) { Item item; item.icon = p_item; item.icon_region = Rect2i(); + item.icon_modulate = Color(1, 1, 1, 1); //item.text=p_item; item.selectable = p_selectable; item.selected = false; @@ -137,6 +140,21 @@ Rect2 ItemList::get_item_icon_region(int p_idx) const { return items[p_idx].icon_region; } +void ItemList::set_item_icon_modulate(int p_idx, const Color &p_modulate) { + + ERR_FAIL_INDEX(p_idx, items.size()); + + items[p_idx].icon_modulate = p_modulate; + update(); +} + +Color ItemList::get_item_icon_modulate(int p_idx) const { + + ERR_FAIL_INDEX_V(p_idx, items.size(), Color()); + + return items[p_idx].icon_modulate; +} + void ItemList::set_item_custom_bg_color(int p_idx, const Color &p_custom_bg_color) { ERR_FAIL_INDEX(p_idx, items.size()); @@ -294,35 +312,21 @@ int ItemList::get_current() const { return current; } -void ItemList::move_item(int p_item, int p_to_pos) { - - ERR_FAIL_INDEX(p_item, items.size()); - ERR_FAIL_INDEX(p_to_pos, items.size() + 1); - - Item it = items[p_item]; - items.remove(p_item); +void ItemList::move_item(int p_from_idx, int p_to_idx) { - if (p_to_pos > p_item) { - p_to_pos--; - } + ERR_FAIL_INDEX(p_from_idx, items.size()); + ERR_FAIL_INDEX(p_to_idx, items.size()); - if (p_to_pos >= items.size()) { - items.push_back(it); - } else { - items.insert(p_to_pos, it); + if (is_anything_selected() && get_selected_items()[0] == p_from_idx) { + current = p_to_idx; } - if (current < 0) { - //do none - } else if (p_item == current) { - current = p_to_pos; - } else if (p_to_pos > p_item && current > p_item && current < p_to_pos) { - current--; - } else if (p_to_pos < p_item && current < p_item && current > p_to_pos) { - current++; - } + Item item = items[p_from_idx]; + items.remove(p_from_idx); + items.insert(p_to_idx, item); update(); + shape_changed = true; } int ItemList::get_item_count() const { @@ -516,11 +520,11 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) { emit_signal("item_rmb_selected", i, get_local_mouse_position()); } else { - bool selected = !items[i].selected; + bool selected = items[i].selected; select(i, select_mode == SELECT_SINGLE || !mb->get_command()); - if (selected) { + if (!selected || allow_reselect) { if (select_mode == SELECT_SINGLE) { emit_signal("item_selected", i); } else @@ -674,7 +678,7 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) { search_string = ""; //any mousepress cancels - if (current % current_columns != (current_columns - 1)) { + if (current % current_columns != (current_columns - 1) && current + 1 < items.size()) { set_current(current + 1); ensure_current_is_visible(); if (select_mode == SELECT_SINGLE) { @@ -960,12 +964,36 @@ void ItemList::_notification(int p_what) { Vector2 base_ofs = bg->get_offset(); base_ofs.y -= int(scroll_bar->get_value()); - Rect2 clip(Point2(), size - bg->get_minimum_size() + Vector2(0, scroll_bar->get_value())); + const Rect2 clip(-base_ofs, size); // visible frame, don't need to draw outside of there + + int first_item_visible; + { + // do a binary search to find the first item whose rect reaches below clip.position.y + int lo = 0; + int hi = items.size(); + while (lo < hi) { + const int mid = (lo + hi) / 2; + const Rect2 &rcache = items[mid].rect_cache; + if (rcache.position.y + rcache.size.y < clip.position.y) { + lo = mid + 1; + } else { + hi = mid; + } + } + // we might have ended up with column 2, or 3, ..., so let's find the first column + while (lo > 0 && items[lo - 1].rect_cache.position.y == items[lo].rect_cache.position.y) { + lo -= 1; + } + first_item_visible = lo; + } - for (int i = 0; i < items.size(); i++) { + for (int i = first_item_visible; i < items.size(); i++) { Rect2 rcache = items[i].rect_cache; + if (rcache.position.y > clip.position.y + clip.size.y) + break; // done + if (!clip.intersects(rcache)) continue; @@ -1034,7 +1062,7 @@ void ItemList::_notification(int p_what) { draw_rect.size = adj.size; } - Color modulate = Color(1, 1, 1, 1); + Color modulate = items[i].icon_modulate; if (items[i].disabled) modulate.a *= 0.5; @@ -1093,6 +1121,7 @@ void ItemList::_notification(int p_what) { text_ofs += base_ofs; text_ofs += items[i].rect_cache.position; + FontDrawer drawer(font, Color(1, 1, 1)); for (int j = 0; j < ss; j++) { if (j == line_limit_cache[line]) { @@ -1101,7 +1130,7 @@ void ItemList::_notification(int p_what) { if (line >= max_text_lines) break; } - ofs += font->draw_char(get_canvas_item(), text_ofs + Vector2(ofs + (max_len - line_size_cache[line]) / 2, line * (font_height + line_separation)).floor(), items[i].text[j], items[i].text[j + 1], modulate); + ofs += drawer.draw_char(get_canvas_item(), text_ofs + Vector2(ofs + (max_len - line_size_cache[line]) / 2, line * (font_height + line_separation)).floor(), items[i].text[j], items[i].text[j + 1], modulate); } //special multiline mode @@ -1137,8 +1166,28 @@ void ItemList::_notification(int p_what) { } } - for (int i = 0; i < separators.size(); i++) { - draw_line(Vector2(bg->get_margin(MARGIN_LEFT), base_ofs.y + separators[i]), Vector2(width, base_ofs.y + separators[i]), guide_color); + int first_visible_separator = 0; + { + // do a binary search to find the first separator that is below clip_position.y + int lo = 0; + int hi = separators.size(); + while (lo < hi) { + const int mid = (lo + hi) / 2; + if (separators[mid] < clip.position.y) { + lo = mid + 1; + } else { + hi = mid; + } + } + first_visible_separator = lo; + } + + for (int i = first_visible_separator; i < separators.size(); i++) { + if (separators[i] > clip.position.y + clip.size.y) + break; // done + + const int y = base_ofs.y + separators[i]; + draw_line(Vector2(bg->get_margin(MARGIN_LEFT), y), Vector2(width, y), guide_color); } } } @@ -1195,7 +1244,7 @@ bool ItemList::is_pos_at_end_of_items(const Point2 &p_pos) const { String ItemList::get_tooltip(const Point2 &p_pos) const { - int closest = get_item_at_position(p_pos); + int closest = get_item_at_position(p_pos, true); if (closest != -1) { if (!items[closest].tooltip_enabled) { @@ -1240,6 +1289,7 @@ int ItemList::find_metadata(const Variant &p_metadata) const { } void ItemList::set_allow_rmb_select(bool p_allow) { + allow_rmb_select = p_allow; } @@ -1248,6 +1298,16 @@ bool ItemList::get_allow_rmb_select() const { return allow_rmb_select; } +void ItemList::set_allow_reselect(bool p_allow) { + + allow_reselect = p_allow; +} + +bool ItemList::get_allow_reselect() const { + + return allow_reselect; +} + void ItemList::set_icon_scale(real_t p_scale) { icon_scale = p_scale; } @@ -1347,6 +1407,9 @@ void ItemList::_bind_methods() { ClassDB::bind_method(D_METHOD("set_item_icon_region", "idx", "rect"), &ItemList::set_item_icon_region); ClassDB::bind_method(D_METHOD("get_item_icon_region", "idx"), &ItemList::get_item_icon_region); + ClassDB::bind_method(D_METHOD("set_item_icon_modulate", "idx", "modulate"), &ItemList::set_item_icon_modulate); + ClassDB::bind_method(D_METHOD("get_item_icon_modulate", "idx"), &ItemList::get_item_icon_modulate); + ClassDB::bind_method(D_METHOD("set_item_selectable", "idx", "selectable"), &ItemList::set_item_selectable); ClassDB::bind_method(D_METHOD("is_item_selectable", "idx"), &ItemList::is_item_selectable); @@ -1367,9 +1430,13 @@ void ItemList::_bind_methods() { ClassDB::bind_method(D_METHOD("select", "idx", "single"), &ItemList::select, DEFVAL(true)); ClassDB::bind_method(D_METHOD("unselect", "idx"), &ItemList::unselect); + ClassDB::bind_method(D_METHOD("unselect_all"), &ItemList::unselect_all); + ClassDB::bind_method(D_METHOD("is_selected", "idx"), &ItemList::is_selected); ClassDB::bind_method(D_METHOD("get_selected_items"), &ItemList::get_selected_items); + ClassDB::bind_method(D_METHOD("move_item", "from_idx", "to_idx"), &ItemList::move_item); + ClassDB::bind_method(D_METHOD("get_item_count"), &ItemList::get_item_count); ClassDB::bind_method(D_METHOD("remove_item", "idx"), &ItemList::remove_item); @@ -1403,9 +1470,14 @@ void ItemList::_bind_methods() { ClassDB::bind_method(D_METHOD("set_allow_rmb_select", "allow"), &ItemList::set_allow_rmb_select); ClassDB::bind_method(D_METHOD("get_allow_rmb_select"), &ItemList::get_allow_rmb_select); + ClassDB::bind_method(D_METHOD("set_allow_reselect", "allow"), &ItemList::set_allow_reselect); + ClassDB::bind_method(D_METHOD("get_allow_reselect"), &ItemList::get_allow_reselect); + ClassDB::bind_method(D_METHOD("set_auto_height", "enable"), &ItemList::set_auto_height); ClassDB::bind_method(D_METHOD("has_auto_height"), &ItemList::has_auto_height); + ClassDB::bind_method(D_METHOD("is_anything_selected"), &ItemList::is_anything_selected); + ClassDB::bind_method(D_METHOD("get_item_at_position", "position", "exact"), &ItemList::get_item_at_position, DEFVAL(false)); ClassDB::bind_method(D_METHOD("ensure_current_is_visible"), &ItemList::ensure_current_is_visible); @@ -1418,9 +1490,10 @@ void ItemList::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_items"), &ItemList::_set_items); ClassDB::bind_method(D_METHOD("_get_items"), &ItemList::_get_items); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_items", "_get_items"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items"); ADD_PROPERTY(PropertyInfo(Variant::INT, "select_mode", PROPERTY_HINT_ENUM, "Single,Multi"), "set_select_mode", "get_select_mode"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_reselect"), "set_allow_reselect", "get_allow_reselect"); ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "allow_rmb_select"), "set_allow_rmb_select", "get_allow_rmb_select"); ADD_PROPERTYNO(PropertyInfo(Variant::INT, "max_text_lines"), "set_max_text_lines", "get_max_text_lines"); ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "auto_height"), "set_auto_height", "has_auto_height"); @@ -1431,6 +1504,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_PROPERTYNO(PropertyInfo(Variant::REAL, "icon_scale"), "set_icon_scale", "get_icon_scale"); + ADD_PROPERTYNO(PropertyInfo(Variant::VECTOR2, "fixed_icon_size"), "set_fixed_icon_size", "get_fixed_icon_size"); BIND_ENUM_CONSTANT(ICON_MODE_TOP); BIND_ENUM_CONSTANT(ICON_MODE_LEFT); @@ -1474,6 +1548,7 @@ ItemList::ItemList() { ensure_selected_visible = false; defer_select_single = -1; allow_rmb_select = false; + allow_reselect = false; do_autoscroll_to_bottom = false; icon_scale = 1.0f; diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h index e56d5e5224..58771c1777 100644 --- a/scene/gui/item_list.h +++ b/scene/gui/item_list.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef ITEMLIST_H #define ITEMLIST_H @@ -53,6 +54,7 @@ private: Ref<Texture> icon; Rect2i icon_region; + Color icon_modulate; Ref<Texture> tag_icon; String text; bool selectable; @@ -105,6 +107,8 @@ private: bool allow_rmb_select; + bool allow_reselect; + real_t icon_scale; bool do_autoscroll_to_bottom; @@ -132,6 +136,9 @@ public: void set_item_icon_region(int p_idx, const Rect2 &p_region); Rect2 get_item_icon_region(int p_idx) const; + void set_item_icon_modulate(int p_idx, const Color &p_modulate); + Color get_item_icon_modulate(int p_idx) const; + void set_item_selectable(int p_idx, bool p_selectable); bool is_item_selectable(int p_idx) const; @@ -166,7 +173,7 @@ public: void set_current(int p_current); int get_current() const; - void move_item(int p_item, int p_to_pos); + void move_item(int p_from_idx, int p_to_idx); int get_item_count() const; void remove_item(int p_idx); @@ -197,6 +204,9 @@ public: void set_allow_rmb_select(bool p_allow); bool get_allow_rmb_select() const; + void set_allow_reselect(bool p_allow); + bool get_allow_reselect() const; + void ensure_current_is_visible(); void sort_items_by_text(); diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index be065bc0fb..f1b0d36f32 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "label.h" #include "print_string.h" #include "project_settings.h" @@ -92,6 +93,7 @@ void Label::_notification(int p_what) { bool use_outline = get_constant("shadow_as_outline"); Point2 shadow_ofs(get_constant("shadow_offset_x"), get_constant("shadow_offset_y")); int line_spacing = get_constant("line_spacing"); + Color font_outline_modulate = get_color("font_outline_modulate"); style->draw(ci, Rect2(Point2(0, 0), get_size())); @@ -149,6 +151,7 @@ void Label::_notification(int p_what) { int line = 0; int line_to = lines_skipped + (lines_visible > 0 ? lines_visible : 1); + FontDrawer drawer(font, font_outline_modulate); while (wc) { /* handle lines not meant to be drawn quickly */ if (line >= line_to) @@ -243,11 +246,11 @@ void Label::_notification(int p_what) { n = String::char_uppercase(c); } - float move = font->draw_char(ci, Point2(x_ofs_shadow, y_ofs) + shadow_ofs, c, n, font_color_shadow); + float move = font->draw_char(ci, Point2(x_ofs_shadow, y_ofs) + shadow_ofs, c, n, font_color_shadow, false); if (use_outline) { - font->draw_char(ci, Point2(x_ofs_shadow, y_ofs) + Vector2(-shadow_ofs.x, shadow_ofs.y), c, n, font_color_shadow); - font->draw_char(ci, Point2(x_ofs_shadow, y_ofs) + Vector2(shadow_ofs.x, -shadow_ofs.y), c, n, font_color_shadow); - font->draw_char(ci, Point2(x_ofs_shadow, y_ofs) + Vector2(-shadow_ofs.x, -shadow_ofs.y), c, n, font_color_shadow); + font->draw_char(ci, Point2(x_ofs_shadow, y_ofs) + Vector2(-shadow_ofs.x, shadow_ofs.y), c, n, font_color_shadow, false); + font->draw_char(ci, Point2(x_ofs_shadow, y_ofs) + Vector2(shadow_ofs.x, -shadow_ofs.y), c, n, font_color_shadow, false); + font->draw_char(ci, Point2(x_ofs_shadow, y_ofs) + Vector2(-shadow_ofs.x, -shadow_ofs.y), c, n, font_color_shadow, false); } x_ofs_shadow += move; chars_total_shadow++; @@ -264,7 +267,7 @@ void Label::_notification(int p_what) { n = String::char_uppercase(c); } - x_ofs += font->draw_char(ci, Point2(x_ofs, y_ofs), c, n, font_color); + x_ofs += drawer.draw_char(ci, Point2(x_ofs, y_ofs), c, n, font_color); chars_total++; } } @@ -563,6 +566,7 @@ void Label::set_visible_characters(int p_amount) { if (get_total_character_count() > 0) { percent_visible = (float)p_amount / (float)total_char_cache; } + _change_notify("percent_visible"); update(); } @@ -583,6 +587,7 @@ void Label::set_percent_visible(float p_percent) { visible_chars = get_total_character_count() * p_percent; percent_visible = p_percent; } + _change_notify("visible_chars"); update(); } @@ -664,6 +669,7 @@ void Label::_bind_methods() { ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "autowrap"), "set_autowrap", "has_autowrap"); ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "is_clipping_text"); ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "uppercase"), "set_uppercase", "is_uppercase"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters", PROPERTY_HINT_RANGE, "-1,128000,1", PROPERTY_USAGE_EDITOR), "set_visible_characters", "get_visible_characters"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "percent_visible", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_percent_visible", "get_percent_visible"); 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"); diff --git a/scene/gui/label.h b/scene/gui/label.h index 160eb66e53..d5e0b60773 100644 --- a/scene/gui/label.h +++ b/scene/gui/label.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef LABEL_H #define LABEL_H diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index dff7722a9e..e57af0a4c0 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -27,8 +27,10 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "line_edit.h" #include "label.h" +#include "message_queue.h" #include "os/keyboard.h" #include "os/os.h" #include "print_string.h" @@ -213,6 +215,12 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { case (KEY_A): { //Select All select(); } break; + case (KEY_LEFT): { // Go to start of text - like HOME key + set_cursor_position(0); + } break; + case (KEY_RIGHT): { // Go to end of text - like END key + set_cursor_position(text.length()); + } break; default: { handled = false; } } @@ -371,12 +379,14 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { case KEY_UP: { shift_selection_check_pre(k->get_shift()); + if (get_cursor_position() == 0) handled = false; set_cursor_position(0); shift_selection_check_post(k->get_shift()); } break; case KEY_DOWN: { shift_selection_check_pre(k->get_shift()); + if (get_cursor_position() == text.length()) handled = false; set_cursor_position(text.length()); shift_selection_check_post(k->get_shift()); } break; @@ -598,6 +608,7 @@ void LineEdit::_notification(int p_what) { } int x_ofs = 0; + int cached_text_width = text.empty() ? cached_placeholder_width : cached_width; switch (align) { @@ -611,15 +622,15 @@ void LineEdit::_notification(int p_what) { if (window_pos != 0) x_ofs = style->get_offset().x; else - x_ofs = int(size.width - (cached_width)) / 2; + x_ofs = MAX(style->get_margin(MARGIN_LEFT), int(size.width - (cached_text_width)) / 2); } break; case ALIGN_RIGHT: { - x_ofs = int(size.width - style->get_offset().x - (cached_width)); + x_ofs = MAX(style->get_margin(MARGIN_LEFT), int(size.width - style->get_margin(MARGIN_RIGHT) - (cached_text_width))); } break; } - int ofs_max = width - style->get_minimum_size().width; + int ofs_max = width - style->get_margin(MARGIN_RIGHT); int char_ofs = window_pos; int y_area = height - style->get_minimum_size().height; @@ -645,6 +656,7 @@ void LineEdit::_notification(int p_what) { } int caret_height = font->get_height() > y_area ? y_area : font->get_height(); + FontDrawer drawer(font, Color(1, 1, 1)); while (true) { //end of string, break! @@ -658,8 +670,8 @@ void LineEdit::_notification(int p_what) { if (ofs >= ime_text.length()) break; - CharType cchar = (pass && !text.empty()) ? '*' : ime_text[ofs]; - CharType next = (pass && !text.empty()) ? '*' : ime_text[ofs + 1]; + CharType cchar = (pass && !text.empty()) ? secret_character[0] : ime_text[ofs]; + CharType next = (pass && !text.empty()) ? secret_character[0] : ime_text[ofs + 1]; int im_char_width = font->get_char_size(cchar, next).width; if ((x_ofs + im_char_width) > ofs_max) @@ -672,7 +684,7 @@ void LineEdit::_notification(int p_what) { VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs + caret_height), Size2(im_char_width, 1)), font_color); } - font->draw_char(ci, Point2(x_ofs, y_ofs + font_ascent), cchar, next, font_color); + drawer.draw_char(ci, Point2(x_ofs, y_ofs + font_ascent), cchar, next, font_color); x_ofs += im_char_width; ofs++; @@ -680,8 +692,8 @@ void LineEdit::_notification(int p_what) { } } - CharType cchar = (pass && !text.empty()) ? '*' : t[char_ofs]; - CharType next = (pass && !text.empty()) ? '*' : t[char_ofs + 1]; + CharType cchar = (pass && !text.empty()) ? secret_character[0] : t[char_ofs]; + CharType next = (pass && !text.empty()) ? secret_character[0] : t[char_ofs + 1]; int char_width = font->get_char_size(cchar, next).width; // end of widget, break! @@ -693,7 +705,7 @@ void LineEdit::_notification(int p_what) { if (selected) VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs), Size2(char_width, caret_height)), selection_color); - font->draw_char(ci, Point2(x_ofs, y_ofs + font_ascent), cchar, next, selected ? font_color_selected : font_color); + drawer.draw_char(ci, Point2(x_ofs, y_ofs + font_ascent), cchar, next, selected ? font_color_selected : font_color); if (char_ofs == cursor_pos && draw_caret) { if (ime_text.length() == 0) { @@ -712,8 +724,8 @@ void LineEdit::_notification(int p_what) { if (ofs >= ime_text.length()) break; - CharType cchar = (pass && !text.empty()) ? '*' : ime_text[ofs]; - CharType next = (pass && !text.empty()) ? '*' : ime_text[ofs + 1]; + CharType cchar = (pass && !text.empty()) ? secret_character[0] : ime_text[ofs]; + CharType next = (pass && !text.empty()) ? secret_character[0] : ime_text[ofs + 1]; int im_char_width = font->get_char_size(cchar, next).width; if ((x_ofs + im_char_width) > ofs_max) @@ -726,7 +738,7 @@ void LineEdit::_notification(int p_what) { VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs + caret_height), Size2(im_char_width, 1)), font_color); } - font->draw_char(ci, Point2(x_ofs, y_ofs + font_ascent), cchar, next, font_color); + drawer.draw_char(ci, Point2(x_ofs, y_ofs + font_ascent), cchar, next, font_color); x_ofs += im_char_width; ofs++; @@ -799,7 +811,12 @@ void LineEdit::paste_text() { if (selection.enabled) selection_delete(); append_at_cursor(paste_buffer); - _text_changed(); + if (!text_changed_dirty) { + if (is_inside_tree()) { + MessageQueue::get_singleton()->push_call(this, "_text_changed"); + } + text_changed_dirty = true; + } } } @@ -872,7 +889,7 @@ void LineEdit::set_cursor_at_pixel_pos(int p_x) { } break; case ALIGN_RIGHT: { - pixel_ofs = int(size.width - style->get_offset().x - (cached_width)); + pixel_ofs = int(size.width - style->get_margin(MARGIN_RIGHT) - (cached_width)); } break; } @@ -973,7 +990,12 @@ void LineEdit::delete_text(int p_from_column, int p_to_column) { window_pos = cursor_pos; } - _text_changed(); + if (!text_changed_dirty) { + if (is_inside_tree()) { + MessageQueue::get_singleton()->push_call(this, "_text_changed"); + } + text_changed_dirty = true; + } } void LineEdit::set_text(String p_text) { @@ -1000,6 +1022,15 @@ String LineEdit::get_text() const { void LineEdit::set_placeholder(String p_text) { placeholder = tr(p_text); + if ((max_length <= 0) || (placeholder.length() <= max_length)) { + Ref<Font> font = get_font("font"); + cached_placeholder_width = 0; + if (font != NULL) { + for (int i = 0; i < placeholder.length(); i++) { + cached_placeholder_width += font->get_char_size(placeholder[i]).width; + } + } + } update(); } @@ -1200,6 +1231,7 @@ void LineEdit::select_all() { selection.enabled = true; update(); } + void LineEdit::set_editable(bool p_editable) { editable = p_editable; @@ -1216,11 +1248,27 @@ void LineEdit::set_secret(bool p_secret) { pass = p_secret; update(); } + bool LineEdit::is_secret() const { return pass; } +void LineEdit::set_secret_character(const String &p_string) { + + // An empty string as the secret character would crash the engine + // It also wouldn't make sense to use multiple characters as the secret character + ERR_EXPLAIN("Secret character must be exactly one character long (" + itos(p_string.length()) + " characters given)"); + ERR_FAIL_COND(p_string.length() != 1); + + secret_character = p_string; + update(); +} + +String LineEdit::get_secret_character() const { + return secret_character; +} + void LineEdit::select(int p_from, int p_to) { if (p_from == 0 && p_to == 0) { @@ -1302,12 +1350,12 @@ PopupMenu *LineEdit::get_menu() const { return menu; } -#ifdef TOOLS_ENABLED void LineEdit::_editor_settings_changed() { +#ifdef TOOLS_ENABLED cursor_set_blink_enabled(EDITOR_DEF("text_editor/cursor/caret_blink", false)); cursor_set_blink_speed(EDITOR_DEF("text_editor/cursor/caret_blink_speed", 0.65)); -} #endif +} void LineEdit::set_expand_to_text_length(bool p_enabled) { @@ -1340,6 +1388,7 @@ void LineEdit::_text_changed() { void LineEdit::_emit_text_change() { emit_signal("text_changed", text); _change_notify("text"); + text_changed_dirty = false; } void LineEdit::_clear_redo() { @@ -1372,11 +1421,10 @@ void LineEdit::_create_undo_state() { void LineEdit::_bind_methods() { + ClassDB::bind_method(D_METHOD("_text_changed"), &LineEdit::_text_changed); ClassDB::bind_method(D_METHOD("_toggle_draw_caret"), &LineEdit::_toggle_draw_caret); -#ifdef TOOLS_ENABLED ClassDB::bind_method("_editor_settings_changed", &LineEdit::_editor_settings_changed); -#endif ClassDB::bind_method(D_METHOD("set_align", "align"), &LineEdit::set_align); ClassDB::bind_method(D_METHOD("get_align"), &LineEdit::get_align); @@ -1407,13 +1455,15 @@ void LineEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("is_editable"), &LineEdit::is_editable); ClassDB::bind_method(D_METHOD("set_secret", "enabled"), &LineEdit::set_secret); ClassDB::bind_method(D_METHOD("is_secret"), &LineEdit::is_secret); + ClassDB::bind_method(D_METHOD("set_secret_character", "character"), &LineEdit::set_secret_character); + ClassDB::bind_method(D_METHOD("get_secret_character"), &LineEdit::get_secret_character); ClassDB::bind_method(D_METHOD("menu_option", "option"), &LineEdit::menu_option); ClassDB::bind_method(D_METHOD("get_menu"), &LineEdit::get_menu); ClassDB::bind_method(D_METHOD("set_context_menu_enabled", "enable"), &LineEdit::set_context_menu_enabled); ClassDB::bind_method(D_METHOD("is_context_menu_enabled"), &LineEdit::is_context_menu_enabled); - ADD_SIGNAL(MethodInfo("text_changed", PropertyInfo(Variant::STRING, "text"))); - ADD_SIGNAL(MethodInfo("text_entered", PropertyInfo(Variant::STRING, "text"))); + ADD_SIGNAL(MethodInfo("text_changed", PropertyInfo(Variant::STRING, "new_text"))); + ADD_SIGNAL(MethodInfo("text_entered", PropertyInfo(Variant::STRING, "new_text"))); BIND_ENUM_CONSTANT(ALIGN_LEFT); BIND_ENUM_CONSTANT(ALIGN_CENTER); @@ -1434,15 +1484,17 @@ void LineEdit::_bind_methods() { ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "max_length"), "set_max_length", "get_max_length"); ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable"); ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "secret"), "set_secret", "is_secret"); + ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "secret_character"), "set_secret_character", "get_secret_character"); ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "expand_to_text_length"), "set_expand_to_text_length", "get_expand_to_text_length"); ADD_PROPERTY(PropertyInfo(Variant::INT, "focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_focus_mode", "get_focus_mode"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled"); ADD_GROUP("Placeholder", "placeholder_"); ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "placeholder_text"), "set_placeholder", "get_placeholder"); ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "placeholder_alpha", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_placeholder_alpha", "get_placeholder_alpha"); ADD_GROUP("Caret", "caret_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_blink"), "cursor_set_blink_enabled", "cursor_get_blink_enabled"); - ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.1"), "cursor_set_blink_speed", "cursor_get_blink_speed"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled"); + ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.01"), "cursor_set_blink_speed", "cursor_get_blink_speed"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "caret_position"), "set_cursor_position", "get_cursor_position"); } LineEdit::LineEdit() { @@ -1451,11 +1503,14 @@ LineEdit::LineEdit() { _create_undo_state(); align = ALIGN_LEFT; cached_width = 0; + cached_placeholder_width = 0; cursor_pos = 0; window_pos = 0; window_has_focus = true; max_length = 0; pass = false; + secret_character = "*"; + text_changed_dirty = false; placeholder_alpha = 0.6; deselect(); @@ -1475,15 +1530,15 @@ LineEdit::LineEdit() { context_menu_enabled = true; menu = memnew(PopupMenu); add_child(menu); - menu->add_item(TTR("Cut"), MENU_CUT, KEY_MASK_CMD | KEY_X); - menu->add_item(TTR("Copy"), MENU_COPY, KEY_MASK_CMD | KEY_C); - menu->add_item(TTR("Paste"), MENU_PASTE, KEY_MASK_CMD | KEY_V); + menu->add_item(RTR("Cut"), MENU_CUT, KEY_MASK_CMD | KEY_X); + menu->add_item(RTR("Copy"), MENU_COPY, KEY_MASK_CMD | KEY_C); + menu->add_item(RTR("Paste"), MENU_PASTE, KEY_MASK_CMD | KEY_V); menu->add_separator(); - menu->add_item(TTR("Select All"), MENU_SELECT_ALL, KEY_MASK_CMD | KEY_A); - menu->add_item(TTR("Clear"), MENU_CLEAR); + menu->add_item(RTR("Select All"), MENU_SELECT_ALL, KEY_MASK_CMD | KEY_A); + menu->add_item(RTR("Clear"), MENU_CLEAR); menu->add_separator(); - menu->add_item(TTR("Undo"), MENU_UNDO, KEY_MASK_CMD | KEY_Z); - menu->add_item(TTR("Redo"), MENU_REDO, KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Z); + menu->add_item(RTR("Undo"), MENU_UNDO, KEY_MASK_CMD | KEY_Z); + menu->add_item(RTR("Redo"), MENU_REDO, KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Z); menu->connect("id_pressed", this, "menu_option"); expand_to_text_length = false; } diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index f57fde2525..e9314ba8dd 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef LINE_EDIT_H #define LINE_EDIT_H @@ -66,10 +67,12 @@ private: bool editable; bool pass; + bool text_changed_dirty; String undo_text; String text; String placeholder; + String secret_character; float placeholder_alpha; String ime_text; Point2 ime_selection; @@ -82,6 +85,7 @@ private: int max_length; // 0 for no maximum int cached_width; + int cached_placeholder_width; struct Selection { @@ -120,7 +124,6 @@ private: void shift_selection_check_post(bool); void selection_fill_at_cursor(); - void selection_delete(); void set_window_pos(int p_pos); void set_cursor_at_pixel_pos(int p_x); @@ -131,9 +134,7 @@ private: void clear_internal(); void changed_internal(); -#ifdef TOOLS_ENABLED void _editor_settings_changed(); -#endif void _gui_input(Ref<InputEvent> p_event); void _notification(int p_what); @@ -156,6 +157,7 @@ public: void select(int p_from = 0, int p_to = -1); void select_all(); + void selection_delete(); void deselect(); void delete_char(); @@ -191,6 +193,9 @@ public: void set_secret(bool p_secret); bool is_secret() const; + void set_secret_character(const String &p_string); + String get_secret_character() const; + virtual Size2 get_minimum_size() const; void set_expand_to_text_length(bool p_enabled); diff --git a/scene/gui/link_button.cpp b/scene/gui/link_button.cpp index dca83dc31f..d862e8669c 100644 --- a/scene/gui/link_button.cpp +++ b/scene/gui/link_button.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "link_button.h" void LinkButton::set_text(const String &p_text) { diff --git a/scene/gui/link_button.h b/scene/gui/link_button.h index 610f481e4e..0821ad9c0d 100644 --- a/scene/gui/link_button.h +++ b/scene/gui/link_button.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef LINKBUTTON_H #define LINKBUTTON_H diff --git a/scene/gui/margin_container.cpp b/scene/gui/margin_container.cpp index 97cc16c22d..5e1d53fe1d 100644 --- a/scene/gui/margin_container.cpp +++ b/scene/gui/margin_container.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "margin_container.h" Size2 MarginContainer::get_minimum_size() const { diff --git a/scene/gui/margin_container.h b/scene/gui/margin_container.h index 443e0769bc..ea75109248 100644 --- a/scene/gui/margin_container.h +++ b/scene/gui/margin_container.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef MARGIN_CONTAINER_H #define MARGIN_CONTAINER_H diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp index caf3b90fa1..87cf4dc334 100644 --- a/scene/gui/menu_button.cpp +++ b/scene/gui/menu_button.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "menu_button.h" #include "os/keyboard.h" #include "scene/main/viewport.h" @@ -58,7 +59,6 @@ void MenuButton::pressed() { popup->set_size(Size2(size.width, 0)); popup->set_parent_rect(Rect2(Point2(gp - popup->get_global_position()), get_size())); popup->popup(); - popup->set_invalidate_click_until_motion(); } void MenuButton::_gui_input(Ref<InputEvent> p_event) { @@ -88,7 +88,7 @@ void MenuButton::_bind_methods() { ClassDB::bind_method(D_METHOD("_get_items"), &MenuButton::_get_items); ClassDB::bind_method(D_METHOD("set_disable_shortcuts", "disabled"), &MenuButton::set_disable_shortcuts); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_items", "_get_items"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items"); ADD_SIGNAL(MethodInfo("about_to_show")); } @@ -108,7 +108,6 @@ MenuButton::MenuButton() { add_child(popup); popup->set_as_toplevel(true); popup->set_pass_on_modal_close_click(false); - connect("button_up", popup, "call_deferred", make_binds("grab_click_focus")); set_process_unhandled_key_input(true); set_action_mode(ACTION_MODE_BUTTON_PRESS); } diff --git a/scene/gui/menu_button.h b/scene/gui/menu_button.h index bfe168adf7..2356444ecb 100644 --- a/scene/gui/menu_button.h +++ b/scene/gui/menu_button.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef MENU_BUTTON_H #define MENU_BUTTON_H diff --git a/scene/gui/nine_patch_rect.cpp b/scene/gui/nine_patch_rect.cpp index 0e753675f7..b8f6ffe6d2 100644 --- a/scene/gui/nine_patch_rect.cpp +++ b/scene/gui/nine_patch_rect.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "nine_patch_rect.h" #include "servers/visual_server.h" diff --git a/scene/gui/nine_patch_rect.h b/scene/gui/nine_patch_rect.h index dc6c5c8a28..b4b4602a7d 100644 --- a/scene/gui/nine_patch_rect.h +++ b/scene/gui/nine_patch_rect.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef NINE_PATCH_RECT_H #define NINE_PATCH_RECT_H diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index a447889971..c5e4149782 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "option_button.h" #include "print_string.h" @@ -74,6 +75,10 @@ void OptionButton::_notification(int p_what) { } } +void OptionButton::_focused(int p_which) { + emit_signal("item_focused", p_which); +} + void OptionButton::_selected(int p_which) { int selid = -1; @@ -107,13 +112,13 @@ void OptionButton::pressed() { void OptionButton::add_icon_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID) { - popup->add_icon_check_item(p_icon, p_label, p_ID); + popup->add_icon_radio_check_item(p_icon, p_label, p_ID); if (popup->get_item_count() == 1) select(0); } void OptionButton::add_item(const String &p_label, int p_ID) { - popup->add_check_item(p_label, p_ID); + popup->add_radio_check_item(p_label, p_ID); if (popup->get_item_count() == 1) select(0); } @@ -289,9 +294,10 @@ void OptionButton::get_translatable_strings(List<String> *p_strings) const { void OptionButton::_bind_methods() { ClassDB::bind_method(D_METHOD("_selected"), &OptionButton::_selected); + ClassDB::bind_method(D_METHOD("_focused"), &OptionButton::_focused); ClassDB::bind_method(D_METHOD("add_item", "label", "id"), &OptionButton::add_item, DEFVAL(-1)); - ClassDB::bind_method(D_METHOD("add_icon_item", "texture", "label", "id"), &OptionButton::add_icon_item); + ClassDB::bind_method(D_METHOD("add_icon_item", "texture", "label", "id"), &OptionButton::add_icon_item, DEFVAL(-1)); ClassDB::bind_method(D_METHOD("set_item_text", "idx", "text"), &OptionButton::set_item_text); ClassDB::bind_method(D_METHOD("set_item_icon", "idx", "texture"), &OptionButton::set_item_icon); ClassDB::bind_method(D_METHOD("set_item_disabled", "idx", "disabled"), &OptionButton::set_item_disabled); @@ -317,9 +323,11 @@ void OptionButton::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_items"), &OptionButton::_set_items); ClassDB::bind_method(D_METHOD("_get_items"), &OptionButton::_get_items); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items"); + // "selected" property must come after "items", otherwise GH-10213 occurs ADD_PROPERTY(PropertyInfo(Variant::INT, "selected"), "_select_int", "get_selected"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_items", "_get_items"); ADD_SIGNAL(MethodInfo("item_selected", PropertyInfo(Variant::INT, "ID"))); + ADD_SIGNAL(MethodInfo("item_focused", PropertyInfo(Variant::INT, "ID"))); } OptionButton::OptionButton() { @@ -334,6 +342,7 @@ OptionButton::OptionButton() { popup->set_as_toplevel(true); popup->set_pass_on_modal_close_click(false); popup->connect("id_pressed", this, "_selected"); + popup->connect("id_focused", this, "_focused"); } OptionButton::~OptionButton() { diff --git a/scene/gui/option_button.h b/scene/gui/option_button.h index c2854e9d1a..d5f866d806 100644 --- a/scene/gui/option_button.h +++ b/scene/gui/option_button.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef OPTION_BUTTON_H #define OPTION_BUTTON_H @@ -42,6 +43,7 @@ class OptionButton : public Button { PopupMenu *popup; int current; + 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); diff --git a/scene/gui/panel.cpp b/scene/gui/panel.cpp index 28a221b63e..4375e03a50 100644 --- a/scene/gui/panel.cpp +++ b/scene/gui/panel.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "panel.h" #include "print_string.h" diff --git a/scene/gui/panel.h b/scene/gui/panel.h index 8641be4423..db8b35372e 100644 --- a/scene/gui/panel.h +++ b/scene/gui/panel.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef PANEL_H #define PANEL_H diff --git a/scene/gui/panel_container.cpp b/scene/gui/panel_container.cpp index 9d73b82952..a778d62659 100644 --- a/scene/gui/panel_container.cpp +++ b/scene/gui/panel_container.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "panel_container.h" Size2 PanelContainer::get_minimum_size() const { diff --git a/scene/gui/panel_container.h b/scene/gui/panel_container.h index 0a412cb091..267e2b921f 100644 --- a/scene/gui/panel_container.h +++ b/scene/gui/panel_container.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef PANEL_CONTAINER_H #define PANEL_CONTAINER_H diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp index 73bf867c37..d18a3a3f2f 100644 --- a/scene/gui/popup.cpp +++ b/scene/gui/popup.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "popup.h" #include "engine.h" diff --git a/scene/gui/popup.h b/scene/gui/popup.h index efe2ee88c2..550e803578 100644 --- a/scene/gui/popup.h +++ b/scene/gui/popup.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef POPUP_H #define POPUP_H diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index 7999f50013..fd2466407e 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "popup_menu.h" #include "os/input.h" #include "os/keyboard.h" @@ -54,7 +55,7 @@ Size2 PopupMenu::get_minimum_size() const { float max_w = 0; int font_h = font->get_height(); - int check_w = get_icon("checked")->get_width(); + int check_w = MAX(get_icon("checked")->get_width(), get_icon("radio_checked")->get_width()); int accel_max_w = 0; for (int i = 0; i < items.size(); i++) { @@ -73,7 +74,7 @@ Size2 PopupMenu::get_minimum_size() const { size.width += items[i].h_ofs; - if (items[i].checkable) { + if (items[i].checkable_type) { size.width += check_w + hseparation; } @@ -122,8 +123,7 @@ int PopupMenu::_get_mouse_over(const Point2 &p_over) const { for (int i = 0; i < items.size(); i++) { - if (i > 0) - ofs.y += vseparation; + ofs.y += vseparation; float h; if (!items[i].icon.is_null()) { @@ -189,88 +189,91 @@ void PopupMenu::_submenu_timeout() { submenu_over = -1; } -void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) { - - Ref<InputEventKey> k = p_event; - - if (k.is_valid()) { - - if (!k->is_pressed()) - return; +void PopupMenu::_scroll(float p_factor, const Point2 &p_over) { - switch (k->get_scancode()) { + const float global_y = get_global_position().y; - case KEY_DOWN: { - - int search_from = mouse_over + 1; - if (search_from >= items.size()) - search_from = 0; - - for (int i = search_from; i < items.size(); i++) { + int vseparation = get_constant("vseparation"); + Ref<Font> font = get_font("font"); - if (i < 0 || i >= items.size()) - continue; + float dy = (vseparation + font->get_height()) * 3 * p_factor; + if (dy > 0 && global_y < 0) + dy = MIN(dy, -global_y - 1); + else if (dy < 0 && global_y + get_size().y > get_viewport_rect().size.y) + dy = -MIN(-dy, global_y + get_size().y - get_viewport_rect().size.y - 1); + set_position(get_position() + Vector2(0, dy)); - if (!items[i].separator && !items[i].disabled) { + Ref<InputEventMouseMotion> ie; + ie.instance(); + ie->set_position(p_over - Vector2(0, dy)); + _gui_input(ie); +} - mouse_over = i; - update(); - break; - } - } - } break; - case KEY_UP: { +void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) { - int search_from = mouse_over - 1; - if (search_from < 0) - search_from = items.size() - 1; + if (p_event->is_action("ui_down") && p_event->is_pressed()) { - for (int i = search_from; i >= 0; i--) { + int search_from = mouse_over + 1; + if (search_from >= items.size()) + search_from = 0; - if (i < 0 || i >= items.size()) - continue; + for (int i = search_from; i < items.size(); i++) { - if (!items[i].separator && !items[i].disabled) { + if (i < 0 || i >= items.size()) + continue; - mouse_over = i; - update(); - break; - } - } - } break; + if (!items[i].separator && !items[i].disabled) { - case KEY_LEFT: { + mouse_over = i; + emit_signal("id_focused", i); + update(); + accept_event(); + break; + } + } + } else if (p_event->is_action("ui_up") && p_event->is_pressed()) { - Node *n = get_parent(); - if (!n) - break; + int search_from = mouse_over - 1; + if (search_from < 0) + search_from = items.size() - 1; - PopupMenu *pm = Object::cast_to<PopupMenu>(n); - if (!pm) - break; + for (int i = search_from; i >= 0; i--) { - hide(); - } break; + if (i < 0 || i >= items.size()) + continue; - case KEY_RIGHT: { + if (!items[i].separator && !items[i].disabled) { - if (mouse_over >= 0 && mouse_over < items.size() && !items[mouse_over].separator && items[mouse_over].submenu != "" && submenu_over != mouse_over) - _activate_submenu(mouse_over); - } break; + mouse_over = i; + emit_signal("id_focused", i); + update(); + accept_event(); + break; + } + } + } else if (p_event->is_action("ui_left") && p_event->is_pressed()) { - case KEY_ENTER: - case KEY_KP_ENTER: { + Node *n = get_parent(); + if (n && Object::cast_to<PopupMenu>(n)) { + hide(); + accept_event(); + } + } else if (p_event->is_action("ui_right") && p_event->is_pressed()) { - if (mouse_over >= 0 && mouse_over < items.size() && !items[mouse_over].separator) { + if (mouse_over >= 0 && mouse_over < items.size() && !items[mouse_over].separator && items[mouse_over].submenu != "" && submenu_over != mouse_over) { + _activate_submenu(mouse_over); + accept_event(); + } + } else if (p_event->is_action("ui_accept") && p_event->is_pressed()) { - if (items[mouse_over].submenu != "" && submenu_over != mouse_over) { - _activate_submenu(mouse_over); - break; - } + if (mouse_over >= 0 && mouse_over < items.size() && !items[mouse_over].separator) { - activate_item(mouse_over); - } - } break; + if (items[mouse_over].submenu != "" && submenu_over != mouse_over) { + _activate_submenu(mouse_over); + } else { + activate_item(mouse_over); + } + accept_event(); } } @@ -281,70 +284,52 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) { if (b->is_pressed()) return; - switch (b->get_button_index()) { + int button_idx = b->get_button_index(); + switch (button_idx) { case BUTTON_WHEEL_DOWN: { if (get_global_position().y + get_size().y > get_viewport_rect().size.y) { - - int vseparation = get_constant("vseparation"); - Ref<Font> font = get_font("font"); - - Point2 pos = get_position(); - int s = (vseparation + font->get_height()) * 3; - pos.y -= (s * b->get_factor()); - set_position(pos); - - //update hover - Ref<InputEventMouseMotion> ie; - ie.instance(); - ie->set_position(b->get_position() + Vector2(0, s)); - _gui_input(ie); + _scroll(-b->get_factor(), b->get_position()); } } break; case BUTTON_WHEEL_UP: { if (get_global_position().y < 0) { - - int vseparation = get_constant("vseparation"); - Ref<Font> font = get_font("font"); - - Point2 pos = get_position(); - int s = (vseparation + font->get_height()) * 3; - pos.y += (s * b->get_factor()); - set_position(pos); - - //update hover - Ref<InputEventMouseMotion> ie; - ie.instance(); - ie->set_position(b->get_position() - Vector2(0, s)); - _gui_input(ie); + _scroll(b->get_factor(), b->get_position()); } } break; - case BUTTON_LEFT: { + default: { + // Allow activating item by releasing the LMB or any that was down when the popup appeared + if (button_idx == BUTTON_LEFT || (initial_button_mask & (1 << (button_idx - 1)))) { - int over = _get_mouse_over(b->get_position()); + bool was_during_grabbed_click = during_grabbed_click; + during_grabbed_click = false; - if (invalidated_click) { - invalidated_click = false; - break; - } - if (over < 0) { - hide(); - break; //non-activable - } + int over = _get_mouse_over(b->get_position()); + + if (invalidated_click) { + invalidated_click = false; + break; + } + if (over < 0) { + if (!was_during_grabbed_click) { + hide(); + } + break; //non-activable + } - if (items[over].separator || items[over].disabled) - break; + if (items[over].separator || items[over].disabled) + break; - if (items[over].submenu != "") { + if (items[over].submenu != "") { - _activate_submenu(over); - return; + _activate_submenu(over); + return; + } + activate_item(over); } - activate_item(over); - - } break; + } } //update(); @@ -387,6 +372,13 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) { update(); } } + + Ref<InputEventPanGesture> pan_gesture = p_event; + if (pan_gesture.is_valid()) { + if (get_global_position().y + get_size().y > get_viewport_rect().size.y || get_global_position().y < 0) { + _scroll(-pan_gesture->get_delta().y, pan_gesture->get_position()); + } + } } bool PopupMenu::has_point(const Point2 &p_point) const { @@ -424,8 +416,9 @@ void PopupMenu::_notification(int p_what) { Ref<StyleBox> style = get_stylebox("panel"); Ref<StyleBox> hover = get_stylebox("hover"); Ref<Font> font = get_font("font"); - Ref<Texture> check = get_icon("checked"); - Ref<Texture> uncheck = get_icon("unchecked"); + // In Item::checkable_type enum order (less the non-checkable member) + Ref<Texture> check[] = { get_icon("checked"), get_icon("radio_checked") }; + Ref<Texture> uncheck[] = { get_icon("unchecked"), get_icon("radio_unchecked") }; Ref<Texture> submenu = get_icon("submenu"); Ref<StyleBox> separator = get_stylebox("separator"); @@ -459,7 +452,7 @@ void PopupMenu::_notification(int p_what) { if (i == mouse_over) { - hover->draw(ci, Rect2(item_ofs + Point2(-hseparation, -vseparation), Size2(get_size().width - style->get_minimum_size().width + hseparation * 2, h + vseparation * 2))); + hover->draw(ci, Rect2(item_ofs + Point2(-hseparation, -vseparation / 2), Size2(get_size().width - style->get_minimum_size().width + hseparation * 2, h + vseparation))); } if (items[i].separator) { @@ -468,14 +461,10 @@ void PopupMenu::_notification(int p_what) { separator->draw(ci, Rect2(item_ofs + Point2(0, Math::floor((h - sep_h) / 2.0)), Size2(get_size().width - style->get_minimum_size().width, sep_h))); } - if (items[i].checkable) { - - if (items[i].checked) - check->draw(ci, item_ofs + Point2(0, Math::floor((h - check->get_height()) / 2.0))); - else - uncheck->draw(ci, item_ofs + Point2(0, Math::floor((h - check->get_height()) / 2.0))); - - item_ofs.x += check->get_width() + hseparation; + if (items[i].checkable_type) { + Texture *icon = (items[i].checked ? check[items[i].checkable_type - 1] : uncheck[items[i].checkable_type - 1]).ptr(); + icon->draw(ci, item_ofs + Point2(0, Math::floor((h - icon->get_height()) / 2.0))); + item_ofs.x += icon->get_width() + hseparation; } if (!items[i].icon.is_null()) { @@ -519,6 +508,11 @@ void PopupMenu::_notification(int p_what) { update(); } } break; + case NOTIFICATION_POST_POPUP: { + + initial_button_mask = Input::get_singleton()->get_mouse_button_mask(); + during_grabbed_click = (bool)initial_button_mask; + } break; case NOTIFICATION_POPUP_HIDE: { if (mouse_over >= 0) { @@ -570,10 +564,11 @@ void PopupMenu::add_icon_check_item(const Ref<Texture> &p_icon, const String &p_ item.xl_text = tr(p_label); item.accel = p_accel; item.ID = p_ID; - item.checkable = true; + item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; items.push_back(item); update(); } + void PopupMenu::add_check_item(const String &p_label, int p_ID, uint32_t p_accel) { Item item; @@ -581,11 +576,25 @@ void PopupMenu::add_check_item(const String &p_label, int p_ID, uint32_t p_accel item.xl_text = tr(p_label); item.accel = p_accel; item.ID = p_ID; - item.checkable = true; + item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; items.push_back(item); update(); } +void PopupMenu::add_radio_check_item(const String &p_label, int p_ID, uint32_t p_accel) { + + add_check_item(p_label, p_ID, p_accel); + items[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; + update(); +} + +void PopupMenu::add_icon_radio_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID, uint32_t p_accel) { + + add_icon_check_item(p_icon, p_label, p_ID, p_accel); + items[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; + update(); +} + void PopupMenu::add_icon_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) { ERR_FAIL_COND(p_shortcut.is_null()); @@ -614,6 +623,7 @@ void PopupMenu::add_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bool p_g items.push_back(item); update(); } + void PopupMenu::add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) { ERR_FAIL_COND(p_shortcut.is_null()); @@ -623,7 +633,7 @@ void PopupMenu::add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<Sh Item item; item.ID = p_ID; item.shortcut = p_shortcut; - item.checkable = true; + item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; item.icon = p_icon; item.shortcut_is_global = p_global; items.push_back(item); @@ -640,11 +650,18 @@ void PopupMenu::add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bo item.ID = p_ID; item.shortcut = p_shortcut; item.shortcut_is_global = p_global; - item.checkable = true; + item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; items.push_back(item); update(); } +void PopupMenu::add_radio_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) { + + add_check_shortcut(p_shortcut, p_ID, p_global); + items[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; + update(); +} + void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int p_default_state, int p_ID, uint32_t p_accel) { Item item; @@ -652,7 +669,6 @@ void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int item.xl_text = tr(p_label); item.accel = p_accel; item.ID = p_ID; - item.checkable = false; item.max_states = p_max_states; item.state = p_default_state; items.push_back(item); @@ -827,7 +843,14 @@ bool PopupMenu::is_item_separator(int p_idx) const { void PopupMenu::set_item_as_checkable(int p_idx, bool p_checkable) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].checkable = p_checkable; + items[p_idx].checkable_type = p_checkable ? Item::CHECKABLE_TYPE_CHECK_BOX : Item::CHECKABLE_TYPE_NONE; + update(); +} + +void PopupMenu::set_item_as_radio_checkable(int p_idx, bool p_radio_checkable) { + + ERR_FAIL_INDEX(p_idx, items.size()); + items[p_idx].checkable_type = p_radio_checkable ? Item::CHECKABLE_TYPE_RADIO_BUTTON : Item::CHECKABLE_TYPE_NONE; update(); } @@ -883,7 +906,12 @@ void PopupMenu::toggle_item_multistate(int p_idx) { bool PopupMenu::is_item_checkable(int p_idx) const { ERR_FAIL_INDEX_V(p_idx, items.size(), false); - return items[p_idx].checkable; + return items[p_idx].checkable_type; +} + +bool PopupMenu::is_item_radio_checkable(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx, items.size(), false); + return items[p_idx].checkable_type == Item::CHECKABLE_TYPE_RADIO_BUTTON; } int PopupMenu::get_item_count() const { @@ -957,7 +985,7 @@ void PopupMenu::activate_item(int p_item) { // We close all parents that are chained together, // with hide_on_item_selection enabled - if (items[p_item].checkable) { + if (items[p_item].checkable_type) { if (!hide_on_checkable_item_selection || !pop->is_hide_on_checkable_item_selection()) break; } else if (0 < items[p_item].max_states) { @@ -974,7 +1002,7 @@ void PopupMenu::activate_item(int p_item) { // Hides popup by default; unless otherwise specified // by using set_hide_on_item_selection and set_hide_on_checkable_item_selection - if (items[p_item].checkable) { + if (items[p_item].checkable_type) { if (!hide_on_checkable_item_selection) return; } else if (0 < items[p_item].max_states) { @@ -1026,7 +1054,9 @@ Array PopupMenu::_get_items() const { items.push_back(get_item_text(i)); items.push_back(get_item_icon(i)); - items.push_back(is_item_checkable(i)); + // For compatibility, use false/true for no/checkbox and integers for other values + int ct = this->items[i].checkable_type; + items.push_back(Variant(ct <= Item::CHECKABLE_TYPE_CHECK_BOX ? is_item_checkable(i) : ct)); items.push_back(is_item_checked(i)); items.push_back(is_item_disabled(i)); @@ -1069,7 +1099,9 @@ void PopupMenu::_set_items(const Array &p_items) { String text = p_items[i + 0]; Ref<Texture> icon = p_items[i + 1]; + // For compatibility, use false/true for no/checkbox and integers for other values bool checkable = p_items[i + 2]; + bool radio_checkable = (int)p_items[i + 2] == Item::CHECKABLE_TYPE_RADIO_BUTTON; bool checked = p_items[i + 3]; bool disabled = p_items[i + 4]; @@ -1082,7 +1114,13 @@ void PopupMenu::_set_items(const Array &p_items) { int idx = get_item_count(); add_item(text, id); set_item_icon(idx, icon); - set_item_as_checkable(idx, checkable); + if (checkable) { + if (radio_checkable) { + set_item_as_radio_checkable(idx, true); + } else { + set_item_as_checkable(idx, true); + } + } set_item_checked(idx, checked); set_item_disabled(idx, disabled); set_item_id(idx, id); @@ -1163,12 +1201,14 @@ void PopupMenu::_bind_methods() { ClassDB::bind_method(D_METHOD("add_item", "label", "id", "accel"), &PopupMenu::add_item, DEFVAL(-1), DEFVAL(0)); ClassDB::bind_method(D_METHOD("add_icon_check_item", "texture", "label", "id", "accel"), &PopupMenu::add_icon_check_item, DEFVAL(-1), DEFVAL(0)); ClassDB::bind_method(D_METHOD("add_check_item", "label", "id", "accel"), &PopupMenu::add_check_item, DEFVAL(-1), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("add_radio_check_item", "label", "id", "accel"), &PopupMenu::add_radio_check_item, DEFVAL(-1), DEFVAL(0)); ClassDB::bind_method(D_METHOD("add_submenu_item", "label", "submenu", "id"), &PopupMenu::add_submenu_item, DEFVAL(-1)); ClassDB::bind_method(D_METHOD("add_icon_shortcut", "texture", "shortcut", "id", "global"), &PopupMenu::add_icon_shortcut, DEFVAL(-1), DEFVAL(false)); ClassDB::bind_method(D_METHOD("add_shortcut", "shortcut", "id", "global"), &PopupMenu::add_shortcut, DEFVAL(-1), DEFVAL(false)); ClassDB::bind_method(D_METHOD("add_icon_check_shortcut", "texture", "shortcut", "id", "global"), &PopupMenu::add_icon_check_shortcut, DEFVAL(-1), DEFVAL(false)); ClassDB::bind_method(D_METHOD("add_check_shortcut", "shortcut", "id", "global"), &PopupMenu::add_check_shortcut, DEFVAL(-1), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("add_radio_check_shortcut", "shortcut", "id", "global"), &PopupMenu::add_radio_check_shortcut, DEFVAL(-1), DEFVAL(false)); ClassDB::bind_method(D_METHOD("set_item_text", "idx", "text"), &PopupMenu::set_item_text); ClassDB::bind_method(D_METHOD("set_item_icon", "idx", "icon"), &PopupMenu::set_item_icon); @@ -1180,6 +1220,7 @@ void PopupMenu::_bind_methods() { ClassDB::bind_method(D_METHOD("set_item_submenu", "idx", "submenu"), &PopupMenu::set_item_submenu); ClassDB::bind_method(D_METHOD("set_item_as_separator", "idx", "enable"), &PopupMenu::set_item_as_separator); ClassDB::bind_method(D_METHOD("set_item_as_checkable", "idx", "enable"), &PopupMenu::set_item_as_checkable); + ClassDB::bind_method(D_METHOD("set_item_as_radio_checkable", "idx", "enable"), &PopupMenu::set_item_as_radio_checkable); ClassDB::bind_method(D_METHOD("set_item_tooltip", "idx", "tooltip"), &PopupMenu::set_item_tooltip); ClassDB::bind_method(D_METHOD("set_item_shortcut", "idx", "shortcut", "global"), &PopupMenu::set_item_shortcut, DEFVAL(false)); ClassDB::bind_method(D_METHOD("set_item_multistate", "idx", "state"), &PopupMenu::set_item_multistate); @@ -1198,6 +1239,7 @@ void PopupMenu::_bind_methods() { ClassDB::bind_method(D_METHOD("get_item_submenu", "idx"), &PopupMenu::get_item_submenu); ClassDB::bind_method(D_METHOD("is_item_separator", "idx"), &PopupMenu::is_item_separator); ClassDB::bind_method(D_METHOD("is_item_checkable", "idx"), &PopupMenu::is_item_checkable); + ClassDB::bind_method(D_METHOD("is_item_radio_checkable", "idx"), &PopupMenu::is_item_radio_checkable); ClassDB::bind_method(D_METHOD("get_item_tooltip", "idx"), &PopupMenu::get_item_tooltip); ClassDB::bind_method(D_METHOD("get_item_shortcut", "idx"), &PopupMenu::get_item_shortcut); @@ -1222,23 +1264,30 @@ void PopupMenu::_bind_methods() { ClassDB::bind_method(D_METHOD("_submenu_timeout"), &PopupMenu::_submenu_timeout); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_items", "_get_items"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items"); ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "hide_on_item_selection"), "set_hide_on_item_selection", "is_hide_on_item_selection"); ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "hide_on_checkable_item_selection"), "set_hide_on_checkable_item_selection", "is_hide_on_checkable_item_selection"); + ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "hide_on_state_item_selection"), "set_hide_on_state_item_selection", "is_hide_on_state_item_selection"); 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"))); } -void PopupMenu::set_invalidate_click_until_motion() { +void PopupMenu::popup(const Rect2 &p_bounds) { + + grab_click_focus(); moved = Vector2(); invalidated_click = true; + Popup::popup(p_bounds); } PopupMenu::PopupMenu() { mouse_over = -1; submenu_over = -1; + initial_button_mask = 0; + during_grabbed_click = false; set_focus_mode(FOCUS_ALL); set_as_toplevel(true); diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h index f51ba81c89..fde91bd845 100644 --- a/scene/gui/popup_menu.h +++ b/scene/gui/popup_menu.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef POPUP_MENU_H #define POPUP_MENU_H @@ -45,7 +46,11 @@ class PopupMenu : public Popup { String text; String xl_text; bool checked; - bool checkable; + enum { + CHECKABLE_TYPE_NONE, + CHECKABLE_TYPE_CHECK_BOX, + CHECKABLE_TYPE_RADIO_BUTTON, + } checkable_type; int max_states; int state; bool separator; @@ -62,7 +67,7 @@ class PopupMenu : public Popup { Item() { checked = false; - checkable = false; + checkable_type = CHECKABLE_TYPE_NONE; separator = false; max_states = 0; state = 0; @@ -77,12 +82,15 @@ class PopupMenu : public Popup { Timer *submenu_timer; List<Rect2> autohide_areas; Vector<Item> items; + int initial_button_mask; + bool during_grabbed_click; int mouse_over; int submenu_over; Rect2 parent_rect; String _get_accel_text(int p_item) const; int _get_mouse_over(const Point2 &p_over) const; virtual Size2 get_minimum_size() const; + void _scroll(float p_factor, const Point2 &p_over); void _gui_input(const Ref<InputEvent> &p_event); void _activate_submenu(int over); void _submenu_timeout(); @@ -113,12 +121,15 @@ public: void add_item(const String &p_label, int p_ID = -1, uint32_t p_accel = 0); void add_icon_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID = -1, uint32_t p_accel = 0); void add_check_item(const String &p_label, int p_ID = -1, uint32_t p_accel = 0); + void add_radio_check_item(const String &p_label, int p_ID = -1, uint32_t p_accel = 0); + void add_icon_radio_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID = -1, uint32_t p_accel = 0); void add_submenu_item(const String &p_label, const String &p_submenu, int p_ID = -1); void add_icon_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false); void add_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false); void add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false); void add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false); + void add_radio_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false); void add_multistate_item(const String &p_label, int p_max_states, int p_default_state, int p_ID = -1, uint32_t p_accel = 0); @@ -132,6 +143,7 @@ public: void set_item_submenu(int p_idx, const String &p_submenu); void set_item_as_separator(int p_idx, bool p_separator); void set_item_as_checkable(int p_idx, bool p_checkable); + 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_h_offset(int p_idx, int p_offset); @@ -152,6 +164,7 @@ public: String get_item_submenu(int p_idx) const; bool is_item_separator(int p_idx) const; bool is_item_checkable(int p_idx) const; + bool is_item_radio_checkable(int p_idx) const; String get_item_tooltip(int p_idx) const; Ref<ShortCut> get_item_shortcut(int p_idx) const; int get_item_state(int p_idx) const; @@ -176,7 +189,6 @@ public: void add_autohide_area(const Rect2 &p_area); void clear_autohide_areas(); - void set_invalidate_click_until_motion(); void set_hide_on_item_selection(bool p_enabled); bool is_hide_on_item_selection() const; @@ -186,6 +198,8 @@ public: void set_hide_on_multistate_item_selection(bool p_enabled); bool is_hide_on_multistate_item_selection() const; + virtual void popup(const Rect2 &p_bounds = Rect2()); + PopupMenu(); ~PopupMenu(); }; diff --git a/scene/gui/progress_bar.cpp b/scene/gui/progress_bar.cpp index 505068f155..37e519e375 100644 --- a/scene/gui/progress_bar.cpp +++ b/scene/gui/progress_bar.cpp @@ -27,18 +27,22 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "progress_bar.h" Size2 ProgressBar::get_minimum_size() const { Ref<StyleBox> bg = get_stylebox("bg"); + Ref<StyleBox> fg = get_stylebox("fg"); Ref<Font> font = get_font("font"); - Size2 ms = bg->get_minimum_size() + bg->get_center_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) { - ms.height = MAX(ms.height, bg->get_minimum_size().height + font->get_height()); + minimum_size.height = MAX(minimum_size.height, bg->get_minimum_size().height + font->get_height()); } - return ms; + return minimum_size; } void ProgressBar::_notification(int p_what) { diff --git a/scene/gui/progress_bar.h b/scene/gui/progress_bar.h index f7b2a194ba..d091c983b1 100644 --- a/scene/gui/progress_bar.h +++ b/scene/gui/progress_bar.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef PROGRESS_BAR_H #define PROGRESS_BAR_H diff --git a/scene/gui/range.cpp b/scene/gui/range.cpp index b1a33906d2..cd6c6bb65c 100644 --- a/scene/gui/range.cpp +++ b/scene/gui/range.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "range.h" void Range::_value_changed_notify() { @@ -247,6 +248,7 @@ void Range::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::REAL, "step"), "set_step", "get_step"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "page"), "set_page", "get_page"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "value"), "set_value", "get_value"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "ratio", PROPERTY_HINT_RANGE, "0,1,0.01", 0), "set_as_ratio", "get_as_ratio"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "exp_edit"), "set_exp_ratio", "is_ratio_exp"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rounded"), "set_use_rounded_values", "is_using_rounded_values"); } diff --git a/scene/gui/range.h b/scene/gui/range.h index 521ce412cf..de9383afd8 100644 --- a/scene/gui/range.h +++ b/scene/gui/range.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef RANGE_H #define RANGE_H diff --git a/scene/gui/reference_rect.cpp b/scene/gui/reference_rect.cpp index 1b25965dab..5e25f43daf 100644 --- a/scene/gui/reference_rect.cpp +++ b/scene/gui/reference_rect.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "reference_rect.h" #include "engine.h" diff --git a/scene/gui/reference_rect.h b/scene/gui/reference_rect.h index 3a062f1b6b..473e348c85 100644 --- a/scene/gui/reference_rect.h +++ b/scene/gui/reference_rect.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef REFERENCE_RECT_H #define REFERENCE_RECT_H diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 4f2cfccd4a..8fc626208b 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "rich_text_label.h" #include "os/keyboard.h" #include "os/os.h" @@ -89,7 +90,7 @@ Rect2 RichTextLabel::_get_text_rect() { Ref<StyleBox> style = get_stylebox("normal"); return Rect2(style->get_offset(), get_size() - style->get_minimum_size()); } -int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref<Font> &p_base_font, const Color &p_base_color, const Point2i &p_click_pos, Item **r_click_item, int *r_click_char, bool *r_outside, int p_char_count) { +int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref<Font> &p_base_font, const Color &p_base_color, const Color &p_font_color_shadow, bool p_shadow_as_outline, const Point2 &shadow_ofs, const Point2i &p_click_pos, Item **r_click_item, int *r_click_char, bool *r_outside, int p_char_count) { RID ci; if (r_outside) @@ -120,8 +121,11 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & if (p_mode == PROCESS_CACHE) { l.offset_caches.clear(); l.height_caches.clear(); + l.ascent_caches.clear(); + l.descent_caches.clear(); l.char_count = 0; l.minimum_width = 0; + l.maximum_width = 0; } int wofs = margin; @@ -139,6 +143,8 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & //line height should be the font height for the first time, this ensures that an empty line will never have zero height and successive newlines are displayed int line_height = cfont->get_height(); + int line_ascent = cfont->get_ascent(); + int line_descent = cfont->get_descent(); int nonblank_line_count = 0; //number of nonblank lines as counted during PROCESS_DRAW @@ -168,16 +174,22 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & case ALIGN_FILL: l.offset_caches.push_back((p_width - margin) - used /*+spaces_size*/); break; \ } \ l.height_caches.push_back(line_height); \ + l.ascent_caches.push_back(line_ascent); \ + l.descent_caches.push_back(line_descent); \ l.space_caches.push_back(spaces); \ } \ y += line_height + get_constant(SceneStringNames::get_singleton()->line_separation); \ line_height = 0; \ + line_ascent = 0; \ + line_descent = 0; \ spaces = 0; \ spaces_size = 0; \ wofs = begin; \ align_ofs = 0; \ if (p_mode != PROCESS_CACHE) { \ lh = line < l.height_caches.size() ? l.height_caches[line] : 1; \ + line_ascent = line < l.ascent_caches.size() ? l.ascent_caches[line] : 1; \ + line_descent = line < l.descent_caches.size() ? l.descent_caches[line] : 1; \ } \ if (p_mode == PROCESS_POINTER && r_click_item && p_click_pos.y >= p_ofs.y + y && p_click_pos.y <= p_ofs.y + y + lh && p_click_pos.x < p_ofs.x + wofs) { \ if (r_outside) *r_outside = true; \ @@ -189,7 +201,8 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & #define ENSURE_WIDTH(m_width) \ if (p_mode == PROCESS_CACHE) { \ - l.minimum_width = MAX(l.minimum_width, wofs + m_width); \ + l.maximum_width = MAX(l.maximum_width, MIN(p_width, wofs + m_width)); \ + l.minimum_width = MAX(l.minimum_width, m_width); \ } \ if (wofs + m_width > p_width) { \ if (p_mode == PROCESS_CACHE) { \ @@ -236,6 +249,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & int rchar = 0; int lh = 0; bool line_is_blank = true; + int fh = 0; while (it) { @@ -251,13 +265,16 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & const CharType *c = text->text.c_str(); const CharType *cf = c; - int fh = font->get_height(); int ascent = font->get_ascent(); + int descent = font->get_descent(); + Color color; + Color font_color_shadow; bool underline = false; if (p_mode == PROCESS_DRAW) { color = _find_color(text, p_base_color); + font_color_shadow = _find_color(text, p_font_color_shadow); underline = _find_underline(text); if (_find_meta(text, &meta)) { @@ -269,7 +286,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & } rchar = 0; - + FontDrawer drawer(font, Color(1, 1, 1)); while (*c) { int end = 0; @@ -279,8 +296,9 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & lh = 0; if (p_mode != PROCESS_CACHE) { lh = line < l.height_caches.size() ? l.height_caches[line] : 1; + line_ascent = line < l.ascent_caches.size() ? l.ascent_caches[line] : 1; + line_descent = line < l.descent_caches.size() ? l.descent_caches[line] : 1; } - while (c[end] != 0 && !(end && c[end - 1] == ' ' && c[end] != ' ')) { int cw = font->get_char_size(c[end], c[end + 1]).width; @@ -297,9 +315,13 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & end++; } - + CHECK_HEIGHT(fh); ENSURE_WIDTH(w); + line_ascent = MAX(line_ascent, ascent); + line_descent = MAX(line_descent, descent); + fh = line_ascent + line_descent; + if (end && c[end - 1] == ' ') { if (p_mode == PROCESS_CACHE) { spaces_size += font->get_char_size(' ').width; @@ -347,23 +369,37 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & int cw = 0; - bool visible = visible_characters < 0 || p_char_count < visible_characters && YRANGE_VISIBLE(y + lh - (fh - 0 * ascent), fh); //getting rid of ascent seems to work?? + bool visible = visible_characters < 0 || p_char_count < visible_characters && YRANGE_VISIBLE(y + lh - line_descent - line_ascent, line_ascent + line_descent); if (visible) line_is_blank = false; if (c[i] == '\t') visible = false; - if (selected) { + if (visible) { + if (selected) { + cw = font->get_char_size(c[i], c[i + 1]).x; + draw_rect(Rect2(p_ofs.x + pofs, p_ofs.y + y, cw, lh), selection_bg); + } - cw = font->get_char_size(c[i], c[i + 1]).x; - draw_rect(Rect2(p_ofs.x + pofs, p_ofs.y + y, cw, lh), selection_bg); - if (visible) - font->draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - (fh - ascent)), c[i], c[i + 1], override_selected_font_color ? selection_fg : color); + if (p_font_color_shadow.a > 0) { + float x_ofs_shadow = align_ofs + pofs; + float y_ofs_shadow = y + lh - line_descent; + float move = font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + shadow_ofs, c[i], c[i + 1], p_font_color_shadow); + + if (p_shadow_as_outline) { + font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, shadow_ofs.y), c[i], c[i + 1], p_font_color_shadow); + font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(shadow_ofs.x, -shadow_ofs.y), c[i], c[i + 1], p_font_color_shadow); + font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, -shadow_ofs.y), c[i], c[i + 1], p_font_color_shadow); + } + x_ofs_shadow += move; + } - } else { - if (visible) - cw = font->draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - (fh - ascent)), c[i], c[i + 1], color); + if (selected) { + drawer.draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent), c[i], c[i + 1], override_selected_font_color ? selection_fg : color); + } else { + cw = drawer.draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent), c[i], c[i + 1], color); + } } p_char_count++; @@ -371,19 +407,20 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & cw = tab_size * font->get_char_size(' ').width; } - if (underline) { - Color uc = color; - uc.a *= 0.5; - int uy = y + lh - fh + ascent + 2; - float underline_width = 1.0; -#ifdef TOOLS_ENABLED - underline_width *= EDSCALE; -#endif - VS::get_singleton()->canvas_item_add_line(ci, p_ofs + Point2(align_ofs + pofs, uy), p_ofs + Point2(align_ofs + pofs + cw, uy), uc, underline_width); - } ofs += cw; } } + + if (underline) { + Color uc = color; + uc.a *= 0.5; + int uy = y + lh - line_descent + 2; + float underline_width = 1.0; +#ifdef TOOLS_ENABLED + underline_width *= EDSCALE; +#endif + VS::get_singleton()->canvas_item_add_line(ci, p_ofs + Point2(align_ofs + wofs, uy), p_ofs + Point2(align_ofs + wofs + w, uy), uc, underline_width); + } } ADVANCE(fw); @@ -441,6 +478,9 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & int vseparation = get_constant("table_vseparation"); Color ccolor = _find_color(table, p_base_color); Vector2 draw_ofs = Point2(wofs, y); + Color font_color_shadow = get_color("font_color_shadow"); + bool use_outline = get_constant("shadow_as_outline"); + Point2 shadow_ofs(get_constant("shadow_offset_x"), get_constant("shadow_offset_y")); if (p_mode == PROCESS_CACHE) { @@ -448,9 +488,12 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & //set minimums to zero for (int i = 0; i < table->columns.size(); i++) { table->columns[i].min_width = 0; + table->columns[i].max_width = 0; table->columns[i].width = 0; } //compute minimum width for each cell + const int available_width = p_width - hseparation * (table->columns.size() - 1) - wofs; + for (List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) { ERR_CONTINUE(E->get()->type != ITEM_FRAME); //children should all be frames ItemFrame *frame = static_cast<ItemFrame *>(E->get()); @@ -461,8 +504,9 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & for (int i = 0; i < frame->lines.size(); i++) { - _process_line(frame, Point2(), ly, p_width, i, PROCESS_CACHE, cfont, Color()); + _process_line(frame, Point2(), ly, available_width, i, PROCESS_CACHE, cfont, Color(), font_color_shadow, use_outline, shadow_ofs); table->columns[column].min_width = MAX(table->columns[column].min_width, frame->lines[i].minimum_width); + table->columns[column].max_width = MAX(table->columns[column].max_width, frame->lines[i].maximum_width); } idx++; } @@ -470,24 +514,58 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & //compute available width and total ratio (for expanders) int total_ratio = 0; - int available_width = p_width - hseparation * (table->columns.size() - 1); + int remaining_width = available_width; table->total_width = hseparation; for (int i = 0; i < table->columns.size(); i++) { - available_width -= table->columns[i].min_width; + remaining_width -= table->columns[i].min_width; + if (table->columns[i].max_width > table->columns[i].min_width) + table->columns[i].expand = true; if (table->columns[i].expand) total_ratio += table->columns[i].expand_ratio; } //assign actual widths - for (int i = 0; i < table->columns.size(); i++) { table->columns[i].width = table->columns[i].min_width; if (table->columns[i].expand) - table->columns[i].width += table->columns[i].expand_ratio * available_width / total_ratio; + table->columns[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio; table->total_width += table->columns[i].width + hseparation; } + //resize to max_width if needed and distribute the remaining space + bool table_need_fit = true; + while (table_need_fit) { + table_need_fit = false; + //fit slim + for (int i = 0; i < table->columns.size(); i++) { + if (!table->columns[i].expand) + continue; + int dif = table->columns[i].width - table->columns[i].max_width; + if (dif > 0) { + table_need_fit = true; + table->columns[i].width = table->columns[i].max_width; + table->total_width -= dif; + total_ratio -= table->columns[i].expand_ratio; + } + } + //grow + remaining_width = available_width - table->total_width; + if (remaining_width > 0 && total_ratio > 0) { + for (int i = 0; i < table->columns.size(); i++) { + if (table->columns[i].expand) { + int dif = table->columns[i].max_width - table->columns[i].width; + if (dif > 0) { + int slice = table->columns[i].expand_ratio * remaining_width / total_ratio; + int incr = MIN(dif, slice); + table->columns[i].width += incr; + table->total_width += incr; + } + } + } + } + } + //compute caches properly again with the right width idx = 0; for (List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) { @@ -499,7 +577,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & for (int i = 0; i < frame->lines.size(); i++) { int ly = 0; - _process_line(frame, Point2(), ly, table->columns[column].width, i, PROCESS_CACHE, cfont, Color()); + _process_line(frame, Point2(), ly, table->columns[column].width, i, PROCESS_CACHE, cfont, Color(), font_color_shadow, use_outline, shadow_ofs); frame->lines[i].height_cache = ly; //actual height frame->lines[i].height_accum_cache = ly; //actual height } @@ -532,9 +610,9 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & if (visible) { if (p_mode == PROCESS_DRAW) { - nonblank_line_count += _process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, PROCESS_DRAW, cfont, ccolor); + nonblank_line_count += _process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, PROCESS_DRAW, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs); } else if (p_mode == PROCESS_POINTER) { - _process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, PROCESS_POINTER, cfont, ccolor, p_click_pos, r_click_item, r_click_char, r_outside); + _process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, PROCESS_POINTER, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs, p_click_pos, r_click_item, r_click_char, r_outside); if (r_click_item && *r_click_item) { RETURN; // exit early } @@ -615,9 +693,7 @@ void RichTextLabel::_scroll_changed(double) { void RichTextLabel::_update_scroll() { - int total_height = 0; - if (main->lines.size()) - total_height = main->lines[main->lines.size() - 1].height_accum_cache + get_stylebox("normal")->get_minimum_size().height; + int total_height = get_content_height(); bool exceeds = total_height > get_size().height && scroll_active; @@ -706,12 +782,18 @@ void RichTextLabel::_notification(int p_what) { int y = (main->lines[from_line].height_accum_cache - main->lines[from_line].height_cache) - ofs; Ref<Font> base_font = get_font("normal_font"); Color base_color = get_color("default_color"); + Color font_color_shadow = get_color("font_color_shadow"); + bool use_outline = get_constant("shadow_as_outline"); + Point2 shadow_ofs(get_constant("shadow_offset_x"), get_constant("shadow_offset_y")); + + float x_ofs = 0; visible_line_count = 0; while (y < size.height && from_line < main->lines.size()) { - visible_line_count += _process_line(main, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, from_line, PROCESS_DRAW, base_font, base_color, Point2i(), NULL, NULL, NULL, total_chars); + visible_line_count += _process_line(main, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, from_line, PROCESS_DRAW, base_font, base_color, font_color_shadow, use_outline, shadow_ofs, Point2i(), NULL, NULL, NULL, total_chars); total_chars += main->lines[from_line].char_count; + from_line++; } } @@ -726,6 +808,9 @@ void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item Size2 size = get_size(); Rect2 text_rect = _get_text_rect(); int ofs = vscroll->get_value(); + Color font_color_shadow = get_color("font_color_shadow"); + bool use_outline = get_constant("shadow_as_outline"); + Point2 shadow_ofs(get_constant("shadow_offset_x"), get_constant("shadow_offset_y")); //todo, change to binary search int from_line = 0; @@ -746,7 +831,7 @@ void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item while (y < text_rect.get_size().height && from_line < p_frame->lines.size()) { - _process_line(p_frame, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, from_line, PROCESS_POINTER, base_font, base_color, p_click, r_click_item, r_click_char, r_outside); + _process_line(p_frame, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, from_line, PROCESS_POINTER, base_font, base_color, font_color_shadow, use_outline, shadow_ofs, p_click, r_click_item, r_click_char, r_outside); if (r_click_item && *r_click_item) return; from_line++; @@ -806,9 +891,9 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) { // Erase previous selection. if (selection.active) { selection.from = NULL; - selection.from_char = NULL; + selection.from_char = '\0'; selection.to = NULL; - selection.to_char = NULL; + selection.to_char = '\0'; selection.active = false; update(); @@ -1121,13 +1206,16 @@ void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) { //validate invalid lines Size2 size = get_size(); Rect2 text_rect = _get_text_rect(); + Color font_color_shadow = get_color("font_color_shadow"); + bool use_outline = get_constant("shadow_as_outline"); + Point2 shadow_ofs(get_constant("shadow_offset_x"), get_constant("shadow_offset_y")); Ref<Font> base_font = get_font("normal_font"); for (int i = p_frame->first_invalid_line; i < p_frame->lines.size(); i++) { int y = 0; - _process_line(p_frame, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, i, PROCESS_CACHE, base_font, Color()); + _process_line(p_frame, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, i, PROCESS_CACHE, base_font, Color(), font_color_shadow, use_outline, shadow_ofs); p_frame->lines[i].height_cache = y; p_frame->lines[i].height_accum_cache = y; @@ -1610,7 +1698,7 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { tag_stack.push_front(tag); } else if (tag.begins_with("cell=")) { - int ratio = tag.substr(6, tag.length()).to_int(); + int ratio = tag.substr(5, tag.length()).to_int(); if (ratio < 1) ratio = 1; //use monospace font @@ -1969,6 +2057,13 @@ float RichTextLabel::get_percent_visible() const { return percent_visible; } +int RichTextLabel::get_content_height() { + int total_height = 0; + if (main->lines.size()) + total_height = main->lines[main->lines.size() - 1].height_accum_cache + get_stylebox("normal")->get_minimum_size().height; + return total_height; +} + void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("_gui_input"), &RichTextLabel::_gui_input); @@ -2035,12 +2130,23 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("get_line_count"), &RichTextLabel::get_line_count); ClassDB::bind_method(D_METHOD("get_visible_line_count"), &RichTextLabel::get_visible_line_count); + ClassDB::bind_method(D_METHOD("get_content_height"), &RichTextLabel::get_content_height); + ADD_GROUP("BBCode", "bbcode_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bbcode_enabled"), "set_use_bbcode", "is_using_bbcode"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "bbcode_text", PROPERTY_HINT_MULTILINE_TEXT), "set_bbcode", "get_bbcode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters", PROPERTY_HINT_RANGE, "-1,128000,1"), "set_visible_characters", "get_visible_characters"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "percent_visible", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_percent_visible", "get_percent_visible"); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "meta_underlined"), "set_meta_underline", "is_meta_underlined"); + 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"), "set_text", "get_text"); + + 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_SIGNAL(MethodInfo("meta_clicked", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT))); diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index c942ecc00f..e054ce3935 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef RICH_TEXT_LABEL_H #define RICH_TEXT_LABEL_H @@ -79,11 +80,14 @@ private: Item *from; Vector<int> offset_caches; Vector<int> height_caches; + Vector<int> ascent_caches; + Vector<int> descent_caches; Vector<int> space_caches; int height_cache; int height_accum_cache; int char_count; int minimum_width; + int maximum_width; Line() { from = NULL; @@ -196,6 +200,7 @@ private: bool expand; int expand_ratio; int min_width; + int max_width; int width; }; @@ -265,7 +270,7 @@ private: int visible_characters; float percent_visible; - int _process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref<Font> &p_base_font, const Color &p_base_color, const Point2i &p_click_pos = Point2i(), Item **r_click_item = NULL, int *r_click_char = NULL, bool *r_outside = NULL, int p_char_count = 0); + int _process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref<Font> &p_base_font, const Color &p_base_color, const Color &p_font_color_shadow, bool p_shadow_as_outline, const Point2 &shadow_ofs, const Point2i &p_click_pos = Point2i(), Item **r_click_item = NULL, int *r_click_char = NULL, bool *r_outside = NULL, int p_char_count = 0); void _find_click(ItemFrame *p_frame, const Point2i &p_click, Item **r_click_item = NULL, int *r_click_char = NULL, bool *r_outside = NULL); Ref<Font> _find_font(Item *p_item); @@ -335,6 +340,8 @@ public: int get_line_count() const; int get_visible_line_count() const; + int get_content_height(); + VScrollBar *get_v_scroll() { return vscroll; } virtual CursorShape get_cursor_shape(const Point2 &p_pos) const; diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp index 3ac1ba0e48..6ec67aca6b 100644 --- a/scene/gui/scroll_bar.cpp +++ b/scene/gui/scroll_bar.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "scroll_bar.h" #include "os/keyboard.h" @@ -113,7 +114,7 @@ void ScrollBar::_gui_input(Ref<InputEvent> p_event) { if (smooth_scroll_enabled) { scrolling = true; - set_physics_process(true); + set_physics_process_internal(true); } else { set_value(target_scroll); } @@ -137,7 +138,7 @@ void ScrollBar::_gui_input(Ref<InputEvent> p_event) { if (smooth_scroll_enabled) { scrolling = true; - set_physics_process(true); + set_physics_process_internal(true); } else { set_value(target_scroll); } @@ -198,54 +199,40 @@ void ScrollBar::_gui_input(Ref<InputEvent> p_event) { } } - Ref<InputEventKey> k = p_event; - - if (k.is_valid()) { - - if (!k->is_pressed()) - return; + if (p_event->is_pressed()) { - switch (k->get_scancode()) { - - case KEY_LEFT: { - - if (orientation != HORIZONTAL) - return; - set_value(get_value() - (custom_step >= 0 ? custom_step : get_step())); + if (p_event->is_action("ui_left")) { - } break; - case KEY_RIGHT: { + if (orientation != HORIZONTAL) + return; + set_value(get_value() - (custom_step >= 0 ? custom_step : get_step())); - if (orientation != HORIZONTAL) - return; - set_value(get_value() + (custom_step >= 0 ? custom_step : get_step())); + } else if (p_event->is_action("ui_right")) { - } break; - case KEY_UP: { + if (orientation != HORIZONTAL) + return; + set_value(get_value() + (custom_step >= 0 ? custom_step : get_step())); - if (orientation != VERTICAL) - return; + } else if (p_event->is_action("ui_up")) { - set_value(get_value() - (custom_step >= 0 ? custom_step : get_step())); + if (orientation != VERTICAL) + return; - } break; - case KEY_DOWN: { + set_value(get_value() - (custom_step >= 0 ? custom_step : get_step())); - if (orientation != VERTICAL) - return; - set_value(get_value() + (custom_step >= 0 ? custom_step : get_step())); + } else if (p_event->is_action("ui_down")) { - } break; - case KEY_HOME: { + if (orientation != VERTICAL) + return; + set_value(get_value() + (custom_step >= 0 ? custom_step : get_step())); - set_value(get_min()); + } else if (p_event->is_action("ui_home")) { - } break; - case KEY_END: { + set_value(get_min()); - set_value(get_max()); + } else if (p_event->is_action("ui_end")) { - } break; + set_value(get_max()); } } } @@ -322,20 +309,20 @@ void ScrollBar::_notification(int p_what) { if (drag_slave) { drag_slave->connect("gui_input", this, "_drag_slave_input"); - drag_slave->connect("tree_exited", this, "_drag_slave_exit", varray(), CONNECT_ONESHOT); + drag_slave->connect("tree_exiting", this, "_drag_slave_exit", varray(), CONNECT_ONESHOT); } } if (p_what == NOTIFICATION_EXIT_TREE) { if (drag_slave) { drag_slave->disconnect("gui_input", this, "_drag_slave_input"); - drag_slave->disconnect("tree_exited", this, "_drag_slave_exit"); + drag_slave->disconnect("tree_exiting", this, "_drag_slave_exit"); } drag_slave = NULL; } - if (p_what == NOTIFICATION_PHYSICS_PROCESS) { + if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) { if (scrolling) { if (get_value() != target_scroll) { @@ -350,7 +337,7 @@ void ScrollBar::_notification(int p_what) { } } else { scrolling = false; - set_physics_process(false); + set_physics_process_internal(false); } } else if (drag_slave_touching) { @@ -410,7 +397,7 @@ void ScrollBar::_notification(int p_what) { } if (turnoff) { - set_physics_process(false); + set_physics_process_internal(false); drag_slave_touching = false; drag_slave_touching_deaccel = false; } @@ -579,7 +566,7 @@ void ScrollBar::_drag_slave_input(const Ref<InputEvent> &p_input) { if (mb->is_pressed()) { if (drag_slave_touching) { - set_physics_process(false); + set_physics_process_internal(false); drag_slave_touching_deaccel = false; drag_slave_touching = false; drag_slave_speed = Vector2(); @@ -599,7 +586,7 @@ void ScrollBar::_drag_slave_input(const Ref<InputEvent> &p_input) { drag_slave_touching_deaccel = false; time_since_motion = 0; if (drag_slave_touching) { - set_physics_process(true); + set_physics_process_internal(true); time_since_motion = 0; } } @@ -611,7 +598,7 @@ void ScrollBar::_drag_slave_input(const Ref<InputEvent> &p_input) { if (drag_slave_speed == Vector2()) { drag_slave_touching_deaccel = false; drag_slave_touching = false; - set_physics_process(false); + set_physics_process_internal(false); } else { drag_slave_touching_deaccel = true; @@ -654,7 +641,7 @@ void ScrollBar::set_drag_slave(const NodePath &p_path) { if (drag_slave) { drag_slave->disconnect("gui_input", this, "_drag_slave_input"); - drag_slave->disconnect("tree_exited", this, "_drag_slave_exit"); + drag_slave->disconnect("tree_exiting", this, "_drag_slave_exit"); } } @@ -670,7 +657,7 @@ void ScrollBar::set_drag_slave(const NodePath &p_path) { if (drag_slave) { drag_slave->connect("gui_input", this, "_drag_slave_input"); - drag_slave->connect("tree_exited", this, "_drag_slave_exit", varray(), CONNECT_ONESHOT); + drag_slave->connect("tree_exiting", this, "_drag_slave_exit", varray(), CONNECT_ONESHOT); } } } diff --git a/scene/gui/scroll_bar.h b/scene/gui/scroll_bar.h index 9d0dc3ccbc..15e037f8bb 100644 --- a/scene/gui/scroll_bar.h +++ b/scene/gui/scroll_bar.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef SCROLL_BAR_H #define SCROLL_BAR_H diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp index 42745a7042..a1dcf3b002 100644 --- a/scene/gui/scroll_container.cpp +++ b/scene/gui/scroll_container.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "scroll_container.h" #include "os/os.h" bool ScrollContainer::clips_input() const { @@ -67,13 +68,19 @@ Size2 ScrollContainer::get_minimum_size() const { }; void ScrollContainer::_cancel_drag() { - set_physics_process(false); + set_physics_process_internal(false); drag_touching_deaccel = false; drag_touching = false; drag_speed = Vector2(); drag_accum = Vector2(); last_drag_accum = Vector2(); drag_from = Vector2(); + + if (beyond_deadzone) { + emit_signal("scroll_ended"); + propagate_notification(NOTIFICATION_SCROLL_END); + beyond_deadzone = false; + } } void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) { @@ -121,13 +128,7 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) { if (mb->is_pressed()) { if (drag_touching) { - set_physics_process(false); - drag_touching_deaccel = false; - drag_touching = false; - drag_speed = Vector2(); - drag_accum = Vector2(); - last_drag_accum = Vector2(); - drag_from = Vector2(); + _cancel_drag(); } if (true) { @@ -137,9 +138,10 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) { drag_from = Vector2(h_scroll->get_value(), v_scroll->get_value()); drag_touching = OS::get_singleton()->has_touchscreen_ui_hint(); drag_touching_deaccel = false; + beyond_deadzone = false; time_since_motion = 0; if (drag_touching) { - set_physics_process(true); + set_physics_process_internal(true); time_since_motion = 0; } } @@ -148,9 +150,7 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) { if (drag_touching) { if (drag_speed == Vector2()) { - drag_touching_deaccel = false; - drag_touching = false; - set_physics_process(false); + _cancel_drag(); } else { drag_touching_deaccel = true; @@ -167,17 +167,27 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) { Vector2 motion = Vector2(mm->get_relative().x, mm->get_relative().y); drag_accum -= motion; - Vector2 diff = drag_from + drag_accum; - - if (scroll_h) - h_scroll->set_value(diff.x); - else - drag_accum.x = 0; - if (scroll_v) - v_scroll->set_value(diff.y); - else - drag_accum.y = 0; - time_since_motion = 0; + + if (beyond_deadzone || scroll_h && Math::abs(drag_accum.x) > deadzone || scroll_v && Math::abs(drag_accum.y) > deadzone) { + if (!beyond_deadzone) { + propagate_notification(NOTIFICATION_SCROLL_BEGIN); + emit_signal("scroll_started"); + + beyond_deadzone = true; + // resetting drag_accum here ensures smooth scrolling after reaching deadzone + drag_accum = -motion; + } + Vector2 diff = drag_from + drag_accum; + if (scroll_h) + h_scroll->set_value(diff.x); + else + drag_accum.x = 0; + if (scroll_v) + v_scroll->set_value(diff.y); + else + drag_accum.y = 0; + time_since_motion = 0; + } } } @@ -268,7 +278,7 @@ void ScrollContainer::_notification(int p_what) { update_scrollbars(); } - if (p_what == NOTIFICATION_PHYSICS_PROCESS) { + if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) { if (drag_touching) { @@ -322,9 +332,7 @@ void ScrollContainer::_notification(int p_what) { drag_speed = Vector2(sgn_x * val_x, sgn_y * val_y); if (turnoff_h && turnoff_v) { - set_physics_process(false); - drag_touching = false; - drag_touching_deaccel = false; + _cancel_drag(); } } else { @@ -429,6 +437,14 @@ void ScrollContainer::set_h_scroll(int p_pos) { _cancel_drag(); } +int ScrollContainer::get_deadzone() const { + return deadzone; +} + +void ScrollContainer::set_deadzone(int p_deadzone) { + deadzone = p_deadzone; +} + String ScrollContainer::get_configuration_warning() const { int found = 0; @@ -461,14 +477,24 @@ void ScrollContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_enable_v_scroll", "enable"), &ScrollContainer::set_enable_v_scroll); ClassDB::bind_method(D_METHOD("is_v_scroll_enabled"), &ScrollContainer::is_v_scroll_enabled); ClassDB::bind_method(D_METHOD("_update_scrollbar_position"), &ScrollContainer::_update_scrollbar_position); - ClassDB::bind_method(D_METHOD("set_h_scroll", "val"), &ScrollContainer::set_h_scroll); + ClassDB::bind_method(D_METHOD("set_h_scroll", "value"), &ScrollContainer::set_h_scroll); ClassDB::bind_method(D_METHOD("get_h_scroll"), &ScrollContainer::get_h_scroll); - ClassDB::bind_method(D_METHOD("set_v_scroll", "val"), &ScrollContainer::set_v_scroll); + ClassDB::bind_method(D_METHOD("set_v_scroll", "value"), &ScrollContainer::set_v_scroll); ClassDB::bind_method(D_METHOD("get_v_scroll"), &ScrollContainer::get_v_scroll); + ClassDB::bind_method(D_METHOD("set_deadzone", "deadzone"), &ScrollContainer::set_deadzone); + ClassDB::bind_method(D_METHOD("get_deadzone"), &ScrollContainer::get_deadzone); + + ADD_SIGNAL(MethodInfo("scroll_started")); + ADD_SIGNAL(MethodInfo("scroll_ended")); ADD_GROUP("Scroll", "scroll_"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_horizontal"), "set_enable_h_scroll", "is_h_scroll_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_vertical"), "set_enable_v_scroll", "is_v_scroll_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_horizontal_enabled"), "set_enable_h_scroll", "is_h_scroll_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_horizontal"), "set_h_scroll", "get_h_scroll"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_vertical_enabled"), "set_enable_v_scroll", "is_v_scroll_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_vertical"), "set_v_scroll", "get_v_scroll"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_deadzone"), "set_deadzone", "get_deadzone"); + + GLOBAL_DEF("gui/common/default_scroll_deadzone", 0); }; ScrollContainer::ScrollContainer() { @@ -487,8 +513,11 @@ ScrollContainer::ScrollContainer() { drag_speed = Vector2(); drag_touching = false; drag_touching_deaccel = false; + beyond_deadzone = false; scroll_h = true; scroll_v = true; + deadzone = GLOBAL_GET("gui/common/default_scroll_deadzone"); + set_clip_contents(true); }; diff --git a/scene/gui/scroll_container.h b/scene/gui/scroll_container.h index 997d03cbf2..3fe1ed447a 100644 --- a/scene/gui/scroll_container.h +++ b/scene/gui/scroll_container.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef SCROLL_CONTAINER_H #define SCROLL_CONTAINER_H @@ -55,10 +56,13 @@ class ScrollContainer : public Container { bool drag_touching; bool drag_touching_deaccel; bool click_handled; + bool beyond_deadzone; bool scroll_h; bool scroll_v; + int deadzone; + void _cancel_drag(); protected: @@ -85,6 +89,9 @@ public: void set_enable_v_scroll(bool p_enable); bool is_v_scroll_enabled() const; + int get_deadzone() const; + void set_deadzone(int p_deadzone); + virtual bool clips_input() const; virtual String get_configuration_warning() const; diff --git a/scene/gui/separator.cpp b/scene/gui/separator.cpp index f65d51cf86..cd0b06da81 100644 --- a/scene/gui/separator.cpp +++ b/scene/gui/separator.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "separator.h" Size2 Separator::get_minimum_size() const { diff --git a/scene/gui/separator.h b/scene/gui/separator.h index 985ccce31d..7949c7ca9b 100644 --- a/scene/gui/separator.h +++ b/scene/gui/separator.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef SEPARATOR_H #define SEPARATOR_H diff --git a/scene/gui/shortcut.cpp b/scene/gui/shortcut.cpp index 4f32786854..36490cf254 100644 --- a/scene/gui/shortcut.cpp +++ b/scene/gui/shortcut.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "shortcut.h" #include "os/keyboard.h" diff --git a/scene/gui/shortcut.h b/scene/gui/shortcut.h index 1d5bfc8bd9..f9240642bf 100644 --- a/scene/gui/shortcut.h +++ b/scene/gui/shortcut.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef SHORTCUT_H #define SHORTCUT_H diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp index 37b4d30e3c..46215c9277 100644 --- a/scene/gui/slider.cpp +++ b/scene/gui/slider.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "slider.h" #include "os/keyboard.h" @@ -117,28 +118,14 @@ void Slider::_gui_input(Ref<InputEvent> p_event) { return; set_value(get_value() - (custom_step >= 0 ? custom_step : get_step())); accept_event(); + } else if (p_event->is_action("ui_home") && p_event->is_pressed()) { - } else { - - Ref<InputEventKey> k = p_event; - - if (!k.is_valid() || !k->is_pressed()) - return; - - switch (k->get_scancode()) { - - case KEY_HOME: { - - set_value(get_min()); - accept_event(); - } break; - case KEY_END: { - - set_value(get_max()); - accept_event(); + set_value(get_min()); + accept_event(); + } else if (p_event->is_action("ui_end") && p_event->is_pressed()) { - } break; - } + set_value(get_max()); + accept_event(); } } } diff --git a/scene/gui/slider.h b/scene/gui/slider.h index c380ab25a5..e77a0b7423 100644 --- a/scene/gui/slider.h +++ b/scene/gui/slider.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef SLIDER_H #define SLIDER_H diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp index 76bcde01af..145981d498 100644 --- a/scene/gui/spin_box.cpp +++ b/scene/gui/spin_box.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "spin_box.h" #include "os/input.h" @@ -184,17 +185,22 @@ void SpinBox::_line_edit_focus_exit() { _text_entered(line_edit->get_text()); } +inline void SpinBox::_adjust_width_for_icon(const Ref<Texture> icon) { + + int w = icon->get_width(); + if (w != last_w) { + line_edit->set_margin(MARGIN_RIGHT, -w); + last_w = w; + } +} + void SpinBox::_notification(int p_what) { if (p_what == NOTIFICATION_DRAW) { Ref<Texture> updown = get_icon("updown"); - int w = updown->get_width(); - if (w != last_w) { - line_edit->set_margin(MARGIN_RIGHT, -w); - last_w = w; - } + _adjust_width_for_icon(updown); RID ci = get_canvas_item(); Size2i size = get_size(); @@ -206,6 +212,7 @@ void SpinBox::_notification(int p_what) { //_value_changed(0); } else if (p_what == NOTIFICATION_ENTER_TREE) { + _adjust_width_for_icon(get_icon("updown")); _value_changed(0); } } diff --git a/scene/gui/spin_box.h b/scene/gui/spin_box.h index ab969556d9..8863f44bef 100644 --- a/scene/gui/spin_box.h +++ b/scene/gui/spin_box.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef SPIN_BOX_H #define SPIN_BOX_H @@ -61,6 +62,8 @@ class SpinBox : public Range { void _line_edit_focus_exit(); + inline void _adjust_width_for_icon(const Ref<Texture> icon); + protected: void _gui_input(const Ref<InputEvent> &p_event); diff --git a/scene/gui/split_container.cpp b/scene/gui/split_container.cpp index d0b7537665..bf7033e8ba 100644 --- a/scene/gui/split_container.cpp +++ b/scene/gui/split_container.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "split_container.h" #include "label.h" @@ -60,127 +61,69 @@ Control *SplitContainer::_getch(int p_idx) const { } void SplitContainer::_resort() { - - /* First pass, determine minimum size AND amount of stretchable elements */ - int axis = vertical ? 1 : 0; - bool has_first = _getch(0); - bool has_second = _getch(1); + Control *first = _getch(0); + Control *second = _getch(1); - if (!has_first && !has_second) { - return; - } else if (!(has_first && has_second)) { - if (has_first) + // If we have only one element + if (!first || !second) { + if (first) { fit_child_in_rect(_getch(0), Rect2(Point2(), get_size())); - else + } else if (second) { fit_child_in_rect(_getch(1), Rect2(Point2(), get_size())); - + } return; } - Control *first = _getch(0); - Control *second = _getch(1); - - bool ratiomode = false; - bool expand_first_mode = false; - + // Determine expanded children + bool first_expanded = false; + bool second_expanded = false; if (vertical) { - - ratiomode = first->get_v_size_flags() & SIZE_EXPAND && second->get_v_size_flags() & SIZE_EXPAND; - expand_first_mode = first->get_v_size_flags() & SIZE_EXPAND && !(second->get_v_size_flags() & SIZE_EXPAND); + first_expanded = first->get_v_size_flags() & SIZE_EXPAND; + second_expanded = second->get_v_size_flags() & SIZE_EXPAND; } else { - - ratiomode = first->get_h_size_flags() & SIZE_EXPAND && second->get_h_size_flags() & SIZE_EXPAND; - expand_first_mode = first->get_h_size_flags() & SIZE_EXPAND && !(second->get_h_size_flags() & SIZE_EXPAND); + first_expanded = first->get_h_size_flags() & SIZE_EXPAND; + second_expanded = second->get_h_size_flags() & SIZE_EXPAND; } - int sep = get_constant("separation"); + // Determine the separation between items Ref<Texture> g = get_icon("grabber"); - + int sep = get_constant("separation"); if (dragger_visibility == DRAGGER_HIDDEN_COLLAPSED) { sep = 0; } else { sep = MAX(sep, vertical ? g->get_height() : g->get_width()); } - int total = vertical ? get_size().height : get_size().width; - - total -= sep; - - int minimum = 0; - + // Compute the minimum size Size2 ms_first = first->get_combined_minimum_size(); Size2 ms_second = second->get_combined_minimum_size(); - if (vertical) { - minimum = ms_first.height + ms_second.height; - } else { - minimum = ms_first.width + ms_second.width; - } + float ratio = first->get_stretch_ratio() / (first->get_stretch_ratio() + second->get_stretch_ratio()); - int available = total - minimum; - if (available < 0) - available = 0; - - middle_sep = 0; - - if (collapsed) { - - if (ratiomode) { - - int first_ratio = first->get_stretch_ratio(); - int second_ratio = second->get_stretch_ratio(); - - float ratio = float(first_ratio) / (first_ratio + second_ratio); - - middle_sep = ms_first[axis] + available * ratio; - - } else if (expand_first_mode) { - - middle_sep = get_size()[axis] - ms_second[axis] - sep; - } else { - - middle_sep = ms_first[axis]; - } - } else if (ratiomode) { - - int first_ratio = first->get_stretch_ratio(); - int second_ratio = second->get_stretch_ratio(); - - float ratio = float(first_ratio) / (first_ratio + second_ratio); - - if (expand_ofs < -(available * ratio)) - expand_ofs = -(available * ratio); - else if (expand_ofs > (available * (1.0 - ratio))) - expand_ofs = (available * (1.0 - ratio)); - - middle_sep = ms_first[axis] + available * ratio + expand_ofs; - } else if (expand_first_mode) { - - if (expand_ofs > 0) - expand_ofs = 0; - else if (expand_ofs < -available) - expand_ofs = -available; - - middle_sep = get_size()[axis] - ms_second[axis] - sep + expand_ofs; + int no_offset_middle_sep = 0; + if (first_expanded && second_expanded) { + no_offset_middle_sep = get_size()[axis] * ratio - sep / 2; + } else if (first_expanded) { + no_offset_middle_sep = get_size()[axis] - ms_second[axis] - sep; } else { + no_offset_middle_sep = ms_first[axis]; + } - if (expand_ofs < 0) - expand_ofs = 0; - else if (expand_ofs > available) - expand_ofs = available; - - middle_sep = ms_first[axis] + expand_ofs; + middle_sep = no_offset_middle_sep; + middle_sep += (collapsed) ? 0 : split_offset; + middle_sep = MIN(middle_sep, get_size()[axis] - ms_second[axis] - sep); + middle_sep = MAX(middle_sep, ms_first[axis]); + if (!collapsed) { + split_offset = middle_sep - no_offset_middle_sep; } if (vertical) { - fit_child_in_rect(first, Rect2(Point2(0, 0), Size2(get_size().width, middle_sep))); int sofs = middle_sep + sep; fit_child_in_rect(second, Rect2(Point2(0, sofs), Size2(get_size().width, get_size().height - sofs))); } else { - fit_child_in_rect(first, Rect2(Point2(0, 0), Size2(middle_sep, get_size().height))); int sofs = middle_sep + sep; fit_child_in_rect(second, Rect2(Point2(sofs, 0), Size2(get_size().width - sofs, get_size().height))); @@ -289,7 +232,7 @@ void SplitContainer::_gui_input(const Ref<InputEvent> &p_event) { dragging = true; drag_from = mb->get_position().y; - drag_ofs = expand_ofs; + drag_ofs = split_offset; } } else { @@ -297,7 +240,7 @@ void SplitContainer::_gui_input(const Ref<InputEvent> &p_event) { dragging = true; drag_from = mb->get_position().x; - drag_ofs = expand_ofs; + drag_ofs = split_offset; } } } else { @@ -311,7 +254,7 @@ void SplitContainer::_gui_input(const Ref<InputEvent> &p_event) { if (mm.is_valid() && dragging) { - expand_ofs = drag_ofs + ((vertical ? mm->get_position().y : mm->get_position().x) - drag_from); + split_offset = drag_ofs + ((vertical ? mm->get_position().y : mm->get_position().x) - drag_from); queue_sort(); emit_signal("dragged", get_split_offset()); } @@ -342,16 +285,16 @@ Control::CursorShape SplitContainer::get_cursor_shape(const Point2 &p_pos) const void SplitContainer::set_split_offset(int p_offset) { - if (expand_ofs == p_offset) + if (split_offset == p_offset) return; - expand_ofs = p_offset; + split_offset = p_offset; queue_sort(); } int SplitContainer::get_split_offset() const { - return expand_ofs; + return split_offset; } void SplitContainer::set_collapsed(bool p_collapsed) { @@ -406,7 +349,7 @@ void SplitContainer::_bind_methods() { SplitContainer::SplitContainer(bool p_vertical) { mouse_inside = false; - expand_ofs = 0; + split_offset = 0; middle_sep = 0; vertical = p_vertical; dragging = false; diff --git a/scene/gui/split_container.h b/scene/gui/split_container.h index 2f3e480434..321f7fd3b7 100644 --- a/scene/gui/split_container.h +++ b/scene/gui/split_container.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef SPLIT_CONTAINER_H #define SPLIT_CONTAINER_H @@ -45,7 +46,7 @@ public: private: bool vertical; - int expand_ofs; + int split_offset; int middle_sep; bool dragging; int drag_from; diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index d00e360642..0363dd44c2 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -27,9 +27,13 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "tab_container.h" #include "message_queue.h" +#include "scene/gui/box_container.h" +#include "scene/gui/label.h" +#include "scene/gui/texture_rect.h" int TabContainer::_get_top_margin() const { @@ -473,21 +477,159 @@ void TabContainer::remove_child_notify(Node *p_child) { Control::remove_child_notify(p_child); + call_deferred("_update_current_tab"); + + p_child->disconnect("renamed", this, "_child_renamed_callback"); + + update(); +} + +void TabContainer::_update_current_tab() { + int tc = get_tab_count(); - if (current == tc - 1) { - current--; - if (current < 0) - current = 0; - else { - call_deferred("set_current_tab", current); + if (current >= tc) + current = tc - 1; + if (current < 0) + current = 0; + else + set_current_tab(current); +} + +Variant TabContainer::get_drag_data(const Point2 &p_point) { + + if (!drag_to_rearrange_enabled) + return Variant(); + + int tab_over = get_tab_idx_at_point(p_point); + + if (tab_over < 0) + return Variant(); + + HBoxContainer *drag_preview = memnew(HBoxContainer); + + Ref<Texture> icon = get_tab_icon(tab_over); + if (!icon.is_null()) { + TextureRect *tf = memnew(TextureRect); + tf->set_texture(icon); + drag_preview->add_child(tf); + } + Label *label = memnew(Label(get_tab_title(tab_over))); + drag_preview->add_child(label); + set_drag_preview(drag_preview); + + Dictionary drag_data; + drag_data["type"] = "tabc_element"; + drag_data["tabc_element"] = tab_over; + drag_data["from_path"] = get_path(); + return drag_data; +} + +bool TabContainer::can_drop_data(const Point2 &p_point, const Variant &p_data) const { + + if (!drag_to_rearrange_enabled) + return false; + + Dictionary d = p_data; + if (!d.has("type")) + return false; + + if (String(d["type"]) == "tabc_element") { + + NodePath from_path = d["from_path"]; + NodePath to_path = get_path(); + if (from_path == to_path) { + return true; + } else if (get_tabs_rearrange_group() != -1) { + // drag and drop between other TabContainers + Node *from_node = get_node(from_path); + TabContainer *from_tabc = Object::cast_to<TabContainer>(from_node); + if (from_tabc && from_tabc->get_tabs_rearrange_group() == get_tabs_rearrange_group()) { + return true; + } } } + return false; +} - p_child->disconnect("renamed", this, "_child_renamed_callback"); +void TabContainer::drop_data(const Point2 &p_point, const Variant &p_data) { + if (!drag_to_rearrange_enabled) + return; + + int hover_now = get_tab_idx_at_point(p_point); + + Dictionary d = p_data; + if (!d.has("type")) + return; + + if (String(d["type"]) == "tabc_element") { + + int tab_from_id = d["tabc_element"]; + NodePath from_path = d["from_path"]; + NodePath to_path = get_path(); + if (from_path == to_path) { + if (hover_now < 0) + hover_now = get_tab_count() - 1; + move_child(get_tab_control(tab_from_id), hover_now); + set_current_tab(hover_now); + } else if (get_tabs_rearrange_group() != -1) { + // drag and drop between TabContainers + Node *from_node = get_node(from_path); + TabContainer *from_tabc = Object::cast_to<TabContainer>(from_node); + if (from_tabc && from_tabc->get_tabs_rearrange_group() == get_tabs_rearrange_group()) { + Control *moving_tabc = from_tabc->get_tab_control(tab_from_id); + from_tabc->remove_child(moving_tabc); + add_child(moving_tabc); + if (hover_now < 0) + hover_now = get_tab_count() - 1; + move_child(moving_tabc, hover_now); + set_current_tab(hover_now); + emit_signal("tab_changed", hover_now); + } + } + } update(); } +int TabContainer::get_tab_idx_at_point(const Point2 &p_point) const { + + if (get_tab_count() == 0) + return -1; + + // must be on tabs in the tab header area. + if (p_point.x < tabs_ofs_cache || p_point.y > _get_top_margin()) + return -1; + + Size2 size = get_size(); + int right_ofs = 0; + + if (popup) { + Ref<Texture> menu = get_icon("menu"); + right_ofs += menu->get_width(); + } + if (buttons_visible_cache) { + Ref<Texture> increment = get_icon("increment"); + Ref<Texture> decrement = get_icon("decrement"); + right_ofs += increment->get_width() + decrement->get_width(); + } + if (p_point.x > size.width - right_ofs) { + return -1; + } + + // get the tab at the point + Vector<Control *> tabs = _get_tabs(); + int px = p_point.x; + px -= tabs_ofs_cache; + for (int i = first_tab_cache; i <= last_tab_cache; i++) { + int tab_width = _get_tab_width(i); + if (px < tab_width) { + return i; + } + px -= tab_width; + } + return -1; +} + void TabContainer::set_tab_align(TabAlign p_align) { ERR_FAIL_INDEX(p_align, 3); @@ -496,6 +638,7 @@ void TabContainer::set_tab_align(TabAlign p_align) { _change_notify("tab_align"); } + TabContainer::TabAlign TabContainer::get_tab_align() const { return align; @@ -639,6 +782,21 @@ Popup *TabContainer::get_popup() const { return popup; } +void TabContainer::set_drag_to_rearrange_enabled(bool p_enabled) { + drag_to_rearrange_enabled = p_enabled; +} + +bool TabContainer::get_drag_to_rearrange_enabled() const { + return drag_to_rearrange_enabled; +} +void TabContainer::set_tabs_rearrange_group(int p_group_id) { + tabs_rearrange_group = p_group_id; +} + +int TabContainer::get_tabs_rearrange_group() const { + return tabs_rearrange_group; +} + void TabContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("_gui_input"), &TabContainer::_gui_input); @@ -660,9 +818,14 @@ void TabContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_tab_disabled", "tab_idx"), &TabContainer::get_tab_disabled); ClassDB::bind_method(D_METHOD("set_popup", "popup"), &TabContainer::set_popup); ClassDB::bind_method(D_METHOD("get_popup"), &TabContainer::get_popup); + ClassDB::bind_method(D_METHOD("set_drag_to_rearrange_enabled", "enabled"), &TabContainer::set_drag_to_rearrange_enabled); + ClassDB::bind_method(D_METHOD("get_drag_to_rearrange_enabled"), &TabContainer::get_drag_to_rearrange_enabled); + ClassDB::bind_method(D_METHOD("set_tabs_rearrange_group", "group_id"), &TabContainer::set_tabs_rearrange_group); + ClassDB::bind_method(D_METHOD("get_tabs_rearrange_group"), &TabContainer::get_tabs_rearrange_group); ClassDB::bind_method(D_METHOD("_child_renamed_callback"), &TabContainer::_child_renamed_callback); ClassDB::bind_method(D_METHOD("_on_theme_changed"), &TabContainer::_on_theme_changed); + ClassDB::bind_method(D_METHOD("_update_current_tab"), &TabContainer::_update_current_tab); ADD_SIGNAL(MethodInfo("tab_changed", PropertyInfo(Variant::INT, "tab"))); ADD_SIGNAL(MethodInfo("tab_selected", PropertyInfo(Variant::INT, "tab"))); @@ -671,6 +834,7 @@ void TabContainer::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_tab_align", "get_tab_align"); ADD_PROPERTY(PropertyInfo(Variant::INT, "current_tab", PROPERTY_HINT_RANGE, "-1,4096,1", PROPERTY_USAGE_EDITOR), "set_current_tab", "get_current_tab"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "tabs_visible"), "set_tabs_visible", "are_tabs_visible"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_to_rearrange_enabled"), "set_drag_to_rearrange_enabled", "get_drag_to_rearrange_enabled"); BIND_ENUM_CONSTANT(ALIGN_LEFT); BIND_ENUM_CONSTANT(ALIGN_CENTER); @@ -689,4 +853,6 @@ TabContainer::TabContainer() { align = ALIGN_CENTER; tabs_visible = true; popup = NULL; + drag_to_rearrange_enabled = false; + tabs_rearrange_group = -1; } diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h index 7374fc2d77..1afe5f7541 100644 --- a/scene/gui/tab_container.h +++ b/scene/gui/tab_container.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef TAB_CONTAINER_H #define TAB_CONTAINER_H @@ -57,10 +58,13 @@ private: Control *_get_tab(int p_idx) const; int _get_top_margin() const; Popup *popup; + bool drag_to_rearrange_enabled; + int tabs_rearrange_group; Vector<Control *> _get_tabs() const; int _get_tab_width(int p_index) const; void _on_theme_changed(); + void _update_current_tab(); protected: void _child_renamed_callback(); @@ -69,6 +73,11 @@ protected: virtual void add_child_notify(Node *p_child); virtual void remove_child_notify(Node *p_child); + Variant get_drag_data(const Point2 &p_point); + bool can_drop_data(const Point2 &p_point, const Variant &p_data) const; + void drop_data(const Point2 &p_point, const Variant &p_data); + int get_tab_idx_at_point(const Point2 &p_point) const; + static void _bind_methods(); public: @@ -102,6 +111,11 @@ public: void set_popup(Node *p_popup); Popup *get_popup() const; + void set_drag_to_rearrange_enabled(bool p_enabled); + bool get_drag_to_rearrange_enabled() const; + void set_tabs_rearrange_group(int p_group_id); + int get_tabs_rearrange_group() const; + TabContainer(); }; diff --git a/scene/gui/tabs.cpp b/scene/gui/tabs.cpp index 3ab031a2ea..b114264de1 100644 --- a/scene/gui/tabs.cpp +++ b/scene/gui/tabs.cpp @@ -27,9 +27,13 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "tabs.h" #include "message_queue.h" +#include "scene/gui/box_container.h" +#include "scene/gui/label.h" +#include "scene/gui/texture_rect.h" Size2 Tabs::get_minimum_size() const { @@ -623,20 +627,105 @@ void Tabs::remove_tab(int p_idx) { Variant Tabs::get_drag_data(const Point2 &p_point) { - return get_tab_idx_at_point(p_point); + if (!drag_to_rearrange_enabled) + return Variant(); + + int tab_over = get_tab_idx_at_point(p_point); + + if (tab_over < 0) + return Variant(); + + HBoxContainer *drag_preview = memnew(HBoxContainer); + + if (!tabs[tab_over].icon.is_null()) { + TextureRect *tf = memnew(TextureRect); + tf->set_texture(tabs[tab_over].icon); + drag_preview->add_child(tf); + } + Label *label = memnew(Label(tabs[tab_over].text)); + drag_preview->add_child(label); + if (!tabs[tab_over].right_button.is_null()) { + TextureRect *tf = memnew(TextureRect); + tf->set_texture(tabs[tab_over].right_button); + drag_preview->add_child(tf); + } + set_drag_preview(drag_preview); + + Dictionary drag_data; + drag_data["type"] = "tab_element"; + drag_data["tab_element"] = tab_over; + drag_data["from_path"] = get_path(); + return drag_data; } bool Tabs::can_drop_data(const Point2 &p_point, const Variant &p_data) const { - return get_tab_idx_at_point(p_point) > -1; + if (!drag_to_rearrange_enabled) + return false; + + Dictionary d = p_data; + if (!d.has("type")) + return false; + + if (String(d["type"]) == "tab_element") { + + NodePath from_path = d["from_path"]; + NodePath to_path = get_path(); + if (from_path == to_path) { + return true; + } else if (get_tabs_rearrange_group() != -1) { + // drag and drop between other Tabs + Node *from_node = get_node(from_path); + Tabs *from_tabs = Object::cast_to<Tabs>(from_node); + if (from_tabs && from_tabs->get_tabs_rearrange_group() == get_tabs_rearrange_group()) { + return true; + } + } + } + return false; } void Tabs::drop_data(const Point2 &p_point, const Variant &p_data) { + if (!drag_to_rearrange_enabled) + return; + int hover_now = get_tab_idx_at_point(p_point); - ERR_FAIL_INDEX(hover_now, tabs.size()); - emit_signal("reposition_active_tab_request", hover_now); + Dictionary d = p_data; + if (!d.has("type")) + return; + + if (String(d["type"]) == "tab_element") { + + int tab_from_id = d["tab_element"]; + NodePath from_path = d["from_path"]; + NodePath to_path = get_path(); + if (from_path == to_path) { + if (hover_now < 0) + hover_now = get_tab_count() - 1; + move_tab(tab_from_id, hover_now); + emit_signal("reposition_active_tab_request", hover_now); + set_current_tab(hover_now); + } else if (get_tabs_rearrange_group() != -1) { + // drag and drop between Tabs + Node *from_node = get_node(from_path); + Tabs *from_tabs = Object::cast_to<Tabs>(from_node); + if (from_tabs && from_tabs->get_tabs_rearrange_group() == get_tabs_rearrange_group()) { + if (tab_from_id >= from_tabs->get_tab_count()) + return; + Tab moving_tab = from_tabs->tabs[tab_from_id]; + if (hover_now < 0) + hover_now = get_tab_count(); + tabs.insert(hover_now, moving_tab); + from_tabs->remove_tab(tab_from_id); + set_current_tab(hover_now); + emit_signal("tab_changed", hover_now); + _update_cache(); + } + } + } + update(); } int Tabs::get_tab_idx_at_point(const Point2 &p_point) const { @@ -816,6 +905,21 @@ bool Tabs::get_scrolling_enabled() const { return scrolling_enabled; } +void Tabs::set_drag_to_rearrange_enabled(bool p_enabled) { + drag_to_rearrange_enabled = p_enabled; +} + +bool Tabs::get_drag_to_rearrange_enabled() const { + return drag_to_rearrange_enabled; +} +void Tabs::set_tabs_rearrange_group(int p_group_id) { + tabs_rearrange_group = p_group_id; +} + +int Tabs::get_tabs_rearrange_group() const { + return tabs_rearrange_group; +} + void Tabs::_bind_methods() { ClassDB::bind_method(D_METHOD("_gui_input"), &Tabs::_gui_input); @@ -841,6 +945,10 @@ void Tabs::_bind_methods() { ClassDB::bind_method(D_METHOD("get_tab_close_display_policy"), &Tabs::get_tab_close_display_policy); ClassDB::bind_method(D_METHOD("set_scrolling_enabled", "enabled"), &Tabs::set_scrolling_enabled); ClassDB::bind_method(D_METHOD("get_scrolling_enabled"), &Tabs::get_scrolling_enabled); + ClassDB::bind_method(D_METHOD("set_drag_to_rearrange_enabled", "enabled"), &Tabs::set_drag_to_rearrange_enabled); + ClassDB::bind_method(D_METHOD("get_drag_to_rearrange_enabled"), &Tabs::get_drag_to_rearrange_enabled); + ClassDB::bind_method(D_METHOD("set_tabs_rearrange_group", "group_id"), &Tabs::set_tabs_rearrange_group); + ClassDB::bind_method(D_METHOD("get_tabs_rearrange_group"), &Tabs::get_tabs_rearrange_group); ADD_SIGNAL(MethodInfo("tab_changed", PropertyInfo(Variant::INT, "tab"))); ADD_SIGNAL(MethodInfo("right_button_pressed", PropertyInfo(Variant::INT, "tab"))); @@ -850,8 +958,10 @@ void Tabs::_bind_methods() { ADD_SIGNAL(MethodInfo("tab_clicked", PropertyInfo(Variant::INT, "tab"))); ADD_PROPERTY(PropertyInfo(Variant::INT, "current_tab", PROPERTY_HINT_RANGE, "-1,4096,1", PROPERTY_USAGE_EDITOR), "set_current_tab", "get_current_tab"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_tab_align", "get_tab_align"); ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "tab_close_display_policy", PROPERTY_HINT_ENUM, "Show Never,Show Active Only,Show Always"), "set_tab_close_display_policy", "get_tab_close_display_policy"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scrolling_enabled"), "set_scrolling_enabled", "get_scrolling_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_to_rearrange_enabled"), "set_drag_to_rearrange_enabled", "get_drag_to_rearrange_enabled"); BIND_ENUM_CONSTANT(ALIGN_LEFT); BIND_ENUM_CONSTANT(ALIGN_CENTER); @@ -880,4 +990,8 @@ Tabs::Tabs() { min_width = 0; scrolling_enabled = true; + buttons_visible = false; + hover = -1; + drag_to_rearrange_enabled = false; + tabs_rearrange_group = -1; } diff --git a/scene/gui/tabs.h b/scene/gui/tabs.h index ffc0944829..3b38e7f2cb 100644 --- a/scene/gui/tabs.h +++ b/scene/gui/tabs.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef TABS_H #define TABS_H @@ -89,6 +90,8 @@ private: int hover; // hovered tab int min_width; bool scrolling_enabled; + bool drag_to_rearrange_enabled; + int tabs_rearrange_group; int get_tab_width(int p_idx) const; void _ensure_no_over_offset(); @@ -142,6 +145,11 @@ public: void set_scrolling_enabled(bool p_enabled); bool get_scrolling_enabled() const; + void set_drag_to_rearrange_enabled(bool p_enabled); + bool get_drag_to_rearrange_enabled() const; + void set_tabs_rearrange_group(int p_group_id); + int get_tabs_rearrange_group() const; + void ensure_tab_visible(int p_idx); void set_min_width(int p_width); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 5fbc0c9064..4c9f515ced 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "text_edit.h" #include "message_queue.h" @@ -183,6 +184,7 @@ void TextEdit::Text::_update_line_cache(int p_line) const { cri.region = j; text[p_line].region_info[i] = cri; i += lr - 1; + break; } @@ -210,13 +212,14 @@ void TextEdit::Text::_update_line_cache(int p_line) const { cri.region = j; text[p_line].region_info[i] = cri; i += lr - 1; + break; } } } } -const Map<int, TextEdit::Text::ColorRegionInfo> &TextEdit::Text::get_color_region_info(int p_line) { +const Map<int, TextEdit::Text::ColorRegionInfo> &TextEdit::Text::get_color_region_info(int p_line) const { static Map<int, ColorRegionInfo> cri; ERR_FAIL_INDEX_V(p_line, text.size(), cri); @@ -454,7 +457,7 @@ void TextEdit::_update_selection_mode_word() { end += 1; } - // inital selection + // initial selection if (!selection.active) { select(row, beg, row, end); selection.selecting_column = beg; @@ -536,7 +539,7 @@ void TextEdit::_notification(int p_what) { draw_caret = false; update(); } break; - case NOTIFICATION_PHYSICS_PROCESS: { + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { if (scrolling && v_scroll->get_value() != target_v_scroll) { double target_y = target_v_scroll - v_scroll->get_value(); double dist = sqrt(target_y * target_y); @@ -545,17 +548,16 @@ void TextEdit::_notification(int p_what) { if (Math::abs(vel) >= dist) { v_scroll->set_value(target_v_scroll); scrolling = false; - set_physics_process(false); + set_physics_process_internal(false); } else { v_scroll->set_value(v_scroll->get_value() + vel); } } else { scrolling = false; - set_physics_process(false); + set_physics_process_internal(false); } } break; case NOTIFICATION_DRAW: { - if ((!has_focus() && !menu->has_focus()) || !window_has_focus) { draw_caret = false; } @@ -618,44 +620,10 @@ void TextEdit::_notification(int p_what) { Color color = cache.font_color; color.a *= readonly_alpha; - int in_region = -1; - if (syntax_coloring) { - if (cache.background_color.a > 0.01) { - VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(), get_size()), cache.background_color); } - //compute actual region to start (may be inside say, a comment). - //slow in very large documments :( but ok for source! - - for (int i = 0; i < cursor.line_ofs; i++) { - - const Map<int, Text::ColorRegionInfo> &cri_map = text.get_color_region_info(i); - - if (in_region >= 0 && color_regions[in_region].line_only) { - in_region = -1; //reset regions that end at end of line - } - - for (const Map<int, Text::ColorRegionInfo>::Element *E = cri_map.front(); E; E = E->next()) { - - const Text::ColorRegionInfo &cri = E->get(); - - if (in_region == -1) { - - if (!cri.end) { - - in_region = cri.region; - } - } else if (in_region == cri.region && !color_regions[cri.region].line_only) { //ignore otherwise - - if (cri.end || color_regions[cri.region].eq) { - - in_region = -1; - } - } - } - } } int brace_open_match_line = -1; @@ -775,7 +743,6 @@ void TextEdit::_notification(int p_what) { j--; } if (escaped) { - j--; cc = '\\'; continue; } @@ -804,7 +771,6 @@ void TextEdit::_notification(int p_what) { } } - int deregion = 0; //force it to clear inrgion Point2 cursor_pos; // get the highlighted words @@ -816,6 +782,7 @@ void TextEdit::_notification(int p_what) { int line = cursor.line_ofs - 1; // another row may be visible during smooth scrolling int draw_amount = visible_rows + (smooth_scroll_enabled ? 1 : 0); + FontDrawer drawer(cache.font, Color(1, 1, 1)); for (int i = 0; i < draw_amount; i++) { line++; @@ -848,19 +815,12 @@ void TextEdit::_notification(int p_what) { if (smooth_scroll_enabled) ofs_y -= ((v_scroll->get_value() - get_line_scroll_pos()) * get_row_height()); - bool prev_is_char = false; - bool prev_is_number = false; - bool in_keyword = false; bool underlined = false; - bool in_word = false; - bool in_function_name = false; - bool in_member_variable = false; - bool is_hex_notation = false; - Color keyword_color; // check if line contains highlighted word int highlighted_text_col = -1; int search_text_col = -1; + int highlighted_word_col = -1; if (!search_text.empty()) search_text_col = _get_column_pos_of_word(search_text, str, search_flags, 0); @@ -868,7 +828,11 @@ void TextEdit::_notification(int p_what) { if (highlighted_text.length() != 0 && highlighted_text != search_text) highlighted_text_col = _get_column_pos_of_word(highlighted_text, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, 0); - const Map<int, Text::ColorRegionInfo> &cri_map = text.get_color_region_info(line); + if (select_identifiers_enabled && highlighted_word.length() != 0) { + if (_is_char(highlighted_word[0])) { + highlighted_word_col = _get_column_pos_of_word(highlighted_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, 0); + } + } if (text.is_marked(line)) { @@ -882,14 +846,14 @@ void TextEdit::_notification(int p_what) { } // give visual indication of empty selected line - if (selection.active && line >= selection.from_line && line <= selection.to_line) { + if (selection.active && line >= selection.from_line && line <= selection.to_line && char_margin >= xmargin_beg) { int char_w = cache.font->get_char_size(' ').width; VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, char_w, get_row_height()), cache.selection_color); } } else { - // if it has text, then draw current line marker in the margin, as line number ect will draw over it, draw the rest of line marker later. + // if it has text, then draw current line marker in the margin, as line number etc will draw over it, draw the rest of line marker later. if (line == cursor.line && highlight_current_line) { - VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(0, ofs_y, xmargin_beg, get_row_height()), cache.current_line_color); + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(0, ofs_y, xmargin_beg + ofs_x, get_row_height()), cache.current_line_color); } } @@ -936,155 +900,28 @@ void TextEdit::_notification(int p_what) { cache.font->draw(ci, Point2(cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + ofs_x, ofs_y + cache.font->get_ascent()), fc, cache.line_number_color); } - //loop through charcters in one line - for (int j = 0; j < str.length(); j++) { - - //look for keyword - - if (deregion > 0) { - deregion--; - if (deregion == 0) - in_region = -1; - } - if (syntax_coloring && deregion == 0) { - - color = cache.font_color; //reset - color.a *= readonly_alpha; - //find keyword - bool is_char = _is_text_char(str[j]); - bool is_symbol = _is_symbol(str[j]); - bool is_number = _is_number(str[j]); - - if (j == 0 && in_region >= 0 && color_regions[in_region].line_only) { - in_region = -1; //reset regions that end at end of line - } - - // allow ABCDEF in hex notation - if (is_hex_notation && (_is_hex_symbol(str[j]) || is_number)) { - is_number = true; - } else { - is_hex_notation = false; - } - - // check for dot or underscore or 'x' for hex notation in floating point number - if ((str[j] == '.' || str[j] == 'x' || str[j] == '_') && !in_word && prev_is_number && !is_number) { - is_number = true; - is_symbol = false; - is_char = false; - - if (str[j] == 'x' && str[j - 1] == '0') { - is_hex_notation = true; - } - } - - if (!in_word && _is_char(str[j]) && !is_number) { - in_word = true; - } - if ((in_keyword || in_word) && !is_hex_notation) { - is_number = false; - } - - if (is_symbol && str[j] != '.' && in_word) { - in_word = false; - } - - if (is_symbol && cri_map.has(j)) { - - const Text::ColorRegionInfo &cri = cri_map[j]; - - if (in_region == -1) { - - if (!cri.end) { - - in_region = cri.region; - } - } else if (in_region == cri.region && !color_regions[cri.region].line_only) { //ignore otherwise - - if (cri.end || color_regions[cri.region].eq) { - - deregion = color_regions[cri.region].eq ? color_regions[cri.region].begin_key.length() : color_regions[cri.region].end_key.length(); - } - } - } - - if (!is_char) { - in_keyword = false; - underlined = false; - } - - if (in_region == -1 && !in_keyword && is_char && !prev_is_char) { - - int to = j; - while (to < str.length() && _is_text_char(str[to])) - to++; - - uint32_t hash = String::hash(&str[j], to - j); - StrRange range(&str[j], to - j); - - const Color *col = keywords.custom_getptr(range, hash); - - if (col) { - - in_keyword = true; - keyword_color = *col; - } - - if (select_identifiers_enabled && highlighted_word != String()) { - if (highlighted_word == range) { - underlined = true; - } - } - } - - if (!in_function_name && in_word && !in_keyword) { - - int k = j; - while (k < str.length() && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') { - k++; - } - - // check for space between name and bracket - while (k < str.length() && (str[k] == '\t' || str[k] == ' ')) { - k++; - } - - if (str[k] == '(') { - in_function_name = true; - } - } + //loop through characters in one line + Map<int, HighlighterInfo> color_map; + if (syntax_coloring) { + color_map = _get_line_syntax_highlighting(line); + } - if (!in_function_name && !in_member_variable && !in_keyword && !is_number && in_word) { - int k = j; - while (k > 0 && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') { - k--; - } + // ensure we at least use the font color + Color current_color = cache.font_color; + if (readonly) { + current_color.a *= readonly_alpha; + } + for (int j = 0; j < str.length(); j++) { - if (str[k] == '.') { - in_member_variable = true; + if (syntax_coloring) { + if (color_map.has(j)) { + current_color = color_map[j].color; + if (readonly) { + current_color.a *= readonly_alpha; } } - - if (is_symbol) { - in_function_name = false; - in_member_variable = false; - } - - if (in_region >= 0) - color = color_regions[in_region].color; - else if (in_keyword) - color = keyword_color; - else if (in_member_variable) - color = cache.member_variable_color; - else if (in_function_name) - color = cache.function_color; - else if (is_symbol) - color = cache.symbol_color; - else if (is_number) - color = cache.number_color; - - prev_is_char = is_char; - prev_is_number = is_number; + color = current_color; } int char_w; @@ -1103,6 +940,19 @@ void TextEdit::_notification(int p_what) { if ((char_ofs + char_margin) < xmargin_beg) { char_ofs += char_w; + + // line highlighting handle horizontal clipping + if (line == cursor.line && highlight_current_line) { + + if (j == str.length() - 1) { + // end of line when last char is skipped + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, xmargin_end - (xmargin_beg + ofs_x), get_row_height()), cache.current_line_color); + + } else if ((char_ofs + char_margin) > xmargin_beg) { + // char next to margin is skipped + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, (char_ofs + char_margin) - xmargin_beg, get_row_height()), cache.current_line_color); + } + } continue; } @@ -1178,13 +1028,20 @@ void TextEdit::_notification(int p_what) { } } + if (highlighted_word_col != -1) { + if (j > highlighted_word_col + highlighted_word.length()) { + highlighted_word_col = _get_column_pos_of_word(highlighted_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, j); + } + underlined = (j >= highlighted_word_col && j < highlighted_word_col + highlighted_word.length()); + } + if (brace_matching_enabled) { if ((brace_open_match_line == line && brace_open_match_column == j) || (cursor.column == j && cursor.line == line && (brace_open_matching || brace_open_mismatch))) { if (brace_open_mismatch) color = cache.brace_mismatch_color; - cache.font->draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, ofs_y + ascent), '_', str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color); + drawer.draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, ofs_y + ascent), '_', str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color); } if ( @@ -1193,7 +1050,7 @@ void TextEdit::_notification(int p_what) { if (brace_close_mismatch) color = cache.brace_mismatch_color; - cache.font->draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, ofs_y + ascent), '_', str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color); + drawer.draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, ofs_y + ascent), '_', str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color); } } @@ -1226,7 +1083,7 @@ void TextEdit::_notification(int p_what) { VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(char_ofs + char_margin, ofs_y + get_row_height()), Size2(im_char_width, 1)), color); } - cache.font->draw_char(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + ascent), cchar, next, color); + drawer.draw_char(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + ascent), cchar, next, color); char_ofs += im_char_width; ofs++; @@ -1253,7 +1110,7 @@ void TextEdit::_notification(int p_what) { } if (str[j] >= 32) { - int w = cache.font->draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, ofs_y + ascent), str[j], str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color); + int w = drawer.draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, ofs_y + ascent), str[j], str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color); if (underlined) { draw_rect(Rect2(char_ofs + char_margin + ofs_x, ofs_y + ascent + 2, w, 1), in_selection && override_selected_font_color ? cache.font_selected_color : color); } @@ -1302,7 +1159,7 @@ void TextEdit::_notification(int p_what) { VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(char_ofs + char_margin, ofs_y + get_row_height()), Size2(im_char_width, 1)), color); } - cache.font->draw_char(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + ascent), cchar, next, color); + drawer.draw_char(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + ascent), cchar, next, color); char_ofs += im_char_width; ofs++; @@ -1484,6 +1341,7 @@ void TextEdit::_notification(int p_what) { OS::get_singleton()->set_ime_position(get_global_position() + cursor_pos + Point2(0, get_row_height())); OS::get_singleton()->set_ime_intermediate_text_callback(_ime_text_callback, this); } + } break; case NOTIFICATION_FOCUS_ENTER: { @@ -1498,9 +1356,8 @@ void TextEdit::_notification(int p_what) { if (OS::get_singleton()->has_virtual_keyboard()) OS::get_singleton()->show_virtual_keyboard(get_text(), get_global_rect()); if (raised_from_completion) { - VisualServer::get_singleton()->canvas_item_set_z(get_canvas_item(), 1); + VisualServer::get_singleton()->canvas_item_set_z_index(get_canvas_item(), 1); } - } break; case NOTIFICATION_FOCUS_EXIT: { @@ -1512,7 +1369,7 @@ void TextEdit::_notification(int p_what) { if (OS::get_singleton()->has_virtual_keyboard()) OS::get_singleton()->hide_virtual_keyboard(); if (raised_from_completion) { - VisualServer::get_singleton()->canvas_item_set_z(get_canvas_item(), 0); + VisualServer::get_singleton()->canvas_item_set_z_index(get_canvas_item(), 0); } } break; } @@ -1747,14 +1604,15 @@ void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) co float rows = p_mouse.y; rows -= cache.style_normal->get_margin(MARGIN_TOP); + rows += (CLAMP(v_scroll->get_value() - get_line_scroll_pos(true), 0, 1) * get_row_height()); rows /= get_row_height(); - int lsp = get_line_scroll_pos(true); - int row = cursor.line_ofs + (rows + (round(v_scroll->get_value()) - lsp)); + int first_vis_line = CLAMP(cursor.line_ofs, 0, text.size() - 1); + int row = first_vis_line + Math::floor(rows); if (is_hiding_enabled()) { // row will be offset by the hidden rows - int f_ofs = num_lines_from(CLAMP(cursor.line_ofs, 0, text.size() - 1), MIN(rows + 1, text.size() - cursor.line_ofs)) - 1; - row = cursor.line_ofs + (f_ofs + (round(v_scroll->get_value()) - lsp)); + int f_ofs = num_lines_from(first_vis_line, rows + 1) - 1; + row = first_vis_line + f_ofs; row = CLAMP(row, 0, text.size() - num_lines_from(text.size() - 1, -1)); } @@ -1822,10 +1680,18 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { if (mb->is_pressed()) { if (mb->get_button_index() == BUTTON_WHEEL_UP && !mb->get_command()) { - _scroll_up(3 * mb->get_factor()); + if (mb->get_shift()) { + h_scroll->set_value(h_scroll->get_value() - (100 * mb->get_factor())); + } else { + _scroll_up(3 * mb->get_factor()); + } } if (mb->get_button_index() == BUTTON_WHEEL_DOWN && !mb->get_command()) { - _scroll_down(3 * mb->get_factor()); + if (mb->get_shift()) { + h_scroll->set_value(h_scroll->get_value() + (100 * mb->get_factor())); + } else { + _scroll_down(3 * mb->get_factor()); + } } if (mb->get_button_index() == BUTTON_WHEEL_LEFT) { h_scroll->set_value(h_scroll->get_value() - (100 * mb->get_factor())); @@ -2104,9 +1970,12 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { if (completion_index > 0) { completion_index--; - completion_current = completion_options[completion_index]; - update(); + } else { + completion_index = completion_options.size() - 1; } + completion_current = completion_options[completion_index]; + update(); + accept_event(); return; } @@ -2115,9 +1984,12 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { if (completion_index < completion_options.size() - 1) { completion_index++; - completion_current = completion_options[completion_index]; - update(); + } else { + completion_index = 0; } + completion_current = completion_options[completion_index]; + update(); + accept_event(); return; } @@ -2539,6 +2411,12 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { cursor_set_line(line); cursor_set_column(column); +#ifdef APPLE_STYLE_KEYS + } else if (k->get_command()) { + int cursor_current_column = cursor.column; + cursor.column = 0; + _remove_text(cursor.line, 0, cursor.line, cursor_current_column); +#endif } else { if (cursor.line > 0 && is_line_hidden(cursor.line - 1)) unfold_line(cursor.line - 1); @@ -2813,7 +2691,11 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { next_line = line; next_column = column; - +#ifdef APPLE_STYLE_KEYS + } else if (k->get_command()) { + next_column = curline_len; + next_line = cursor.line; +#endif } else { next_column = cursor.column < curline_len ? (cursor.column + 1) : 0; } @@ -2962,13 +2844,64 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { } break; case KEY_A: { +#ifndef APPLE_STYLE_KEYS if (!k->get_command() || k->get_shift() || k->get_alt()) { scancode_handled = false; break; } - select_all(); +#else + if (k->get_alt()) { + scancode_handled = false; + break; + } + if (!k->get_shift() && k->get_command()) + select_all(); + else if (k->get_control()) { + if (k->get_shift()) + _pre_shift_selection(); + int current_line_whitespace_len = 0; + while (current_line_whitespace_len < text[cursor.line].length()) { + CharType c = text[cursor.line][current_line_whitespace_len]; + if (c != '\t' && c != ' ') + break; + current_line_whitespace_len++; + } + + if (cursor_get_column() == current_line_whitespace_len) + cursor_set_column(0); + else + cursor_set_column(current_line_whitespace_len); + + if (k->get_shift()) + _post_shift_selection(); + else if (k->get_command() || k->get_control()) + deselect(); + } + } break; + case KEY_E: { + + if (!k->get_control() || k->get_command() || k->get_alt()) { + scancode_handled = false; + break; + } + + if (k->get_shift()) + _pre_shift_selection(); + + if (k->get_command()) + cursor_set_line(text.size() - 1, true, false); + cursor_set_column(text[cursor.line].length()); + + if (k->get_shift()) + _post_shift_selection(); + else if (k->get_command() || k->get_control()) + deselect(); + + _cancel_completion(); + completion_hint = ""; +#endif } break; case KEY_X: { if (readonly) { @@ -3093,7 +3026,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { return; } - if (!scancode_handled && !k->get_command()) { //for german kbds + if (!scancode_handled && !k->get_command()) { //for German kbds if (k->get_unicode() >= 32) { @@ -3156,7 +3089,7 @@ void TextEdit::_scroll_up(real_t p_delta) { v_scroll->set_value(target_v_scroll); } else { scrolling = true; - set_physics_process(true); + set_physics_process_internal(true); } } else { v_scroll->set_value(target_v_scroll); @@ -3189,7 +3122,7 @@ void TextEdit::_scroll_down(real_t p_delta) { v_scroll->set_value(target_v_scroll); } else { scrolling = true; - set_physics_process(true); + set_physics_process_internal(true); } } else { v_scroll->set_value(target_v_scroll); @@ -3313,6 +3246,7 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i MessageQueue::get_singleton()->push_call(this, "_text_changed_emit"); text_changed_dirty = true; } + _line_edited_from(p_line); } String TextEdit::_base_get_text(int p_from_line, int p_from_column, int p_to_line, int p_to_column) const { @@ -3363,6 +3297,7 @@ void TextEdit::_base_remove_text(int p_from_line, int p_from_column, int p_to_li MessageQueue::get_singleton()->push_call(this, "_text_changed_emit"); text_changed_dirty = true; } + _line_edited_from(p_from_line); } void TextEdit::_insert_text(int p_line, int p_char, const String &p_text, int *r_end_line, int *r_end_char) { @@ -3485,6 +3420,13 @@ void TextEdit::_insert_text_at_cursor(const String &p_text) { update(); } +void TextEdit::_line_edited_from(int p_line) { + int cache_size = color_region_cache.size(); + for (int i = p_line; i < cache_size; i++) { + color_region_cache.erase(i); + } +} + int TextEdit::get_char_count() { int totalsize = 0; @@ -3632,9 +3574,10 @@ void TextEdit::center_viewport_to_cursor() { int visible_rows = get_visible_rows(); if (h_scroll->is_visible_in_tree()) visible_rows -= ((h_scroll->get_combined_minimum_size().height - 1) / get_row_height()); - - int max_ofs = text.size() - (scroll_past_end_of_file_enabled ? 1 : num_lines_from(text.size() - 1, -visible_rows)); - cursor.line_ofs = CLAMP(cursor.line - num_lines_from(cursor.line - visible_rows / 2, -visible_rows / 2), 0, max_ofs); + if (text.size() >= visible_rows) { + int max_ofs = text.size() - (scroll_past_end_of_file_enabled ? 1 : MAX(num_lines_from(text.size() - 1, -visible_rows), 0)); + cursor.line_ofs = CLAMP(cursor.line - num_lines_from(MAX(cursor.line - visible_rows / 2, 0), -visible_rows / 2), 0, max_ofs); + } int cursor_x = get_column_x_offset(cursor.column, text[cursor.line]); if (cursor_x > (cursor.x_ofs + visible_width)) @@ -4037,11 +3980,21 @@ void TextEdit::set_wrap(bool p_wrap) { wrap = p_wrap; } +bool TextEdit::is_wrapping() const { + + return wrap; +} + void TextEdit::set_max_chars(int p_max_chars) { max_chars = p_max_chars; } +int TextEdit::get_max_chars() const { + + return max_chars; +} + void TextEdit::_reset_caret_blink_timer() { if (caret_blink_enabled) { caret_blink_timer->stop(); @@ -4096,12 +4049,88 @@ void TextEdit::_update_caches() { cache.can_fold_icon = get_icon("GuiTreeArrowDown", "EditorIcons"); cache.folded_eol_icon = get_icon("GuiEllipsis", "EditorIcons"); text.set_font(cache.font); + + if (syntax_highlighter) { + syntax_highlighter->_update_cache(); + } +} + +SyntaxHighlighter *TextEdit::_get_syntax_highlighting() { + return syntax_highlighter; +} + +void TextEdit::_set_syntax_highlighting(SyntaxHighlighter *p_syntax_highlighter) { + syntax_highlighter = p_syntax_highlighter; + if (syntax_highlighter) { + syntax_highlighter->set_text_editor(this); + syntax_highlighter->_update_cache(); + } + update(); +} + +int TextEdit::_is_line_in_region(int p_line) { + + // do we have in cache? + if (color_region_cache.has(p_line)) { + return color_region_cache[p_line]; + } + + // if not find the closest line we have + int previous_line = p_line - 1; + for (previous_line; previous_line > -1; previous_line--) { + if (color_region_cache.has(p_line)) { + break; + } + } + + // calculate up to line we need and update the cache along the way. + int in_region = color_region_cache[previous_line]; + if (previous_line == -1) { + in_region = -1; + } + for (int i = previous_line; i < p_line; i++) { + const Map<int, Text::ColorRegionInfo> &cri_map = _get_line_color_region_info(i); + for (const Map<int, Text::ColorRegionInfo>::Element *E = cri_map.front(); E; E = E->next()) { + const Text::ColorRegionInfo &cri = E->get(); + if (in_region == -1) { + if (!cri.end) { + in_region = cri.region; + } + } else if (in_region == cri.region && !_get_color_region(cri.region).line_only) { + if (cri.end || _get_color_region(cri.region).eq) { + in_region = -1; + } + } + } + + if (in_region >= 0 && _get_color_region(in_region).line_only) { + in_region = -1; + } + + color_region_cache[i + 1] = in_region; + } + return in_region; +} + +TextEdit::ColorRegion TextEdit::_get_color_region(int p_region) const { + if (p_region < 0 || p_region >= color_regions.size()) { + return ColorRegion(); + } + return color_regions[p_region]; +} + +Map<int, TextEdit::Text::ColorRegionInfo> TextEdit::_get_line_color_region_info(int p_line) const { + if (p_line < 0 || p_line > text.size() - 1) { + return Map<int, Text::ColorRegionInfo>(); + } + return text.get_color_region_info(p_line); } void TextEdit::clear_colors() { keywords.clear(); color_regions.clear(); + color_region_cache.clear(); text.clear_caches(); } @@ -4111,6 +4140,14 @@ void TextEdit::add_keyword_color(const String &p_keyword, const Color &p_color) update(); } +bool TextEdit::has_keyword_color(String p_keyword) const { + return keywords.has(p_keyword); +} + +Color TextEdit::get_keyword_color(String p_keyword) const { + return keywords[p_keyword]; +} + void TextEdit::add_color_region(const String &p_begin_key, const String &p_end_key, const Color &p_color, bool p_line_only) { color_regions.push_back(ColorRegion(p_begin_key, p_end_key, p_color, p_line_only)); @@ -4118,6 +4155,24 @@ void TextEdit::add_color_region(const String &p_begin_key, const String &p_end_k update(); } +void TextEdit::add_member_keyword(const String &p_keyword, const Color &p_color) { + member_keywords[p_keyword] = p_color; + update(); +} + +bool TextEdit::has_member_color(String p_member) const { + return member_keywords.has(p_member); +} + +Color TextEdit::get_member_color(String p_member) const { + return member_keywords[p_member]; +} + +void TextEdit::clear_member_keywords() { + member_keywords.clear(); + update(); +} + void TextEdit::set_syntax_coloring(bool p_enabled) { syntax_coloring = p_enabled; @@ -4412,7 +4467,7 @@ bool TextEdit::search(const String &p_key, uint32_t p_search_flags, int p_from_l ERR_FAIL_INDEX_V(p_from_line, text.size(), false); ERR_FAIL_INDEX_V(p_from_column, text[p_from_line].length() + 1, false); - //search through the whole documment, but start by current line + //search through the whole document, but start by current line int line = p_from_line; int pos = -1; @@ -4457,31 +4512,44 @@ bool TextEdit::search(const String &p_key, uint32_t p_search_flags, int p_from_l int pos_from = 0; int last_pos = -1; - while ((last_pos = (p_search_flags & SEARCH_MATCH_CASE) ? text_line.find(p_key, pos_from) : text_line.findn(p_key, pos_from)) != -1) { - if (p_search_flags & SEARCH_BACKWARDS) { + while (true) { - if (last_pos > from_column) - break; - pos = last_pos; + while ((last_pos = (p_search_flags & SEARCH_MATCH_CASE) ? text_line.find(p_key, pos_from) : text_line.findn(p_key, pos_from)) != -1) { - } else { + if (p_search_flags & SEARCH_BACKWARDS) { - if (last_pos >= from_column) { + if (last_pos > from_column) + break; pos = last_pos; - break; + + } else { + + if (last_pos >= from_column) { + pos = last_pos; + break; + } } + + pos_from = last_pos + p_key.length(); } - pos_from = last_pos + p_key.length(); - } + bool is_match = true; + + if (pos != -1 && (p_search_flags & SEARCH_WHOLE_WORDS)) { + //validate for whole words + if (pos > 0 && _is_text_char(text_line[pos - 1])) + is_match = false; + else if (pos + p_key.length() < text_line.length() && _is_text_char(text_line[pos + p_key.length()])) + is_match = false; + } + + if (is_match || last_pos == -1 || pos == -1) { + break; + } - if (pos != -1 && (p_search_flags & SEARCH_WHOLE_WORDS)) { - //validate for whole words - if (pos > 0 && _is_text_char(text_line[pos - 1])) - pos = -1; - else if (_is_text_char(text_line[pos + p_key.length()])) - pos = -1; + pos_from = pos + 1; + pos = -1; } if (pos != -1) @@ -4638,8 +4706,6 @@ int TextEdit::get_indent_level(int p_line) const { tab_count++; } else if (text[p_line][i] == ' ') { whitespace_count++; - } else if (text[p_line][i] == '#') { - break; } else { break; } @@ -4647,6 +4713,31 @@ int TextEdit::get_indent_level(int p_line) const { return tab_count + whitespace_count / indent_size; } +bool TextEdit::is_line_comment(int p_line) const { + + // checks to see if this line is the start of a comment + ERR_FAIL_INDEX_V(p_line, text.size(), false); + + const Map<int, Text::ColorRegionInfo> &cri_map = text.get_color_region_info(p_line); + + int line_length = text[p_line].size(); + for (int i = 0; i < line_length - 1; i++) { + if (_is_symbol(text[p_line][i]) && cri_map.has(i)) { + const Text::ColorRegionInfo &cri = cri_map[i]; + if (color_regions[cri.region].begin_key == "#" || color_regions[cri.region].begin_key == "//") { + return true; + } else { + return false; + } + } else if (_is_whitespace(text[p_line][i])) { + continue; + } else { + break; + } + } + return false; +} + bool TextEdit::can_fold(int p_line) const { ERR_FAIL_INDEX_V(p_line, text.size(), false); @@ -4660,6 +4751,8 @@ bool TextEdit::can_fold(int p_line) const { return false; if (is_line_hidden(p_line)) return false; + if (is_line_comment(p_line)) + return false; int start_indent = get_indent_level(p_line); @@ -4667,10 +4760,13 @@ bool TextEdit::can_fold(int p_line) const { if (text[i].size() == 0) continue; int next_indent = get_indent_level(i); - if (next_indent > start_indent) + if (is_line_comment(i)) { + continue; + } else if (next_indent > start_indent) { return true; - else + } else { return false; + } } return false; @@ -4699,7 +4795,9 @@ void TextEdit::fold_line(int p_line) { int last_line = start_indent; for (int i = p_line + 1; i < text.size(); i++) { if (text[i].strip_edges().size() != 0) { - if (get_indent_level(i) > start_indent) { + if (is_line_comment(i)) { + continue; + } else if (get_indent_level(i) > start_indent) { last_line = i; } else { break; @@ -4820,6 +4918,8 @@ void TextEdit::undo() { else undo_stack_pos = undo_stack_pos->prev(); + deselect(); + TextOperation op = undo_stack_pos->get(); _do_text_op(op, true); current_op.version = op.prev_version; @@ -4854,6 +4954,8 @@ void TextEdit::redo() { if (undo_stack_pos == NULL) return; //nothing to do. + deselect(); + TextOperation op = undo_stack_pos->get(); _do_text_op(op, false); current_op.version = op.version; @@ -4938,6 +5040,11 @@ void TextEdit::set_indent_size(const int p_size) { update(); } +int TextEdit::get_indent_size() { + + return indent_size; +} + void TextEdit::set_draw_tabs(bool p_draw) { draw_tabs = p_draw; @@ -5054,7 +5161,7 @@ void TextEdit::_confirm_completion() { void TextEdit::_cancel_code_hint() { - VisualServer::get_singleton()->canvas_item_set_z(get_canvas_item(), 0); + VisualServer::get_singleton()->canvas_item_set_z_index(get_canvas_item(), 0); raised_from_completion = false; completion_hint = ""; update(); @@ -5062,7 +5169,7 @@ void TextEdit::_cancel_code_hint() { void TextEdit::_cancel_completion() { - VisualServer::get_singleton()->canvas_item_set_z(get_canvas_item(), 0); + VisualServer::get_singleton()->canvas_item_set_z_index(get_canvas_item(), 0); raised_from_completion = false; if (!completion_active) return; @@ -5127,7 +5234,7 @@ void TextEdit::_update_completion_candidates() { } else { - while (cofs > 0 && l[cofs - 1] > 32 && _is_completable(l[cofs - 1])) { + while (cofs > 0 && l[cofs - 1] > 32 && (l[cofs - 1] == '/' || _is_completable(l[cofs - 1]))) { s = String::chr(l[cofs - 1]) + s; if (l[cofs - 1] == '\'' || l[cofs - 1] == '"' || l[cofs - 1] == '$') break; @@ -5237,7 +5344,7 @@ void TextEdit::query_code_comple() { void TextEdit::set_code_hint(const String &p_hint) { - VisualServer::get_singleton()->canvas_item_set_z(get_canvas_item(), 1); + VisualServer::get_singleton()->canvas_item_set_z_index(get_canvas_item(), 1); raised_from_completion = true; completion_hint = p_hint; completion_hint_offset = -0xFFFF; @@ -5246,7 +5353,7 @@ void TextEdit::set_code_hint(const String &p_hint) { void TextEdit::code_complete(const Vector<String> &p_strings, bool p_forced) { - VisualServer::get_singleton()->canvas_item_set_z(get_canvas_item(), 1); + VisualServer::get_singleton()->canvas_item_set_z_index(get_canvas_item(), 1); raised_from_completion = true; completion_strings = p_strings; completion_active = true; @@ -5520,7 +5627,9 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("is_readonly"), &TextEdit::is_readonly); ClassDB::bind_method(D_METHOD("set_wrap", "enable"), &TextEdit::set_wrap); - ClassDB::bind_method(D_METHOD("set_max_chars", "amount"), &TextEdit::set_max_chars); + ClassDB::bind_method(D_METHOD("is_wrapping"), &TextEdit::is_wrapping); + // ClassDB::bind_method(D_METHOD("set_max_chars", "amount"), &TextEdit::set_max_chars); + // ClassDB::bind_method(D_METHOD("get_max_char"), &TextEdit::get_max_chars); ClassDB::bind_method(D_METHOD("set_context_menu_enabled", "enable"), &TextEdit::set_context_menu_enabled); ClassDB::bind_method(D_METHOD("is_context_menu_enabled"), &TextEdit::is_context_menu_enabled); @@ -5578,6 +5687,8 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("get_v_scroll_speed"), &TextEdit::get_v_scroll_speed); ClassDB::bind_method(D_METHOD("add_keyword_color", "keyword", "color"), &TextEdit::add_keyword_color); + ClassDB::bind_method(D_METHOD("has_keyword_color", "keyword"), &TextEdit::has_keyword_color); + ClassDB::bind_method(D_METHOD("get_keyword_color", "keyword"), &TextEdit::get_keyword_color); ClassDB::bind_method(D_METHOD("add_color_region", "begin_key", "end_key", "color", "line_only"), &TextEdit::add_color_region, DEFVAL(false)); ClassDB::bind_method(D_METHOD("clear_colors"), &TextEdit::clear_colors); ClassDB::bind_method(D_METHOD("menu_option", "option"), &TextEdit::menu_option); @@ -5594,11 +5705,13 @@ void TextEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_scrolling"), "set_smooth_scroll_enable", "is_smooth_scroll_enabled"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "v_scroll_speed"), "set_v_scroll_speed", "get_v_scroll_speed"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hiding_enabled"), "set_hiding_enabled", "is_hiding_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "wrap_lines"), "set_wrap", "is_wrapping"); + // ADD_PROPERTY(PropertyInfo(Variant::BOOL, "max_chars"), "set_max_chars", "get_max_chars"); ADD_GROUP("Caret", "caret_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_block_mode"), "cursor_set_block_mode", "cursor_is_block_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_blink"), "cursor_set_blink_enabled", "cursor_get_blink_enabled"); - ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.1"), "cursor_set_blink_speed", "cursor_get_blink_speed"); + ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.01"), "cursor_set_blink_speed", "cursor_get_blink_speed"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_moving_by_right_click"), "set_right_click_moves_caret", "is_right_click_moving_caret"); ADD_SIGNAL(MethodInfo("cursor_changed")); @@ -5629,6 +5742,7 @@ TextEdit::TextEdit() { clear(); wrap = false; set_focus_mode(FOCUS_ALL); + syntax_highlighter = NULL; _update_caches(); cache.size = Size2(1, 1); cache.row_height = 1; @@ -5642,7 +5756,7 @@ TextEdit::TextEdit() { indent_size = 4; text.set_indent_size(indent_size); text.clear(); - //text.insert(1,"Mongolia.."); + //text.insert(1,"Mongolia..."); //text.insert(2,"PAIS GENEROSO!!"); text.set_color_regions(&color_regions); @@ -5732,16 +5846,213 @@ TextEdit::TextEdit() { context_menu_enabled = true; menu = memnew(PopupMenu); add_child(menu); - menu->add_item(TTR("Cut"), MENU_CUT, KEY_MASK_CMD | KEY_X); - menu->add_item(TTR("Copy"), MENU_COPY, KEY_MASK_CMD | KEY_C); - menu->add_item(TTR("Paste"), MENU_PASTE, KEY_MASK_CMD | KEY_V); + menu->add_item(RTR("Cut"), MENU_CUT, KEY_MASK_CMD | KEY_X); + menu->add_item(RTR("Copy"), MENU_COPY, KEY_MASK_CMD | KEY_C); + menu->add_item(RTR("Paste"), MENU_PASTE, KEY_MASK_CMD | KEY_V); menu->add_separator(); - menu->add_item(TTR("Select All"), MENU_SELECT_ALL, KEY_MASK_CMD | KEY_A); - menu->add_item(TTR("Clear"), MENU_CLEAR); + menu->add_item(RTR("Select All"), MENU_SELECT_ALL, KEY_MASK_CMD | KEY_A); + menu->add_item(RTR("Clear"), MENU_CLEAR); menu->add_separator(); - menu->add_item(TTR("Undo"), MENU_UNDO, KEY_MASK_CMD | KEY_Z); + menu->add_item(RTR("Undo"), MENU_UNDO, KEY_MASK_CMD | KEY_Z); menu->connect("id_pressed", this, "menu_option"); } TextEdit::~TextEdit() { } + +/////////////////////////////////////////////////////////////////////////////// + +Map<int, TextEdit::HighlighterInfo> TextEdit::_get_line_syntax_highlighting(int p_line) { + if (syntax_highlighter != NULL) { + return syntax_highlighter->_get_line_syntax_highlighting(p_line); + } + + Map<int, HighlighterInfo> color_map; + + bool prev_is_char = false; + bool prev_is_number = false; + bool in_keyword = false; + bool in_word = false; + bool in_function_name = false; + bool in_member_variable = false; + bool is_hex_notation = false; + Color keyword_color; + Color color; + + int in_region = _is_line_in_region(p_line); + int deregion = 0; + + const Map<int, TextEdit::Text::ColorRegionInfo> cri_map = text.get_color_region_info(p_line); + const String &str = text[p_line]; + Color prev_color; + for (int j = 0; j < str.length(); j++) { + HighlighterInfo highlighter_info; + + if (deregion > 0) { + deregion--; + if (deregion == 0) { + in_region = -1; + } + } + + if (deregion != 0) { + if (color != prev_color) { + prev_color = color; + highlighter_info.color = color; + color_map[j] = highlighter_info; + } + continue; + } + + color = cache.font_color; + + bool is_char = _is_text_char(str[j]); + bool is_symbol = _is_symbol(str[j]); + bool is_number = _is_number(str[j]); + + // allow ABCDEF in hex notation + if (is_hex_notation && (_is_hex_symbol(str[j]) || is_number)) { + is_number = true; + } else { + is_hex_notation = false; + } + + // check for dot or underscore or 'x' for hex notation in floating point number + if ((str[j] == '.' || str[j] == 'x' || str[j] == '_') && !in_word && prev_is_number && !is_number) { + is_number = true; + is_symbol = false; + is_char = false; + + if (str[j] == 'x' && str[j - 1] == '0') { + is_hex_notation = true; + } + } + + if (!in_word && _is_char(str[j]) && !is_number) { + in_word = true; + } + + if ((in_keyword || in_word) && !is_hex_notation) { + is_number = false; + } + + if (is_symbol && str[j] != '.' && in_word) { + in_word = false; + } + + if (is_symbol && cri_map.has(j)) { + const TextEdit::Text::ColorRegionInfo &cri = cri_map[j]; + + if (in_region == -1) { + if (!cri.end) { + in_region = cri.region; + } + } else if (in_region == cri.region && !color_regions[cri.region].line_only) { //ignore otherwise + if (cri.end || color_regions[cri.region].eq) { + deregion = color_regions[cri.region].eq ? color_regions[cri.region].begin_key.length() : color_regions[cri.region].end_key.length(); + } + } + } + + if (!is_char) { + in_keyword = false; + } + + if (in_region == -1 && !in_keyword && is_char && !prev_is_char) { + + int to = j; + while (to < str.length() && _is_text_char(str[to])) + to++; + + uint32_t hash = String::hash(&str[j], to - j); + StrRange range(&str[j], to - j); + + const Color *col = keywords.custom_getptr(range, hash); + + if (!col) { + col = member_keywords.custom_getptr(range, hash); + + if (col) { + for (int k = j - 1; k >= 0; k--) { + if (str[k] == '.') { + col = NULL; //member indexing not allowed + break; + } else if (str[k] > 32) { + break; + } + } + } + } + + if (col) { + in_keyword = true; + keyword_color = *col; + } + } + + if (!in_function_name && in_word && !in_keyword) { + + int k = j; + while (k < str.length() && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') { + k++; + } + + // check for space between name and bracket + while (k < str.length() && (str[k] == '\t' || str[k] == ' ')) { + k++; + } + + if (str[k] == '(') { + in_function_name = true; + } + } + + if (!in_function_name && !in_member_variable && !in_keyword && !is_number && in_word) { + int k = j; + while (k > 0 && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') { + k--; + } + + if (str[k] == '.') { + in_member_variable = true; + } + } + + if (is_symbol) { + in_function_name = false; + in_member_variable = false; + } + + if (in_region >= 0) + color = color_regions[in_region].color; + else if (in_keyword) + color = keyword_color; + else if (in_member_variable) + color = cache.member_variable_color; + else if (in_function_name) + color = cache.function_color; + else if (is_symbol) + color = cache.symbol_color; + else if (is_number) + color = cache.number_color; + + prev_is_char = is_char; + prev_is_number = is_number; + + if (color != prev_color) { + prev_color = color; + highlighter_info.color = color; + color_map[j] = highlighter_info; + } + } + + return color_map; +} + +void SyntaxHighlighter::set_text_editor(TextEdit *p_text_editor) { + text_editor = p_text_editor; +} + +TextEdit *SyntaxHighlighter::get_text_editor() { + return text_editor; +} diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index ccd7ba8278..60c6ab4929 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef TEXT_EDIT_H #define TEXT_EDIT_H @@ -35,10 +36,82 @@ #include "scene/gui/scroll_bar.h" #include "scene/main/timer.h" +class SyntaxHighlighter; + class TextEdit : public Control { - GDCLASS(TextEdit, Control); + GDCLASS(TextEdit, Control) + +public: + struct HighlighterInfo { + Color color; + }; + + struct ColorRegion { + + Color color; + String begin_key; + String end_key; + bool line_only; + bool eq; + ColorRegion(const String &p_begin_key = "", const String &p_end_key = "", const Color &p_color = Color(), bool p_line_only = false) { + begin_key = p_begin_key; + end_key = p_end_key; + color = p_color; + line_only = p_line_only || p_end_key == ""; + eq = begin_key == end_key; + } + }; + + class Text { + public: + struct ColorRegionInfo { + + int region; + bool end; + }; + + struct Line { + int width_cache : 24; + bool marked : 1; + bool breakpoint : 1; + bool hidden : 1; + Map<int, ColorRegionInfo> region_info; + String data; + }; + + private: + const Vector<ColorRegion> *color_regions; + mutable Vector<Line> text; + Ref<Font> font; + int indent_size; + + void _update_line_cache(int p_line) const; + + public: + void set_indent_size(int p_indent_size); + void set_font(const Ref<Font> &p_font); + void set_color_regions(const Vector<ColorRegion> *p_regions) { color_regions = p_regions; } + int get_line_width(int p_line) const; + int get_max_width(bool p_exclude_hidden = false) const; + const Map<int, ColorRegionInfo> &get_color_region_info(int p_line) const; + void set(int p_line, const String &p_text); + void set_marked(int p_line, bool p_marked) { text[p_line].marked = p_marked; } + bool is_marked(int p_line) const { return text[p_line].marked; } + void set_breakpoint(int p_line, bool p_breakpoint) { text[p_line].breakpoint = p_breakpoint; } + bool is_breakpoint(int p_line) const { return text[p_line].breakpoint; } + void set_hidden(int p_line, bool p_hidden) { text[p_line].hidden = p_hidden; } + bool is_hidden(int p_line) const { return text[p_line].hidden; } + void insert(int p_at, const String &p_text); + void remove(int p_at); + int size() const { return text.size(); } + void clear(); + void clear_caches(); + _FORCE_INLINE_ const String &operator[](int p_line) const { return text[p_line].data; } + Text() { indent_size = 4; } + }; +private: struct Cursor { int last_fit_x; int line, column; ///< cursor @@ -114,69 +187,7 @@ class TextEdit : public Control { Size2 size; } cache; - struct ColorRegion { - - Color color; - String begin_key; - String end_key; - bool line_only; - bool eq; - ColorRegion(const String &p_begin_key = "", const String &p_end_key = "", const Color &p_color = Color(), bool p_line_only = false) { - begin_key = p_begin_key; - end_key = p_end_key; - color = p_color; - line_only = p_line_only || p_end_key == ""; - eq = begin_key == end_key; - } - }; - - class Text { - public: - struct ColorRegionInfo { - - int region; - bool end; - }; - - struct Line { - int width_cache : 24; - bool marked : 1; - bool breakpoint : 1; - bool hidden : 1; - Map<int, ColorRegionInfo> region_info; - String data; - }; - - private: - const Vector<ColorRegion> *color_regions; - mutable Vector<Line> text; - Ref<Font> font; - int indent_size; - - void _update_line_cache(int p_line) const; - - public: - void set_indent_size(int p_indent_size); - void set_font(const Ref<Font> &p_font); - void set_color_regions(const Vector<ColorRegion> *p_regions) { color_regions = p_regions; } - int get_line_width(int p_line) const; - int get_max_width(bool p_exclude_hidden = false) const; - const Map<int, ColorRegionInfo> &get_color_region_info(int p_line); - void set(int p_line, const String &p_text); - void set_marked(int p_line, bool p_marked) { text[p_line].marked = p_marked; } - bool is_marked(int p_line) const { return text[p_line].marked; } - void set_breakpoint(int p_line, bool p_breakpoint) { text[p_line].breakpoint = p_breakpoint; } - bool is_breakpoint(int p_line) const { return text[p_line].breakpoint; } - void set_hidden(int p_line, bool p_hidden) { text[p_line].hidden = p_hidden; } - bool is_hidden(int p_line) const { return text[p_line].hidden; } - void insert(int p_at, const String &p_text); - void remove(int p_at); - int size() const { return text.size(); } - void clear(); - void clear_caches(); - _FORCE_INLINE_ const String &operator[](int p_line) const { return text[p_line].data; } - Text() { indent_size = 4; } - }; + Map<int, int> color_region_cache; struct TextOperation { @@ -208,7 +219,11 @@ class TextEdit : public Control { void _do_text_op(const TextOperation &p_op, bool p_reverse); //syntax coloring + SyntaxHighlighter *syntax_highlighter; HashMap<String, Color> keywords; + HashMap<String, Color> member_keywords; + + Map<int, HighlighterInfo> _get_line_syntax_highlighting(int p_line); Vector<ColorRegion> color_regions; @@ -353,6 +368,7 @@ class TextEdit : public Control { void _update_caches(); void _cursor_changed_emit(); void _text_changed_emit(); + void _line_edited_from(int p_line); void _push_current_op(); @@ -389,6 +405,13 @@ protected: static void _bind_methods(); public: + SyntaxHighlighter *_get_syntax_highlighting(); + void _set_syntax_highlighting(SyntaxHighlighter *p_syntax_highlighter); + + int _is_line_in_region(int p_line); + ColorRegion _get_color_region(int p_region) const; + Map<int, Text::ColorRegionInfo> _get_line_color_region_info(int p_line) const; + enum MenuItems { MENU_CUT, MENU_COPY, @@ -448,6 +471,7 @@ public: void indent_left(); void indent_right(); int get_indent_level(int p_line) const; + bool is_line_comment(int p_line) const; inline void set_scroll_pass_end_of_file(bool p_enabled) { scroll_past_end_of_file_enabled = p_enabled; @@ -490,7 +514,10 @@ public: bool is_readonly() const; void set_max_chars(int p_max_chars); + int get_max_chars() const; + void set_wrap(bool p_wrap); + bool is_wrapping() const; void clear(); @@ -530,6 +557,7 @@ public: void set_indent_using_spaces(const bool p_use_spaces); bool is_indent_using_spaces() const; void set_indent_size(const int p_size); + int get_indent_size(); void set_draw_tabs(bool p_draw); bool is_drawing_tabs() const; void set_override_selected_font_color(bool p_override_selected_font_color); @@ -539,9 +567,17 @@ public: bool is_insert_mode() const; void add_keyword_color(const String &p_keyword, const Color &p_color); + bool has_keyword_color(String p_keyword) const; + Color get_keyword_color(String p_keyword) const; + void add_color_region(const String &p_begin_key = String(), const String &p_end_key = String(), const Color &p_color = Color(), bool p_line_only = false); void clear_colors(); + void add_member_keyword(const String &p_keyword, const Color &p_color); + bool has_member_color(String p_member) const; + Color get_member_color(String p_member) const; + void clear_member_keywords(); + int get_v_scroll() const; void set_v_scroll(int p_scroll); @@ -612,4 +648,19 @@ public: VARIANT_ENUM_CAST(TextEdit::MenuItems); VARIANT_ENUM_CAST(TextEdit::SearchFlags); +class SyntaxHighlighter { +protected: + TextEdit *text_editor; + +public: + virtual void _update_cache() = 0; + virtual Map<int, TextEdit::HighlighterInfo> _get_line_syntax_highlighting(int p_line) = 0; + + virtual String get_name() = 0; + virtual List<String> get_supported_languages() = 0; + + void set_text_editor(TextEdit *p_text_editor); + TextEdit *get_text_editor(); +}; + #endif // TEXT_EDIT_H diff --git a/scene/gui/texture_button.cpp b/scene/gui/texture_button.cpp index ec77fae1fa..6bd3b26280 100644 --- a/scene/gui/texture_button.cpp +++ b/scene/gui/texture_button.cpp @@ -27,7 +27,10 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "texture_button.h" +#include "core/typedefs.h" +#include <stdlib.h> Size2 TextureButton::get_minimum_size() const { @@ -57,10 +60,50 @@ bool TextureButton::has_point(const Point2 &p_point) const { if (click_mask.is_valid()) { - Point2i p = p_point; - if (p.x < 0 || p.x >= click_mask->get_size().width || p.y < 0 || p.y >= click_mask->get_size().height) + Point2 point = p_point; + Rect2 rect = Rect2(); + Size2 mask_size = click_mask->get_size(); + + if (_tile) { + // if the stretch mode is tile we offset the point to keep it inside the mask size + rect.size = mask_size; + if (_position_rect.has_point(point)) { + int cols = (int)Math::ceil(_position_rect.size.x / mask_size.x); + int rows = (int)Math::ceil(_position_rect.size.y / mask_size.y); + int col = (int)(point.x / mask_size.x) % cols; + int row = (int)(point.y / mask_size.y) % rows; + point.x -= mask_size.x * col; + point.y -= mask_size.y * row; + } + } else { + // we need to transform the point from our scaled / translated image back to our mask image + Point2 ofs = _position_rect.position; + Size2 scale = mask_size / _position_rect.size; + + switch (stretch_mode) { + case STRETCH_KEEP_ASPECT_COVERED: { + // if the stretch mode is aspect covered the image uses a texture region so we need to take that into account + float min = MIN(scale.x, scale.y); + scale.x = min; + scale.y = min; + ofs -= _texture_region.position / min; + } break; + } + + // offset and scale the new point position to adjust it to the bitmask size + point -= ofs; + point *= scale; + + // finally, we need to check if the point is inside a rectangle with a position >= 0,0 and a size <= mask_size + rect.position = Point2(MAX(0, _texture_region.position.x), MAX(0, _texture_region.position.y)); + rect.size = Size2(MIN(mask_size.x, _texture_region.size.x), MIN(mask_size.y, _texture_region.size.y)); + } + + if (!rect.has_point(point)) { return false; + } + Point2i p = point; return click_mask->get_bit(p); } @@ -117,8 +160,8 @@ void TextureButton::_notification(int p_what) { if (texdraw.is_valid()) { Point2 ofs; Size2 size = texdraw->get_size(); - Rect2 tex_regin = Rect2(Point2(), texdraw->get_size()); - bool tile = false; + _texture_region = Rect2(Point2(), texdraw->get_size()); + _tile = false; if (expand) { switch (stretch_mode) { case STRETCH_KEEP: @@ -129,7 +172,7 @@ void TextureButton::_notification(int p_what) { break; case STRETCH_TILE: size = get_size(); - tile = true; + _tile = true; break; case STRETCH_KEEP_CENTERED: ofs = (get_size() - texdraw->get_size()) / 2; @@ -160,14 +203,15 @@ void TextureButton::_notification(int p_what) { float scale = scaleSize.width > scaleSize.height ? scaleSize.width : scaleSize.height; Size2 scaledTexSize = tex_size * scale; Point2 ofs = ((scaledTexSize - size) / scale).abs() / 2.0f; - tex_regin = Rect2(ofs, size / scale); + _texture_region = Rect2(ofs, size / scale); } break; } } - if (tile) - draw_texture_rect(texdraw, Rect2(ofs, size), tile); + _position_rect = Rect2(ofs, size); + if (_tile) + draw_texture_rect(texdraw, _position_rect, _tile); else - draw_texture_rect_region(texdraw, Rect2(ofs, size), tex_regin); + draw_texture_rect_region(texdraw, _position_rect, _texture_region); } if (has_focus() && focused.is_valid()) { @@ -298,4 +342,8 @@ TextureButton::StretchMode TextureButton::get_stretch_mode() const { TextureButton::TextureButton() { expand = false; stretch_mode = STRETCH_SCALE; + + _texture_region = Rect2(); + _position_rect = Rect2(); + _tile = false; } diff --git a/scene/gui/texture_button.h b/scene/gui/texture_button.h index c6b05703ed..d42df390e8 100644 --- a/scene/gui/texture_button.h +++ b/scene/gui/texture_button.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef TEXTURE_BUTTON_H #define TEXTURE_BUTTON_H @@ -57,6 +58,10 @@ private: bool expand; StretchMode stretch_mode; + Rect2 _texture_region; + Rect2 _position_rect; + bool _tile; + protected: virtual Size2 get_minimum_size() const; virtual bool has_point(const Point2 &p_point) const; diff --git a/scene/gui/texture_progress.cpp b/scene/gui/texture_progress.cpp index 5f0c7f7385..82d983184b 100644 --- a/scene/gui/texture_progress.cpp +++ b/scene/gui/texture_progress.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "texture_progress.h" #include "engine.h" @@ -105,6 +106,33 @@ Ref<Texture> TextureProgress::get_progress_texture() const { return progress; } +void TextureProgress::set_tint_under(const Color &p_tint) { + tint_under = p_tint; + update(); +} + +Color TextureProgress::get_tint_under() const { + return tint_under; +} + +void TextureProgress::set_tint_progress(const Color &p_tint) { + tint_progress = p_tint; + update(); +} + +Color TextureProgress::get_tint_progress() const { + return tint_progress; +} + +void TextureProgress::set_tint_over(const Color &p_tint) { + tint_over = p_tint; + update(); +} + +Color TextureProgress::get_tint_over() const { + return tint_over; +} + Point2 TextureProgress::unit_val_to_uv(float val) { if (progress.is_null()) return Point2(); @@ -116,22 +144,45 @@ Point2 TextureProgress::unit_val_to_uv(float val) { Point2 p = get_relative_center(); - if (val < 0.125) - return Point2(p.x + (1 - p.x) * val * 8, 0); - if (val < 0.25) - return Point2(1, p.y * (val - 0.125) * 8); - if (val < 0.375) - return Point2(1, p.y + (1 - p.y) * (val - 0.25) * 8); - if (val < 0.5) - return Point2(1 - (1 - p.x) * (val - 0.375) * 8, 1); - if (val < 0.625) - return Point2(p.x * (1 - (val - 0.5) * 8), 1); - if (val < 0.75) - return Point2(0, 1 - ((1 - p.y) * (val - 0.625) * 8)); - if (val < 0.875) - return Point2(0, p.y - p.y * (val - 0.75) * 8); - else - return Point2(p.x * (val - 0.875) * 8, 0); + // Minimal version of Liang-Barsky clipping algorithm + float angle = (val * Math_TAU) - Math_PI * 0.5; + Point2 dir = Vector2(Math::cos(angle), Math::sin(angle)); + float t1 = 1.0; + float cp; + float cq; + float cr; + float edgeLeft = 0.0; + float edgeRight = 1.0; + float edgeBottom = 0.0; + float edgeTop = 1.0; + + for (int edge = 0; edge < 4; edge++) { + if (edge == 0) { + if (dir.x > 0) + continue; + cp = -dir.x; + cq = -(edgeLeft - p.x); + } else if (edge == 1) { + if (dir.x < 0) + continue; + cp = dir.x; + cq = (edgeRight - p.x); + } else if (edge == 2) { + if (dir.y > 0) + continue; + cp = -dir.y; + cq = -(edgeBottom - p.y); + } else if (edge == 3) { + if (dir.y < 0) + continue; + cp = dir.y; + cq = (edgeTop - p.y); + } + cr = cq / cp; + if (cr >= 0 && cr < t1) + t1 = cr; + } + return (p + t1 * dir); } Point2 TextureProgress::get_relative_center() { @@ -146,7 +197,7 @@ Point2 TextureProgress::get_relative_center() { return p; } -void TextureProgress::draw_nine_patch_stretched(const Ref<Texture> &p_texture, FillMode p_mode, double p_ratio) { +void TextureProgress::draw_nine_patch_stretched(const Ref<Texture> &p_texture, FillMode p_mode, double p_ratio, const Color &p_modulate) { Vector2 texture_size = p_texture->get_size(); Vector2 topleft = Vector2(stretch_margin[MARGIN_LEFT], stretch_margin[MARGIN_TOP]); Vector2 bottomright = Vector2(stretch_margin[MARGIN_RIGHT], stretch_margin[MARGIN_BOTTOM]); @@ -157,7 +208,7 @@ void TextureProgress::draw_nine_patch_stretched(const Ref<Texture> &p_texture, F if (p_ratio < 1.0) { // Drawing a partially-filled 9-patch is a little tricky - // texture is divided by 3 sections toward fill direction, - // then middle section is streching while the other two aren't. + // then middle section is stretching while the other two aren't. double width_total = 0.0; double width_texture = 0.0; @@ -216,7 +267,7 @@ void TextureProgress::draw_nine_patch_stretched(const Ref<Texture> &p_texture, F } RID ci = get_canvas_item(); - VS::get_singleton()->canvas_item_add_nine_patch(ci, dst_rect, src_rect, p_texture->get_rid(), topleft, bottomright); + VS::get_singleton()->canvas_item_add_nine_patch(ci, dst_rect, src_rect, p_texture->get_rid(), topleft, bottomright, VS::NINE_PATCH_STRETCH, VS::NINE_PATCH_STRETCH, true, p_modulate); } void TextureProgress::_notification(int p_what) { @@ -227,42 +278,42 @@ void TextureProgress::_notification(int p_what) { if (nine_patch_stretch && (mode == FILL_LEFT_TO_RIGHT || mode == FILL_RIGHT_TO_LEFT || mode == FILL_TOP_TO_BOTTOM || mode == FILL_BOTTOM_TO_TOP)) { if (under.is_valid()) { - draw_nine_patch_stretched(under, FILL_LEFT_TO_RIGHT, 1.0); + draw_nine_patch_stretched(under, FILL_LEFT_TO_RIGHT, 1.0, tint_under); } if (progress.is_valid()) { - draw_nine_patch_stretched(progress, mode, get_as_ratio()); + draw_nine_patch_stretched(progress, mode, get_as_ratio(), tint_progress); } if (over.is_valid()) { - draw_nine_patch_stretched(over, FILL_LEFT_TO_RIGHT, 1.0); + draw_nine_patch_stretched(over, FILL_LEFT_TO_RIGHT, 1.0, tint_over); } } else { if (under.is_valid()) - draw_texture(under, Point2()); + draw_texture(under, Point2(), tint_under); if (progress.is_valid()) { Size2 s = progress->get_size(); switch (mode) { case FILL_LEFT_TO_RIGHT: { Rect2 region = Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)); - draw_texture_rect_region(progress, region, region); + draw_texture_rect_region(progress, region, region, tint_progress); } break; case FILL_RIGHT_TO_LEFT: { Rect2 region = Rect2(Point2(s.x - s.x * get_as_ratio(), 0), Size2(s.x * get_as_ratio(), s.y)); - draw_texture_rect_region(progress, region, region); + draw_texture_rect_region(progress, region, region, tint_progress); } break; case FILL_TOP_TO_BOTTOM: { Rect2 region = Rect2(Point2(), Size2(s.x, s.y * get_as_ratio())); - draw_texture_rect_region(progress, region, region); + draw_texture_rect_region(progress, region, region, tint_progress); } break; case FILL_BOTTOM_TO_TOP: { Rect2 region = Rect2(Point2(0, s.y - s.y * get_as_ratio()), Size2(s.x, s.y * get_as_ratio())); - draw_texture_rect_region(progress, region, region); + draw_texture_rect_region(progress, region, region, tint_progress); } break; case FILL_CLOCKWISE: case FILL_COUNTER_CLOCKWISE: { float val = get_as_ratio() * rad_max_degrees / 360; if (val == 1) { Rect2 region = Rect2(Point2(), s); - draw_texture_rect_region(progress, region, region); + draw_texture_rect_region(progress, region, region, tint_progress); } else if (val != 0) { Array pts; float direction = mode == FILL_CLOCKWISE ? 1 : -1; @@ -287,7 +338,9 @@ void TextureProgress::_notification(int p_what) { uvs.push_back(uv); points.push_back(Point2(uv.x * s.x, uv.y * s.y)); } - draw_polygon(points, Vector<Color>(), uvs, progress); + Vector<Color> colors; + colors.push_back(tint_progress); + draw_polygon(points, colors, uvs, progress); } if (Engine::get_singleton()->is_editor_hint()) { Point2 p = progress->get_size(); @@ -299,11 +352,11 @@ void TextureProgress::_notification(int p_what) { } } break; default: - draw_texture_rect_region(progress, Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y))); + draw_texture_rect_region(progress, Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), tint_progress); } } if (over.is_valid()) - draw_texture(over, Point2()); + draw_texture(over, Point2(), tint_over); } } break; @@ -365,6 +418,15 @@ void TextureProgress::_bind_methods() { ClassDB::bind_method(D_METHOD("set_fill_mode", "mode"), &TextureProgress::set_fill_mode); ClassDB::bind_method(D_METHOD("get_fill_mode"), &TextureProgress::get_fill_mode); + ClassDB::bind_method(D_METHOD("set_tint_under", "tint"), &TextureProgress::set_tint_under); + ClassDB::bind_method(D_METHOD("get_tint_under"), &TextureProgress::get_tint_under); + + ClassDB::bind_method(D_METHOD("set_tint_progress", "tint"), &TextureProgress::set_tint_progress); + ClassDB::bind_method(D_METHOD("get_tint_progress"), &TextureProgress::get_tint_progress); + + ClassDB::bind_method(D_METHOD("set_tint_over", "tint"), &TextureProgress::set_tint_over); + ClassDB::bind_method(D_METHOD("get_tint_over"), &TextureProgress::get_tint_over); + ClassDB::bind_method(D_METHOD("set_radial_initial_angle", "mode"), &TextureProgress::set_radial_initial_angle); ClassDB::bind_method(D_METHOD("get_radial_initial_angle"), &TextureProgress::get_radial_initial_angle); @@ -385,6 +447,10 @@ void TextureProgress::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_over", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_over_texture", "get_over_texture"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_progress", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_progress_texture", "get_progress_texture"); ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "fill_mode", PROPERTY_HINT_ENUM, "Left to Right,Right to Left,Top to Bottom,Bottom to Top,Clockwise,Counter Clockwise"), "set_fill_mode", "get_fill_mode"); + ADD_GROUP("Tint", "tint_"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_under", PROPERTY_HINT_COLOR_NO_ALPHA), "set_tint_under", "get_tint_under"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_over", PROPERTY_HINT_COLOR_NO_ALPHA), "set_tint_over", "get_tint_over"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_progress", PROPERTY_HINT_COLOR_NO_ALPHA), "set_tint_progress", "get_tint_progress"); ADD_GROUP("Radial Fill", "radial_"); ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "radial_initial_angle", PROPERTY_HINT_RANGE, "0.0,360.0,0.1,slider"), "set_radial_initial_angle", "get_radial_initial_angle"); ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "radial_fill_degrees", PROPERTY_HINT_RANGE, "0.0,360.0,0.1,slider"), "set_fill_degrees", "get_fill_degrees"); @@ -416,4 +482,6 @@ TextureProgress::TextureProgress() { stretch_margin[MARGIN_RIGHT] = 0; stretch_margin[MARGIN_BOTTOM] = 0; stretch_margin[MARGIN_TOP] = 0; + + tint_under = tint_progress = tint_over = Color(1, 1, 1); } diff --git a/scene/gui/texture_progress.h b/scene/gui/texture_progress.h index ab1f42fe3b..34158b5db5 100644 --- a/scene/gui/texture_progress.h +++ b/scene/gui/texture_progress.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef TEXTURE_PROGRESS_H #define TEXTURE_PROGRESS_H @@ -81,6 +82,15 @@ public: void set_nine_patch_stretch(bool p_stretch); bool get_nine_patch_stretch() const; + void set_tint_under(const Color &p_tint); + Color get_tint_under() const; + + void set_tint_progress(const Color &p_tint); + Color get_tint_progress() const; + + void set_tint_over(const Color &p_tint); + Color get_tint_over() const; + Size2 get_minimum_size() const; TextureProgress(); @@ -92,10 +102,11 @@ private: Point2 rad_center_off; bool nine_patch_stretch; int stretch_margin[4]; + Color tint_under, tint_progress, tint_over; Point2 unit_val_to_uv(float val); Point2 get_relative_center(); - void draw_nine_patch_stretched(const Ref<Texture> &p_texture, FillMode p_mode, double p_ratio); + void draw_nine_patch_stretched(const Ref<Texture> &p_texture, FillMode p_mode, double p_ratio, const Color &p_modulate); }; VARIANT_ENUM_CAST(TextureProgress::FillMode); diff --git a/scene/gui/texture_rect.cpp b/scene/gui/texture_rect.cpp index 5b689cfce2..f4285525f6 100644 --- a/scene/gui/texture_rect.cpp +++ b/scene/gui/texture_rect.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "texture_rect.h" #include "servers/visual_server.h" diff --git a/scene/gui/texture_rect.h b/scene/gui/texture_rect.h index b6ccdff217..b684ac816c 100644 --- a/scene/gui/texture_rect.h +++ b/scene/gui/texture_rect.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef TEXTURE_FRAME_H #define TEXTURE_FRAME_H diff --git a/scene/gui/tool_button.cpp b/scene/gui/tool_button.cpp index e86776af90..4220a6b5ce 100644 --- a/scene/gui/tool_button.cpp +++ b/scene/gui/tool_button.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "tool_button.h" ToolButton::ToolButton() { diff --git a/scene/gui/tool_button.h b/scene/gui/tool_button.h index 14a7cdc2fe..b8be18e560 100644 --- a/scene/gui/tool_button.h +++ b/scene/gui/tool_button.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef TOOL_BUTTON_H #define TOOL_BUTTON_H diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 08f1bdff3d..1d27612766 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "tree.h" #include <limits.h> @@ -44,12 +45,12 @@ void TreeItem::move_to_top() { - if (!parent || parent->childs == this) + if (!parent || parent->children == this) return; //already on top TreeItem *prev = get_prev(); prev->next = next; - next = parent->childs; - parent->childs = this; + next = parent->children; + parent->children = this; } void TreeItem::move_to_bottom() { @@ -64,7 +65,7 @@ void TreeItem::move_to_bottom() { if (prev) { prev->next = next; } else { - parent->childs = next; + parent->children = next; } last->next = this; next = NULL; @@ -367,10 +368,10 @@ TreeItem *TreeItem::get_next() { TreeItem *TreeItem::get_prev() { - if (!parent || parent->childs == this) + if (!parent || parent->children == this) return NULL; - TreeItem *prev = parent->childs; + TreeItem *prev = parent->children; while (prev && prev->next != this) prev = prev->next; @@ -384,7 +385,7 @@ TreeItem *TreeItem::get_parent() { TreeItem *TreeItem::get_children() { - return childs; + return children; } TreeItem *TreeItem::get_prev_visible() { @@ -401,10 +402,10 @@ TreeItem *TreeItem::get_prev_visible() { } else { current = prev; - while (!current->collapsed && current->childs) { + while (!current->collapsed && current->children) { //go to the very end - current = current->childs; + current = current->children; while (current->next) current = current->next; } @@ -417,9 +418,9 @@ TreeItem *TreeItem::get_next_visible() { TreeItem *current = this; - if (!current->collapsed && current->childs) { + if (!current->collapsed && current->children) { - current = current->childs; + current = current->children; } else if (current->next) { @@ -443,7 +444,7 @@ TreeItem *TreeItem::get_next_visible() { void TreeItem::remove_child(TreeItem *p_item) { ERR_FAIL_NULL(p_item); - TreeItem **c = &childs; + TreeItem **c = &children; while (*c) { @@ -783,6 +784,10 @@ void TreeItem::_bind_methods() { ClassDB::bind_method(D_METHOD("set_disable_folding", "disable"), &TreeItem::set_disable_folding); ClassDB::bind_method(D_METHOD("is_folding_disabled"), &TreeItem::is_folding_disabled); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collapsed"), "set_collapsed", "is_collapsed"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disable_folding"), "set_disable_folding", "is_folding_disabled"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "custom_minimum_height", PROPERTY_HINT_RANGE, "0,1000,1"), "set_custom_minimum_height", "get_custom_minimum_height"); + BIND_ENUM_CONSTANT(CELL_MODE_STRING); BIND_ENUM_CONSTANT(CELL_MODE_CHECK); BIND_ENUM_CONSTANT(CELL_MODE_RANGE); @@ -797,16 +802,16 @@ void TreeItem::_bind_methods() { void TreeItem::clear_children() { - TreeItem *c = childs; + TreeItem *c = children; while (c) { TreeItem *aux = c; c = c->get_next(); - aux->parent = 0; // so it wont try to recursively autoremove from me in here + aux->parent = 0; // so it won't try to recursively autoremove from me in here memdelete(aux); } - childs = 0; + children = 0; }; TreeItem::TreeItem(Tree *p_tree) { @@ -818,7 +823,7 @@ TreeItem::TreeItem(Tree *p_tree) { parent = 0; // parent item next = 0; // next in list - childs = 0; //child items + children = 0; //child items } TreeItem::~TreeItem() { @@ -915,6 +920,7 @@ int Tree::compute_item_height(TreeItem *p_item) const { if (p_item == root && hide_root) return 0; + ERR_FAIL_COND_V(cache.font.is_null(), 0); int height = cache.font->get_height(); for (int i = 0; i < columns.size(); i++) { @@ -971,9 +977,9 @@ int Tree::get_item_height(TreeItem *p_item) const { int height = compute_item_height(p_item); height += cache.vseparation; - if (!p_item->collapsed) { /* if not collapsed, check the childs */ + if (!p_item->collapsed) { /* if not collapsed, check the children */ - TreeItem *c = p_item->childs; + TreeItem *c = p_item->children; while (c) { @@ -988,6 +994,8 @@ int Tree::get_item_height(TreeItem *p_item) const { void Tree::draw_item_rect(const TreeItem::Cell &p_cell, const Rect2i &p_rect, const Color &p_color, const Color &p_icon_color) { + ERR_FAIL_COND(cache.font.is_null()); + Rect2i rect = p_rect; Ref<Font> font = cache.font; String text = p_cell.text; @@ -1057,6 +1065,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 //draw separation. //if (p_item->get_parent()!=root || !hide_root) + ERR_FAIL_COND_V(cache.font.is_null(), -1); Ref<Font> font = cache.font; int font_ascent = font->get_ascent(); @@ -1377,7 +1386,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 } } - if (!p_item->disable_folding && !hide_folding && p_item->childs) { //has childs, draw the guide box + if (!p_item->disable_folding && !hide_folding && p_item->children) { //has children, draw the guide box Ref<Texture> arrow; @@ -1404,9 +1413,9 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 children_pos.y += htotal; } - if (!p_item->collapsed) { /* if not collapsed, check the childs */ + if (!p_item->collapsed) { /* if not collapsed, check the children */ - TreeItem *c = p_item->childs; + TreeItem *c = p_item->children; while (c) { @@ -1423,17 +1432,33 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 #endif 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; - VisualServer::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); - VisualServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y), parent_pos, cache.relationship_line_color, line_width); + + if (root_pos.y + line_width >= 0) { + VisualServer::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); + VisualServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y), parent_pos, cache.relationship_line_color, line_width); + } + + if (htotal < 0) { + return -1; + } } - int child_h = draw_item(children_pos, p_draw_ofs, p_draw_size, c); + if (htotal >= 0) { + int child_h = draw_item(children_pos, p_draw_ofs, p_draw_size, c); - if (child_h < 0 && cache.draw_relationship_lines == 0) - return -1; // break, stop drawing, no need to anymore + if (child_h < 0) { + if (cache.draw_relationship_lines == 0) { + return -1; // break, stop drawing, no need to anymore + } else { + htotal = -1; + children_pos.y = cache.offset.y + p_draw_size.height; + } + } else { + htotal += child_h; + children_pos.y += child_h; + } + } - htotal += child_h; - children_pos.y += child_h; c = c->next; } } @@ -1544,7 +1569,7 @@ void Tree::select_single_item(TreeItem *p_selected, TreeItem *p_current, int p_c *r_in_range = false; } - TreeItem *c = p_current->childs; + TreeItem *c = p_current->children; while (c) { @@ -1610,7 +1635,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool if (!p_item->disable_folding && !hide_folding && (p_pos.x >= x_ofs && p_pos.x < (x_ofs + cache.item_margin))) { - if (p_item->childs) + if (p_item->children) p_item->set_collapsed(!p_item->is_collapsed()); return -1; //handled! @@ -1917,9 +1942,9 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool new_pos.y -= item_h; } - if (!p_item->collapsed) { /* if not collapsed, check the childs */ + if (!p_item->collapsed) { /* if not collapsed, check the children */ - TreeItem *c = p_item->childs; + TreeItem *c = p_item->children; while (c) { @@ -2036,323 +2061,317 @@ void Tree::popup_select(int p_option) { item_edited(popup_edited_item_col, popup_edited_item); } -void Tree::_gui_input(Ref<InputEvent> p_event) { - - Ref<InputEventKey> k = p_event; +void Tree::_go_left() { + if (selected_col == 0) { + if (selected_item->get_children() != NULL && !selected_item->is_collapsed()) { + selected_item->set_collapsed(true); + } else { + if (columns.size() == 1) { // goto parent with one column + TreeItem *parent = selected_item->get_parent(); + if (selected_item != get_root() && parent && parent->is_selectable(selected_col) && !(hide_root && parent == get_root())) { + select_single_item(parent, get_root(), selected_col); + } + } else if (selected_item->get_prev_visible()) { + selected_col = columns.size() - 1; + _go_up(); // go to upper column if possible + } + } + } else { + if (select_mode == SELECT_MULTI) { + selected_col--; + emit_signal("cell_selected"); + } else { - if (k.is_valid()) { + selected_item->select(selected_col - 1); + } + } + update(); + accept_event(); + ensure_cursor_is_visible(); +} - if (!k->is_pressed()) - return; - if (k->get_command() || (k->get_shift() && k->get_unicode() == 0) || k->get_metakey()) - return; - if (!root) +void Tree::_go_right() { + if (selected_col == (columns.size() - 1)) { + if (selected_item->get_children() != NULL && selected_item->is_collapsed()) { + selected_item->set_collapsed(false); + } else if (selected_item->get_next_visible()) { + selected_item->select(0); + _go_down(); return; + } + } else { + if (select_mode == SELECT_MULTI) { + selected_col++; + emit_signal("cell_selected"); + } else { - if (hide_root && !root->get_next_visible()) + selected_item->select(selected_col + 1); + } + } + update(); + ensure_cursor_is_visible(); + accept_event(); +} + +void Tree::_go_up() { + TreeItem *prev = NULL; + if (!selected_item) { + prev = get_last_item(); + selected_col = 0; + } else { + + prev = selected_item->get_prev_visible(); + if (last_keypress != 0) { + //incr search next + int col; + prev = _search_item_text(prev, incr_search, &col, true, true); + if (!prev) { + accept_event(); + return; + } + } + } + + if (select_mode == SELECT_MULTI) { + + if (!prev) return; + selected_item = prev; + emit_signal("cell_selected"); + update(); + } else { - switch (k->get_scancode()) { -#define EXIT_BREAK \ - { \ - if (!cursor_can_exit_tree) accept_event(); \ - break; \ + int col = selected_col < 0 ? 0 : selected_col; + while (prev && !prev->cells[col].selectable) + prev = prev->get_prev_visible(); + if (!prev) + return; // do nothing.. + prev->select(col); } - case KEY_RIGHT: { - bool dobreak = true; - //TreeItem *next = NULL; - if (!selected_item) - break; - if (select_mode == SELECT_ROW) { - EXIT_BREAK; - } - if (selected_col > (columns.size() - 1)) { - EXIT_BREAK; - } - if (k->get_alt()) { - selected_item->set_collapsed(false); - TreeItem *next = selected_item->get_children(); - while (next && next != selected_item->next) { - next->set_collapsed(false); - next = next->get_next_visible(); - } - } else if (selected_col == (columns.size() - 1)) { - if (selected_item->get_children() != NULL && selected_item->is_collapsed()) { - selected_item->set_collapsed(false); - } else { - selected_col = 0; - dobreak = false; // fall through to key_down - } - } else { - if (select_mode == SELECT_MULTI) { - selected_col++; - emit_signal("cell_selected"); - } else { + ensure_cursor_is_visible(); + accept_event(); +} - selected_item->select(selected_col + 1); - } - } - update(); - ensure_cursor_is_visible(); +void Tree::_go_down() { + TreeItem *next = NULL; + if (!selected_item) { + + next = hide_root ? root->get_next_visible() : root; + selected_item = 0; + } else { + + next = selected_item->get_next_visible(); + + if (last_keypress != 0) { + //incr search next + int col; + next = _search_item_text(next, incr_search, &col, true); + if (!next) { accept_event(); - if (dobreak) { - break; - } + return; } - case KEY_DOWN: { + } + } - TreeItem *next = NULL; - if (!selected_item) { + if (select_mode == SELECT_MULTI) { - next = hide_root ? root->get_next_visible() : root; - selected_item = 0; - } else { + if (!next) { + return; + } - next = selected_item->get_next_visible(); + selected_item = next; + emit_signal("cell_selected"); + update(); + } else { - //if (diff < uint64_t(GLOBAL_DEF("gui/incr_search_max_interval_msec",2000))) { - if (last_keypress != 0) { - //incr search next - int col; - next = _search_item_text(next, incr_search, &col, true); - if (!next) { - accept_event(); - return; - } - } - } + int col = selected_col < 0 ? 0 : selected_col; - if (select_mode == SELECT_MULTI) { + while (next && !next->cells[col].selectable) + next = next->get_next_visible(); + if (!next) { + return; // do nothing.. + } + next->select(col); + } - if (!next) - EXIT_BREAK; + ensure_cursor_is_visible(); + accept_event(); +} - selected_item = next; - emit_signal("cell_selected"); - update(); - } else { +void Tree::_gui_input(Ref<InputEvent> p_event) { - int col = selected_col < 0 ? 0 : selected_col; + Ref<InputEventKey> k = p_event; - while (next && !next->cells[col].selectable) - next = next->get_next_visible(); - if (!next) - EXIT_BREAK; // do nothing.. - next->select(col); - } + if (p_event->is_action("ui_right") && p_event->is_pressed()) { - ensure_cursor_is_visible(); - accept_event(); + if (!cursor_can_exit_tree) accept_event(); - } break; - case KEY_LEFT: { - bool dobreak = true; + if (!selected_item || select_mode == SELECT_ROW || selected_col > (columns.size() - 1)) { + return; + } + if (k.is_valid() && k->get_alt()) { + selected_item->set_collapsed(false); + TreeItem *next = selected_item->get_children(); + while (next && next != selected_item->next) { + next->set_collapsed(false); + next = next->get_next_visible(); + } + } else { + _go_right(); + } + } else if (p_event->is_action("ui_left") && p_event->is_pressed()) { - //TreeItem *next = NULL; - if (!selected_item) - break; - if (select_mode == SELECT_ROW) { - EXIT_BREAK; - } - if (selected_col < 0) { - EXIT_BREAK; - } - if (k->get_alt()) { - selected_item->set_collapsed(true); - TreeItem *next = selected_item->get_children(); - while (next && next != selected_item->next) { - next->set_collapsed(true); - next = next->get_next_visible(); - } - } else if (selected_col == 0) { - if (selected_item->get_children() != NULL && !selected_item->is_collapsed()) { - selected_item->set_collapsed(true); - } else { - if (columns.size() == 1) { // goto parent with one column - TreeItem *parent = selected_item->get_parent(); - if (selected_item != get_root() && parent && parent->is_selectable(selected_col) && !(hide_root && parent == get_root())) { - select_single_item(parent, get_root(), selected_col); - } - } else { - selected_col = columns.size() - 1; - dobreak = false; // fall through to key_up - } - } - } else { - if (select_mode == SELECT_MULTI) { - selected_col--; - emit_signal("cell_selected"); - } else { + if (!cursor_can_exit_tree) accept_event(); - selected_item->select(selected_col - 1); - } - } - update(); - accept_event(); - ensure_cursor_is_visible(); + if (!selected_item || select_mode == SELECT_ROW || selected_col < 0) { + return; + } - if (dobreak) { - break; - } + if (k.is_valid() && k->get_alt()) { + selected_item->set_collapsed(true); + TreeItem *next = selected_item->get_children(); + while (next && next != selected_item->next) { + next->set_collapsed(true); + next = next->get_next_visible(); } - case KEY_UP: { + } else { + _go_left(); + } - TreeItem *prev = NULL; - if (!selected_item) { - prev = get_last_item(); - selected_col = 0; - } else { + } else if (p_event->is_action("ui_up") && p_event->is_pressed()) { - prev = selected_item->get_prev_visible(); - if (last_keypress != 0) { - //incr search next - int col; - prev = _search_item_text(prev, incr_search, &col, true, true); - if (!prev) { - accept_event(); - return; - } - } - } + if (!cursor_can_exit_tree) accept_event(); - if (select_mode == SELECT_MULTI) { + _go_up(); - if (!prev) - break; - selected_item = prev; - emit_signal("cell_selected"); - update(); - } else { + } else if (p_event->is_action("ui_down") && p_event->is_pressed()) { - int col = selected_col < 0 ? 0 : selected_col; - while (prev && !prev->cells[col].selectable) - prev = prev->get_prev_visible(); - if (!prev) - break; // do nothing.. - prev->select(col); - } + if (!cursor_can_exit_tree) accept_event(); - ensure_cursor_is_visible(); - accept_event(); + _go_down(); - } break; - case KEY_PAGEDOWN: { + } else if (p_event->is_action("ui_page_down") && p_event->is_pressed()) { - TreeItem *next = NULL; - if (!selected_item) - break; - next = selected_item; + if (!cursor_can_exit_tree) accept_event(); - for (int i = 0; i < 10; i++) { + TreeItem *next = NULL; + if (!selected_item) + return; + next = selected_item; - TreeItem *_n = next->get_next_visible(); - if (_n) { - next = _n; - } else { + for (int i = 0; i < 10; i++) { - break; - } - } - if (next == selected_item) - break; + TreeItem *_n = next->get_next_visible(); + if (_n) { + next = _n; + } else { - if (select_mode == SELECT_MULTI) { + return; + } + } + if (next == selected_item) + return; - selected_item = next; - emit_signal("cell_selected"); - update(); - } else { + if (select_mode == SELECT_MULTI) { - while (next && !next->cells[selected_col].selectable) - next = next->get_next_visible(); - if (!next) - EXIT_BREAK; // do nothing.. - next->select(selected_col); - } + selected_item = next; + emit_signal("cell_selected"); + update(); + } else { - ensure_cursor_is_visible(); - } break; - case KEY_PAGEUP: { + while (next && !next->cells[selected_col].selectable) + next = next->get_next_visible(); + if (!next) { + return; // do nothing.. + } + next->select(selected_col); + } - TreeItem *prev = NULL; - if (!selected_item) - break; - prev = selected_item; + ensure_cursor_is_visible(); + } else if (p_event->is_action("ui_page_up") && p_event->is_pressed()) { - for (int i = 0; i < 10; i++) { + if (!cursor_can_exit_tree) accept_event(); - TreeItem *_n = prev->get_prev_visible(); - if (_n) { - prev = _n; - } else { + TreeItem *prev = NULL; + if (!selected_item) + return; + prev = selected_item; - break; - } - } - if (prev == selected_item) - break; + for (int i = 0; i < 10; i++) { + + TreeItem *_n = prev->get_prev_visible(); + if (_n) { + prev = _n; + } else { - if (select_mode == SELECT_MULTI) { + return; + } + } + if (prev == selected_item) + return; - selected_item = prev; - emit_signal("cell_selected"); - update(); - } else { + if (select_mode == SELECT_MULTI) { - while (prev && !prev->cells[selected_col].selectable) - prev = prev->get_prev_visible(); - if (!prev) - EXIT_BREAK; // do nothing.. - prev->select(selected_col); - } + selected_item = prev; + emit_signal("cell_selected"); + update(); + } else { - ensure_cursor_is_visible(); + while (prev && !prev->cells[selected_col].selectable) + prev = prev->get_prev_visible(); + if (!prev) { + return; // do nothing.. + } + prev->select(selected_col); + } + ensure_cursor_is_visible(); + } else if (p_event->is_action("ui_accept") && p_event->is_pressed()) { - } break; - case KEY_F2: - case KEY_ENTER: - case KEY_KP_ENTER: { - - if (selected_item) { - //bring up editor if possible - if (!edit_selected()) { - emit_signal("item_activated"); - incr_search.clear(); - } - } - accept_event(); + if (selected_item) { + //bring up editor if possible + if (!edit_selected()) { + emit_signal("item_activated"); + incr_search.clear(); + } + } + accept_event(); + } else if (p_event->is_action("ui_select") && p_event->is_pressed()) { - } break; - case KEY_SPACE: { - if (select_mode == SELECT_MULTI) { - if (!selected_item) - break; - if (selected_item->is_selected(selected_col)) { - selected_item->deselect(selected_col); - emit_signal("multi_selected", selected_item, selected_col, false); - } else if (selected_item->is_selectable(selected_col)) { - selected_item->select(selected_col); - emit_signal("multi_selected", selected_item, selected_col, true); - } - } - accept_event(); + if (select_mode == SELECT_MULTI) { + if (!selected_item) + return; + if (selected_item->is_selected(selected_col)) { + selected_item->deselect(selected_col); + emit_signal("multi_selected", selected_item, selected_col, false); + } else if (selected_item->is_selectable(selected_col)) { + selected_item->select(selected_col); + emit_signal("multi_selected", selected_item, selected_col, true); + } + } + accept_event(); + } - } break; - default: { + if (k.is_valid()) { // Incremental search - if (k->get_unicode() > 0) { + if (!k->is_pressed()) + return; + if (k->get_command() || (k->get_shift() && k->get_unicode() == 0) || k->get_metakey()) + return; + if (!root) + return; - _do_incr_search(String::chr(k->get_unicode())); - accept_event(); + if (hide_root && !root->get_next_visible()) + return; - return; - } else { - if (k->get_scancode() != KEY_SHIFT) - last_keypress = 0; - } - } break; + if (k->get_unicode() > 0) { + + _do_incr_search(String::chr(k->get_unicode())); + accept_event(); + return; + } else { + if (k->get_scancode() != KEY_SHIFT) last_keypress = 0; } } @@ -2361,7 +2380,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { if (mm.is_valid()) { - if (cache.font.is_null()) // avoid a strange case that may fuckup stuff + if (cache.font.is_null()) // avoid a strange case that may corrupt stuff update_cache(); Ref<StyleBox> bg = cache.bg; @@ -2460,7 +2479,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { Ref<InputEventMouseButton> b = p_event; if (b.is_valid()) { - if (cache.font.is_null()) // avoid a strange case that may fuckup stuff + if (cache.font.is_null()) // avoid a strange case that may corrupt stuff update_cache(); if (!b->is_pressed()) { @@ -2526,7 +2545,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { if (drag_speed == 0) { drag_touching_deaccel = false; drag_touching = false; - set_physics_process(false); + set_physics_process_internal(false); } else { drag_touching_deaccel = true; @@ -2592,7 +2611,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { break; if (drag_touching) { - set_physics_process(false); + set_physics_process_internal(false); drag_touching_deaccel = false; drag_touching = false; drag_speed = 0; @@ -2607,7 +2626,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { drag_touching = OS::get_singleton()->has_touchscreen_ui_hint(); drag_touching_deaccel = false; if (drag_touching) { - set_physics_process(true); + set_physics_process_internal(true); } if (b->get_button_index() == BUTTON_LEFT) { @@ -2779,6 +2798,7 @@ void Tree::update_scrollbars() { int Tree::_get_title_button_height() const { + ERR_FAIL_COND_V(cache.font.is_null() || cache.title_button.is_null(), 0); return show_column_titles ? cache.font->get_height() + cache.title_button->get_minimum_size().height : 0; } @@ -2809,7 +2829,7 @@ void Tree::_notification(int p_what) { drop_mode_flags = 0; scrolling = false; - set_physics_process(false); + set_physics_process_internal(false); update(); } if (p_what == NOTIFICATION_DRAG_BEGIN) { @@ -2817,10 +2837,10 @@ void Tree::_notification(int p_what) { single_select_defer = NULL; if (cache.scroll_speed > 0 && get_rect().has_point(get_viewport()->get_mouse_position() - get_global_position())) { scrolling = true; - set_physics_process(true); + set_physics_process_internal(true); } } - if (p_what == NOTIFICATION_PHYSICS_PROCESS) { + if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) { if (drag_touching) { @@ -2833,7 +2853,7 @@ void Tree::_notification(int p_what) { if (pos < 0) { pos = 0; turnoff = true; - set_physics_process(false); + set_physics_process_internal(false); drag_touching = false; drag_touching_deaccel = false; } @@ -2853,7 +2873,7 @@ void Tree::_notification(int p_what) { drag_speed = sgn * val; if (turnoff) { - set_physics_process(false); + set_physics_process_internal(false); drag_touching = false; drag_touching_deaccel = false; } @@ -2964,7 +2984,7 @@ TreeItem *Tree::create_item(TreeItem *p_parent, int p_idx) { ti->cells.resize(columns.size()); TreeItem *prev = NULL; - TreeItem *c = p_parent->childs; + TreeItem *c = p_parent->children; int idx = 0; while (c) { @@ -2979,7 +2999,7 @@ TreeItem *Tree::create_item(TreeItem *p_parent, int p_idx) { if (prev) prev->next = ti; else - p_parent->childs = ti; + p_parent->children = ti; ti->parent = p_parent; } else { @@ -3012,8 +3032,8 @@ TreeItem *Tree::get_last_item() { if (last->next) last = last->next; - else if (last->childs) - last = last->childs; + else if (last->children) + last = last->children; else break; } @@ -3046,6 +3066,7 @@ void Tree::item_selected(int p_column, TreeItem *p_item) { p_item->cells[p_column].selected = true; //emit_signal("multi_selected",p_item,p_column,true); - NO this is for TreeItem::select + selected_col = p_column; } else { select_single_item(p_item, root, p_column); @@ -3066,12 +3087,19 @@ void Tree::set_select_mode(SelectMode p_mode) { select_mode = p_mode; } +Tree::SelectMode Tree::get_select_mode() const { + + return select_mode; +} + void Tree::deselect_all() { TreeItem *item = get_next_selected(get_root()); while (item) { item->deselect(selected_col); + TreeItem *prev_item = item; item = get_next_selected(get_root()); + ERR_FAIL_COND(item == prev_item); } selected_item = NULL; @@ -3119,6 +3147,11 @@ void Tree::set_hide_root(bool p_enabled) { update(); } +bool Tree::is_root_hidden() const { + + return hide_root; +} + void Tree::set_column_min_width(int p_column, int p_min_width) { ERR_FAIL_INDEX(p_column, columns.size()); @@ -3171,9 +3204,9 @@ TreeItem *Tree::get_next_selected(TreeItem *p_item) { p_item = root; } else { - if (p_item->childs) { + if (p_item->children) { - p_item = p_item->childs; + p_item = p_item->children; } else if (p_item->next) { @@ -3288,9 +3321,9 @@ int Tree::get_item_offset(TreeItem *p_item) const { ofs += compute_item_height(it) + cache.vseparation; - if (it->childs && !it->collapsed) { + if (it->children && !it->collapsed) { - it = it->childs; + it = it->children; } else if (it->next) { @@ -3511,7 +3544,7 @@ TreeItem *Tree::_find_item_at_pos(TreeItem *p_item, const Point2 &p_pos, int &r_ } if (p_item->is_collapsed()) - return NULL; // do not try childs, it's collapsed + return NULL; // do not try children, it's collapsed TreeItem *n = p_item->get_children(); while (n) { @@ -3739,11 +3772,13 @@ void Tree::_bind_methods() { ClassDB::bind_method(D_METHOD("get_column_width", "column"), &Tree::get_column_width); ClassDB::bind_method(D_METHOD("set_hide_root", "enable"), &Tree::set_hide_root); + ClassDB::bind_method(D_METHOD("is_root_hidden"), &Tree::is_root_hidden); ClassDB::bind_method(D_METHOD("get_next_selected", "from"), &Tree::_get_next_selected); ClassDB::bind_method(D_METHOD("get_selected"), &Tree::get_selected); ClassDB::bind_method(D_METHOD("get_selected_column"), &Tree::get_selected_column); ClassDB::bind_method(D_METHOD("get_pressed_button"), &Tree::get_pressed_button); ClassDB::bind_method(D_METHOD("set_select_mode", "mode"), &Tree::set_select_mode); + ClassDB::bind_method(D_METHOD("get_select_mode"), &Tree::get_select_mode); ClassDB::bind_method(D_METHOD("set_columns", "amount"), &Tree::set_columns); ClassDB::bind_method(D_METHOD("get_columns"), &Tree::get_columns); @@ -3777,6 +3812,14 @@ void Tree::_bind_methods() { ClassDB::bind_method(D_METHOD("set_allow_reselect", "allow"), &Tree::set_allow_reselect); ClassDB::bind_method(D_METHOD("get_allow_reselect"), &Tree::get_allow_reselect); + ADD_PROPERTY(PropertyInfo(Variant::INT, "columns"), "set_columns", "get_columns"); + 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, "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"); + ADD_SIGNAL(MethodInfo("item_selected")); ADD_SIGNAL(MethodInfo("cell_selected")); ADD_SIGNAL(MethodInfo("multi_selected", PropertyInfo(Variant::OBJECT, "item"), PropertyInfo(Variant::INT, "column"), PropertyInfo(Variant::BOOL, "selected"))); @@ -3864,6 +3907,8 @@ Tree::Tree() { cache.click_id = -1; cache.click_item = NULL; cache.click_column = 0; + cache.hover_cell = -1; + cache.hover_index = -1; last_keypress = 0; focus_in_id = 0; diff --git a/scene/gui/tree.h b/scene/gui/tree.h index a6ebf6c3da..5af66c5faa 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef TREE_H #define TREE_H @@ -143,13 +144,13 @@ private: Vector<Cell> cells; - bool collapsed; // wont show childs + bool collapsed; // won't show children bool disable_folding; int custom_min_height; TreeItem *parent; // parent item TreeItem *next; // next in list - TreeItem *childs; //child items + TreeItem *children; //child items Tree *tree; //tree (for reference) TreeItem(Tree *p_tree); @@ -506,6 +507,10 @@ private: ValueEvaluator *evaluator; int _count_selected_items(TreeItem *p_from) const; + void _go_left(); + void _go_right(); + void _go_down(); + void _go_up(); protected: static void _bind_methods(); @@ -541,11 +546,13 @@ public: int get_column_width(int p_column) const; void set_hide_root(bool p_enabled); + bool is_root_hidden() const; TreeItem *get_next_selected(TreeItem *p_item); TreeItem *get_selected() const; int get_selected_column() const; int get_pressed_button() const; void set_select_mode(SelectMode p_mode); + SelectMode get_select_mode() const; void deselect_all(); bool is_anything_selected(); diff --git a/scene/gui/video_player.cpp b/scene/gui/video_player.cpp index 06be83bf08..88e1847533 100644 --- a/scene/gui/video_player.cpp +++ b/scene/gui/video_player.cpp @@ -27,7 +27,9 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "video_player.h" +#include "scene/scene_string_names.h" #include "os/os.h" #include "servers/audio_server.h" @@ -37,11 +39,6 @@ int VideoPlayer::sp_get_channel_count() const { return playback->get_channels(); } -void VideoPlayer::sp_set_mix_rate(int p_rate) { - - server_mix_rate = p_rate; -} - bool VideoPlayer::mix(AudioFrame *p_buffer, int p_frames) { // Check the amount resampler can really handle. @@ -163,11 +160,7 @@ void VideoPlayer::_notification(int p_notification) { bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus); - if (stream.is_null()) - return; - if (paused) - return; - if (!playback->is_playing()) + if (stream.is_null() || paused || !playback->is_playing()) return; double audio_time = USEC_TO_SEC(OS::get_singleton()->get_ticks_usec()); @@ -178,7 +171,11 @@ void VideoPlayer::_notification(int p_notification) { if (delta == 0) return; - playback->update(delta); + playback->update(delta); // playback->is_playing() returns false in the last video frame + + if (!playback->is_playing()) { + emit_signal(SceneStringNames::get_singleton()->finished); + } } break; @@ -240,7 +237,7 @@ void VideoPlayer::set_stream(const Ref<VideoStream> &p_stream) { AudioServer::get_singleton()->lock(); if (channels > 0) - resampler.setup(channels, playback->get_mix_rate(), server_mix_rate, buffering_ms, 0); + resampler.setup(channels, playback->get_mix_rate(), AudioServer::get_singleton()->get_mix_rate(), buffering_ms, 0); else resampler.clear(); AudioServer::get_singleton()->unlock(); @@ -471,13 +468,19 @@ void VideoPlayer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_video_texture"), &VideoPlayer::get_video_texture); + ADD_SIGNAL(MethodInfo("finished")); + ADD_PROPERTY(PropertyInfo(Variant::INT, "audio_track", PROPERTY_HINT_RANGE, "0,128,1"), "set_audio_track", "get_audio_track"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "VideoStream"), "set_stream", "get_stream"); //ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/loop"), "set_loop", "has_loop") ; ADD_PROPERTY(PropertyInfo(Variant::REAL, "volume_db", PROPERTY_HINT_RANGE, "-80,24,0.01"), "set_volume_db", "get_volume_db"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "volume", PROPERTY_HINT_EXP_RANGE, "0,15,0.01", 0), "set_volume", "get_volume"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "has_autoplay"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "paused"), "set_paused", "is_paused"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand"), "set_expand", "has_expand"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "buffering_msec", PROPERTY_HINT_RANGE, "10,1000"), "set_buffering_msec", "get_buffering_msec"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "stream_position", PROPERTY_HINT_RANGE, "0,1280000,0.1", 0), "set_stream_position", "get_stream_position"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus"); } @@ -493,7 +496,6 @@ VideoPlayer::VideoPlayer() { bus_index = 0; buffering_ms = 500; - server_mix_rate = 44100; // internal_stream.player=this; // stream_rid=AudioServer::get_singleton()->audio_stream_create(&internal_stream); diff --git a/scene/gui/video_player.h b/scene/gui/video_player.h index e772851b0e..5c379b5620 100644 --- a/scene/gui/video_player.h +++ b/scene/gui/video_player.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef VIDEO_PLAYER_H #define VIDEO_PLAYER_H @@ -49,7 +50,6 @@ class VideoPlayer : public Control { Ref<VideoStream> stream; int sp_get_channel_count() const; - void sp_set_mix_rate(int p_rate); //notify the stream of the mix rate bool mix(AudioFrame *p_buffer, int p_frames); RID stream_rid; @@ -68,7 +68,6 @@ class VideoPlayer : public Control { bool expand; bool loops; int buffering_ms; - int server_mix_rate; int audio_track; int bus_index; diff --git a/scene/gui/viewport_container.cpp b/scene/gui/viewport_container.cpp index e949c20c36..ac5e6020eb 100644 --- a/scene/gui/viewport_container.cpp +++ b/scene/gui/viewport_container.cpp @@ -27,8 +27,10 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "viewport_container.h" +#include "core/engine.h" #include "scene/main/viewport.h" Size2 ViewportContainer::get_minimum_size() const { @@ -138,8 +140,34 @@ void ViewportContainer::_notification(int p_what) { } } +void ViewportContainer::_input(const Ref<InputEvent> &p_event) { + + if (Engine::get_singleton()->is_editor_hint()) + return; + + Transform2D xform = get_global_transform(); + + if (stretch) { + Transform2D scale_xf; + scale_xf.scale(Vector2(shrink, shrink)); + xform *= scale_xf; + } + + Ref<InputEvent> ev = p_event->xformed_by(xform.affine_inverse()); + + for (int i = 0; i < get_child_count(); i++) { + + Viewport *c = Object::cast_to<Viewport>(get_child(i)); + if (!c || c->is_input_disabled()) + continue; + + c->input(ev); + } +} + void ViewportContainer::_bind_methods() { + ClassDB::bind_method(D_METHOD("_input", "event"), &ViewportContainer::_input); ClassDB::bind_method(D_METHOD("set_stretch", "enable"), &ViewportContainer::set_stretch); ClassDB::bind_method(D_METHOD("is_stretch_enabled"), &ViewportContainer::is_stretch_enabled); @@ -154,4 +182,5 @@ ViewportContainer::ViewportContainer() { stretch = false; shrink = 1; + set_process_input(true); } diff --git a/scene/gui/viewport_container.h b/scene/gui/viewport_container.h index c28b3075aa..45c4cd03a1 100644 --- a/scene/gui/viewport_container.h +++ b/scene/gui/viewport_container.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef VIEWPORTCONTAINER_H #define VIEWPORTCONTAINER_H @@ -47,6 +48,7 @@ public: void set_stretch(bool p_enable); bool is_stretch_enabled() const; + void _input(const Ref<InputEvent> &p_event); void set_stretch_shrink(int p_shrink); int get_stretch_shrink() const; diff --git a/scene/main/canvas_layer.cpp b/scene/main/canvas_layer.cpp index 939de346eb..8414210952 100644 --- a/scene/main/canvas_layer.cpp +++ b/scene/main/canvas_layer.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "canvas_layer.h" #include "viewport.h" @@ -34,7 +35,7 @@ void CanvasLayer::set_layer(int p_xform) { layer = p_xform; if (viewport.is_valid()) - VisualServer::get_singleton()->viewport_set_canvas_layer(viewport, canvas->get_canvas(), layer); + VisualServer::get_singleton()->viewport_set_canvas_layer(viewport, canvas, layer); } int CanvasLayer::get_layer() const { @@ -47,7 +48,7 @@ void CanvasLayer::set_transform(const Transform2D &p_xform) { transform = p_xform; locrotscale_dirty = true; if (viewport.is_valid()) - VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas->get_canvas(), transform); + VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas, transform); } Transform2D CanvasLayer::get_transform() const { @@ -60,7 +61,7 @@ void CanvasLayer::_update_xform() { transform.set_rotation_and_scale(rot, scale); transform.set_origin(ofs); if (viewport.is_valid()) - VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas->get_canvas(), transform); + VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas, transform); } void CanvasLayer::_update_locrotscale() { @@ -132,11 +133,6 @@ Vector2 CanvasLayer::get_scale() const { return scale; } -Ref<World2D> CanvasLayer::get_world_2d() const { - - return canvas; -} - void CanvasLayer::_notification(int p_what) { switch (p_what) { @@ -152,14 +148,14 @@ void CanvasLayer::_notification(int p_what) { ERR_FAIL_COND(!vp); viewport = vp->get_viewport_rid(); - VisualServer::get_singleton()->viewport_attach_canvas(viewport, canvas->get_canvas()); - VisualServer::get_singleton()->viewport_set_canvas_layer(viewport, canvas->get_canvas(), layer); - VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas->get_canvas(), transform); + VisualServer::get_singleton()->viewport_attach_canvas(viewport, canvas); + VisualServer::get_singleton()->viewport_set_canvas_layer(viewport, canvas, layer); + VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas, transform); } break; case NOTIFICATION_EXIT_TREE: { - VisualServer::get_singleton()->viewport_remove_canvas(viewport, canvas->get_canvas()); + VisualServer::get_singleton()->viewport_remove_canvas(viewport, canvas); viewport = RID(); } break; @@ -183,7 +179,7 @@ RID CanvasLayer::get_viewport() const { void CanvasLayer::set_custom_viewport(Node *p_viewport) { ERR_FAIL_NULL(p_viewport); if (is_inside_tree()) { - VisualServer::get_singleton()->viewport_remove_canvas(viewport, canvas->get_canvas()); + VisualServer::get_singleton()->viewport_remove_canvas(viewport, canvas); viewport = RID(); } @@ -204,9 +200,9 @@ void CanvasLayer::set_custom_viewport(Node *p_viewport) { viewport = vp->get_viewport_rid(); - VisualServer::get_singleton()->viewport_attach_canvas(viewport, canvas->get_canvas()); - VisualServer::get_singleton()->viewport_set_canvas_layer(viewport, canvas->get_canvas(), layer); - VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas->get_canvas(), transform); + VisualServer::get_singleton()->viewport_attach_canvas(viewport, canvas); + VisualServer::get_singleton()->viewport_set_canvas_layer(viewport, canvas, layer); + VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas, transform); } } @@ -224,6 +220,10 @@ int CanvasLayer::get_sort_index() { return sort_index++; } +RID CanvasLayer::get_canvas() const { + + return canvas; +} void CanvasLayer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_layer", "layer"), &CanvasLayer::set_layer); @@ -247,14 +247,17 @@ void CanvasLayer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_custom_viewport", "viewport"), &CanvasLayer::set_custom_viewport); ClassDB::bind_method(D_METHOD("get_custom_viewport"), &CanvasLayer::get_custom_viewport); - ClassDB::bind_method(D_METHOD("get_world_2d"), &CanvasLayer::get_world_2d); + ClassDB::bind_method(D_METHOD("get_canvas"), &CanvasLayer::get_canvas); //ClassDB::bind_method(D_METHOD("get_viewport"),&CanvasLayer::get_viewport); ADD_PROPERTY(PropertyInfo(Variant::INT, "layer", PROPERTY_HINT_RANGE, "-128,128,1"), "set_layer", "get_layer"); //ADD_PROPERTY( PropertyInfo(Variant::MATRIX32,"transform",PROPERTY_HINT_RANGE),"set_transform","get_transform") ; ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "rotation"), "set_rotation_degrees", "get_rotation_degrees"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "rotation_degrees", PROPERTY_HINT_RANGE, "-1440,1440,0.1", PROPERTY_USAGE_EDITOR), "set_rotation_degrees", "get_rotation_degrees"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_rotation", "get_rotation"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scale"), "set_scale", "get_scale"); + ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "transform"), "set_transform", "get_transform"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "custom_viewport", PROPERTY_HINT_RESOURCE_TYPE, "Viewport", 0), "set_custom_viewport", "get_custom_viewport"); } CanvasLayer::CanvasLayer() { @@ -264,8 +267,13 @@ CanvasLayer::CanvasLayer() { rot = 0; locrotscale_dirty = false; layer = 1; - canvas = Ref<World2D>(memnew(World2D)); + canvas = VS::get_singleton()->canvas_create(); custom_viewport = NULL; custom_viewport_id = 0; sort_index = 0; } + +CanvasLayer::~CanvasLayer() { + + VS::get_singleton()->free(canvas); +} diff --git a/scene/main/canvas_layer.h b/scene/main/canvas_layer.h index 81d2616cdc..aae23fbb12 100644 --- a/scene/main/canvas_layer.h +++ b/scene/main/canvas_layer.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef CANVAS_LAYER_H #define CANVAS_LAYER_H @@ -44,7 +45,7 @@ class CanvasLayer : public Node { real_t rot; int layer; Transform2D transform; - Ref<World2D> canvas; + RID canvas; ObjectID custom_viewport_id; // to check validity Viewport *custom_viewport; @@ -80,8 +81,6 @@ public: void set_scale(const Size2 &p_scale); Size2 get_scale() const; - Ref<World2D> get_world_2d() const; - Size2 get_viewport_size() const; RID get_viewport() const; @@ -92,7 +91,10 @@ public: void reset_sort_index(); int get_sort_index(); + RID get_canvas() const; + CanvasLayer(); + ~CanvasLayer(); }; #endif // CANVAS_LAYER_H diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp index e99a21fbce..ae21775c55 100644 --- a/scene/main/http_request.cpp +++ b/scene/main/http_request.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "http_request.h" #include "version.h" @@ -120,7 +121,7 @@ Error HTTPRequest::request(const String &p_url, const Vector<String> &p_custom_h } if (!has_user_agent) { - headers.push_back("User-Agent: GodotEngine/" + String(VERSION_MKSTRING) + " (" + OS::get_singleton()->get_name() + ")"); + headers.push_back("User-Agent: GodotEngine/" + String(VERSION_FULL_BUILD) + " (" + OS::get_singleton()->get_name() + ")"); } if (!has_accept) { @@ -523,6 +524,7 @@ void HTTPRequest::_bind_methods() { ClassDB::bind_method(D_METHOD("_redirect_request"), &HTTPRequest::_redirect_request); ClassDB::bind_method(D_METHOD("_request_done"), &HTTPRequest::_request_done); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "download_file", PROPERTY_HINT_FILE), "set_download_file", "get_download_file"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_threads"), "set_use_threads", "is_using_threads"); ADD_PROPERTY(PropertyInfo(Variant::INT, "body_size_limit", PROPERTY_HINT_RANGE, "-1,2000000000"), "set_body_size_limit", "get_body_size_limit"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_redirects", PROPERTY_HINT_RANGE, "-1,64"), "set_max_redirects", "get_max_redirects"); diff --git a/scene/main/http_request.h b/scene/main/http_request.h index 1777a7f8f1..eb5d020bc5 100644 --- a/scene/main/http_request.h +++ b/scene/main/http_request.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef HTTPREQUEST_H #define HTTPREQUEST_H diff --git a/scene/main/instance_placeholder.cpp b/scene/main/instance_placeholder.cpp index a2c29d8209..1443d5efbf 100644 --- a/scene/main/instance_placeholder.cpp +++ b/scene/main/instance_placeholder.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "instance_placeholder.h" #include "io/resource_loader.h" @@ -51,6 +52,7 @@ bool InstancePlaceholder::_get(const StringName &p_name, Variant &r_ret) const { } return false; } + void InstancePlaceholder::_get_property_list(List<PropertyInfo> *p_list) const { for (const List<PropSet>::Element *E = stored_values.front(); E; E = E->next()) { @@ -72,13 +74,14 @@ String InstancePlaceholder::get_instance_path() const { return path; } -void InstancePlaceholder::replace_by_instance(const Ref<PackedScene> &p_custom_scene) { - ERR_FAIL_COND(!is_inside_tree()); +Node *InstancePlaceholder::create_instance(bool p_replace, const Ref<PackedScene> &p_custom_scene) { + + ERR_FAIL_COND_V(!is_inside_tree(), NULL); Node *base = get_parent(); if (!base) - return; + return NULL; Ref<PackedScene> ps; if (p_custom_scene.is_valid()) @@ -87,7 +90,7 @@ void InstancePlaceholder::replace_by_instance(const Ref<PackedScene> &p_custom_s ps = ResourceLoader::load(path, "PackedScene"); if (!ps.is_valid()) - return; + return NULL; Node *scene = ps->instance(); scene->set_name(get_name()); int pos = get_position_in_parent(); @@ -96,11 +99,20 @@ void InstancePlaceholder::replace_by_instance(const Ref<PackedScene> &p_custom_s scene->set(E->get().name, E->get().value); } - queue_delete(); + if (p_replace) { + queue_delete(); + base->remove_child(this); + } - base->remove_child(this); base->add_child(scene); base->move_child(scene, pos); + + return scene; +} + +void InstancePlaceholder::replace_by_instance(const Ref<PackedScene> &p_custom_scene) { + //Deprecated by + create_instance(true, p_custom_scene); } Dictionary InstancePlaceholder::get_stored_values(bool p_with_order) { @@ -123,6 +135,7 @@ Dictionary InstancePlaceholder::get_stored_values(bool p_with_order) { void InstancePlaceholder::_bind_methods() { ClassDB::bind_method(D_METHOD("get_stored_values", "with_order"), &InstancePlaceholder::get_stored_values, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("create_instance", "replace", "custom_scene"), &InstancePlaceholder::create_instance, DEFVAL(false), DEFVAL(Variant())); ClassDB::bind_method(D_METHOD("replace_by_instance", "custom_scene"), &InstancePlaceholder::replace_by_instance, DEFVAL(Variant())); ClassDB::bind_method(D_METHOD("get_instance_path"), &InstancePlaceholder::get_instance_path); } diff --git a/scene/main/instance_placeholder.h b/scene/main/instance_placeholder.h index 9b2f1e1168..2158257c93 100644 --- a/scene/main/instance_placeholder.h +++ b/scene/main/instance_placeholder.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef INSTANCE_PLACEHOLDER_H #define INSTANCE_PLACEHOLDER_H @@ -59,6 +60,7 @@ public: Dictionary get_stored_values(bool p_with_order = false); + Node *create_instance(bool p_replace = false, const Ref<PackedScene> &p_custom_scene = Ref<PackedScene>()); void replace_by_instance(const Ref<PackedScene> &p_custom_scene = Ref<PackedScene>()); InstancePlaceholder(); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index d82000e3fa..67ee246252 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "node.h" #include "core/core_string_names.h" @@ -135,7 +136,6 @@ void Node::_notification(int p_notification) { get_script_instance()->call_multilevel_reversed(SceneStringNames::get_singleton()->_ready, NULL, 0); } - //emit_signal(SceneStringNames::get_singleton()->enter_tree); } break; case NOTIFICATION_POSTINITIALIZE: { @@ -179,11 +179,12 @@ void Node::_propagate_ready() { if (data.ready_first) { data.ready_first = false; notification(NOTIFICATION_READY); + emit_signal(SceneStringNames::get_singleton()->ready); } } void Node::_propagate_enter_tree() { - // this needs to happen to all childs before any enter_tree + // this needs to happen to all children before any enter_tree if (data.parent) { data.tree = data.parent->data.tree; @@ -274,7 +275,7 @@ void Node::_propagate_exit_tree() { get_script_instance()->call_multilevel(SceneStringNames::get_singleton()->_exit_tree, NULL, 0); } - emit_signal(SceneStringNames::get_singleton()->tree_exited); + emit_signal(SceneStringNames::get_singleton()->tree_exiting); notification(NOTIFICATION_EXIT_TREE, true); if (data.tree) @@ -296,6 +297,8 @@ void Node::_propagate_exit_tree() { data.ready_notified = false; data.tree = NULL; data.depth = -1; + + emit_signal(SceneStringNames::get_singleton()->tree_exited); } void Node::move_child(Node *p_child, int p_pos) { @@ -474,7 +477,7 @@ bool Node::is_network_master() const { ERR_FAIL_COND_V(!is_inside_tree(), false); - return get_tree()->get_network_unique_id() == data.network_master; + return get_multiplayer_api()->get_network_unique_id() == data.network_master; } /***** RPC CONFIG ********/ @@ -664,200 +667,16 @@ Variant Node::_rpc_unreliable_id_bind(const Variant **p_args, int p_argcount, Va } void Node::rpcp(int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount) { - ERR_FAIL_COND(!is_inside_tree()); - - bool skip_rpc = false; - bool call_local_native = false; - bool call_local_script = false; - - if (p_peer_id == 0 || p_peer_id == get_tree()->get_network_unique_id() || (p_peer_id < 0 && p_peer_id != -get_tree()->get_network_unique_id())) { - //check that send mode can use local call - - Map<StringName, RPCMode>::Element *E = data.rpc_methods.find(p_method); - if (E) { - - switch (E->get()) { - - case RPC_MODE_DISABLED: { - //do nothing - } break; - case RPC_MODE_REMOTE: { - //do nothing also, no need to call local - } break; - case RPC_MODE_SYNC: { - //call it, sync always results in call - call_local_native = true; - } break; - case RPC_MODE_MASTER: { - call_local_native = is_network_master(); - if (call_local_native) { - skip_rpc = true; //no other master so.. - } - } break; - case RPC_MODE_SLAVE: { - call_local_native = !is_network_master(); - } break; - } - } - - if (call_local_native) { - // done below - } else if (get_script_instance()) { - //attempt with script - ScriptInstance::RPCMode rpc_mode = get_script_instance()->get_rpc_mode(p_method); - - switch (rpc_mode) { - - case ScriptInstance::RPC_MODE_DISABLED: { - //do nothing - } break; - case ScriptInstance::RPC_MODE_REMOTE: { - //do nothing also, no need to call local - } break; - case ScriptInstance::RPC_MODE_SYNC: { - //call it, sync always results in call - call_local_script = true; - } break; - case ScriptInstance::RPC_MODE_MASTER: { - call_local_script = is_network_master(); - if (call_local_script) { - skip_rpc = true; //no other master so.. - } - } break; - case ScriptInstance::RPC_MODE_SLAVE: { - call_local_script = !is_network_master(); - } break; - } - } - } - - if (!skip_rpc) { - get_tree()->_rpc(this, p_peer_id, p_unreliable, false, p_method, p_arg, p_argcount); - } - - if (call_local_native) { - Variant::CallError ce; - call(p_method, p_arg, p_argcount, ce); - if (ce.error != Variant::CallError::CALL_OK) { - String error = Variant::get_call_error_text(this, p_method, p_arg, p_argcount, ce); - error = "rpc() aborted in local call: - " + error; - ERR_PRINTS(error); - return; - } - } - - if (call_local_script) { - Variant::CallError ce; - ce.error = Variant::CallError::CALL_OK; - get_script_instance()->call(p_method, p_arg, p_argcount, ce); - if (ce.error != Variant::CallError::CALL_OK) { - String error = Variant::get_call_error_text(this, p_method, p_arg, p_argcount, ce); - error = "rpc() aborted in script local call: - " + error; - ERR_PRINTS(error); - return; - } - } + get_multiplayer_api()->rpcp(this, p_peer_id, p_unreliable, p_method, p_arg, p_argcount); } -/******** RSET *********/ - void Node::rsetp(int p_peer_id, bool p_unreliable, const StringName &p_property, const Variant &p_value) { - ERR_FAIL_COND(!is_inside_tree()); - - bool skip_rset = false; - - if (p_peer_id == 0 || p_peer_id == get_tree()->get_network_unique_id() || (p_peer_id < 0 && p_peer_id != -get_tree()->get_network_unique_id())) { - //check that send mode can use local call - - bool set_local = false; - - Map<StringName, RPCMode>::Element *E = data.rpc_properties.find(p_property); - if (E) { - - switch (E->get()) { - - case RPC_MODE_DISABLED: { - //do nothing - } break; - case RPC_MODE_REMOTE: { - //do nothing also, no need to call local - } break; - case RPC_MODE_SYNC: { - //call it, sync always results in call - set_local = true; - } break; - case RPC_MODE_MASTER: { - set_local = is_network_master(); - if (set_local) { - skip_rset = true; - } - - } break; - case RPC_MODE_SLAVE: { - set_local = !is_network_master(); - } break; - } - } - - if (set_local) { - bool valid; - set(p_property, p_value, &valid); - - if (!valid) { - String error = "rset() aborted in local set, property not found: - " + String(p_property); - ERR_PRINTS(error); - return; - } - } else if (get_script_instance()) { - //attempt with script - ScriptInstance::RPCMode rpc_mode = get_script_instance()->get_rset_mode(p_property); - - switch (rpc_mode) { - - case ScriptInstance::RPC_MODE_DISABLED: { - //do nothing - } break; - case ScriptInstance::RPC_MODE_REMOTE: { - //do nothing also, no need to call local - } break; - case ScriptInstance::RPC_MODE_SYNC: { - //call it, sync always results in call - set_local = true; - } break; - case ScriptInstance::RPC_MODE_MASTER: { - set_local = is_network_master(); - if (set_local) { - skip_rset = true; - } - } break; - case ScriptInstance::RPC_MODE_SLAVE: { - set_local = !is_network_master(); - } break; - } - - if (set_local) { - - bool valid = get_script_instance()->set(p_property, p_value); - - if (!valid) { - String error = "rset() aborted in local script set, property not found: - " + String(p_property); - ERR_PRINTS(error); - return; - } - } - } - } - - if (skip_rset) - return; - - const Variant *vptr = &p_value; - - get_tree()->_rpc(this, p_peer_id, p_unreliable, true, p_property, &vptr, 1); + get_multiplayer_api()->rsetp(this, p_peer_id, p_unreliable, p_property, p_value); } +/******** RSET *********/ void Node::rset(const StringName &p_property, const Variant &p_value) { rsetp(0, false, p_property, p_value); @@ -879,6 +698,30 @@ void Node::rset_unreliable_id(int p_peer_id, const StringName &p_property, const } //////////// end of rpc +Ref<MultiplayerAPI> Node::get_multiplayer_api() const { + if (multiplayer_api.is_valid()) + return multiplayer_api; + if (!is_inside_tree()) + return Ref<MultiplayerAPI>(); + return get_tree()->get_multiplayer_api(); +} + +Ref<MultiplayerAPI> Node::get_custom_multiplayer_api() const { + return multiplayer_api; +} + +void Node::set_custom_multiplayer_api(Ref<MultiplayerAPI> p_multiplayer_api) { + + multiplayer_api = p_multiplayer_api; +} + +const Map<StringName, Node::RPCMode>::Element *Node::get_node_rpc_mode(const StringName &p_method) { + return data.rpc_methods.find(p_method); +} + +const Map<StringName, Node::RPCMode>::Element *Node::get_node_rset_mode(const StringName &p_property) { + return data.rpc_properties.find(p_property); +} bool Node::can_call_rpc(const StringName &p_method, int p_from) const { @@ -1136,9 +979,23 @@ void Node::_set_name_nocheck(const StringName &p_name) { data.name = p_name; } +String Node::invalid_character = ". : @ / \""; + +bool Node::_validate_node_name(String &p_name) { + String name = p_name; + Vector<String> chars = Node::invalid_character.split(" "); + for (int i = 0; i < chars.size(); i++) { + name = name.replace(chars[i], ""); + } + bool is_valid = name == p_name; + p_name = name; + return is_valid; +} + void Node::set_name(const String &p_name) { - String name = p_name.replace(":", "").replace("/", "").replace("@", ""); + String name = p_name; + _validate_node_name(name); ERR_FAIL_COND(name == ""); data.name = name; @@ -1199,13 +1056,13 @@ void Node::_validate_child_name(Node *p_child, bool p_force_human_readable) { unique = false; } else { //check if exists - Node **childs = data.children.ptrw(); + Node **children = data.children.ptrw(); int cc = data.children.size(); for (int i = 0; i < cc; i++) { - if (childs[i] == p_child) + if (children[i] == p_child) continue; - if (childs[i]->data.name == p_child->data.name) { + if (children[i]->data.name == p_child->data.name) { unique = false; break; } @@ -1308,7 +1165,7 @@ void Node::_add_child_nocheck(Node *p_child, const StringName &p_name) { } /* Notify */ - //recognize childs created in this node constructor + //recognize children created in this node constructor p_child->data.parent_owned = data.in_constructor; add_child_notify(p_child); } @@ -1865,11 +1722,18 @@ bool Node::has_persistent_groups() const { return false; } -void Node::_print_tree(const Node *p_node) { +void Node::_print_tree_pretty(const String prefix, const bool last) { - print_line(String(p_node->get_path_to(this))); - for (int i = 0; i < data.children.size(); i++) - data.children[i]->_print_tree(p_node); + String new_prefix = last ? String::utf8(" ┖╴") : String::utf8(" ┠╴"); + print_line(prefix + new_prefix + String(get_name())); + for (int i = 0; i < data.children.size(); i++) { + new_prefix = last ? String::utf8(" ") : String::utf8(" ┃ "); + data.children[i]->_print_tree_pretty(prefix + new_prefix, i == data.children.size() - 1); + } +} + +void Node::print_tree_pretty() { + _print_tree_pretty("", true); } void Node::print_tree() { @@ -1877,6 +1741,12 @@ void Node::print_tree() { _print_tree(this); } +void Node::_print_tree(const Node *p_node) { + print_line(String(p_node->get_path_to(this))); + for (int i = 0; i < data.children.size(); i++) + data.children[i]->_print_tree(p_node); +} + void Node::_propagate_reverse_notification(int p_notification) { data.blocked++; @@ -2007,7 +1877,7 @@ void Node::set_editable_instance(Node *p_node, bool p_editable) { if (!p_editable) { data.editable_instances.erase(p); // Avoid this flag being needlessly saved; - // also give more visual feedback if editable children is reenabled + // also give more visual feedback if editable children is re-enabled set_display_folded(false); } else { data.editable_instances[p] = true; @@ -2160,15 +2030,18 @@ Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const if (name == script_property_name) continue; - Variant value = N->get()->get(name); - // Duplicate dictionaries and arrays, mainly needed for __meta__ - if (value.get_type() == Variant::DICTIONARY) { - value = Dictionary(value).duplicate(); - } else if (value.get_type() == Variant::ARRAY) { - value = Array(value).duplicate(); - } + Variant value = N->get()->get(name).duplicate(true); - current_node->set(name, value); + if (E->get().usage & PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE) { + + Resource *res = Object::cast_to<Resource>(value); + if (res) // Duplicate only if it's a resource + current_node->set(name, res->duplicate()); + + } else { + + current_node->set(name, value); + } } } @@ -2301,13 +2174,7 @@ void Node::_duplicate_and_reown(Node *p_new_parent, const Map<Node *, Node *> &p continue; String name = E->get().name; - Variant value = get(name); - // Duplicate dictionaries and arrays, mainly needed for __meta__ - if (value.get_type() == Variant::DICTIONARY) { - value = Dictionary(value).duplicate(); - } else if (value.get_type() == Variant::ARRAY) { - value = Array(value).duplicate(); - } + Variant value = get(name).duplicate(true); node->set(name, value); } @@ -2490,7 +2357,10 @@ void Node::replace_by(Node *p_node, bool p_keep_data) { Node *child = get_child(0); remove_child(child); - p_node->add_child(child); + if (!child->is_owned_by_parent()) { + // add the custom children to the p_node + p_node->add_child(child); + } } p_node->set_owner(owner); @@ -2711,18 +2581,21 @@ Array Node::_get_children() const { return arr; } -#ifdef TOOLS_ENABLED void Node::set_import_path(const NodePath &p_import_path) { +#ifdef TOOLS_ENABLED data.import_path = p_import_path; +#endif } NodePath Node::get_import_path() const { +#ifdef TOOLS_ENABLED return data.import_path; -} - +#else + return NodePath(); #endif +} static void _add_nodes_to_options(const Node *p_base, const Node *p_node, List<String> *r_options) { @@ -2825,6 +2698,7 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("remove_and_skip"), &Node::remove_and_skip); ClassDB::bind_method(D_METHOD("get_index"), &Node::get_index); ClassDB::bind_method(D_METHOD("print_tree"), &Node::print_tree); + ClassDB::bind_method(D_METHOD("print_tree_pretty"), &Node::print_tree_pretty); ClassDB::bind_method(D_METHOD("set_filename", "filename"), &Node::set_filename); ClassDB::bind_method(D_METHOD("get_filename"), &Node::get_filename); ClassDB::bind_method(D_METHOD("propagate_notification", "what"), &Node::propagate_notification); @@ -2874,15 +2748,15 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("is_network_master"), &Node::is_network_master); + ClassDB::bind_method(D_METHOD("get_multiplayer_api"), &Node::get_multiplayer_api); + ClassDB::bind_method(D_METHOD("get_custom_multiplayer_api"), &Node::get_custom_multiplayer_api); + ClassDB::bind_method(D_METHOD("set_custom_multiplayer_api", "api"), &Node::set_custom_multiplayer_api); ClassDB::bind_method(D_METHOD("rpc_config", "method", "mode"), &Node::rpc_config); ClassDB::bind_method(D_METHOD("rset_config", "property", "mode"), &Node::rset_config); -#ifdef TOOLS_ENABLED ClassDB::bind_method(D_METHOD("_set_import_path", "import_path"), &Node::set_import_path); ClassDB::bind_method(D_METHOD("_get_import_path"), &Node::get_import_path); - ADD_PROPERTYNZ(PropertyInfo(Variant::NODE_PATH, "_import_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_import_path", "_get_import_path"); - -#endif + ADD_PROPERTYNZ(PropertyInfo(Variant::NODE_PATH, "_import_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_import_path", "_get_import_path"); { MethodInfo mi; @@ -2940,8 +2814,10 @@ void Node::_bind_methods() { BIND_ENUM_CONSTANT(DUPLICATE_SCRIPTS); BIND_ENUM_CONSTANT(DUPLICATE_USE_INSTANCING); + ADD_SIGNAL(MethodInfo("ready")); ADD_SIGNAL(MethodInfo("renamed")); ADD_SIGNAL(MethodInfo("tree_entered")); + ADD_SIGNAL(MethodInfo("tree_exiting")); ADD_SIGNAL(MethodInfo("tree_exited")); //ADD_PROPERTYNZ( PropertyInfo( Variant::BOOL, "process/process" ),"set_process","is_processing") ; @@ -2950,7 +2826,12 @@ void Node::_bind_methods() { //ADD_PROPERTYNZ( PropertyInfo( Variant::BOOL, "process/unhandled_input" ), "set_process_unhandled_input","is_processing_unhandled_input" ) ; ADD_GROUP("Pause", "pause_"); ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "pause_mode", PROPERTY_HINT_ENUM, "Inherit,Stop,Process"), "set_pause_mode", "get_pause_mode"); - ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "editor/display_folded", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_display_folded", "is_displayed_folded"); + ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "editor/display_folded", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_display_folded", "is_displayed_folded"); + ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "name", PROPERTY_HINT_NONE, "", 0), "set_name", "get_name"); + ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "filename", PROPERTY_HINT_NONE, "", 0), "set_filename", "get_filename"); + ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "owner", PROPERTY_HINT_RESOURCE_TYPE, "Node", 0), "set_owner", "get_owner"); + ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "multiplayer_api", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", 0), "", "get_multiplayer_api"); + ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "custom_multiplayer_api", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", 0), "set_custom_multiplayer_api", "get_custom_multiplayer_api"); BIND_VMETHOD(MethodInfo("_process", PropertyInfo(Variant::REAL, "delta"))); BIND_VMETHOD(MethodInfo("_physics_process", PropertyInfo(Variant::REAL, "delta"))); diff --git a/scene/main/node.h b/scene/main/node.h index 2aca84324d..b5a956116d 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef NODE_H #define NODE_H @@ -103,7 +104,7 @@ private: StringName name; SceneTree *tree; bool inside_tree; - bool ready_notified; //this is a small hack, so if a node is added during _ready() to the tree, it corretly gets the _ready() notification + bool ready_notified; //this is a small hack, so if a node is added during _ready() to the tree, it correctly gets the _ready() notification bool ready_first; #ifdef TOOLS_ENABLED NodePath import_path; //path used when imported, used by scene editors to keep tracking @@ -150,6 +151,9 @@ private: NAME_CASING_SNAKE_CASE }; + Ref<MultiplayerAPI> multiplayer_api; + + void _print_tree_pretty(const String prefix, const bool last); void _print_tree(const Node *p_node); Node *_get_node(const NodePath &p_path) const; @@ -186,6 +190,12 @@ private: void _set_tree(SceneTree *p_tree); +#ifdef TOOLS_ENABLED + friend class SceneTreeEditor; +#endif + static String invalid_character; + static bool _validate_node_name(String &p_name); + protected: void _block() { data.blocked++; } void _unblock() { data.blocked--; } @@ -286,6 +296,7 @@ public: int get_index() const; void print_tree(); + void print_tree_pretty(); void set_filename(const String &p_filename); String get_filename() const; @@ -363,16 +374,14 @@ public: void queue_delete(); - //shitty hacks for speed + //hacks for speed static void set_human_readable_collision_renaming(bool p_enabled); static void init_node_hrcr(); void force_parent_owned() { data.parent_owned = true; } //hack to avoid duplicate nodes -#ifdef TOOLS_ENABLED void set_import_path(const NodePath &p_import_path); //path used when imported, used by scene editors to keep tracking NodePath get_import_path() const; -#endif bool is_owned_by_parent() const; @@ -402,15 +411,20 @@ public: void rpc_id(int p_peer_id, const StringName &p_method, VARIANT_ARG_LIST); //rpc call, honors RPCMode void rpc_unreliable_id(int p_peer_id, const StringName &p_method, VARIANT_ARG_LIST); //rpc call, honors RPCMode - void rpcp(int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount); - void rset(const StringName &p_property, const Variant &p_value); //remote set call, honors RPCMode void rset_unreliable(const StringName &p_property, const Variant &p_value); //remote set call, honors RPCMode void rset_id(int p_peer_id, const StringName &p_property, const Variant &p_value); //remote set call, honors RPCMode void rset_unreliable_id(int p_peer_id, const StringName &p_property, const Variant &p_value); //remote set call, honors RPCMode + void rpcp(int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount); void rsetp(int p_peer_id, bool p_unreliable, const StringName &p_property, const Variant &p_value); + Ref<MultiplayerAPI> get_multiplayer_api() const; + Ref<MultiplayerAPI> get_custom_multiplayer_api() const; + void set_custom_multiplayer_api(Ref<MultiplayerAPI> p_multiplayer_api); + const Map<StringName, RPCMode>::Element *get_node_rpc_mode(const StringName &p_method); + const Map<StringName, RPCMode>::Element *get_node_rset_mode(const StringName &p_property); + bool can_call_rpc(const StringName &p_method, int p_from) const; bool can_call_rset(const StringName &p_property, int p_from) const; diff --git a/scene/main/resource_preloader.cpp b/scene/main/resource_preloader.cpp index 76fa40cc7c..dbe7daa604 100644 --- a/scene/main/resource_preloader.cpp +++ b/scene/main/resource_preloader.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "resource_preloader.h" void ResourcePreloader::_set_resources(const Array &p_data) { @@ -160,7 +161,7 @@ void ResourcePreloader::_bind_methods() { ClassDB::bind_method(D_METHOD("get_resource", "name"), &ResourcePreloader::get_resource); ClassDB::bind_method(D_METHOD("get_resource_list"), &ResourcePreloader::_get_resource_list); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "resources", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_resources", "_get_resources"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "resources", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_resources", "_get_resources"); } ResourcePreloader::ResourcePreloader() { diff --git a/scene/main/resource_preloader.h b/scene/main/resource_preloader.h index 479cde51ae..98c7b21f37 100644 --- a/scene/main/resource_preloader.h +++ b/scene/main/resource_preloader.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef RESOURCE_PRELOADER_H #define RESOURCE_PRELOADER_H diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 632d912e43..011087b487 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -27,11 +27,13 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "scene_tree.h" #include "editor/editor_node.h" #include "io/marshalls.h" #include "io/resource_loader.h" +#include "main/input_default.h" #include "message_queue.h" #include "node.h" #include "os/keyboard.h" @@ -54,6 +56,8 @@ void SceneTreeTimer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_time_left", "time"), &SceneTreeTimer::set_time_left); ClassDB::bind_method(D_METHOD("get_time_left"), &SceneTreeTimer::get_time_left); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "time_left"), "set_time_left", "get_time_left"); + ADD_SIGNAL(MethodInfo("timeout")); } @@ -481,7 +485,7 @@ bool SceneTree::idle(float p_time) { idle_process_time = p_time; - _network_poll(); + multiplayer_api->poll(); emit_signal("idle_frame"); @@ -495,14 +499,14 @@ bool SceneTree::idle(float p_time) { Size2 win_size = Size2(OS::get_singleton()->get_video_mode().width, OS::get_singleton()->get_video_mode().height); if (win_size != last_screen_size) { + last_screen_size = win_size; + _update_root_rect(); + if (use_font_oversampling) { DynamicFontAtSize::font_oversampling = OS::get_singleton()->get_window_size().width / root->get_visible_rect().size.width; DynamicFont::update_oversampling(); } - last_screen_size = win_size; - _update_root_rect(); - emit_signal("screen_resized"); } @@ -612,9 +616,18 @@ void SceneTree::_notification(int p_notification) { } } break; case NOTIFICATION_OS_MEMORY_WARNING: + case NOTIFICATION_WM_MOUSE_ENTER: + case NOTIFICATION_WM_MOUSE_EXIT: case NOTIFICATION_WM_FOCUS_IN: case NOTIFICATION_WM_FOCUS_OUT: { + if (p_notification == NOTIFICATION_WM_FOCUS_IN) { + InputDefault *id = Object::cast_to<InputDefault>(Input::get_singleton()); + if (id) { + id->ensure_touch_mouse_raised(); + } + } + get_root()->propagate_notification(p_notification); } break; case NOTIFICATION_TRANSLATION_CHANGED: { @@ -1192,16 +1205,20 @@ void SceneTree::set_screen_stretch(StretchMode p_mode, StretchAspect p_aspect, c _update_root_rect(); } -#ifdef TOOLS_ENABLED void SceneTree::set_edited_scene_root(Node *p_node) { +#ifdef TOOLS_ENABLED edited_scene_root = p_node; +#endif } Node *SceneTree::get_edited_scene_root() const { +#ifdef TOOLS_ENABLED return edited_scene_root; -} +#else + return NULL; #endif +} void SceneTree::set_current_scene(Node *p_scene) { @@ -1628,16 +1645,11 @@ Ref<SceneTreeTimer> SceneTree::create_timer(float p_delay_sec, bool p_process_pa void SceneTree::_network_peer_connected(int p_id) { - connected_peers.insert(p_id); - path_get_cache.insert(p_id, PathGetCache()); - emit_signal("network_peer_connected", p_id); } void SceneTree::_network_peer_disconnected(int p_id) { - connected_peers.erase(p_id); - path_get_cache.erase(p_id); //I no longer need your cache, sorry emit_signal("network_peer_disconnected", p_id); } @@ -1656,466 +1668,70 @@ void SceneTree::_server_disconnected() { emit_signal("server_disconnected"); } -void SceneTree::set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_network_peer) { - if (network_peer.is_valid()) { - network_peer->disconnect("peer_connected", this, "_network_peer_connected"); - network_peer->disconnect("peer_disconnected", this, "_network_peer_disconnected"); - network_peer->disconnect("connection_succeeded", this, "_connected_to_server"); - network_peer->disconnect("connection_failed", this, "_connection_failed"); - network_peer->disconnect("server_disconnected", this, "_server_disconnected"); - connected_peers.clear(); - path_get_cache.clear(); - path_send_cache.clear(); - last_send_cache_id = 1; +Ref<MultiplayerAPI> SceneTree::get_multiplayer_api() const { + return multiplayer_api; +} + +void SceneTree::set_multiplayer_api(Ref<MultiplayerAPI> p_multiplayer_api) { + ERR_FAIL_COND(!p_multiplayer_api.is_valid()); + + if (multiplayer_api.is_valid()) { + multiplayer_api->disconnect("network_peer_connected", this, "_network_peer_connected"); + multiplayer_api->disconnect("network_peer_disconnected", this, "_network_peer_disconnected"); + multiplayer_api->disconnect("connected_to_server", this, "_connected_to_server"); + multiplayer_api->disconnect("connection_failed", this, "_connection_failed"); + multiplayer_api->disconnect("server_disconnected", this, "_server_disconnected"); } - ERR_EXPLAIN("Supplied NetworkedNetworkPeer must be connecting or connected."); - ERR_FAIL_COND(p_network_peer.is_valid() && p_network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED); + multiplayer_api = p_multiplayer_api; + multiplayer_api->set_root_node(root); - network_peer = p_network_peer; + multiplayer_api->connect("network_peer_connected", this, "_network_peer_connected"); + multiplayer_api->connect("network_peer_disconnected", this, "_network_peer_disconnected"); + multiplayer_api->connect("connected_to_server", this, "_connected_to_server"); + multiplayer_api->connect("connection_failed", this, "_connection_failed"); + multiplayer_api->connect("server_disconnected", this, "_server_disconnected"); +} - if (network_peer.is_valid()) { - network_peer->connect("peer_connected", this, "_network_peer_connected"); - network_peer->connect("peer_disconnected", this, "_network_peer_disconnected"); - network_peer->connect("connection_succeeded", this, "_connected_to_server"); - network_peer->connect("connection_failed", this, "_connection_failed"); - network_peer->connect("server_disconnected", this, "_server_disconnected"); - } +void SceneTree::set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_network_peer) { + + multiplayer_api->set_network_peer(p_network_peer); +} + +Ref<NetworkedMultiplayerPeer> SceneTree::get_network_peer() const { + + return multiplayer_api->get_network_peer(); } bool SceneTree::is_network_server() const { - ERR_FAIL_COND_V(!network_peer.is_valid(), false); - return network_peer->is_server(); + return multiplayer_api->is_network_server(); } bool SceneTree::has_network_peer() const { - return network_peer.is_valid(); + return multiplayer_api->has_network_peer(); } int SceneTree::get_network_unique_id() const { - ERR_FAIL_COND_V(!network_peer.is_valid(), 0); - return network_peer->get_unique_id(); + return multiplayer_api->get_network_unique_id(); } Vector<int> SceneTree::get_network_connected_peers() const { - ERR_FAIL_COND_V(!network_peer.is_valid(), Vector<int>()); - - Vector<int> ret; - for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) { - ret.push_back(E->get()); - } - return ret; + return multiplayer_api->get_network_connected_peers(); } int SceneTree::get_rpc_sender_id() const { - return rpc_sender_id; + return multiplayer_api->get_rpc_sender_id(); } void SceneTree::set_refuse_new_network_connections(bool p_refuse) { - ERR_FAIL_COND(!network_peer.is_valid()); - network_peer->set_refuse_new_connections(p_refuse); + multiplayer_api->set_refuse_new_network_connections(p_refuse); } bool SceneTree::is_refusing_new_network_connections() const { - - ERR_FAIL_COND_V(!network_peer.is_valid(), false); - - return network_peer->is_refusing_new_connections(); -} - -void SceneTree::_rpc(Node *p_from, int p_to, bool p_unreliable, bool p_set, const StringName &p_name, const Variant **p_arg, int p_argcount) { - - if (network_peer.is_null()) { - ERR_EXPLAIN("Attempt to remote call/set when networking is not active in SceneTree."); - ERR_FAIL(); - } - - if (network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_CONNECTING) { - ERR_EXPLAIN("Attempt to remote call/set when networking is not connected yet in SceneTree."); - ERR_FAIL(); - } - - if (network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED) { - ERR_EXPLAIN("Attempt to remote call/set when networking is disconnected."); - ERR_FAIL(); - } - - if (p_argcount > 255) { - ERR_EXPLAIN("Too many arguments >255."); - ERR_FAIL(); - } - - if (p_to != 0 && !connected_peers.has(ABS(p_to))) { - if (p_to == get_network_unique_id()) { - ERR_EXPLAIN("Attempt to remote call/set yourself! unique ID: " + itos(get_network_unique_id())); - } else { - ERR_EXPLAIN("Attempt to remote call unexisting ID: " + itos(p_to)); - } - - ERR_FAIL(); - } - - NodePath from_path = p_from->get_path(); - ERR_FAIL_COND(from_path.is_empty()); - - //see if the path is cached - PathSentCache *psc = path_send_cache.getptr(from_path); - if (!psc) { - //path is not cached, create - path_send_cache[from_path] = PathSentCache(); - psc = path_send_cache.getptr(from_path); - psc->id = last_send_cache_id++; - } - - //create base packet, lots of harcode 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 type - MAKE_ROOM(1); - packet_cache[0] = p_set ? NETWORK_COMMAND_REMOTE_SET : NETWORK_COMMAND_REMOTE_CALL; - ofs += 1; - - //encode ID - MAKE_ROOM(ofs + 4); - encode_uint32(psc->id, &packet_cache[ofs]); - ofs += 4; - - //encode function name - CharString name = String(p_name).utf8(); - int len = encode_cstring(name.get_data(), NULL); - MAKE_ROOM(ofs + len); - encode_cstring(name.get_data(), &packet_cache[ofs]); - ofs += len; - - if (p_set) { - //set argument - Error err = encode_variant(*p_arg[0], NULL, len); - ERR_FAIL_COND(err != OK); - MAKE_ROOM(ofs + len); - encode_variant(*p_arg[0], &packet_cache[ofs], len); - ofs += len; - - } else { - //call arguments - MAKE_ROOM(ofs + 1); - packet_cache[ofs] = p_argcount; - ofs += 1; - for (int i = 0; i < p_argcount; i++) { - Error err = encode_variant(*p_arg[i], NULL, len); - ERR_FAIL_COND(err != OK); - MAKE_ROOM(ofs + len); - encode_variant(*p_arg[i], &packet_cache[ofs], len); - ofs += len; - } - } - - //see if all peers have cached path (is so, call can be fast) - bool has_all_peers = true; - - List<int> peers_to_add; //if one is missing, take note to add it - - for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) { - - if (p_to < 0 && E->get() == -p_to) - continue; //continue, excluded - - if (p_to > 0 && E->get() != p_to) - continue; //continue, not for this peer - - Map<int, bool>::Element *F = psc->confirmed_peers.find(E->get()); - - if (!F || F->get() == false) { - //path was not cached, or was cached but is unconfirmed - if (!F) { - //not cached at all, take note - peers_to_add.push_back(E->get()); - } - - has_all_peers = false; - } - } - - //those that need to be added, send a message for this - - for (List<int>::Element *E = peers_to_add.front(); E; E = E->next()) { - - //encode function name - CharString pname = String(from_path).utf8(); - int len = encode_cstring(pname.get_data(), NULL); - - Vector<uint8_t> packet; - - packet.resize(1 + 4 + len); - packet[0] = NETWORK_COMMAND_SIMPLIFY_PATH; - encode_uint32(psc->id, &packet[1]); - encode_cstring(pname.get_data(), &packet[5]); - - network_peer->set_target_peer(E->get()); //to all of you - network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); - network_peer->put_packet(packet.ptr(), packet.size()); - - psc->confirmed_peers.insert(E->get(), false); //insert into confirmed, but as false since it was not confirmed - } - - //take chance and set transfer mode, since all send methods will use it - network_peer->set_transfer_mode(p_unreliable ? NetworkedMultiplayerPeer::TRANSFER_MODE_UNRELIABLE : NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); - - if (has_all_peers) { - - //they all have verified paths, so send fast - network_peer->set_target_peer(p_to); //to all of you - network_peer->put_packet(packet_cache.ptr(), ofs); //a message with love - } else { - //not all verified path, so send one by one - - //apend 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(), NULL); - MAKE_ROOM(ofs + path_len); - encode_cstring(pname.get_data(), &packet_cache[ofs]); - - for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) { - - if (p_to < 0 && E->get() == -p_to) - continue; //continue, excluded - - if (p_to > 0 && E->get() != p_to) - continue; //continue, not for this peer - - Map<int, bool>::Element *F = psc->confirmed_peers.find(E->get()); - ERR_CONTINUE(!F); //should never happen - - network_peer->set_target_peer(E->get()); //to this one specifically - - if (F->get() == true) { - //this one confirmed path, so use id - encode_uint32(psc->id, &packet_cache[1]); - network_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[1]); //offset to path and flag - network_peer->put_packet(packet_cache.ptr(), ofs + path_len); - } - } - } -} - -void SceneTree::_network_process_packet(int p_from, const uint8_t *p_packet, int p_packet_len) { - - ERR_FAIL_COND(p_packet_len < 5); - - uint8_t packet_type = p_packet[0]; - - switch (packet_type) { - - case NETWORK_COMMAND_REMOTE_CALL: - case NETWORK_COMMAND_REMOTE_SET: { - - ERR_FAIL_COND(p_packet_len < 5); - uint32_t target = decode_uint32(&p_packet[1]); - - Node *node = NULL; - - if (target & 0x80000000) { - //use full path (not cached yet) - - int ofs = target & 0x7FFFFFFF; - ERR_FAIL_COND(ofs >= p_packet_len); - - String paths; - paths.parse_utf8((const char *)&p_packet[ofs], p_packet_len - ofs); - - NodePath np = paths; - - node = get_root()->get_node(np); - if (node == NULL) { - ERR_EXPLAIN("Failed to get path from RPC: " + String(np)); - ERR_FAIL_COND(node == NULL); - } - } else { - //use cached path - int id = target; - - Map<int, PathGetCache>::Element *E = path_get_cache.find(p_from); - ERR_FAIL_COND(!E); - - Map<int, PathGetCache::NodeInfo>::Element *F = E->get().nodes.find(id); - ERR_FAIL_COND(!F); - - PathGetCache::NodeInfo *ni = &F->get(); - //do proper caching later - - node = get_root()->get_node(ni->path); - if (node == NULL) { - ERR_EXPLAIN("Failed to get cached path from RPC: " + String(ni->path)); - ERR_FAIL_COND(node == NULL); - } - } - - ERR_FAIL_COND(p_packet_len < 6); - - //detect cstring end - int len_end = 5; - for (; len_end < p_packet_len; len_end++) { - if (p_packet[len_end] == 0) { - break; - } - } - - ERR_FAIL_COND(len_end >= p_packet_len); - - StringName name = String::utf8((const char *)&p_packet[5]); - - if (packet_type == NETWORK_COMMAND_REMOTE_CALL) { - - if (!node->can_call_rpc(name, p_from)) - return; - - int ofs = len_end + 1; - - ERR_FAIL_COND(ofs >= p_packet_len); - - int argc = p_packet[ofs]; - Vector<Variant> args; - Vector<const Variant *> argp; - args.resize(argc); - argp.resize(argc); - - ofs++; - - for (int i = 0; i < argc; i++) { - - ERR_FAIL_COND(ofs >= p_packet_len); - int vlen; - Error err = decode_variant(args[i], &p_packet[ofs], p_packet_len - ofs, &vlen); - ERR_FAIL_COND(err != OK); - //args[i]=p_packet[3+i]; - argp[i] = &args[i]; - ofs += vlen; - } - - Variant::CallError ce; - - node->call(name, (const Variant **)argp.ptr(), argc, ce); - if (ce.error != Variant::CallError::CALL_OK) { - String error = Variant::get_call_error_text(node, name, (const Variant **)argp.ptr(), argc, ce); - error = "RPC - " + error; - ERR_PRINTS(error); - } - - } else { - - if (!node->can_call_rset(name, p_from)) - return; - - int ofs = len_end + 1; - - ERR_FAIL_COND(ofs >= p_packet_len); - - Variant value; - decode_variant(value, &p_packet[ofs], p_packet_len - ofs); - - bool valid; - - node->set(name, value, &valid); - if (!valid) { - String error = "Error setting remote property '" + String(name) + "', not found in object of type " + node->get_class(); - ERR_PRINTS(error); - } - } - - } break; - case NETWORK_COMMAND_SIMPLIFY_PATH: { - - ERR_FAIL_COND(p_packet_len < 5); - int id = decode_uint32(&p_packet[1]); - - String paths; - paths.parse_utf8((const char *)&p_packet[5], p_packet_len - 5); - - NodePath path = paths; - - if (!path_get_cache.has(p_from)) { - path_get_cache[p_from] = PathGetCache(); - } - - PathGetCache::NodeInfo ni; - ni.path = path; - ni.instance = 0; - - path_get_cache[p_from].nodes[id] = ni; - - { - //send ack - - //encode path - CharString pname = String(path).utf8(); - int len = encode_cstring(pname.get_data(), NULL); - - Vector<uint8_t> packet; - - packet.resize(1 + len); - packet[0] = NETWORK_COMMAND_CONFIRM_PATH; - encode_cstring(pname.get_data(), &packet[1]); - - network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); - network_peer->set_target_peer(p_from); - network_peer->put_packet(packet.ptr(), packet.size()); - } - } break; - case NETWORK_COMMAND_CONFIRM_PATH: { - - String paths; - paths.parse_utf8((const char *)&p_packet[1], p_packet_len - 1); - - NodePath path = paths; - - PathSentCache *psc = path_send_cache.getptr(path); - ERR_FAIL_COND(!psc); - - Map<int, bool>::Element *E = psc->confirmed_peers.find(p_from); - ERR_FAIL_COND(!E); - E->get() = true; - } break; - } -} - -void SceneTree::_network_poll() { - - if (!network_peer.is_valid() || network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED) - return; - - network_peer->poll(); - - if (!network_peer.is_valid()) //it's possible that polling might have resulted in a disconnection, so check here - return; - - while (network_peer->get_available_packet_count()) { - - int sender = network_peer->get_packet_peer(); - const uint8_t *packet; - int len; - - Error err = network_peer->get_packet(&packet, len); - if (err != OK) { - ERR_PRINT("Error getting packet!"); - } - - rpc_sender_id = sender; - _network_process_packet(sender, packet, len); - rpc_sender_id = 0; - - if (!network_peer.is_valid()) { - break; //it's also possible that a packet or RPC caused a disconnection, so also check here - } - } + return multiplayer_api->is_refusing_new_network_connections(); } void SceneTree::_bind_methods() { @@ -2133,10 +1749,8 @@ void SceneTree::_bind_methods() { ClassDB::bind_method(D_METHOD("set_debug_navigation_hint", "enable"), &SceneTree::set_debug_navigation_hint); ClassDB::bind_method(D_METHOD("is_debugging_navigation_hint"), &SceneTree::is_debugging_navigation_hint); -#ifdef TOOLS_ENABLED ClassDB::bind_method(D_METHOD("set_edited_scene_root", "scene"), &SceneTree::set_edited_scene_root); ClassDB::bind_method(D_METHOD("get_edited_scene_root"), &SceneTree::get_edited_scene_root); -#endif ClassDB::bind_method(D_METHOD("set_pause", "enable"), &SceneTree::set_pause); ClassDB::bind_method(D_METHOD("is_paused"), &SceneTree::is_paused); @@ -2186,7 +1800,10 @@ void SceneTree::_bind_methods() { ClassDB::bind_method(D_METHOD("_change_scene"), &SceneTree::_change_scene); + ClassDB::bind_method(D_METHOD("set_multiplayer_api", "multiplayer_api"), &SceneTree::set_multiplayer_api); + ClassDB::bind_method(D_METHOD("get_multiplayer_api"), &SceneTree::get_multiplayer_api); ClassDB::bind_method(D_METHOD("set_network_peer", "peer"), &SceneTree::set_network_peer); + ClassDB::bind_method(D_METHOD("get_network_peer"), &SceneTree::get_network_peer); ClassDB::bind_method(D_METHOD("is_network_server"), &SceneTree::is_network_server); ClassDB::bind_method(D_METHOD("has_network_peer"), &SceneTree::has_network_peer); ClassDB::bind_method(D_METHOD("get_network_connected_peers"), &SceneTree::get_network_connected_peers); @@ -2203,6 +1820,17 @@ void SceneTree::_bind_methods() { ClassDB::bind_method(D_METHOD("set_use_font_oversampling", "enable"), &SceneTree::set_use_font_oversampling); ClassDB::bind_method(D_METHOD("is_using_font_oversampling"), &SceneTree::is_using_font_oversampling); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "debug_collisions_hint"), "set_debug_collisions_hint", "is_debugging_collisions_hint"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "debug_navigation_hint"), "set_debug_navigation_hint", "is_debugging_navigation_hint"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "paused"), "set_pause", "is_paused"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_network_connections"), "set_refuse_new_network_connections", "is_refusing_new_network_connections"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_font_oversampling"), "set_use_font_oversampling", "is_using_font_oversampling"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "edited_scene_root", PROPERTY_HINT_RESOURCE_TYPE, "Node", 0), "set_edited_scene_root", "get_edited_scene_root"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "current_scene", PROPERTY_HINT_RESOURCE_TYPE, "Node", 0), "set_current_scene", "get_current_scene"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "network_peer", PROPERTY_HINT_RESOURCE_TYPE, "NetworkedMultiplayerPeer", 0), "set_network_peer", "get_network_peer"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "root", PROPERTY_HINT_RESOURCE_TYPE, "Node", 0), "", "get_root"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multiplayer_api", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", 0), "set_multiplayer_api", "get_multiplayer_api"); + ADD_SIGNAL(MethodInfo("tree_changed")); ADD_SIGNAL(MethodInfo("node_added", PropertyInfo(Variant::OBJECT, "node"))); ADD_SIGNAL(MethodInfo("node_removed", PropertyInfo(Variant::OBJECT, "node"))); @@ -2297,7 +1925,6 @@ SceneTree::SceneTree() { call_lock = 0; root_lock = 0; node_count = 0; - rpc_sender_id = 0; //create with mainloop @@ -2306,6 +1933,9 @@ SceneTree::SceneTree() { if (!root->get_world().is_valid()) root->set_world(Ref<World>(memnew(World))); + // Initialize network state + set_multiplayer_api(Ref<MultiplayerAPI>(memnew(MultiplayerAPI))); + //root->set_world_2d( Ref<World2D>( memnew( World2D ))); root->set_as_audio_listener(true); root->set_as_audio_listener_2d(true); @@ -2350,7 +1980,7 @@ SceneTree::SceneTree() { ProjectSettings::get_singleton()->set("rendering/environment/default_environment", ""); } else { //file was erased, notify user. - ERR_PRINTS(RTR("Default Environment as specified in Project Setings (Rendering -> Environment -> Default Environment) could not be loaded.")); + ERR_PRINTS(RTR("Default Environment as specified in Project Settings (Rendering -> Environment -> Default Environment) could not be loaded.")); } } } @@ -2400,8 +2030,6 @@ SceneTree::SceneTree() { live_edit_root = NodePath("/root"); - last_send_cache_id = 1; - #endif use_font_oversampling = false; diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h index dab446e226..9c06e4ded3 100644 --- a/scene/main/scene_tree.h +++ b/scene/main/scene_tree.h @@ -27,10 +27,11 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef SCENE_MAIN_LOOP_H #define SCENE_MAIN_LOOP_H -#include "io/networked_multiplayer_peer.h" +#include "io/multiplayer_api.h" #include "os/main_loop.h" #include "os/thread_safe.h" #include "scene/resources/mesh.h" @@ -184,16 +185,8 @@ private: ///network/// - enum NetworkCommands { - NETWORK_COMMAND_REMOTE_CALL, - NETWORK_COMMAND_REMOTE_SET, - NETWORK_COMMAND_SIMPLIFY_PATH, - NETWORK_COMMAND_CONFIRM_PATH, - }; - - Ref<NetworkedMultiplayerPeer> network_peer; + Ref<MultiplayerAPI> multiplayer_api; - Set<int> connected_peers; void _network_peer_connected(int p_id); void _network_peer_disconnected(int p_id); @@ -201,39 +194,9 @@ private: void _connection_failed(); void _server_disconnected(); - int rpc_sender_id; - - //path sent caches - struct PathSentCache { - Map<int, bool> confirmed_peers; - int id; - }; - - HashMap<NodePath, PathSentCache> path_send_cache; - int last_send_cache_id; - - //path get caches - struct PathGetCache { - struct NodeInfo { - NodePath path; - ObjectID instance; - }; - - Map<int, NodeInfo> nodes; - }; - - Map<int, PathGetCache> path_get_cache; - - Vector<uint8_t> packet_cache; - - void _network_process_packet(int p_from, const uint8_t *p_packet, int p_packet_len); - void _network_poll(); - static SceneTree *singleton; friend class Node; - void _rpc(Node *p_from, int p_to, bool p_unreliable, bool p_set, const StringName &p_name, const Variant **p_arg, int p_argcount); - void tree_changed(); void node_added(Node *p_node); void node_removed(Node *p_node); @@ -427,10 +390,8 @@ public: //void change_scene(const String& p_path); //Node *get_loaded_scene(); -#ifdef TOOLS_ENABLED void set_edited_scene_root(Node *p_node); Node *get_edited_scene_root() const; -#endif void set_current_scene(Node *p_scene); Node *get_current_scene() const; @@ -449,7 +410,10 @@ public: //network API + Ref<MultiplayerAPI> get_multiplayer_api() const; + void set_multiplayer_api(Ref<MultiplayerAPI> p_multiplayer_api); void set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_network_peer); + Ref<NetworkedMultiplayerPeer> get_network_peer() const; bool is_network_server() const; bool has_network_peer() const; int get_network_unique_id() const; diff --git a/scene/main/timer.cpp b/scene/main/timer.cpp index 90cec07f56..c285694dfa 100755 --- a/scene/main/timer.cpp +++ b/scene/main/timer.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "timer.h" #include "engine.h" @@ -106,7 +107,10 @@ bool Timer::has_autostart() const { return autostart; } -void Timer::start() { +void Timer::start(float p_time) { + if (p_time > 0) { + set_wait_time(p_time); + } time_left = wait_time; _set_process(true); } @@ -184,7 +188,7 @@ void Timer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_autostart", "enable"), &Timer::set_autostart); ClassDB::bind_method(D_METHOD("has_autostart"), &Timer::has_autostart); - ClassDB::bind_method(D_METHOD("start"), &Timer::start); + ClassDB::bind_method(D_METHOD("start", "time_sec"), &Timer::start, DEFVAL(-1)); ClassDB::bind_method(D_METHOD("stop"), &Timer::stop); ClassDB::bind_method(D_METHOD("set_paused", "paused"), &Timer::set_paused); @@ -203,6 +207,8 @@ void Timer::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::REAL, "wait_time", PROPERTY_HINT_EXP_RANGE, "0.01,4096,0.01"), "set_wait_time", "get_wait_time"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "is_one_shot"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autostart"), "set_autostart", "has_autostart"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "paused", PROPERTY_HINT_NONE, "", 0), "set_paused", "is_paused"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "time_left", PROPERTY_HINT_NONE, "", 0), "", "get_time_left"); BIND_ENUM_CONSTANT(TIMER_PROCESS_PHYSICS); BIND_ENUM_CONSTANT(TIMER_PROCESS_IDLE); diff --git a/scene/main/timer.h b/scene/main/timer.h index 8c15e2eac7..2f42252a7e 100755 --- a/scene/main/timer.h +++ b/scene/main/timer.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef TIMER_H #define TIMER_H @@ -63,7 +64,7 @@ public: void set_autostart(bool p_start); bool has_autostart() const; - void start(); + void start(float p_time = -1); void stop(); void set_paused(bool p_paused); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 78706ee14a..295f131db3 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "viewport.h" #include "os/input.h" @@ -180,6 +181,7 @@ public: Viewport::GUI::GUI() { mouse_focus = NULL; + mouse_click_grabber = NULL; mouse_focus_button = -1; key_focus = NULL; mouse_over = NULL; @@ -187,16 +189,16 @@ Viewport::GUI::GUI() { tooltip = NULL; tooltip_popup = NULL; tooltip_label = NULL; + subwindow_visibility_dirty = false; subwindow_order_dirty = false; } ///////////////////////////////////// + void Viewport::_update_stretch_transform() { if (size_override_stretch && size_override) { - //print_line("sive override size "+size_override_size); - //print_line("rect size "+size); stretch_transform = Transform2D(); Size2 scale = size / (size_override_size + size_override_margin * 2); stretch_transform.scale(scale); @@ -210,114 +212,6 @@ void Viewport::_update_stretch_transform() { _update_global_transform(); } -void Viewport::_update_rect() { - - if (!is_inside_tree()) - return; - - /*if (!render_target && parent_control) { - - Control *c = parent_control; - - rect.pos=Point2(); - rect.size=c->get_size(); - }*/ - /* - VisualServer::ViewportRect vr; - vr.x=rect.pos.x; - vr.y=rect.pos.y; - - if (render_target) { - vr.x=0; - vr.y=0; - } - vr.width=rect.size.width; - vr.height=rect.size.height; - - VisualServer::get_singleton()->viewport_set_rect(viewport,vr); - last_vp_rect=rect; - - if (canvas_item.is_valid()) { - VisualServer::get_singleton()->canvas_item_set_custom_rect(canvas_item,true,rect); - } - - emit_signal("size_changed"); - texture->emit_changed(); -*/ -} - -void Viewport::_parent_resized() { - - _update_rect(); -} - -void Viewport::_parent_draw() { -} - -void Viewport::_parent_visibility_changed() { - - /* - if (parent_control) { - - Control *c = parent_control; - VisualServer::get_singleton()->canvas_item_set_visible(canvas_item,c->is_visible_in_tree()); - - _update_listener(); - _update_listener_2d(); - } -*/ -} - -void Viewport::_vp_enter_tree() { - - /* if (parent_control) { - - Control *cparent=parent_control; - RID parent_ci = cparent->get_canvas_item(); - ERR_FAIL_COND(!parent_ci.is_valid()); - canvas_item = VisualServer::get_singleton()->canvas_item_create(); - - VisualServer::get_singleton()->canvas_item_set_parent(canvas_item,parent_ci); - VisualServer::get_singleton()->canvas_item_set_visible(canvas_item,false); - //VisualServer::get_singleton()->canvas_item_attach_viewport(canvas_item,viewport); - parent_control->connect("resized",this,"_parent_resized"); - parent_control->connect("visibility_changed",this,"_parent_visibility_changed"); - } else if (!parent){ - - //VisualServer::get_singleton()->viewport_attach_to_screen(viewport,0); - - } -*/ -} - -void Viewport::_vp_exit_tree() { - - /* - if (parent_control) { - - parent_control->disconnect("resized",this,"_parent_resized"); - } - - if (parent_control) { - - parent_control->disconnect("visibility_changed",this,"_parent_visibility_changed"); - } - - if (canvas_item.is_valid()) { - - VisualServer::get_singleton()->free(canvas_item); - canvas_item=RID(); - - } - - if (!parent) { - - VisualServer::get_singleton()->viewport_detach(viewport); - - } -*/ -} - void Viewport::update_worlds() { if (!is_inside_tree()) @@ -375,7 +269,6 @@ void Viewport::_notification(int p_what) { _update_listener(); _update_listener_2d(); - _update_rect(); find_world_2d()->_register_viewport(this, Rect2()); @@ -428,6 +321,11 @@ void Viewport::_notification(int p_what) { first->make_current(); } #endif + + // Enable processing for tooltips, collision debugging, physics object picking, etc. + set_process_internal(true); + set_physics_process_internal(true); + } break; case NOTIFICATION_EXIT_TREE: { @@ -435,11 +333,6 @@ void Viewport::_notification(int p_what) { if (world_2d.is_valid()) world_2d->_remove_viewport(this); - /* - if (!render_target) - _vp_exit_tree(); - */ - VisualServer::get_singleton()->viewport_set_scenario(viewport, RID()); // SpatialSoundServer::get_singleton()->listener_set_space(internal_listener, RID()); VisualServer::get_singleton()->viewport_remove_canvas(viewport, current_canvas); @@ -460,15 +353,18 @@ void Viewport::_notification(int p_what) { VS::get_singleton()->viewport_set_active(viewport, false); } break; - case NOTIFICATION_PHYSICS_PROCESS: { + case NOTIFICATION_INTERNAL_PROCESS: { if (gui.tooltip_timer >= 0) { - gui.tooltip_timer -= get_physics_process_delta_time(); + gui.tooltip_timer -= get_process_delta_time(); if (gui.tooltip_timer < 0) { _gui_show_tooltip(); } } + } break; + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { + if (get_tree()->is_debugging_collisions_hint() && contact_2d_debug.is_valid()) { VisualServer::get_singleton()->canvas_item_clear(contact_2d_debug); @@ -678,6 +574,20 @@ void Viewport::_notification(int p_what) { } } break; + case SceneTree::NOTIFICATION_WM_FOCUS_OUT: { + if (gui.mouse_focus) { + //if mouse is being pressed, send a release event + Ref<InputEventMouseButton> mb; + mb.instance(); + mb->set_position(gui.mouse_focus->get_local_mouse_position()); + mb->set_global_position(gui.mouse_focus->get_local_mouse_position()); + mb->set_button_index(gui.mouse_focus_button); + mb->set_pressed(false); + Control *c = gui.mouse_focus; + gui.mouse_focus = NULL; + c->call_multilevel(SceneStringNames::get_singleton()->_gui_input, mb); + } + } break; } } @@ -703,7 +613,6 @@ void Viewport::set_size(const Size2 &p_size) { size = p_size.floor(); VS::get_singleton()->viewport_set_size(viewport, size.width, size.height); - _update_rect(); _update_stretch_transform(); emit_signal("size_changed"); @@ -1152,7 +1061,7 @@ void Viewport::set_size_override(bool p_enable, const Size2 &p_size, const Vecto size_override_size = p_size; } size_override_margin = p_margin; - _update_rect(); + _update_stretch_transform(); emit_signal("size_changed"); } @@ -1171,9 +1080,6 @@ void Viewport::set_size_override_stretch(bool p_enable) { return; size_override_stretch = p_enable; - if (size_override) { - _update_rect(); - } _update_stretch_transform(); } @@ -1346,6 +1252,24 @@ void Viewport::warp_mouse(const Vector2 &p_pos) { Input::get_singleton()->warp_mouse_position(gpos); } +void Viewport::_gui_prepare_subwindows() { + + if (gui.subwindow_visibility_dirty) { + + gui.subwindows.clear(); + for (List<Control *>::Element *E = gui.all_known_subwindows.front(); E; E = E->next()) { + if (E->get()->is_visible_in_tree()) { + gui.subwindows.push_back(E->get()); + } + } + + gui.subwindow_visibility_dirty = false; + gui.subwindow_order_dirty = true; + } + + _gui_sort_subwindows(); +} + void Viewport::_gui_sort_subwindows() { if (!gui.subwindow_order_dirty) @@ -1488,7 +1412,7 @@ void Viewport::_gui_call_input(Control *p_control, const Ref<InputEvent> &p_inpu Control *Viewport::_gui_find_control(const Point2 &p_global) { - _gui_sort_subwindows(); + _gui_prepare_subwindows(); for (List<Control *>::Element *E = gui.subwindows.back(); E; E = E->prev()) { @@ -1909,7 +1833,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { } if (!over) { - OS::get_singleton()->set_cursor_shape(OS::CURSOR_ARROW); + OS::get_singleton()->set_cursor_shape((OS::CursorShape)Input::get_singleton()->get_default_cursor_shape()); return; } @@ -2030,6 +1954,8 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { Ref<InputEventGesture> gesture_event = p_event; if (gesture_event.is_valid()) { + gui.key_event_accepted = false; + _gui_cancel_tooltip(); Size2 pos = gesture_event->get_position(); @@ -2122,6 +2048,9 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { top->notification(Control::NOTIFICATION_MODAL_CLOSE); top->_modal_stack_remove(); top->hide(); + // Close modal, set input as handled + get_tree()->set_input_as_handled(); + return; } } @@ -2183,8 +2112,14 @@ List<Control *>::Element *Viewport::_gui_add_root_control(Control *p_control) { List<Control *>::Element *Viewport::_gui_add_subwindow_control(Control *p_control) { - gui.subwindow_order_dirty = true; - return gui.subwindows.push_back(p_control); + p_control->connect("visibility_changed", this, "_subwindow_visibility_changed"); + + if (p_control->is_visible_in_tree()) { + gui.subwindow_order_dirty = true; + gui.subwindows.push_back(p_control); + } + + return gui.all_known_subwindows.push_back(p_control); } void Viewport::_gui_set_subwindow_order_dirty() { @@ -2258,9 +2193,7 @@ void Viewport::_gui_set_drag_preview(Control *p_base, Control *p_control) { 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(); - if (gui.drag_preview) { - memdelete(gui.drag_preview); - } + gui.drag_preview = p_control; } @@ -2271,7 +2204,17 @@ void Viewport::_gui_remove_root_control(List<Control *>::Element *RI) { void Viewport::_gui_remove_subwindow_control(List<Control *>::Element *SI) { - gui.subwindows.erase(SI); + ERR_FAIL_COND(!SI); + + Control *control = SI->get(); + + control->disconnect("visibility_changed", this, "_subwindow_visibility_changed"); + + List<Control *>::Element *E = gui.subwindows.find(control); + if (E) + gui.subwindows.erase(E); + + gui.all_known_subwindows.erase(SI); } void Viewport::_gui_unfocus_control(Control *p_control) { @@ -2298,7 +2241,7 @@ void Viewport::_gui_hid_control(Control *p_control) { */ if (gui.key_focus == p_control) - gui.key_focus = NULL; + _gui_remove_focus(); if (gui.mouse_over == p_control) gui.mouse_over = NULL; if (gui.tooltip == p_control) @@ -2369,19 +2312,16 @@ List<Control *>::Element *Viewport::_gui_show_modal(Control *p_control) { else p_control->_modal_set_prev_focus_owner(0); - if (gui.mouse_focus && !p_control->is_a_parent_of(gui.mouse_focus)) { + if (gui.mouse_focus && !p_control->is_a_parent_of(gui.mouse_focus) && !gui.mouse_click_grabber) { Ref<InputEventMouseButton> mb; mb.instance(); mb->set_position(gui.mouse_focus->get_local_mouse_position()); mb->set_global_position(gui.mouse_focus->get_local_mouse_position()); mb->set_button_index(gui.mouse_focus_button); mb->set_pressed(false); - gui.mouse_focus->call_multilevel(SceneStringNames::get_singleton()->_gui_input, mb); - - //if (gui.mouse_over == gui.mouse_focus) { - // gui.mouse_focus->notification(Control::NOTIFICATION_MOUSE_EXIT); - //} + Control *c = gui.mouse_focus; gui.mouse_focus = NULL; + c->call_multilevel(SceneStringNames::get_singleton()->_gui_input, mb); } return gui.modal_stack.back(); @@ -2394,9 +2334,22 @@ Control *Viewport::_gui_get_focus_owner() { void Viewport::_gui_grab_click_focus(Control *p_control) { + gui.mouse_click_grabber = p_control; + call_deferred("_post_gui_grab_click_focus"); +} + +void Viewport::_post_gui_grab_click_focus() { + + Control *focus_grabber = gui.mouse_click_grabber; + if (!focus_grabber) { + // Redundant grab requests were made + return; + } + gui.mouse_click_grabber = NULL; + if (gui.mouse_focus) { - if (gui.mouse_focus == p_control) + if (gui.mouse_focus == focus_grabber) return; Ref<InputEventMouseButton> mb; mb.instance(); @@ -2407,9 +2360,9 @@ void Viewport::_gui_grab_click_focus(Control *p_control) { mb->set_position(click); mb->set_button_index(gui.mouse_focus_button); mb->set_pressed(false); - gui.mouse_focus->call_deferred(SceneStringNames::get_singleton()->_gui_input, mb); + gui.mouse_focus->call_multilevel(SceneStringNames::get_singleton()->_gui_input, mb); - gui.mouse_focus = p_control; + gui.mouse_focus = focus_grabber; gui.focus_inv_xform = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse(); click = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse().xform(gui.last_mouse_pos); mb->set_position(click); @@ -2512,9 +2465,14 @@ Rect2 Viewport::get_attach_to_screen_rect() const { void Viewport::set_physics_object_picking(bool p_enable) { physics_object_picking = p_enable; - set_physics_process(physics_object_picking); - if (!physics_object_picking) + if (!physics_object_picking) { physics_picking_events.clear(); + } +} + +bool Viewport::get_physics_object_picking() { + + return physics_object_picking; } Vector2 Viewport::get_camera_coords(const Vector2 &p_viewport_coords) const { @@ -2528,11 +2486,6 @@ Vector2 Viewport::get_camera_rect_size() const { return size; } -bool Viewport::get_physics_object_picking() { - - return physics_object_picking; -} - bool Viewport::gui_has_modal_stack() const { return gui.modal_stack.size(); @@ -2557,6 +2510,16 @@ bool Viewport::is_3d_disabled() const { return disable_3d; } +void Viewport::set_keep_3d_linear(bool p_keep_3d_linear) { + keep_3d_linear = p_keep_3d_linear; + VS::get_singleton()->viewport_set_keep_3d_linear(viewport, keep_3d_linear); +} + +bool Viewport::get_keep_3d_linear() const { + + return keep_3d_linear; +} + Variant Viewport::gui_get_drag_data() const { return gui.drag_data; } @@ -2672,9 +2635,6 @@ 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("_parent_visibility_changed"), &Viewport::_parent_visibility_changed); - - ClassDB::bind_method(D_METHOD("_parent_resized"), &Viewport::_parent_resized); ClassDB::bind_method(D_METHOD("_vp_input"), &Viewport::_vp_input); ClassDB::bind_method(D_METHOD("_vp_input_text", "text"), &Viewport::_vp_input_text); ClassDB::bind_method(D_METHOD("_vp_unhandled_input"), &Viewport::_vp_unhandled_input); @@ -2743,8 +2703,12 @@ void Viewport::_bind_methods() { ClassDB::bind_method(D_METHOD("set_disable_3d", "disable"), &Viewport::set_disable_3d); ClassDB::bind_method(D_METHOD("is_3d_disabled"), &Viewport::is_3d_disabled); + ClassDB::bind_method(D_METHOD("set_keep_3d_linear", "keep_3d_linear"), &Viewport::set_keep_3d_linear); + ClassDB::bind_method(D_METHOD("get_keep_3d_linear"), &Viewport::get_keep_3d_linear); + ClassDB::bind_method(D_METHOD("_gui_show_tooltip"), &Viewport::_gui_show_tooltip); ClassDB::bind_method(D_METHOD("_gui_remove_focus"), &Viewport::_gui_remove_focus); + 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); @@ -2755,17 +2719,20 @@ void Viewport::_bind_methods() { 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("_subwindow_visibility_changed"), &Viewport::_subwindow_visibility_changed); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "arvr"), "set_use_arvr", "use_arvr"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "own_world"), "set_use_own_world", "is_using_own_world"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "world", PROPERTY_HINT_RESOURCE_TYPE, "World"), "set_world", "get_world"); - //ADD_PROPERTY( PropertyInfo(Variant::OBJECT,"world_2d",PROPERTY_HINT_RESOURCE_TYPE,"World2D"), "set_world_2d", "get_world_2d") ; + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "world_2d", PROPERTY_HINT_RESOURCE_TYPE, "World2D", 0), "set_world_2d", "get_world_2d"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "transparent_bg"), "set_transparent_background", "has_transparent_background"); ADD_GROUP("Rendering", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "msaa", PROPERTY_HINT_ENUM, "Disabled,2x,4x,8x,16x"), "set_msaa", "get_msaa"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hdr"), "set_hdr", "get_hdr"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disable_3d"), "set_disable_3d", "is_3d_disabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_3d_linear"), "set_keep_3d_linear", "get_keep_3d_linear"); ADD_PROPERTY(PropertyInfo(Variant::INT, "usage", PROPERTY_HINT_ENUM, "2D,2D No-Sampling,3D,3D No-Effects"), "set_usage", "get_usage"); ADD_PROPERTY(PropertyInfo(Variant::INT, "debug_draw", PROPERTY_HINT_ENUM, "Disabled,Unshaded,Overdraw,Wireframe"), "set_debug_draw", "get_debug_draw"); ADD_GROUP("Render Target", "render_target_"); @@ -2786,6 +2753,8 @@ void Viewport::_bind_methods() { 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_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "canvas_transform", PROPERTY_HINT_NONE, "", 0), "set_canvas_transform", "get_canvas_transform"); + ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "global_canvas_transform", PROPERTY_HINT_NONE, "", 0), "set_global_canvas_transform", "get_global_canvas_transform"); ADD_SIGNAL(MethodInfo("size_changed")); @@ -2832,6 +2801,13 @@ void Viewport::_bind_methods() { BIND_ENUM_CONSTANT(CLEAR_MODE_ONLY_NEXT_FRAME); } +void Viewport::_subwindow_visibility_changed() { + + // unfortunately, we don't know the sender, i.e. which subwindow changed; + // so we have to check them all. + gui.subwindow_visibility_dirty = true; +} + Viewport::Viewport() { world_2d = Ref<World2D>(memnew(World2D)); @@ -2886,6 +2862,7 @@ Viewport::Viewport() { disable_input = false; disable_3d = false; + keep_3d_linear = false; //window tooltip gui.tooltip_timer = -1; @@ -2898,6 +2875,7 @@ Viewport::Viewport() { gui.drag_preview = NULL; gui.drag_attempted = false; gui.canvas_sort_index = 0; + gui.roots_order_dirty = false; msaa = MSAA_DISABLED; hdr = true; diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 92c53a7ab8..363414bbad 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -1,4 +1,3 @@ - /*************************************************************************/ /* viewport.h */ /*************************************************************************/ @@ -28,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef VIEWPORT_H #define VIEWPORT_H @@ -206,12 +206,6 @@ private: void _test_new_mouseover(ObjectID new_collider); Map<ObjectID, uint64_t> physics_2d_mouseover; - void _update_rect(); - - void _parent_resized(); - void _parent_draw(); - void _parent_visibility_changed(); - Ref<World2D> world_2d; Ref<World> world; Ref<World> own_world; @@ -232,6 +226,7 @@ private: void _update_global_transform(); bool disable_3d; + bool keep_3d_linear; UpdateMode update_mode; RID texture_rid; uint32_t texture_flags; @@ -254,6 +249,7 @@ private: bool key_event_accepted; Control *mouse_focus; + Control *mouse_click_grabber; int mouse_focus_button; Control *key_focus; Control *mouse_over; @@ -271,7 +267,9 @@ private: List<Control *> modal_stack; Transform2D focus_inv_xform; bool subwindow_order_dirty; - List<Control *> subwindows; + bool subwindow_visibility_dirty; + List<Control *> subwindows; // visible subwindows + List<Control *> all_known_subwindows; bool roots_order_dirty; List<Control *> roots; int canvas_sort_index; //for sorting items with canvas as root @@ -282,6 +280,7 @@ private: bool disable_input; void _gui_call_input(Control *p_control, const Ref<InputEvent> &p_input); + void _gui_prepare_subwindows(); void _gui_sort_subwindows(); void _gui_sort_roots(); void _gui_sort_modal_stack(); @@ -294,9 +293,6 @@ private: _FORCE_INLINE_ Transform2D _get_input_pre_xform() const; - void _vp_enter_tree(); - void _vp_exit_tree(); - void _vp_input(const Ref<InputEvent> &p_ev); void _vp_input_text(const String &p_text); void _vp_unhandled_input(const Ref<InputEvent> &p_ev); @@ -332,6 +328,7 @@ private: bool _gui_control_has_focus(const Control *p_control); void _gui_control_grab_focus(Control *p_control); void _gui_grab_click_focus(Control *p_control); + void _post_gui_grab_click_focus(); void _gui_accept_event(); Control *_gui_get_focus_owner(); @@ -440,6 +437,9 @@ public: void set_disable_3d(bool p_disable); bool is_3d_disabled() const; + void set_keep_3d_linear(bool p_keep_3d_linear); + bool get_keep_3d_linear() const; + void set_attach_to_screen_rect(const Rect2 &p_rect); Rect2 get_attach_to_screen_rect() const; @@ -470,6 +470,8 @@ public: void set_snap_controls_to_pixels(bool p_enable); bool is_snap_controls_to_pixels_enabled() const; + void _subwindow_visibility_changed(); + Viewport(); ~Viewport(); }; diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 4036735807..7533fa5f6c 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "register_scene_types.h" #include "core/class_db.h" @@ -45,6 +46,7 @@ #include "scene/2d/light_2d.h" #include "scene/2d/light_occluder_2d.h" #include "scene/2d/line_2d.h" +#include "scene/2d/mesh_instance_2d.h" #include "scene/2d/navigation2d.h" #include "scene/2d/parallax_background.h" #include "scene/2d/parallax_layer.h" @@ -56,6 +58,7 @@ #include "scene/2d/ray_cast_2d.h" #include "scene/2d/remote_transform_2d.h" #include "scene/2d/screen_button.h" +#include "scene/2d/skeleton_2d.h" #include "scene/2d/sprite.h" #include "scene/2d/tile_map.h" #include "scene/2d/visibility_notifier_2d.h" @@ -200,6 +203,8 @@ static ResourceFormatLoaderDynamicFont *resource_loader_dynamic_font = NULL; static ResourceFormatLoaderStreamTexture *resource_loader_stream_texture = NULL; +static ResourceFormatLoaderBMFont *resource_loader_bmfont = NULL; + static ResourceFormatSaverShader *resource_saver_shader = NULL; static ResourceFormatLoaderShader *resource_loader_shader = NULL; @@ -220,31 +225,20 @@ void register_scene_types() { resource_loader_theme = memnew(ResourceFormatLoaderTheme); ResourceLoader::add_resource_format_loader(resource_loader_theme); - bool default_theme_hidpi = GLOBAL_DEF("gui/theme/use_hidpi", false); - ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/use_hidpi", PropertyInfo(Variant::BOOL, "gui/theme/use_hidpi", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)); - String theme_path = GLOBAL_DEF("gui/theme/custom", ""); - ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/custom", PropertyInfo(Variant::STRING, "gui/theme/custom", PROPERTY_HINT_FILE, "*.tres,*.res", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)); - String font_path = GLOBAL_DEF("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,*.font", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)); + resource_saver_text = memnew(ResourceFormatSaverText); + ResourceSaver::add_resource_format_saver(resource_saver_text, true); - bool has_theme = false; - if (theme_path != String()) { - Ref<Theme> theme = ResourceLoader::load(theme_path); - if (theme.is_valid()) { - Theme::set_default(theme); - has_theme = true; - } else { - ERR_PRINTS("Error loading custom theme '" + theme_path + "'"); - } - } + resource_loader_text = memnew(ResourceFormatLoaderText); + ResourceLoader::add_resource_format_loader(resource_loader_text, true); - if (!has_theme) { - Ref<Font> font; - if (font_path != String()) { - font = ResourceLoader::load(font_path); - } - make_default_theme(default_theme_hidpi, font); - } + resource_saver_shader = memnew(ResourceFormatSaverShader); + ResourceSaver::add_resource_format_saver(resource_saver_shader, true); + + resource_loader_shader = memnew(ResourceFormatLoaderShader); + ResourceLoader::add_resource_format_loader(resource_loader_shader, true); + + resource_loader_bmfont = memnew(ResourceFormatLoaderBMFont); + ResourceLoader::add_resource_format_loader(resource_loader_bmfont, true); OS::get_singleton()->yield(); //may take time to init @@ -396,6 +390,7 @@ void register_scene_types() { ClassDB::register_class<RigidBody>(); ClassDB::register_class<KinematicCollision>(); ClassDB::register_class<KinematicBody>(); + ClassDB::register_class<PhysicalBone>(); ClassDB::register_class<VehicleBody>(); ClassDB::register_class<VehicleWheel>(); @@ -442,6 +437,7 @@ void register_scene_types() { ClassDB::register_class<AnimatedSprite>(); ClassDB::register_class<Position2D>(); ClassDB::register_class<Line2D>(); + ClassDB::register_class<MeshInstance2D>(); ClassDB::register_virtual_class<CollisionObject2D>(); ClassDB::register_virtual_class<PhysicsBody2D>(); ClassDB::register_class<StaticBody2D>(); @@ -455,6 +451,8 @@ void register_scene_types() { ClassDB::register_class<VisibilityNotifier2D>(); ClassDB::register_class<VisibilityEnabler2D>(); ClassDB::register_class<Polygon2D>(); + ClassDB::register_class<Skeleton2D>(); + ClassDB::register_class<Bone2D>(); ClassDB::register_class<Light2D>(); ClassDB::register_class<LightOccluder2D>(); ClassDB::register_class<OccluderPolygon2D>(); @@ -604,24 +602,42 @@ void register_scene_types() { OS::get_singleton()->yield(); //may take time to init - resource_saver_text = memnew(ResourceFormatSaverText); - ResourceSaver::add_resource_format_saver(resource_saver_text, true); - - resource_loader_text = memnew(ResourceFormatLoaderText); - ResourceLoader::add_resource_format_loader(resource_loader_text, true); - - resource_saver_shader = memnew(ResourceFormatSaverShader); - ResourceSaver::add_resource_format_saver(resource_saver_shader, true); - - resource_loader_shader = memnew(ResourceFormatLoaderShader); - ResourceLoader::add_resource_format_loader(resource_loader_shader, true); - for (int i = 0; i < 20; i++) { GLOBAL_DEF("layer_names/2d_render/layer_" + itos(i + 1), ""); GLOBAL_DEF("layer_names/2d_physics/layer_" + itos(i + 1), ""); GLOBAL_DEF("layer_names/3d_render/layer_" + itos(i + 1), ""); GLOBAL_DEF("layer_names/3d_physics/layer_" + itos(i + 1), ""); } + + bool default_theme_hidpi = GLOBAL_DEF("gui/theme/use_hidpi", false); + ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/use_hidpi", PropertyInfo(Variant::BOOL, "gui/theme/use_hidpi", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)); + String theme_path = GLOBAL_DEF("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("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,*.font", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)); + + Ref<Font> font; + if (font_path != String()) { + font = ResourceLoader::load(font_path); + if (!font.is_valid()) { + ERR_PRINTS("Error loading custom font '" + font_path + "'"); + } + } + + // Always make the default theme to avoid invalid default font/icon/style in the given theme + make_default_theme(default_theme_hidpi, font); + + if (theme_path != String()) { + Ref<Theme> theme = ResourceLoader::load(theme_path); + if (theme.is_valid()) { + Theme::set_default(theme); + if (font.is_valid()) { + Theme::set_default_font(font); + } + } else { + ERR_PRINTS("Error loading custom theme '" + theme_path + "'"); + } + } } void unregister_scene_types() { @@ -647,6 +663,9 @@ void unregister_scene_types() { if (resource_loader_shader) { memdelete(resource_loader_shader); } + if (resource_loader_bmfont) { + memdelete(resource_loader_bmfont); + } SpatialMaterial::finish_shaders(); ParticlesMaterial::finish_shaders(); diff --git a/scene/register_scene_types.h b/scene/register_scene_types.h index da7e290f25..9121c015fd 100644 --- a/scene/register_scene_types.h +++ b/scene/register_scene_types.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef REGISTER_SCENE_TYPES_H #define REGISTER_SCENE_TYPES_H diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index 367c8235f5..7a1fffaa26 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "animation.h" #include "geometry.h" @@ -35,13 +36,7 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) { String name = p_name; - if (name == "length") - set_length(p_value); - else if (name == "loop") - set_loop(p_value); - else if (name == "step") - set_step(p_value); - else if (name.begins_with("tracks/")) { + if (name.begins_with("tracks/")) { int track = name.get_slicec('/', 1).to_int(); String what = name.get_slicec('/', 2); @@ -383,20 +378,15 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const { } void Animation::_get_property_list(List<PropertyInfo> *p_list) const { - - p_list->push_back(PropertyInfo(Variant::REAL, "length", PROPERTY_HINT_RANGE, "0.001,99999,0.001")); - p_list->push_back(PropertyInfo(Variant::BOOL, "loop")); - p_list->push_back(PropertyInfo(Variant::REAL, "step", PROPERTY_HINT_RANGE, "0,4096,0.001")); - for (int i = 0; i < tracks.size(); i++) { - p_list->push_back(PropertyInfo(Variant::STRING, "tracks/" + itos(i) + "/type", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::NODE_PATH, "tracks/" + itos(i) + "/path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::INT, "tracks/" + itos(i) + "/interp", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/loop_wrap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/imported", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::ARRAY, "tracks/" + itos(i) + "/keys", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::STRING, "tracks/" + itos(i) + "/type", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::NODE_PATH, "tracks/" + itos(i) + "/path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::INT, "tracks/" + itos(i) + "/interp", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/loop_wrap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/imported", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::ARRAY, "tracks/" + itos(i) + "/keys", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); } } @@ -1613,19 +1603,19 @@ float Animation::get_step() const { return step; } -void Animation::copy_track(int src_track, Ref<Animation> p_to_animation) { +void Animation::copy_track(int p_track, Ref<Animation> p_to_animation) { ERR_FAIL_COND(p_to_animation.is_null()); - ERR_FAIL_INDEX(src_track, get_track_count()); + ERR_FAIL_INDEX(p_track, get_track_count()); int dst_track = p_to_animation->get_track_count(); - p_to_animation->add_track(track_get_type(src_track)); - - p_to_animation->track_set_path(dst_track, track_get_path(src_track)); - p_to_animation->track_set_imported(dst_track, track_is_imported(src_track)); - p_to_animation->track_set_enabled(dst_track, track_is_enabled(src_track)); - p_to_animation->track_set_interpolation_type(dst_track, track_get_interpolation_type(src_track)); - p_to_animation->track_set_interpolation_loop_wrap(dst_track, track_get_interpolation_loop_wrap(src_track)); - for (int i = 0; i < track_get_key_count(src_track); i++) { - p_to_animation->track_insert_key(dst_track, track_get_key_time(src_track, i), track_get_key_value(src_track, i), track_get_key_transition(src_track, i)); + p_to_animation->add_track(track_get_type(p_track)); + + p_to_animation->track_set_path(dst_track, track_get_path(p_track)); + p_to_animation->track_set_imported(dst_track, track_is_imported(p_track)); + p_to_animation->track_set_enabled(dst_track, track_is_enabled(p_track)); + p_to_animation->track_set_interpolation_type(dst_track, track_get_interpolation_type(p_track)); + p_to_animation->track_set_interpolation_loop_wrap(dst_track, track_get_interpolation_loop_wrap(p_track)); + for (int i = 0; i < track_get_key_count(p_track); i++) { + p_to_animation->track_insert_key(dst_track, track_get_key_time(p_track, i), track_get_key_value(p_track, i), track_get_key_transition(p_track, i)); } } @@ -1689,6 +1679,10 @@ void Animation::_bind_methods() { ClassDB::bind_method(D_METHOD("clear"), &Animation::clear); ClassDB::bind_method(D_METHOD("copy_track", "track", "to_animation"), &Animation::copy_track); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "length", PROPERTY_HINT_RANGE, "0.001,99999,0.001"), "set_length", "get_length"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "step", PROPERTY_HINT_RANGE, "0,4096,0.001"), "set_step", "get_step"); + BIND_ENUM_CONSTANT(TYPE_VALUE); BIND_ENUM_CONSTANT(TYPE_TRANSFORM); BIND_ENUM_CONSTANT(TYPE_METHOD); diff --git a/scene/resources/animation.h b/scene/resources/animation.h index 2975c7fd9d..73691a69f2 100644 --- a/scene/resources/animation.h +++ b/scene/resources/animation.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef ANIMATION_H #define ANIMATION_H diff --git a/scene/resources/audio_stream_sample.cpp b/scene/resources/audio_stream_sample.cpp index e52a83d17e..b77143cd9d 100644 --- a/scene/resources/audio_stream_sample.cpp +++ b/scene/resources/audio_stream_sample.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "audio_stream_sample.h" void AudioStreamPlaybackSample::start(float p_from_pos) { @@ -76,7 +77,7 @@ void AudioStreamPlaybackSample::seek(float p_time) { if (base->format == AudioStreamSample::FORMAT_IMA_ADPCM) return; //no seeking in ima-adpcm - float max = get_length(); + float max = base->get_length(); if (p_time < 0) { p_time = 0; } else if (p_time >= max) { @@ -389,22 +390,6 @@ void AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, in } } -float AudioStreamPlaybackSample::get_length() const { - - int len = base->data_bytes; - switch (base->format) { - case AudioStreamSample::FORMAT_8_BITS: len /= 1; break; - case AudioStreamSample::FORMAT_16_BITS: len /= 2; break; - case AudioStreamSample::FORMAT_IMA_ADPCM: len *= 2; break; - } - - if (base->stereo) { - len /= 2; - } - - return float(len) / base->mix_rate; -} - AudioStreamPlaybackSample::AudioStreamPlaybackSample() { active = false; @@ -468,6 +453,22 @@ bool AudioStreamSample::is_stereo() const { return stereo; } +float AudioStreamSample::get_length() const { + + int len = data_bytes; + switch (format) { + case AudioStreamSample::FORMAT_8_BITS: len /= 1; break; + case AudioStreamSample::FORMAT_16_BITS: len /= 2; break; + case AudioStreamSample::FORMAT_IMA_ADPCM: len *= 2; break; + } + + if (stereo) { + len /= 2; + } + + return float(len) / mix_rate; +} + void AudioStreamSample::set_data(const PoolVector<uint8_t> &p_data) { AudioServer::get_singleton()->lock(); @@ -541,8 +542,8 @@ void AudioStreamSample::_bind_methods() { 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_data", "data"), &AudioStreamSample::set_data); - ClassDB::bind_method(D_METHOD("get_data"), &AudioStreamSample::get_data); + ClassDB::bind_method(D_METHOD("_set_data", "data"), &AudioStreamSample::set_data); + ClassDB::bind_method(D_METHOD("_get_data"), &AudioStreamSample::get_data); ADD_PROPERTY(PropertyInfo(Variant::INT, "format", PROPERTY_HINT_ENUM, "8-Bit,16-Bit,IMA-ADPCM"), "set_format", "get_format"); ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_mode", PROPERTY_HINT_ENUM, "Disabled,Forward,Ping-Pong"), "set_loop_mode", "get_loop_mode"); @@ -550,7 +551,7 @@ void AudioStreamSample::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_end"), "set_loop_end", "get_loop_end"); ADD_PROPERTY(PropertyInfo(Variant::INT, "mix_rate"), "set_mix_rate", "get_mix_rate"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stereo"), "set_stereo", "is_stereo"); - ADD_PROPERTY(PropertyInfo(Variant::POOL_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_data", "get_data"); + ADD_PROPERTY(PropertyInfo(Variant::POOL_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); BIND_ENUM_CONSTANT(FORMAT_8_BITS); BIND_ENUM_CONSTANT(FORMAT_16_BITS); diff --git a/scene/resources/audio_stream_sample.h b/scene/resources/audio_stream_sample.h index a51483aa21..5fe65c194e 100644 --- a/scene/resources/audio_stream_sample.h +++ b/scene/resources/audio_stream_sample.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef AUDIOSTREAMSAMPLE_H #define AUDIOSTREAMSAMPLE_H @@ -76,8 +77,6 @@ public: virtual void mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames); - virtual float get_length() const; //if supported, otherwise return 0 - AudioStreamPlaybackSample(); }; @@ -136,6 +135,8 @@ public: void set_stereo(bool p_enable); bool is_stereo() const; + virtual float get_length() const; //if supported, otherwise return 0 + void set_data(const PoolVector<uint8_t> &p_data); PoolVector<uint8_t> get_data() const; diff --git a/scene/resources/bit_mask.cpp b/scene/resources/bit_mask.cpp index 85bcfc9ecd..29ffefd9d6 100644 --- a/scene/resources/bit_mask.cpp +++ b/scene/resources/bit_mask.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "bit_mask.h" #include "io/image_loader.h" @@ -41,7 +42,7 @@ void BitMap::create(const Size2 &p_size) { zeromem(bitmask.ptrw(), bitmask.size()); } -void BitMap::create_from_image_alpha(const Ref<Image> &p_image) { +void BitMap::create_from_image_alpha(const Ref<Image> &p_image, float p_threshold) { ERR_FAIL_COND(p_image.is_null() || p_image->empty()); Ref<Image> img = p_image->duplicate(); @@ -57,8 +58,9 @@ void BitMap::create_from_image_alpha(const Ref<Image> &p_image) { int bbyte = i / 8; int bbit = i % 8; - if (r[i * 2]) + if (r[i * 2 + 1] / 255.0 > p_threshold) { w[bbyte] |= (1 << bbit); + } } } @@ -80,7 +82,7 @@ void BitMap::set_bit_rect(const Rect2 &p_rect, bool p_value) { if (p_value) b |= (1 << bbit); else - b &= !(1 << bbit); + b &= ~(1 << bbit); data[bbyte] = b; } @@ -111,8 +113,8 @@ int BitMap::get_true_bit_count() const { void BitMap::set_bit(const Point2 &p_pos, bool p_value) { - int x = Math::fast_ftoi(p_pos.x); - int y = Math::fast_ftoi(p_pos.y); + int x = p_pos.x; + int y = p_pos.y; ERR_FAIL_INDEX(x, width); ERR_FAIL_INDEX(y, height); @@ -126,7 +128,7 @@ void BitMap::set_bit(const Point2 &p_pos, bool p_value) { if (p_value) b |= (1 << bbit); else - b &= !(1 << bbit); + b &= ~(1 << bbit); bitmask[bbyte] = b; } @@ -167,10 +169,351 @@ Dictionary BitMap::_get_data() const { return d; } +Vector<Vector2> BitMap::_march_square(const Rect2i &rect, const Point2i &start) const { + + int stepx = 0; + int stepy = 0; + int prevx = 0; + int prevy = 0; + int startx = start.x; + int starty = start.y; + int curx = startx; + int cury = starty; + unsigned int count = 0; + Set<Point2i> case9s; + Set<Point2i> case6s; + int i; + Vector<Vector2> _points; + do { + int sv = 0; + { //square value + + /* + checking the 2x2 pixel grid, assigning these values to each pixel, if not transparent + +---+---+ + | 1 | 2 | + +---+---+ + | 4 | 8 | <- current pixel (curx,cury) + +---+---+ + */ + //NOTE: due to the way we pick points from texture, rect needs to be smaller, otherwise it goes outside 1 pixel + Rect2i fixed_rect = Rect2i(rect.position, rect.size - Size2i(2, 2)); + Point2i tl = Point2i(curx - 1, cury - 1); + sv += (fixed_rect.has_point(tl) && get_bit(tl)) ? 1 : 0; + Point2i tr = Point2i(curx, cury - 1); + sv += (fixed_rect.has_point(tr) && get_bit(tr)) ? 2 : 0; + Point2i bl = Point2i(curx - 1, cury); + sv += (fixed_rect.has_point(bl) && get_bit(bl)) ? 4 : 0; + Point2i br = Point2i(curx, cury); + sv += (fixed_rect.has_point(br) && get_bit(br)) ? 8 : 0; + ERR_FAIL_COND_V(sv == 0 || sv == 15, Vector<Vector2>()); + } + + switch (sv) { + + case 1: + case 5: + case 13: + /* going UP with these cases: + 1 5 13 + +---+---+ +---+---+ +---+---+ + | 1 | | | 1 | | | 1 | | + +---+---+ +---+---+ +---+---+ + | | | | 4 | | | 4 | 8 | + +---+---+ +---+---+ +---+---+ + */ + stepx = 0; + stepy = -1; + break; + + case 8: + case 10: + case 11: + /* going DOWN with these cases: + 8 10 11 + +---+---+ +---+---+ +---+---+ + | | | | | 2 | | 1 | 2 | + +---+---+ +---+---+ +---+---+ + | | 8 | | | 8 | | | 8 | + +---+---+ +---+---+ +---+---+ + */ + stepx = 0; + stepy = 1; + break; + + case 4: + case 12: + case 14: + /* going LEFT with these cases: + 4 12 14 + +---+---+ +---+---+ +---+---+ + | | | | | | | | 2 | + +---+---+ +---+---+ +---+---+ + | 4 | | | 4 | 8 | | 4 | 8 | + +---+---+ +---+---+ +---+---+ + */ + stepx = -1; + stepy = 0; + break; + + case 2: + case 3: + case 7: + /* going RIGHT with these cases: + 2 3 7 + +---+---+ +---+---+ +---+---+ + | | 2 | | 1 | 2 | | 1 | 2 | + +---+---+ +---+---+ +---+---+ + | | | | | | | 4 | | + +---+---+ +---+---+ +---+---+ + */ + stepx = 1; + stepy = 0; + break; + case 9: + /* + +---+---+ + | 1 | | + +---+---+ + | | 8 | + +---+---+ + this should normally go UP, but if we already been here, we go down + */ + if (case9s.has(Point2i(curx, cury))) { + //found, so we go down, and delete from case9s; + stepx = 0; + stepy = 1; + case9s.erase(Point2i(curx, cury)); + } else { + //not found, we go up, and add to case9s; + stepx = 0; + stepy = -1; + case9s.insert(Point2i(curx, cury)); + } + break; + case 6: + /* + 6 + +---+---+ + | | 2 | + +---+---+ + | 4 | | + +---+---+ + this normally go RIGHT, but if its coming from UP, it should go LEFT + */ + if (case6s.has(Point2i(curx, cury))) { + //found, so we go down, and delete from case6s; + stepx = -1; + stepy = 0; + case6s.erase(Point2i(curx, cury)); + } else { + //not found, we go up, and add to case6s; + stepx = 1; + stepy = 0; + case6s.insert(Point2i(curx, cury)); + } + break; + 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 + curx += stepx; + cury += stepy; + if (stepx == prevx && stepy == prevy) { + _points[_points.size() - 1].x = (float)(curx - rect.position.x); + _points[_points.size() - 1].y = (float)(cury + rect.position.y); + } else { + _points.push_back(Vector2((float)(curx - rect.position.x), (float)(cury + rect.position.y))); + } + + count++; + prevx = stepx; + prevy = stepy; + + ERR_FAIL_COND_V(count > width * height, _points); + } while (curx != startx || cury != starty); + return _points; +} + +static float perpendicular_distance(const Vector2 &i, const Vector2 &start, const Vector2 &end) { + float res; + float slope; + float intercept; + + if (start.x == end.x) { + res = Math::absf(i.x - end.x); + } else if (start.y == end.y) { + res = Math::absf(i.y - end.y); + } else { + slope = (end.y - start.y) / (end.x - start.x); + intercept = start.y - (slope * start.x); + res = Math::absf(slope * i.x - i.y + intercept) / Math::sqrt(Math::pow(slope, 2.0f) + 1.0); + } + return res; +} + +static Vector<Vector2> rdp(const Vector<Vector2> &v, float optimization) { + if (v.size() < 3) + return v; + + int index = -1; + float dist = 0; + //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) { + dist = cdist; + index = static_cast<int>(i); + } + } + if (dist > optimization) { + + Vector<Vector2> left, right; + left.resize(index); + for (int i = 0; i < index; i++) { + left[i] = v[i]; + } + right.resize(v.size() - index); + for (int i = 0; i < right.size(); i++) { + right[i] = v[index + i]; + } + Vector<Vector2> r1 = rdp(left, optimization); + Vector<Vector2> r2 = rdp(right, optimization); + + int middle = r1.size(); + r1.resize(r1.size() + r2.size()); + for (int i = 0; i < r2.size(); i++) { + r1[middle + i] = r2[i]; + } + return r1; + } else { + Vector<Vector2> ret; + ret.push_back(v[0]); + ret.push_back(v[v.size() - 1]); + return ret; + } +} + +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 + 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 (size < 9) { + return points; + } + + float maxEp = MIN(rect.size.width, rect.size.height); + float ep = CLAMP(epsilon, 0.0, maxEp / 2); + Vector<Vector2> result = rdp(points, ep); + + Vector2 last = result[result.size() - 1]; + + if (last.y > result[0].y && last.distance_to(result[0]) < ep * 0.5f) { + result[0].y = last.y; + result.resize(result.size() - 1); + } + return result; +} + +static void fill_bits(const BitMap *p_src, Ref<BitMap> &p_map, const Point2i &p_pos, const Rect2i &rect) { + + for (int i = p_pos.x - 1; i <= p_pos.x + 1; i++) { + for (int j = p_pos.y - 1; j <= p_pos.y + 1; j++) { + + if (i < rect.position.x || i >= rect.position.x + rect.size.x) + continue; + if (j < rect.position.y || j >= rect.position.y + rect.size.y) + continue; + + if (p_map->get_bit(Vector2(i, j))) + continue; + + else if (p_src->get_bit(Vector2(i, j))) { + p_map->set_bit(Vector2(i, j), true); + fill_bits(p_src, p_map, Point2i(i, j), rect); + } + } + } +} +Vector<Vector<Vector2> > BitMap::clip_opaque_to_polygons(const Rect2 &p_rect, float p_epsilon) const { + + Rect2i r = Rect2i(0, 0, width, height).clip(p_rect); + + print_line("Rect: " + r); + Point2i from; + Ref<BitMap> fill; + fill.instance(); + fill->create(get_size()); + + 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))) { + + Vector<Vector2> polygon = _march_square(r, Point2i(j, i)); + print_line("pre reduce: " + itos(polygon.size())); + polygon = reduce(polygon, r, p_epsilon); + print_line("post reduce: " + itos(polygon.size())); + polygons.push_back(polygon); + fill_bits(this, fill, Point2i(j, i), r); + } + } + } + + return polygons; +} + +void BitMap::grow_mask(int p_pixels, const Rect2 &p_rect) { + + Rect2i r = Rect2i(0, 0, width, height).clip(p_rect); + + Ref<BitMap> copy; + copy.instance(); + copy->create(get_size()); + copy->bitmask = bitmask; + + 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 (copy->get_bit(Point2(j, i))) + continue; + + bool found = false; + + for (int y = i - p_pixels; y <= i + p_pixels; y++) { + for (int x = j - p_pixels; x <= j + p_pixels; x++) { + + if (x < p_rect.position.x || x >= p_rect.position.x + p_rect.size.x) + continue; + if (y < p_rect.position.y || y >= p_rect.position.y + p_rect.size.y) + continue; + + float d = Point2(j, i).distance_to(Point2(x, y)) - CMP_EPSILON; + if (d > p_pixels) + continue; + + if (copy->get_bit(Point2(x, y))) { + found = true; + break; + } + } + if (found) + break; + } + + if (found) { + set_bit(Point2(j, i), true); + } + } + } +} + void BitMap::_bind_methods() { ClassDB::bind_method(D_METHOD("create", "size"), &BitMap::create); - ClassDB::bind_method(D_METHOD("create_from_image_alpha", "image"), &BitMap::create_from_image_alpha); + 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); @@ -183,7 +526,7 @@ void BitMap::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_data"), &BitMap::_set_data); ClassDB::bind_method(D_METHOD("_get_data"), &BitMap::_get_data); - ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_data", "_get_data"); + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); } BitMap::BitMap() { diff --git a/scene/resources/bit_mask.h b/scene/resources/bit_mask.h index 403f151004..dcd5edb4fb 100644 --- a/scene/resources/bit_mask.h +++ b/scene/resources/bit_mask.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef BIT_MASK_H #define BIT_MASK_H @@ -38,12 +39,13 @@ class BitMap : public Resource { GDCLASS(BitMap, Resource); OBJ_SAVE_TYPE(BitMap); - RES_BASE_EXTENSION("pbm"); Vector<uint8_t> bitmask; int width; int height; + Vector<Vector2> _march_square(const Rect2i &rect, const Point2i &start) const; + protected: void _set_data(const Dictionary &p_d); Dictionary _get_data() const; @@ -52,7 +54,7 @@ protected: public: void create(const Size2 &p_size); - void create_from_image_alpha(const Ref<Image> &p_image); + 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; @@ -61,6 +63,10 @@ public: Size2 get_size() const; + void grow_mask(int p_pixels, const Rect2 &p_rect); + + Vector<Vector<Vector2> > clip_opaque_to_polygons(const Rect2 &p_rect, float p_epsilon = 2.0) const; + BitMap(); }; diff --git a/scene/resources/bounds.cpp b/scene/resources/bounds.cpp index 3e4be0defa..b115d92be3 100644 --- a/scene/resources/bounds.cpp +++ b/scene/resources/bounds.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "bounds.h" void Bounds::_bind_methods() { diff --git a/scene/resources/bounds.h b/scene/resources/bounds.h index 1454064853..dfe2fe40c6 100644 --- a/scene/resources/bounds.h +++ b/scene/resources/bounds.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef BOUNDS_H #define BOUNDS_H diff --git a/scene/resources/box_shape.cpp b/scene/resources/box_shape.cpp index d93791ef4f..e9e01ed98a 100644 --- a/scene/resources/box_shape.cpp +++ b/scene/resources/box_shape.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "box_shape.h" #include "servers/physics_server.h" diff --git a/scene/resources/box_shape.h b/scene/resources/box_shape.h index 2bbf455a46..5ef16b4766 100644 --- a/scene/resources/box_shape.h +++ b/scene/resources/box_shape.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef BOX_SHAPE_H #define BOX_SHAPE_H diff --git a/scene/resources/canvas.cpp b/scene/resources/canvas.cpp index e46b40736e..8da1da9541 100644 --- a/scene/resources/canvas.cpp +++ b/scene/resources/canvas.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "canvas.h" #include "servers/visual_server.h" diff --git a/scene/resources/canvas.h b/scene/resources/canvas.h index 1bb57987e3..dfdea82ca5 100644 --- a/scene/resources/canvas.h +++ b/scene/resources/canvas.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef CANVAS_H #define CANVAS_H diff --git a/scene/resources/capsule_shape.cpp b/scene/resources/capsule_shape.cpp index bb721759c9..101970bad8 100644 --- a/scene/resources/capsule_shape.cpp +++ b/scene/resources/capsule_shape.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "capsule_shape.h" #include "servers/physics_server.h" diff --git a/scene/resources/capsule_shape.h b/scene/resources/capsule_shape.h index 6a0b27bb0b..f89d07c5f1 100644 --- a/scene/resources/capsule_shape.h +++ b/scene/resources/capsule_shape.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef CAPSULE_SHAPE_H #define CAPSULE_SHAPE_H diff --git a/scene/resources/capsule_shape_2d.cpp b/scene/resources/capsule_shape_2d.cpp index 2722ff7207..95ab34abb2 100644 --- a/scene/resources/capsule_shape_2d.cpp +++ b/scene/resources/capsule_shape_2d.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "capsule_shape_2d.h" #include "servers/physics_2d_server.h" diff --git a/scene/resources/capsule_shape_2d.h b/scene/resources/capsule_shape_2d.h index 784f2c02c8..04d663c010 100644 --- a/scene/resources/capsule_shape_2d.h +++ b/scene/resources/capsule_shape_2d.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef CAPSULE_SHAPE_2D_H #define CAPSULE_SHAPE_2D_H diff --git a/scene/resources/circle_shape_2d.cpp b/scene/resources/circle_shape_2d.cpp index 669b1d8e7d..1c7bb76b26 100644 --- a/scene/resources/circle_shape_2d.cpp +++ b/scene/resources/circle_shape_2d.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "circle_shape_2d.h" #include "servers/physics_2d_server.h" diff --git a/scene/resources/circle_shape_2d.h b/scene/resources/circle_shape_2d.h index 3668521a55..06bb433170 100644 --- a/scene/resources/circle_shape_2d.h +++ b/scene/resources/circle_shape_2d.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef CIRCLE_SHAPE_2D_H #define CIRCLE_SHAPE_2D_H diff --git a/scene/resources/color_ramp.cpp b/scene/resources/color_ramp.cpp index 22d52ef1ec..b2f586d02d 100644 --- a/scene/resources/color_ramp.cpp +++ b/scene/resources/color_ramp.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "color_ramp.h" #include "core_string_names.h" @@ -70,8 +71,8 @@ void Gradient::_bind_methods() { ClassDB::bind_method(D_METHOD(COLOR_RAMP_SET_COLORS, "colors"), &Gradient::set_colors); ClassDB::bind_method(D_METHOD(COLOR_RAMP_GET_COLORS), &Gradient::get_colors); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "offsets"), COLOR_RAMP_SET_OFFSETS, COLOR_RAMP_GET_OFFSETS); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "colors"), COLOR_RAMP_SET_COLORS, COLOR_RAMP_GET_COLORS); + ADD_PROPERTY(PropertyInfo(Variant::POOL_REAL_ARRAY, "offsets"), COLOR_RAMP_SET_OFFSETS, COLOR_RAMP_GET_OFFSETS); + ADD_PROPERTY(PropertyInfo(Variant::POOL_COLOR_ARRAY, "colors"), COLOR_RAMP_SET_COLORS, COLOR_RAMP_GET_COLORS); } Vector<float> Gradient::get_offsets() const { diff --git a/scene/resources/color_ramp.h b/scene/resources/color_ramp.h index 4e041355b2..c042a0d3d0 100644 --- a/scene/resources/color_ramp.h +++ b/scene/resources/color_ramp.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef SCENE_RESOURCES_COLOR_RAMP_H_ #define SCENE_RESOURCES_COLOR_RAMP_H_ diff --git a/scene/resources/concave_polygon_shape.cpp b/scene/resources/concave_polygon_shape.cpp index 718aaeb27a..935f041837 100644 --- a/scene/resources/concave_polygon_shape.cpp +++ b/scene/resources/concave_polygon_shape.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "concave_polygon_shape.h" #include "servers/physics_server.h" @@ -63,29 +64,6 @@ Vector<Vector3> ConcavePolygonShape::_gen_debug_mesh_lines() { return points; } -bool ConcavePolygonShape::_set(const StringName &p_name, const Variant &p_value) { - - if (p_name == "data") - PhysicsServer::get_singleton()->shape_set_data(get_shape(), p_value); - else - return false; - - return true; -} - -bool ConcavePolygonShape::_get(const StringName &p_name, Variant &r_ret) const { - - if (p_name == "data") - r_ret = PhysicsServer::get_singleton()->shape_get_data(get_shape()); - else - return false; - return true; -} -void ConcavePolygonShape::_get_property_list(List<PropertyInfo> *p_list) const { - - p_list->push_back(PropertyInfo(Variant::ARRAY, "data")); -} - void ConcavePolygonShape::_update_shape() { } @@ -104,6 +82,7 @@ void ConcavePolygonShape::_bind_methods() { ClassDB::bind_method(D_METHOD("set_faces", "faces"), &ConcavePolygonShape::set_faces); ClassDB::bind_method(D_METHOD("get_faces"), &ConcavePolygonShape::get_faces); + ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR3_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_faces", "get_faces"); } ConcavePolygonShape::ConcavePolygonShape() : diff --git a/scene/resources/concave_polygon_shape.h b/scene/resources/concave_polygon_shape.h index b94e3eca9c..2cc9095abf 100644 --- a/scene/resources/concave_polygon_shape.h +++ b/scene/resources/concave_polygon_shape.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef CONCAVE_POLYGON_SHAPE_H #define CONCAVE_POLYGON_SHAPE_H @@ -57,9 +58,6 @@ class ConcavePolygonShape : public Shape { }; 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; static void _bind_methods(); virtual void _update_shape(); diff --git a/scene/resources/concave_polygon_shape_2d.cpp b/scene/resources/concave_polygon_shape_2d.cpp index 69765796f6..d4343680ef 100644 --- a/scene/resources/concave_polygon_shape_2d.cpp +++ b/scene/resources/concave_polygon_shape_2d.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "concave_polygon_shape_2d.h" #include "servers/physics_2d_server.h" diff --git a/scene/resources/concave_polygon_shape_2d.h b/scene/resources/concave_polygon_shape_2d.h index 7ca14d4d66..e8fa7369ac 100644 --- a/scene/resources/concave_polygon_shape_2d.h +++ b/scene/resources/concave_polygon_shape_2d.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef CONCAVE_POLYGON_SHAPE_2D_H #define CONCAVE_POLYGON_SHAPE_2D_H diff --git a/scene/resources/convex_polygon_shape.cpp b/scene/resources/convex_polygon_shape.cpp index e022b76907..a2e0996996 100644 --- a/scene/resources/convex_polygon_shape.cpp +++ b/scene/resources/convex_polygon_shape.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "convex_polygon_shape.h" #include "quick_hull.h" #include "servers/physics_server.h" diff --git a/scene/resources/convex_polygon_shape.h b/scene/resources/convex_polygon_shape.h index 0a840f0bc5..62567fc031 100644 --- a/scene/resources/convex_polygon_shape.h +++ b/scene/resources/convex_polygon_shape.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef CONVEX_POLYGON_SHAPE_H #define CONVEX_POLYGON_SHAPE_H diff --git a/scene/resources/convex_polygon_shape_2d.cpp b/scene/resources/convex_polygon_shape_2d.cpp index 0402a70898..d061c4ea1e 100644 --- a/scene/resources/convex_polygon_shape_2d.cpp +++ b/scene/resources/convex_polygon_shape_2d.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "convex_polygon_shape_2d.h" #include "geometry.h" diff --git a/scene/resources/convex_polygon_shape_2d.h b/scene/resources/convex_polygon_shape_2d.h index 320cf94a5e..69c237a0e6 100644 --- a/scene/resources/convex_polygon_shape_2d.h +++ b/scene/resources/convex_polygon_shape_2d.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef CONVEX_POLYGON_SHAPE_2D_H #define CONVEX_POLYGON_SHAPE_2D_H diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp index 95cb336740..4ec1e8973d 100644 --- a/scene/resources/curve.cpp +++ b/scene/resources/curve.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "curve.h" #include "core_string_names.h" @@ -510,7 +511,7 @@ void Curve::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::REAL, "min_value", PROPERTY_HINT_RANGE, "-1024,1024,0.01"), "set_min_value", "get_min_value"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_value", PROPERTY_HINT_RANGE, "-1024,1024,0.01"), "set_max_value", "get_max_value"); ADD_PROPERTY(PropertyInfo(Variant::INT, "bake_resolution", PROPERTY_HINT_RANGE, "1,1000,1"), "set_bake_resolution", "get_bake_resolution"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_data", "_get_data"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); ADD_SIGNAL(MethodInfo(SIGNAL_RANGE_CHANGED)); @@ -805,6 +806,87 @@ float Curve2D::get_bake_interval() const { return bake_interval; } +Vector2 Curve2D::get_closest_point(const Vector2 &p_to_point) const { + // Brute force method + + if (baked_cache_dirty) + _bake(); + + //validate// + int pc = baked_point_cache.size(); + if (pc == 0) { + ERR_EXPLAIN("No points in Curve2D"); + ERR_FAIL_COND_V(pc == 0, Vector2()); + } + + if (pc == 1) + return baked_point_cache.get(0); + + PoolVector2Array::Read r = baked_point_cache.read(); + + Vector2 nearest; + float nearest_dist = -1.0f; + + for (int i = 0; i < pc - 1; i++) { + Vector2 origin = r[i]; + Vector2 direction = (r[i + 1] - origin) / bake_interval; + + float d = CLAMP((p_to_point - origin).dot(direction), 0.0f, bake_interval); + Vector2 proj = origin + direction * d; + + float dist = proj.distance_squared_to(p_to_point); + + if (nearest_dist < 0.0f || dist < nearest_dist) { + nearest = proj; + nearest_dist = dist; + } + } + + return nearest; +} + +float Curve2D::get_closest_offset(const Vector2 &p_to_point) const { + // Brute force method + + if (baked_cache_dirty) + _bake(); + + //validate// + int pc = baked_point_cache.size(); + if (pc == 0) { + ERR_EXPLAIN("No points in Curve2D"); + ERR_FAIL_COND_V(pc == 0, 0.0f); + } + + if (pc == 1) + return 0.0f; + + PoolVector2Array::Read r = baked_point_cache.read(); + + float nearest = 0.0f; + float nearest_dist = -1.0f; + float offset = 0.0f; + + for (int i = 0; i < pc - 1; i++) { + Vector2 origin = r[i]; + Vector2 direction = (r[i + 1] - origin) / bake_interval; + + float d = CLAMP((p_to_point - origin).dot(direction), 0.0f, bake_interval); + Vector2 proj = origin + direction * d; + + float dist = proj.distance_squared_to(p_to_point); + + if (nearest_dist < 0.0f || dist < nearest_dist) { + nearest = offset + d; + nearest_dist = dist; + } + + offset += bake_interval; + } + + return nearest; +} + Dictionary Curve2D::_get_data() const { Dictionary dc; @@ -908,13 +990,15 @@ void Curve2D::_bind_methods() { 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("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); ADD_PROPERTY(PropertyInfo(Variant::REAL, "bake_interval", PROPERTY_HINT_RANGE, "0.01,512,0.01"), "set_bake_interval", "get_bake_interval"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_data", "_get_data"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); } Curve2D::Curve2D() { @@ -1275,6 +1359,87 @@ PoolRealArray Curve3D::get_baked_tilts() const { return baked_tilt_cache; } +Vector3 Curve3D::get_closest_point(const Vector3 &p_to_point) const { + // Brute force method + + if (baked_cache_dirty) + _bake(); + + //validate// + int pc = baked_point_cache.size(); + if (pc == 0) { + ERR_EXPLAIN("No points in Curve3D"); + ERR_FAIL_COND_V(pc == 0, Vector3()); + } + + if (pc == 1) + return baked_point_cache.get(0); + + PoolVector3Array::Read r = baked_point_cache.read(); + + Vector3 nearest; + float nearest_dist = -1.0f; + + for (int i = 0; i < pc - 1; i++) { + Vector3 origin = r[i]; + Vector3 direction = (r[i + 1] - origin) / bake_interval; + + float d = CLAMP((p_to_point - origin).dot(direction), 0.0f, bake_interval); + Vector3 proj = origin + direction * d; + + float dist = proj.distance_squared_to(p_to_point); + + if (nearest_dist < 0.0f || dist < nearest_dist) { + nearest = proj; + nearest_dist = dist; + } + } + + return nearest; +} + +float Curve3D::get_closest_offset(const Vector3 &p_to_point) const { + // Brute force method + + if (baked_cache_dirty) + _bake(); + + //validate// + int pc = baked_point_cache.size(); + if (pc == 0) { + ERR_EXPLAIN("No points in Curve3D"); + ERR_FAIL_COND_V(pc == 0, 0.0f); + } + + if (pc == 1) + return 0.0f; + + PoolVector3Array::Read r = baked_point_cache.read(); + + float nearest = 0.0f; + float nearest_dist = -1.0f; + float offset = 0.0f; + + for (int i = 0; i < pc - 1; i++) { + Vector3 origin = r[i]; + Vector3 direction = (r[i + 1] - origin) / bake_interval; + + float d = CLAMP((p_to_point - origin).dot(direction), 0.0f, bake_interval); + Vector3 proj = origin + direction * d; + + float dist = proj.distance_squared_to(p_to_point); + + if (nearest_dist < 0.0f || dist < nearest_dist) { + nearest = offset + d; + nearest_dist = dist; + } + + offset += bake_interval; + } + + return nearest; +} + void Curve3D::set_bake_interval(float p_tolerance) { bake_interval = p_tolerance; @@ -1403,13 +1568,15 @@ void Curve3D::_bind_methods() { ClassDB::bind_method(D_METHOD("interpolate_baked", "offset", "cubic"), &Curve3D::interpolate_baked, 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_closest_point", "to_point"), &Curve3D::get_closest_point); + ClassDB::bind_method(D_METHOD("get_closest_offset", "to_point"), &Curve3D::get_closest_offset); 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); ADD_PROPERTY(PropertyInfo(Variant::REAL, "bake_interval", PROPERTY_HINT_RANGE, "0.01,512,0.01"), "set_bake_interval", "get_bake_interval"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_data", "_get_data"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); } Curve3D::Curve3D() { diff --git a/scene/resources/curve.h b/scene/resources/curve.h index 4d421246bf..492eb05d1e 100644 --- a/scene/resources/curve.h +++ b/scene/resources/curve.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef CURVE_H #define CURVE_H @@ -198,6 +199,8 @@ public: float get_baked_length() const; Vector2 interpolate_baked(float p_offset, bool p_cubic = false) const; PoolVector2Array get_baked_points() const; //useful for going through + Vector2 get_closest_point(const Vector2 &p_to_point) const; + float get_closest_offset(const Vector2 &p_to_point) const; PoolVector2Array tessellate(int p_max_stages = 5, float p_tolerance = 4) const; //useful for display @@ -267,6 +270,8 @@ public: float interpolate_baked_tilt(float p_offset) const; PoolVector3Array get_baked_points() const; //useful for going through PoolRealArray get_baked_tilts() const; //useful for going through + Vector3 get_closest_point(const Vector3 &p_to_point) const; + float get_closest_offset(const Vector3 &p_to_point) const; PoolVector3Array tessellate(int p_max_stages = 5, float p_tolerance = 4) const; //useful for display diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index b18583dfb1..5ac9344f31 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -415,6 +415,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("font_color", "Label", Color(1, 1, 1)); theme->set_color("font_color_shadow", "Label", Color(0, 0, 0, 0)); + theme->set_color("font_outline_modulate", "Label", Color(1, 1, 1)); theme->set_constant("shadow_offset_x", "Label", 1 * scale); theme->set_constant("shadow_offset_y", "Label", 1 * scale); @@ -448,10 +449,10 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const // TextEdit - theme->set_stylebox("normal", "TextEdit", make_stylebox(tree_bg_png, 3, 3, 3, 3)); + theme->set_stylebox("normal", "TextEdit", make_stylebox(tree_bg_png, 3, 3, 3, 3, 0, 0, 0, 0)); theme->set_stylebox("focus", "TextEdit", focus); - theme->set_stylebox("read_only", "TextEdit", make_stylebox(tree_bg_disabled_png, 4, 4, 4, 4)); - theme->set_stylebox("completion", "TextEdit", make_stylebox(tree_bg_png, 3, 3, 3, 3)); + theme->set_stylebox("read_only", "TextEdit", make_stylebox(tree_bg_disabled_png, 4, 4, 4, 4, 0, 0, 0, 0)); + theme->set_stylebox("completion", "TextEdit", make_stylebox(tree_bg_png, 3, 3, 3, 3, 0, 0, 0, 0)); theme->set_icon("tab", "TextEdit", make_icon(tab_png)); @@ -582,6 +583,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("checked", "PopupMenu", make_icon(checked_png)); theme->set_icon("unchecked", "PopupMenu", make_icon(unchecked_png)); + theme->set_icon("radio_checked", "PopupMenu", make_icon(radio_checked_png)); + theme->set_icon("radio_unchecked", "PopupMenu", make_icon(radio_unchecked_png)); theme->set_icon("submenu", "PopupMenu", make_icon(submenu_png)); theme->set_font("font", "PopupMenu", default_font); @@ -816,6 +819,12 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("font_color_selected", "RichTextLabel", font_color_selection); theme->set_color("selection_color", "RichTextLabel", Color(0.1, 0.1, 1, 0.8)); + theme->set_color("font_color_shadow", "RichTextLabel", Color(0, 0, 0, 0)); + + theme->set_constant("shadow_offset_x", "RichTextLabel", 1 * scale); + theme->set_constant("shadow_offset_y", "RichTextLabel", 1 * scale); + theme->set_constant("shadow_as_outline", "RichTextLabel", 0 * scale); + theme->set_constant("line_separation", "RichTextLabel", 1 * scale); theme->set_constant("table_hseparation", "RichTextLabel", 3 * scale); theme->set_constant("table_vseparation", "RichTextLabel", 3 * scale); @@ -880,7 +889,7 @@ void make_default_theme(bool p_hidpi, Ref<Font> p_font) { Ref<StyleBox> default_style; Ref<Texture> default_icon; - Ref<BitmapFont> default_font; + Ref<Font> default_font; if (p_font.is_valid()) { default_font = p_font; } else if (p_hidpi) { @@ -888,7 +897,7 @@ void make_default_theme(bool p_hidpi, Ref<Font> p_font) { } else { default_font = make_font2(_lodpi_font_height, _lodpi_font_ascent, _lodpi_font_charcount, &_lodpi_font_charrects[0][0], _lodpi_font_kerning_pair_count, &_lodpi_font_kerning_pairs[0][0], _lodpi_font_img_width, _lodpi_font_img_height, _lodpi_font_img_data); } - Ref<BitmapFont> large_font = default_font; + Ref<Font> large_font = default_font; fill_default_theme(t, default_font, large_font, default_icon, default_style, p_hidpi ? 2.0 : 1.0); Theme::set_default(t); diff --git a/scene/resources/dynamic_font.cpp b/scene/resources/dynamic_font.cpp index d52002ad5c..f41a26a680 100644 --- a/scene/resources/dynamic_font.cpp +++ b/scene/resources/dynamic_font.cpp @@ -27,20 +27,19 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifdef FREETYPE_ENABLED #include "dynamic_font.h" #include "os/file_access.h" #include "os/os.h" -bool DynamicFontData::CacheID::operator<(CacheID right) const { +#include FT_STROKER_H - if (size < right.size) - return true; - if (mipmaps != right.mipmaps) - return right.mipmaps; - if (filter != right.filter) - return right.filter; - return false; +#define __STDC_LIMIT_MACROS +#include <stdint.h> + +bool DynamicFontData::CacheID::operator<(CacheID right) const { + return key < right.key; } Ref<DynamicFontAtSize> DynamicFontData::_get_dynamic_font_at_size(CacheID p_cache_id) { @@ -85,6 +84,14 @@ void DynamicFontData::set_force_autohinter(bool p_force) { void DynamicFontData::_bind_methods() { ClassDB::bind_method(D_METHOD("set_font_path", "path"), &DynamicFontData::set_font_path); ClassDB::bind_method(D_METHOD("get_font_path"), &DynamicFontData::get_font_path); + ClassDB::bind_method(D_METHOD("set_hinting", "mode"), &DynamicFontData::set_hinting); + ClassDB::bind_method(D_METHOD("get_hinting"), &DynamicFontData::get_hinting); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal"), "set_hinting", "get_hinting"); + + BIND_ENUM_CONSTANT(HINTING_NONE); + BIND_ENUM_CONSTANT(HINTING_LIGHT); + BIND_ENUM_CONSTANT(HINTING_NORMAL); ADD_PROPERTY(PropertyInfo(Variant::STRING, "font_path", PROPERTY_HINT_FILE, "*.ttf,*.otf"), "set_font_path", "get_font_path"); } @@ -92,6 +99,7 @@ void DynamicFontData::_bind_methods() { DynamicFontData::DynamicFontData() { force_autohinter = false; + hinting = DynamicFontData::HINTING_NORMAL; font_mem = NULL; font_mem_size = 0; } @@ -191,10 +199,25 @@ Error DynamicFontAtSize::_load() { ERR_FAIL_COND_V( error, ERR_INVALID_PARAMETER ); }*/ - error = FT_Set_Pixel_Sizes(face, 0, id.size * oversampling); + if (FT_HAS_COLOR(face)) { + int best_match = 0; + int diff = ABS(id.size - face->available_sizes[0].width); + scale_color_font = float(id.size) / face->available_sizes[0].width; + for (int i = 1; i < face->num_fixed_sizes; i++) { + int ndiff = ABS(id.size - face->available_sizes[i].width); + if (ndiff < diff) { + best_match = i; + diff = ndiff; + scale_color_font = float(id.size) / face->available_sizes[i].width; + } + } + error = FT_Select_Size(face, best_match); + } else { + error = FT_Set_Pixel_Sizes(face, 0, id.size * oversampling); + } - ascent = (face->size->metrics.ascender >> 6) / oversampling; - descent = (-face->size->metrics.descender >> 6) / oversampling; + ascent = (face->size->metrics.ascender / 64.0) / oversampling * scale_color_font; + descent = (-face->size->metrics.descender / 64.0) / oversampling * scale_color_font; linegap = 0; texture_flags = 0; if (id.mipmaps) @@ -202,8 +225,6 @@ Error DynamicFontAtSize::_load() { if (id.filter) texture_flags |= Texture::FLAG_FILTER; - //print_line("ASCENT: "+itos(ascent)+" descent "+itos(descent)+" hinted: "+itos(face->face_flags&FT_FACE_FLAG_HINTER)); - valid = true; return OK; } @@ -224,18 +245,11 @@ float DynamicFontAtSize::get_descent() const { return descent; } -Size2 DynamicFontAtSize::get_char_size(CharType p_char, CharType p_next, const Vector<Ref<DynamicFontAtSize> > &p_fallbacks) const { +const Pair<const DynamicFontAtSize::Character *, DynamicFontAtSize *> DynamicFontAtSize::_find_char_with_font(CharType p_char, const Vector<Ref<DynamicFontAtSize> > &p_fallbacks) const { + const Character *chr = char_map.getptr(p_char); + ERR_FAIL_COND_V(!chr, (Pair<const Character *, DynamicFontAtSize *>(NULL, NULL))); - if (!valid) - return Size2(1, 1); - const_cast<DynamicFontAtSize *>(this)->_update_char(p_char); - - const Character *c = char_map.getptr(p_char); - ERR_FAIL_COND_V(!c, Size2()); - - Size2 ret(0, get_height()); - - if (!c->found) { + if (!chr->found) { //not found, try in fallbacks for (int i = 0; i < p_fallbacks.size(); i++) { @@ -245,53 +259,56 @@ Size2 DynamicFontAtSize::get_char_size(CharType p_char, CharType p_next, const V continue; fb->_update_char(p_char); - const Character *ch = fb->char_map.getptr(p_char); - ERR_CONTINUE(!ch); + const Character *fallback_chr = fb->char_map.getptr(p_char); + ERR_CONTINUE(!fallback_chr); - if (!ch->found) + if (!fallback_chr->found) continue; - c = ch; - break; + return Pair<const Character *, DynamicFontAtSize *>(fallback_chr, fb); } - //not found, try 0xFFFD to display 'not found'. - if (!c->found) { - - const_cast<DynamicFontAtSize *>(this)->_update_char(0xFFFD); - c = char_map.getptr(0xFFFD); - ERR_FAIL_COND_V(!c, Size2()); - } + //not found, try 0xFFFD to display 'not found'. + const_cast<DynamicFontAtSize *>(this)->_update_char(0xFFFD); + chr = char_map.getptr(0xFFFD); + ERR_FAIL_COND_V(!chr, (Pair<const Character *, DynamicFontAtSize *>(NULL, NULL))); } - if (c->found) { - ret.x = c->advance; - } + return Pair<const Character *, DynamicFontAtSize *>(chr, const_cast<DynamicFontAtSize *>(this)); +} + +float DynamicFontAtSize::_get_kerning_advance(const DynamicFontAtSize *font, CharType p_char, CharType p_next) const { + float advance = 0.0; if (p_next) { FT_Vector delta; - FT_Get_Kerning(face, p_char, p_next, FT_KERNING_DEFAULT, &delta); + FT_Get_Kerning(font->face, p_char, p_next, FT_KERNING_DEFAULT, &delta); + advance = (delta.x / 64.0) / oversampling; + } - if (delta.x == 0) { - for (int i = 0; i < p_fallbacks.size(); i++) { + return advance; +} - DynamicFontAtSize *fb = const_cast<DynamicFontAtSize *>(p_fallbacks[i].ptr()); - if (!fb->valid) - continue; +Size2 DynamicFontAtSize::get_char_size(CharType p_char, CharType p_next, const Vector<Ref<DynamicFontAtSize> > &p_fallbacks) const { - FT_Get_Kerning(fb->face, p_char, p_next, FT_KERNING_DEFAULT, &delta); + if (!valid) + return Size2(1, 1); + const_cast<DynamicFontAtSize *>(this)->_update_char(p_char); - if (delta.x == 0) - continue; + Pair<const Character *, DynamicFontAtSize *> char_pair_with_font = _find_char_with_font(p_char, p_fallbacks); + const Character *ch = char_pair_with_font.first; + DynamicFontAtSize *font = char_pair_with_font.second; + ERR_FAIL_COND_V(!ch, Size2()); - ret.x += (delta.x >> 6) / oversampling; - break; - } - } else { - ret.x += (delta.x >> 6) / oversampling; - } + Size2 ret(0, get_height()); + + if (ch->found) { + ret.x = ch->advance; } + ret.x += _get_kerning_advance(font, p_char, p_next); + // ensures oversampled glyphs will have enough space when this value is used by clipping/wrapping algorithms + ret.x = Math::ceil(ret.x); return ret; } @@ -305,92 +322,41 @@ void DynamicFontAtSize::set_texture_flags(uint32_t p_flags) { } } -float DynamicFontAtSize::draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate, const Vector<Ref<DynamicFontAtSize> > &p_fallbacks) const { +float DynamicFontAtSize::draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate, const Vector<Ref<DynamicFontAtSize> > &p_fallbacks, bool p_advance_only) const { if (!valid) return 0; const_cast<DynamicFontAtSize *>(this)->_update_char(p_char); - const Character *c = char_map.getptr(p_char); - - float advance = 0; - - if (!c->found) { - - //not found, try in fallbacks - bool used_fallback = false; + Pair<const Character *, DynamicFontAtSize *> char_pair_with_font = _find_char_with_font(p_char, p_fallbacks); + const Character *ch = char_pair_with_font.first; + DynamicFontAtSize *font = char_pair_with_font.second; - for (int i = 0; i < p_fallbacks.size(); i++) { + ERR_FAIL_COND_V(!ch, 0.0); - DynamicFontAtSize *fb = const_cast<DynamicFontAtSize *>(p_fallbacks[i].ptr()); - if (!fb->valid) - continue; + float advance = 0.0; - fb->_update_char(p_char); - const Character *ch = fb->char_map.getptr(p_char); - ERR_CONTINUE(!ch); - - if (!ch->found) - continue; + if (ch->found) { + ERR_FAIL_COND_V(ch->texture_idx < -1 || ch->texture_idx >= font->textures.size(), 0); + if (!p_advance_only && ch->texture_idx != -1) { Point2 cpos = p_pos; cpos.x += ch->h_align; - cpos.y -= get_ascent(); + cpos.y -= font->get_ascent(); cpos.y += ch->v_align; - ERR_FAIL_COND_V(ch->texture_idx < -1 || ch->texture_idx >= fb->textures.size(), 0); - if (ch->texture_idx != -1) - VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, Rect2(cpos, ch->rect.size), fb->textures[ch->texture_idx].texture->get_rid(), ch->rect_uv, p_modulate, false, RID(), false); - advance = ch->advance; - used_fallback = true; - break; - } - //not found, try 0xFFFD to display 'not found'. - - if (!used_fallback) { - - const_cast<DynamicFontAtSize *>(this)->_update_char(0xFFFD); - c = char_map.getptr(0xFFFD); + Color modulate = p_modulate; + if (FT_HAS_COLOR(face)) { + modulate.r = modulate.g = modulate.b = 1.0; + } + RID texture = font->textures[ch->texture_idx].texture->get_rid(); + VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, Rect2(cpos, ch->rect.size * Vector2(font->scale_color_font, font->scale_color_font)), texture, ch->rect_uv, modulate, false, RID(), false); } - } - if (c->found) { - - Point2 cpos = p_pos; - cpos.x += c->h_align; - cpos.y -= get_ascent(); - cpos.y += c->v_align; - ERR_FAIL_COND_V(c->texture_idx < -1 || c->texture_idx >= textures.size(), 0); - if (c->texture_idx != -1) - VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, Rect2(cpos, c->rect.size), textures[c->texture_idx].texture->get_rid(), c->rect_uv, p_modulate, false, RID(), false); - advance = c->advance; - //textures[c->texture_idx].texture->draw(p_canvas_item,Vector2()); + advance = ch->advance; } - if (p_next) { - - FT_Vector delta; - FT_Get_Kerning(face, p_char, p_next, FT_KERNING_DEFAULT, &delta); - - if (delta.x == 0) { - for (int i = 0; i < p_fallbacks.size(); i++) { - - DynamicFontAtSize *fb = const_cast<DynamicFontAtSize *>(p_fallbacks[i].ptr()); - if (!fb->valid) - continue; - - FT_Get_Kerning(fb->face, p_char, p_next, FT_KERNING_DEFAULT, &delta); - - if (delta.x == 0) - continue; - - advance += (delta.x >> 6) / oversampling; - break; - } - } else { - advance += (delta.x >> 6) / oversampling; - } - } + advance += _get_kerning_advance(font, p_char, p_next); return advance; } @@ -415,79 +381,37 @@ void DynamicFontAtSize::_ft_stream_close(FT_Stream stream) { memdelete(f); } -void DynamicFontAtSize::_update_char(CharType p_char) { - - if (char_map.has(p_char)) - return; - - _THREAD_SAFE_METHOD_ - - FT_GlyphSlot slot = face->glyph; - - if (FT_Get_Char_Index(face, p_char) == 0) { - //not found - Character ch; - ch.texture_idx = -1; - ch.advance = 0; - ch.h_align = 0; - ch.v_align = 0; - ch.found = false; - - char_map[p_char] = ch; - return; - } - int error = FT_Load_Char(face, p_char, FT_LOAD_RENDER | (font->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0)); - if (!error) { - error = FT_Render_Glyph(face->glyph, ft_render_mode_normal); - } - if (error) { - - int advance = 0; - //stbtt_GetCodepointHMetrics(&font->info, p_char, &advance, 0); - //print_line("char has no bitmap: "+itos(p_char)+" but advance is "+itos(advance*scale)); - Character ch; - ch.texture_idx = -1; - ch.advance = advance; - ch.h_align = 0; - ch.v_align = 0; - ch.found = false; - - char_map[p_char] = ch; - - return; - } - - int w = slot->bitmap.width; - int h = slot->bitmap.rows; - //int p = slot->bitmap.pitch; - int yofs = slot->bitmap_top; - int xofs = slot->bitmap_left; - int advance = slot->advance.x >> 6; - - int mw = w + rect_margin * 2; - int mh = h + rect_margin * 2; - - if (mw > 4096 || mh > 4096) { - - ERR_FAIL_COND(mw > 4096); - ERR_FAIL_COND(mh > 4096); - } +DynamicFontAtSize::Character DynamicFontAtSize::Character::not_found() { + Character ch; + ch.texture_idx = -1; + ch.advance = 0; + ch.h_align = 0; + ch.v_align = 0; + ch.found = false; + return ch; +} - //find a texture to fit this... +DynamicFontAtSize::TexturePosition DynamicFontAtSize::_find_texture_pos_for_glyph(int p_color_size, Image::Format p_image_format, int p_width, int p_height) { + TexturePosition ret; + ret.index = -1; + ret.x = 0; + ret.y = 0; - int tex_index = -1; - int tex_x = 0; - int tex_y = 0; + int mw = p_width; + int mh = p_height; for (int i = 0; i < textures.size(); i++) { CharTexture &ct = textures[i]; + if (ct.texture->get_format() != p_image_format) + continue; + if (mw > ct.texture_size || mh > ct.texture_size) //too big for this texture continue; - tex_y = 0x7FFFFFFF; - tex_x = 0; + ret.y = 0x7FFFFFFF; + ret.x = 0; for (int j = 0; j < ct.texture_size - mw; j++) { @@ -500,27 +424,27 @@ void DynamicFontAtSize::_update_char(CharType p_char) { max_y = y; } - if (max_y < tex_y) { - tex_y = max_y; - tex_x = j; + if (max_y < ret.y) { + ret.y = max_y; + ret.x = j; } } - if (tex_y == 0x7FFFFFFF || tex_y + mh > ct.texture_size) + if (ret.y == 0x7FFFFFFF || ret.y + mh > ct.texture_size) continue; //fail, could not fit it here - tex_index = i; + ret.index = i; break; } //print_line("CHAR: "+String::chr(p_char)+" TEX INDEX: "+itos(tex_index)+" X: "+itos(tex_x)+" Y: "+itos(tex_y)); - if (tex_index == -1) { + if (ret.index == -1) { //could not find texture to fit, create one - tex_x = 0; - tex_y = 0; + ret.x = 0; + ret.y = 0; - int texsize = MAX(id.size * 8, 256); + int texsize = MAX(id.size * oversampling * 8, 256); if (mw > texsize) texsize = mw; //special case, adapt to it? if (mh > texsize) @@ -532,13 +456,13 @@ void DynamicFontAtSize::_update_char(CharType p_char) { CharTexture tex; tex.texture_size = texsize; - tex.imgdata.resize(texsize * texsize * 2); //grayscale alpha + tex.imgdata.resize(texsize * texsize * p_color_size); //grayscale alpha { //zero texture PoolVector<uint8_t>::Write w = tex.imgdata.write(); - ERR_FAIL_COND(texsize * texsize * 2 > tex.imgdata.size()); - for (int i = 0; i < texsize * texsize * 2; i++) { + ERR_FAIL_COND_V(texsize * texsize * p_color_size > tex.imgdata.size(), ret); + for (int i = 0; i < texsize * texsize * p_color_size; i++) { w[i] = 0; } } @@ -547,12 +471,31 @@ void DynamicFontAtSize::_update_char(CharType p_char) { tex.offsets[i] = 0; textures.push_back(tex); - tex_index = textures.size() - 1; + ret.index = textures.size() - 1; } + return ret; +} + +DynamicFontAtSize::Character DynamicFontAtSize::_bitmap_to_character(FT_Bitmap bitmap, int yofs, int xofs, float advance) { + int w = bitmap.width; + int h = bitmap.rows; + + int mw = w + rect_margin * 2; + int mh = h + rect_margin * 2; + + ERR_FAIL_COND_V(mw > 4096, Character::not_found()); + ERR_FAIL_COND_V(mh > 4096, Character::not_found()); + + int color_size = bitmap.pixel_mode == FT_PIXEL_MODE_BGRA ? 4 : 2; + Image::Format require_format = color_size == 4 ? Image::FORMAT_RGBA8 : Image::FORMAT_LA8; + + TexturePosition tex_pos = _find_texture_pos_for_glyph(color_size, require_format, mw, mh); + ERR_FAIL_COND_V(tex_pos.index < 0, Character::not_found()); + //fit character in char texture - CharTexture &tex = textures[tex_index]; + CharTexture &tex = textures[tex_pos.index]; { PoolVector<uint8_t>::Write wr = tex.imgdata.write(); @@ -560,23 +503,30 @@ void DynamicFontAtSize::_update_char(CharType p_char) { for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { - int ofs = ((i + tex_y + rect_margin) * tex.texture_size + j + tex_x + rect_margin) * 2; - ERR_FAIL_COND(ofs >= tex.imgdata.size()); - switch (slot->bitmap.pixel_mode) { + int ofs = ((i + tex_pos.y + rect_margin) * tex.texture_size + j + tex_pos.x + rect_margin) * color_size; + ERR_FAIL_COND_V(ofs >= tex.imgdata.size(), Character::not_found()); + switch (bitmap.pixel_mode) { case FT_PIXEL_MODE_MONO: { - int byte = i * slot->bitmap.pitch + (j >> 3); + int byte = i * bitmap.pitch + (j >> 3); int bit = 1 << (7 - (j % 8)); wr[ofs + 0] = 255; //grayscale as 1 - wr[ofs + 1] = slot->bitmap.buffer[byte] & bit ? 255 : 0; + wr[ofs + 1] = bitmap.buffer[byte] & bit ? 255 : 0; } break; case FT_PIXEL_MODE_GRAY: wr[ofs + 0] = 255; //grayscale as 1 - wr[ofs + 1] = slot->bitmap.buffer[i * slot->bitmap.pitch + j]; + wr[ofs + 1] = bitmap.buffer[i * bitmap.pitch + j]; break; - // TODO: FT_PIXEL_MODE_LCD, FT_PIXEL_MODE_BGRA + case FT_PIXEL_MODE_BGRA: { + int ofs_color = i * bitmap.pitch + (j << 2); + wr[ofs + 2] = bitmap.buffer[ofs_color + 0]; + wr[ofs + 1] = bitmap.buffer[ofs_color + 1]; + wr[ofs + 0] = bitmap.buffer[ofs_color + 2]; + wr[ofs + 3] = bitmap.buffer[ofs_color + 3]; + } break; + // TODO: FT_PIXEL_MODE_LCD default: - ERR_EXPLAIN("Font uses unsupported pixel format: " + itos(slot->bitmap.pixel_mode)); - ERR_FAIL(); + ERR_EXPLAIN("Font uses unsupported pixel format: " + itos(bitmap.pixel_mode)); + ERR_FAIL_V(Character::not_found()); break; } } @@ -586,7 +536,7 @@ void DynamicFontAtSize::_update_char(CharType p_char) { //blit to image and texture { - Ref<Image> img = memnew(Image(tex.texture_size, tex.texture_size, 0, Image::FORMAT_LA8, tex.imgdata)); + Ref<Image> img = memnew(Image(tex.texture_size, tex.texture_size, 0, require_format, tex.imgdata)); if (tex.texture.is_null()) { tex.texture.instance(); @@ -598,41 +548,112 @@ void DynamicFontAtSize::_update_char(CharType p_char) { // update height array - for (int k = tex_x; k < tex_x + mw; k++) { - - tex.offsets[k] = tex_y + mh; + for (int k = tex_pos.x; k < tex_pos.x + mw; k++) { + tex.offsets[k] = tex_pos.y + mh; } Character chr; - chr.h_align = xofs / oversampling; - chr.v_align = ascent - (yofs / oversampling); // + ascent - descent; - chr.advance = advance / oversampling; - chr.texture_idx = tex_index; + chr.h_align = xofs * scale_color_font / oversampling; + chr.v_align = ascent - (yofs * scale_color_font / oversampling); // + ascent - descent; + chr.advance = advance * scale_color_font / oversampling; + chr.texture_idx = tex_pos.index; chr.found = true; - chr.rect_uv = Rect2(tex_x + rect_margin, tex_y + rect_margin, w, h); + chr.rect_uv = Rect2(tex_pos.x + rect_margin, tex_pos.y + rect_margin, w, h); chr.rect = chr.rect_uv; chr.rect.position /= oversampling; - chr.rect.size /= oversampling; + chr.rect.size = chr.rect.size * scale_color_font / oversampling; + return chr; +} + +DynamicFontAtSize::Character DynamicFontAtSize::_make_outline_char(CharType p_char) { + Character ret = Character::not_found(); + + if (FT_Load_Char(face, p_char, FT_LOAD_NO_BITMAP | (font->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0)) != 0) + return ret; - //print_line("CHAR: "+String::chr(p_char)+" TEX INDEX: "+itos(tex_index)+" RECT: "+chr.rect+" X OFS: "+itos(xofs)+" Y OFS: "+itos(yofs)); + FT_Stroker stroker; + if (FT_Stroker_New(library, &stroker) != 0) + return ret; - char_map[p_char] = chr; + FT_Stroker_Set(stroker, (int)(id.outline_size * oversampling * 64.0), FT_STROKER_LINECAP_BUTT, FT_STROKER_LINEJOIN_ROUND, 0); + FT_Glyph glyph; + FT_BitmapGlyph glyph_bitmap; + + if (FT_Get_Glyph(face->glyph, &glyph) != 0) + goto cleanup_stroker; + if (FT_Glyph_Stroke(&glyph, stroker, 1) != 0) + goto cleanup_glyph; + if (FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1) != 0) + goto cleanup_glyph; + + glyph_bitmap = (FT_BitmapGlyph)glyph; + ret = _bitmap_to_character(glyph_bitmap->bitmap, glyph_bitmap->top, glyph_bitmap->left, glyph->advance.x / 65536.0); + +cleanup_glyph: + FT_Done_Glyph(glyph); +cleanup_stroker: + FT_Stroker_Done(stroker); + return ret; } -bool DynamicFontAtSize::update_oversampling() { - if (oversampling == font_oversampling) - return false; - if (!valid) - return false; +void DynamicFontAtSize::_update_char(CharType p_char) { + + if (char_map.has(p_char)) + return; + + _THREAD_SAFE_METHOD_ + + Character character = Character::not_found(); + + FT_GlyphSlot slot = face->glyph; + + if (FT_Get_Char_Index(face, p_char) == 0) { + char_map[p_char] = character; + return; + } + + int ft_hinting; + + switch (font->hinting) { + case DynamicFontData::HINTING_NONE: + ft_hinting = FT_LOAD_NO_HINTING; + break; + case DynamicFontData::HINTING_LIGHT: + ft_hinting = FT_LOAD_TARGET_LIGHT; + break; + default: + ft_hinting = FT_LOAD_TARGET_NORMAL; + break; + } + + int error = FT_Load_Char(face, p_char, FT_HAS_COLOR(face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT | (font->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0)); + if (error) { + char_map[p_char] = character; + return; + } + + if (id.outline_size > 0) { + character = _make_outline_char(p_char); + } else { + error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); + if (!error) + character = _bitmap_to_character(slot->bitmap, slot->bitmap_top, slot->bitmap_left, slot->advance.x / 64.0); + } + + char_map[p_char] = character; +} + +void DynamicFontAtSize::update_oversampling() { + if (oversampling == font_oversampling || !valid) + return; FT_Done_FreeType(library); textures.clear(); char_map.clear(); oversampling = font_oversampling; + valid = false; _load(); - - return true; } DynamicFontAtSize::DynamicFontAtSize() { @@ -644,14 +665,16 @@ DynamicFontAtSize::DynamicFontAtSize() { linegap = 1; texture_flags = 0; oversampling = font_oversampling; + scale_color_font = 1; } DynamicFontAtSize::~DynamicFontAtSize() { if (valid) { FT_Done_FreeType(library); - font->size_cache.erase(id); } + font->size_cache.erase(id); + font.unref(); } ///////////////////////// @@ -659,11 +682,27 @@ DynamicFontAtSize::~DynamicFontAtSize() { void DynamicFont::_reload_cache() { ERR_FAIL_COND(cache_id.size < 1); - if (!data.is_valid()) + if (!data.is_valid()) { + data_at_size.unref(); + outline_data_at_size.unref(); + fallback_data_at_size.resize(0); + fallback_outline_data_at_size.resize(0); return; + } + data_at_size = data->_get_dynamic_font_at_size(cache_id); + if (outline_cache_id.outline_size > 0) { + outline_data_at_size = data->_get_dynamic_font_at_size(outline_cache_id); + fallback_outline_data_at_size.resize(fallback_data_at_size.size()); + } else { + outline_data_at_size.unref(); + fallback_outline_data_at_size.resize(0); + } + for (int i = 0; i < fallbacks.size(); i++) { fallback_data_at_size[i] = fallbacks[i]->_get_dynamic_font_at_size(cache_id); + if (outline_cache_id.outline_size > 0) + fallback_outline_data_at_size[i] = fallbacks[i]->_get_dynamic_font_at_size(outline_cache_id); } emit_changed(); @@ -673,12 +712,10 @@ void DynamicFont::_reload_cache() { void DynamicFont::set_font_data(const Ref<DynamicFontData> &p_data) { data = p_data; - if (data.is_valid()) - data_at_size = data->_get_dynamic_font_at_size(cache_id); - else - data_at_size = Ref<DynamicFontAtSize>(); + _reload_cache(); emit_changed(); + _change_notify(); } Ref<DynamicFontData> DynamicFont::get_font_data() const { @@ -691,6 +728,7 @@ void DynamicFont::set_size(int p_size) { if (cache_id.size == p_size) return; cache_id.size = p_size; + outline_cache_id.size = p_size; _reload_cache(); } @@ -699,6 +737,30 @@ int DynamicFont::get_size() const { return cache_id.size; } +void DynamicFont::set_outline_size(int p_size) { + if (outline_cache_id.outline_size == p_size) + return; + ERR_FAIL_COND(p_size < 0 || p_size > UINT8_MAX); + outline_cache_id.outline_size = p_size; + _reload_cache(); +} + +int DynamicFont::get_outline_size() const { + return outline_cache_id.outline_size; +} + +void DynamicFont::set_outline_color(Color p_color) { + if (p_color != outline_color) { + outline_color = p_color; + emit_changed(); + _change_notify(); + } +} + +Color DynamicFont::get_outline_color() const { + return outline_color; +} + bool DynamicFont::get_use_mipmaps() const { return cache_id.mipmaps; @@ -709,6 +771,7 @@ void DynamicFont::set_use_mipmaps(bool p_enable) { if (cache_id.mipmaps == p_enable) return; cache_id.mipmaps = p_enable; + outline_cache_id.mipmaps = p_enable; _reload_cache(); } @@ -722,9 +785,22 @@ void DynamicFont::set_use_filter(bool p_enable) { if (cache_id.filter == p_enable) return; cache_id.filter = p_enable; + outline_cache_id.filter = p_enable; _reload_cache(); } +DynamicFontData::Hinting DynamicFontData::get_hinting() const { + + return hinting; +} + +void DynamicFontData::set_hinting(Hinting p_hinting) { + + if (hinting == p_hinting) + return; + hinting = p_hinting; +} + int DynamicFont::get_spacing(int p_type) const { if (p_type == SPACING_TOP) { @@ -799,13 +875,24 @@ bool DynamicFont::is_distance_field_hint() const { return false; } -float DynamicFont::draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate) const { +bool DynamicFont::has_outline() const { + return outline_cache_id.outline_size > 0; +} + +float DynamicFont::draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate, bool p_outline) const { + const Ref<DynamicFontAtSize> &font_at_size = p_outline && outline_cache_id.outline_size > 0 ? outline_data_at_size : data_at_size; - if (!data_at_size.is_valid()) + if (!font_at_size.is_valid()) return 0; - return data_at_size->draw_char(p_canvas_item, p_pos, p_char, p_next, p_modulate, fallback_data_at_size) + spacing_char; + const Vector<Ref<DynamicFontAtSize> > &fallbacks = p_outline && outline_cache_id.outline_size > 0 ? fallback_outline_data_at_size : fallback_data_at_size; + Color color = p_outline && outline_cache_id.outline_size > 0 ? p_modulate * outline_color : p_modulate; + + // If requested outline draw, but no outline is present, simply return advance without drawing anything + bool advance_only = p_outline && outline_cache_id.outline_size == 0; + return font_at_size->draw_char(p_canvas_item, p_pos, p_char, p_next, color, fallbacks, advance_only) + spacing_char; } + void DynamicFont::set_fallback(int p_idx, const Ref<DynamicFontData> &p_data) { ERR_FAIL_COND(p_data.is_null()); @@ -819,6 +906,8 @@ void DynamicFont::add_fallback(const Ref<DynamicFontData> &p_data) { ERR_FAIL_COND(p_data.is_null()); fallbacks.push_back(p_data); fallback_data_at_size.push_back(fallbacks[fallbacks.size() - 1]->_get_dynamic_font_at_size(cache_id)); //const.. + if (outline_cache_id.outline_size > 0) + fallback_outline_data_at_size.push_back(fallbacks[fallbacks.size() - 1]->_get_dynamic_font_at_size(outline_cache_id)); _change_notify(); emit_changed(); @@ -903,6 +992,12 @@ void DynamicFont::_bind_methods() { ClassDB::bind_method(D_METHOD("set_size", "data"), &DynamicFont::set_size); ClassDB::bind_method(D_METHOD("get_size"), &DynamicFont::get_size); + ClassDB::bind_method(D_METHOD("set_outline_size", "size"), &DynamicFont::set_outline_size); + ClassDB::bind_method(D_METHOD("get_outline_size"), &DynamicFont::get_outline_size); + + ClassDB::bind_method(D_METHOD("set_outline_color", "color"), &DynamicFont::set_outline_color); + ClassDB::bind_method(D_METHOD("get_outline_color"), &DynamicFont::get_outline_color); + ClassDB::bind_method(D_METHOD("set_use_mipmaps", "enable"), &DynamicFont::set_use_mipmaps); ClassDB::bind_method(D_METHOD("get_use_mipmaps"), &DynamicFont::get_use_mipmaps); ClassDB::bind_method(D_METHOD("set_use_filter", "enable"), &DynamicFont::set_use_filter); @@ -918,6 +1013,8 @@ void DynamicFont::_bind_methods() { ADD_GROUP("Settings", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "size"), "set_size", "get_size"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "outline_size"), "set_outline_size", "get_outline_size"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "outline_color"), "set_outline_color", "get_outline_color"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_mipmaps"), "set_use_mipmaps", "get_use_mipmaps"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_filter"), "set_use_filter", "get_use_filter"); ADD_GROUP("Extra Spacing", "extra_spacing"); @@ -945,6 +1042,7 @@ DynamicFont::DynamicFont() : spacing_bottom = 0; spacing_char = 0; spacing_space = 0; + outline_color = Color(1, 1, 1); if (dynamic_font_mutex) dynamic_font_mutex->lock(); dynamic_fonts.add(&font_list); @@ -972,14 +1070,27 @@ void DynamicFont::finish_dynamic_fonts() { void DynamicFont::update_oversampling() { + Vector<Ref<DynamicFont> > changed; + + if (dynamic_font_mutex) + dynamic_font_mutex->lock(); + SelfList<DynamicFont> *E = dynamic_fonts.first(); while (E) { - if (E->self()->data_at_size.is_valid() && E->self()->data_at_size->update_oversampling()) { - E->self()->emit_changed(); + if (E->self()->data_at_size.is_valid()) { + E->self()->data_at_size->update_oversampling(); + changed.push_back(Ref<DynamicFont>(E->self())); } E = E->next(); } + + if (dynamic_font_mutex) + dynamic_font_mutex->unlock(); + + for (int i = 0; i < changed.size(); i++) { + changed[i]->emit_changed(); + } } ///////////////////////// diff --git a/scene/resources/dynamic_font.h b/scene/resources/dynamic_font.h index 1a890dd65d..f460bca2d4 100644 --- a/scene/resources/dynamic_font.h +++ b/scene/resources/dynamic_font.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef DYNAMIC_FONT_H #define DYNAMIC_FONT_H @@ -34,6 +35,7 @@ #include "io/resource_loader.h" #include "os/mutex.h" #include "os/thread_safe.h" +#include "pair.h" #include "scene/resources/font.h" #include <ft2build.h> @@ -48,23 +50,35 @@ class DynamicFontData : public Resource { public: struct CacheID { - - int size; - bool mipmaps; - bool filter; - + union { + struct { + uint32_t size : 16; + uint32_t outline_size : 8; + bool mipmaps : 1; + bool filter : 1; + }; + uint32_t key; + }; bool operator<(CacheID right) const; CacheID() { - size = 16; - mipmaps = false; - filter = false; + key = 0; } }; + enum Hinting { + HINTING_NONE, + HINTING_LIGHT, + HINTING_NORMAL + }; + + Hinting get_hinting() const; + void set_hinting(Hinting p_hinting); + private: const uint8_t *font_mem; int font_mem_size; bool force_autohinter; + Hinting hinting; String font_path; Map<CacheID, DynamicFontAtSize *> size_cache; @@ -88,6 +102,8 @@ public: ~DynamicFontData(); }; +VARIANT_ENUM_CAST(DynamicFontData::Hinting); + class DynamicFontAtSize : public Reference { GDCLASS(DynamicFontAtSize, Reference) @@ -103,6 +119,7 @@ class DynamicFontAtSize : public Reference { float linegap; float rect_margin; float oversampling; + float scale_color_font; uint32_t texture_flags; @@ -132,8 +149,22 @@ class DynamicFontAtSize : public Reference { texture_idx = 0; v_align = 0; } + + static Character not_found(); }; + struct TexturePosition { + int index; + int x; + int y; + }; + + const Pair<const Character *, DynamicFontAtSize *> _find_char_with_font(CharType p_char, const Vector<Ref<DynamicFontAtSize> > &p_fallbacks) const; + Character _make_outline_char(CharType p_char); + float _get_kerning_advance(const DynamicFontAtSize *font, CharType p_char, CharType p_next) const; + TexturePosition _find_texture_pos_for_glyph(int p_color_size, Image::Format p_image_format, int p_width, int p_height); + Character _bitmap_to_character(FT_Bitmap bitmap, int yofs, int xofs, float advance); + static unsigned long _ft_stream_io(FT_Stream stream, unsigned long offset, unsigned char *buffer, unsigned long count); static void _ft_stream_close(FT_Stream stream); @@ -158,10 +189,10 @@ public: Size2 get_char_size(CharType p_char, CharType p_next, const Vector<Ref<DynamicFontAtSize> > &p_fallbacks) const; - float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate, const Vector<Ref<DynamicFontAtSize> > &p_fallbacks) const; + float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate, const Vector<Ref<DynamicFontAtSize> > &p_fallbacks, bool p_advance_only = false) const; void set_texture_flags(uint32_t p_flags); - bool update_oversampling(); + void update_oversampling(); DynamicFontAtSize(); ~DynamicFontAtSize(); @@ -184,17 +215,23 @@ public: private: Ref<DynamicFontData> data; Ref<DynamicFontAtSize> data_at_size; + Ref<DynamicFontAtSize> outline_data_at_size; Vector<Ref<DynamicFontData> > fallbacks; Vector<Ref<DynamicFontAtSize> > fallback_data_at_size; + Vector<Ref<DynamicFontAtSize> > fallback_outline_data_at_size; DynamicFontData::CacheID cache_id; + DynamicFontData::CacheID outline_cache_id; + bool valid; int spacing_top; int spacing_bottom; int spacing_char; int spacing_space; + Color outline_color; + protected: void _reload_cache(); @@ -211,6 +248,12 @@ public: void set_size(int p_size); int get_size() const; + void set_outline_size(int p_size); + int get_outline_size() const; + + void set_outline_color(Color p_color); + Color get_outline_color() const; + bool get_use_mipmaps() const; void set_use_mipmaps(bool p_enable); @@ -235,7 +278,9 @@ public: virtual bool is_distance_field_hint() const; - virtual float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next = 0, const Color &p_modulate = Color(1, 1, 1)) const; + virtual bool has_outline() const; + + virtual float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next = 0, const Color &p_modulate = Color(1, 1, 1), bool p_outline = false) const; SelfList<DynamicFont> font_list; diff --git a/scene/resources/dynamic_font_stb.cpp b/scene/resources/dynamic_font_stb.cpp index 70495e9bbf..29f1106d16 100644 --- a/scene/resources/dynamic_font_stb.cpp +++ b/scene/resources/dynamic_font_stb.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "dynamic_font_stb.h" #ifndef FREETYPE_ENABLED @@ -161,7 +162,7 @@ Size2 DynamicFontAtSize::get_char_size(CharType p_char, CharType p_next) const { return ret; } -float DynamicFontAtSize::draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate) const { +float DynamicFontAtSize::draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate, bool p_outline) const { const_cast<DynamicFontAtSize *>(this)->_update_char(p_char); @@ -171,13 +172,15 @@ float DynamicFontAtSize::draw_char(RID p_canvas_item, const Point2 &p_pos, CharT return 0; } - Point2 cpos = p_pos; - cpos.x += c->h_align; - cpos.y -= get_ascent(); - cpos.y += c->v_align; - ERR_FAIL_COND_V(c->texture_idx < -1 || c->texture_idx >= textures.size(), 0); - if (c->texture_idx != -1) - VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, Rect2(cpos, c->rect.size), textures[c->texture_idx].texture->get_rid(), c->rect, p_modulate); + if (!p_outline) { + Point2 cpos = p_pos; + cpos.x += c->h_align; + cpos.y -= get_ascent(); + cpos.y += c->v_align; + ERR_FAIL_COND_V(c->texture_idx < -1 || c->texture_idx >= textures.size(), 0); + if (c->texture_idx != -1) + VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, Rect2(cpos, c->rect.size), textures[c->texture_idx].texture->get_rid(), c->rect, p_modulate); + } //textures[c->texture_idx].texture->draw(p_canvas_item,Vector2()); @@ -458,12 +461,12 @@ bool DynamicFont::is_distance_field_hint() const { return false; } -float DynamicFont::draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate) const { +float DynamicFont::draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate, bool p_outline) const { if (!data_at_size.is_valid()) return 0; - return data_at_size->draw_char(p_canvas_item, p_pos, p_char, p_next, p_modulate); + return data_at_size->draw_char(p_canvas_item, p_pos, p_char, p_next, p_modulate, p_outline); } DynamicFont::DynamicFont() { diff --git a/scene/resources/dynamic_font_stb.h b/scene/resources/dynamic_font_stb.h index 3d4ad4389c..feae29c0c2 100644 --- a/scene/resources/dynamic_font_stb.h +++ b/scene/resources/dynamic_font_stb.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef DYNAMICFONT_STB_H #define DYNAMICFONT_STB_H @@ -135,7 +136,7 @@ public: Size2 get_char_size(CharType p_char, CharType p_next = 0) const; - float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next = 0, const Color &p_modulate = Color(1, 1, 1)) const; + float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next = 0, const Color &p_modulate = Color(1, 1, 1), bool p_outline = false) const; DynamicFontAtSize(); ~DynamicFontAtSize(); @@ -170,7 +171,7 @@ public: virtual bool is_distance_field_hint() const; - virtual float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next = 0, const Color &p_modulate = Color(1, 1, 1)) const; + virtual float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next = 0, const Color &p_modulate = Color(1, 1, 1), bool p_outline = false) const; DynamicFont(); ~DynamicFont(); diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp index 6216893667..3fab4d3cfc 100644 --- a/scene/resources/environment.cpp +++ b/scene/resources/environment.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "environment.h" #include "project_settings.h" #include "servers/visual_server.h" @@ -269,19 +270,19 @@ void Environment::_validate_property(PropertyInfo &property) const { if (property.name == "background_sky" || property.name == "background_sky_custom_fov" || property.name == "ambient_light/sky_contribution") { if (bg_mode != BG_SKY && bg_mode != BG_COLOR_SKY) { - property.usage = PROPERTY_USAGE_NOEDITOR; + property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL; } } if (property.name == "background_color") { if (bg_mode != BG_COLOR && bg_mode != BG_COLOR_SKY) { - property.usage = PROPERTY_USAGE_NOEDITOR; + property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL; } } if (property.name == "background_canvas_max_layer") { if (bg_mode != BG_CANVAS) { - property.usage = PROPERTY_USAGE_NOEDITOR; + property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL; } } @@ -304,7 +305,7 @@ void Environment::_validate_property(PropertyInfo &property) const { String enabled = prefix + "enabled"; if (property.name.begins_with(prefix) && property.name != enabled && !bool(get(enabled))) { - property.usage = PROPERTY_USAGE_NOEDITOR; + property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL; return; } @@ -1218,7 +1219,7 @@ Environment::Environment() { ssao_radius2 = 0; ssao_intensity2 = 1; ssao_bias = 0.01; - ssao_direct_light_affect = false; + ssao_direct_light_affect = 0.0; ssao_blur = SSAO_BLUR_3x3; set_ssao_edge_sharpness(4); set_ssao_quality(SSAO_QUALITY_LOW); diff --git a/scene/resources/environment.h b/scene/resources/environment.h index 9e2461a2ce..27fd57aa09 100644 --- a/scene/resources/environment.h +++ b/scene/resources/environment.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef ENVIRONMENT_H #define ENVIRONMENT_H diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index 7b4bd273bd..0bae9d9b2d 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -27,16 +27,17 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "font.h" #include "core/io/resource_loader.h" #include "core/os/file_access.h" +#include "method_bind_ext.gen.inc" -void Font::draw_halign(RID p_canvas_item, const Point2 &p_pos, HAlign p_align, float p_width, const String &p_text, const Color &p_modulate) const { - +void Font::draw_halign(RID p_canvas_item, const Point2 &p_pos, HAlign p_align, float p_width, const String &p_text, const Color &p_modulate, const Color &p_outline_modulate) const { float length = get_string_size(p_text).width; if (length >= p_width) { - draw(p_canvas_item, p_pos, p_text, p_modulate, p_width); + draw(p_canvas_item, p_pos, p_text, p_modulate, p_width, p_outline_modulate); return; } @@ -55,13 +56,14 @@ void Font::draw_halign(RID p_canvas_item, const Point2 &p_pos, HAlign p_align, f ERR_PRINT("Unknown halignment type"); } break; } - draw(p_canvas_item, p_pos + Point2(ofs, 0), p_text, p_modulate, p_width); + draw(p_canvas_item, p_pos + Point2(ofs, 0), p_text, p_modulate, p_width, p_outline_modulate); } -void Font::draw(RID p_canvas_item, const Point2 &p_pos, const String &p_text, const Color &p_modulate, int p_clip_w) const { - +void Font::draw(RID p_canvas_item, const Point2 &p_pos, const String &p_text, const Color &p_modulate, int p_clip_w, const Color &p_outline_modulate) const { Vector2 ofs; + int chars_drawn = 0; + bool with_outline = has_outline(); for (int i = 0; i < p_text.length(); i++) { int width = get_char_size(p_text[i]).width; @@ -69,7 +71,15 @@ void Font::draw(RID p_canvas_item, const Point2 &p_pos, const String &p_text, co if (p_clip_w >= 0 && (ofs.x + width) > p_clip_w) break; //clip - ofs.x += draw_char(p_canvas_item, p_pos + ofs, p_text[i], p_text[i + 1], p_modulate); + ofs.x += draw_char(p_canvas_item, p_pos + ofs, p_text[i], p_text[i + 1], with_outline ? p_outline_modulate : p_modulate, with_outline); + ++chars_drawn; + } + + if (has_outline()) { + ofs = Vector2(0, 0); + for (int i = 0; i < chars_drawn; i++) { + ofs.x += draw_char(p_canvas_item, p_pos + ofs, p_text[i], p_text[i + 1], p_modulate, false); + } } } @@ -80,13 +90,14 @@ void Font::update_changes() { void Font::_bind_methods() { - ClassDB::bind_method(D_METHOD("draw", "canvas_item", "position", "string", "modulate", "clip_w"), &Font::draw, DEFVAL(Color(1, 1, 1)), DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("draw", "canvas_item", "position", "string", "modulate", "clip_w", "outline_modulate"), &Font::draw, DEFVAL(Color(1, 1, 1)), DEFVAL(-1), DEFVAL(Color(1, 1, 1))); ClassDB::bind_method(D_METHOD("get_ascent"), &Font::get_ascent); ClassDB::bind_method(D_METHOD("get_descent"), &Font::get_descent); ClassDB::bind_method(D_METHOD("get_height"), &Font::get_height); ClassDB::bind_method(D_METHOD("is_distance_field_hint"), &Font::is_distance_field_hint); ClassDB::bind_method(D_METHOD("get_string_size", "string"), &Font::get_string_size); - ClassDB::bind_method(D_METHOD("draw_char", "canvas_item", "position", "char", "next", "modulate"), &Font::draw_char, DEFVAL(-1), DEFVAL(Color(1, 1, 1))); + ClassDB::bind_method(D_METHOD("has_outline"), &Font::has_outline); + ClassDB::bind_method(D_METHOD("draw_char", "canvas_item", "position", "char", "next", "modulate", "outline"), &Font::draw_char, DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(false)); ClassDB::bind_method(D_METHOD("update_changes"), &Font::update_changes); } @@ -493,23 +504,24 @@ Ref<BitmapFont> BitmapFont::get_fallback() const { return fallback; } -float BitmapFont::draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate) const { +float BitmapFont::draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate, bool p_outline) const { const Character *c = char_map.getptr(p_char); if (!c) { if (fallback.is_valid()) - return fallback->draw_char(p_canvas_item, p_pos, p_char, p_next, p_modulate); + return fallback->draw_char(p_canvas_item, p_pos, p_char, p_next, p_modulate, p_outline); return 0; } - Point2 cpos = p_pos; - cpos.x += c->h_align; - cpos.y -= ascent; - cpos.y += c->v_align; ERR_FAIL_COND_V(c->texture_idx < -1 || c->texture_idx >= textures.size(), 0); - if (c->texture_idx != -1) + if (!p_outline && c->texture_idx != -1) { + Point2 cpos = p_pos; + cpos.x += c->h_align; + cpos.y -= ascent; + cpos.y += c->v_align; VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, Rect2(cpos, c->rect.size), textures[c->texture_idx]->get_rid(), c->rect, p_modulate, false, RID(), false); + } return get_char_size(p_char, p_next).width; } @@ -576,9 +588,9 @@ void BitmapFont::_bind_methods() { ClassDB::bind_method(D_METHOD("set_fallback", "fallback"), &BitmapFont::set_fallback); ClassDB::bind_method(D_METHOD("get_fallback"), &BitmapFont::get_fallback); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "textures", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_textures", "_get_textures"); - ADD_PROPERTY(PropertyInfo(Variant::POOL_INT_ARRAY, "chars", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_chars", "_get_chars"); - ADD_PROPERTY(PropertyInfo(Variant::POOL_INT_ARRAY, "kernings", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_kernings", "_get_kernings"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "textures", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_textures", "_get_textures"); + ADD_PROPERTY(PropertyInfo(Variant::POOL_INT_ARRAY, "chars", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_chars", "_get_chars"); + ADD_PROPERTY(PropertyInfo(Variant::POOL_INT_ARRAY, "kernings", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_kernings", "_get_kernings"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "height", PROPERTY_HINT_RANGE, "-1024,1024,1"), "set_height", "get_height"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "ascent", PROPERTY_HINT_RANGE, "-1024,1024,1"), "set_ascent", "get_ascent"); @@ -595,3 +607,42 @@ BitmapFont::~BitmapFont() { clear(); } + +//////////// + +RES ResourceFormatLoaderBMFont::load(const String &p_path, const String &p_original_path, Error *r_error) { + + if (r_error) + *r_error = ERR_FILE_CANT_OPEN; + + Ref<BitmapFont> font; + font.instance(); + + Error err = font->create_from_fnt(p_path); + + if (err) { + if (r_error) + *r_error = err; + return RES(); + } + + return font; +} + +void ResourceFormatLoaderBMFont::get_recognized_extensions(List<String> *p_extensions) const { + + p_extensions->push_back("fnt"); +} + +bool ResourceFormatLoaderBMFont::handles_type(const String &p_type) const { + + return (p_type == "BitmapFont"); +} + +String ResourceFormatLoaderBMFont::get_resource_type(const String &p_path) const { + + String el = p_path.get_extension().to_lower(); + if (el == "fnt") + return "BitmapFont"; + return ""; +} diff --git a/scene/resources/font.h b/scene/resources/font.h index 5eaf82d6bc..4e295b6035 100644 --- a/scene/resources/font.h +++ b/scene/resources/font.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef FONT_H #define FONT_H @@ -55,14 +56,55 @@ public: virtual bool is_distance_field_hint() const = 0; - void draw(RID p_canvas_item, const Point2 &p_pos, const String &p_text, const Color &p_modulate = Color(1, 1, 1), int p_clip_w = -1) const; - void draw_halign(RID p_canvas_item, const Point2 &p_pos, HAlign p_align, float p_width, const String &p_text, const Color &p_modulate = Color(1, 1, 1)) const; - virtual float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next = 0, const Color &p_modulate = Color(1, 1, 1)) const = 0; + void draw(RID p_canvas_item, const Point2 &p_pos, const String &p_text, const Color &p_modulate = Color(1, 1, 1), int p_clip_w = -1, const Color &p_outline_modulate = Color(1, 1, 1)) const; + void draw_halign(RID p_canvas_item, const Point2 &p_pos, HAlign p_align, float p_width, const String &p_text, const Color &p_modulate = Color(1, 1, 1), const Color &p_outline_modulate = Color(1, 1, 1)) const; + + virtual bool has_outline() const { return false; } + virtual float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next = 0, const Color &p_modulate = Color(1, 1, 1), bool p_outline = false) const = 0; void update_changes(); Font(); }; +// Helper class to that draws outlines immediately and draws characters in its destructor. +class FontDrawer { + const Ref<Font> &font; + Color outline_color; + bool has_outline; + + struct PendingDraw { + RID canvas_item; + Point2 pos; + CharType chr; + CharType next; + Color modulate; + }; + + Vector<PendingDraw> pending_draws; + +public: + FontDrawer(const Ref<Font> &p_font, const Color &p_outline_color) : + font(p_font), + outline_color(p_outline_color) { + has_outline = p_font->has_outline(); + } + + float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next = 0, const Color &p_modulate = Color(1, 1, 1)) { + if (has_outline) { + PendingDraw draw = { p_canvas_item, p_pos, p_char, p_next, p_modulate }; + pending_draws.push_back(draw); + } + return font->draw_char(p_canvas_item, p_pos, p_char, p_next, has_outline ? outline_color : p_modulate, has_outline); + } + + ~FontDrawer() { + for (int i = 0; i < pending_draws.size(); ++i) { + const PendingDraw &draw = pending_draws[i]; + font->draw_char(draw.canvas_item, draw.pos, draw.chr, draw.next, draw.modulate, false); + } + } +}; + class BitmapFont : public Font { GDCLASS(BitmapFont, Font); @@ -152,10 +194,18 @@ public: void set_distance_field_hint(bool p_distance_field); bool is_distance_field_hint() const; - float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next = 0, const Color &p_modulate = Color(1, 1, 1)) const; + float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next = 0, const Color &p_modulate = Color(1, 1, 1), bool p_outline = false) const; BitmapFont(); ~BitmapFont(); }; +class ResourceFormatLoaderBMFont : public ResourceFormatLoader { +public: + virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); + 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; +}; + #endif diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 4e3991b573..1282ce767a 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "material.h" #include "scene/scene_string_names.h" @@ -102,25 +103,19 @@ Material::~Material() { bool ShaderMaterial::_set(const StringName &p_name, const Variant &p_value) { - if (p_name == SceneStringNames::get_singleton()->shader) { - set_shader(p_value); - return true; - } else { - - if (shader.is_valid()) { + 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 (pr) { - VisualServer::get_singleton()->material_set_param(_get_material(), pr, p_value); - return true; + 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 (pr) { + VisualServer::get_singleton()->material_set_param(_get_material(), pr, p_value); + return true; + } } return false; @@ -128,20 +123,12 @@ bool ShaderMaterial::_set(const StringName &p_name, const Variant &p_value) { bool ShaderMaterial::_get(const StringName &p_name, Variant &r_ret) const { - if (p_name == SceneStringNames::get_singleton()->shader) { - - r_ret = get_shader(); - return true; - - } else { - - if (shader.is_valid()) { + if (shader.is_valid()) { - StringName pr = shader->remap_param(p_name); - if (pr) { - r_ret = VisualServer::get_singleton()->material_get_param(_get_material(), pr); - return true; - } + StringName pr = shader->remap_param(p_name); + if (pr) { + r_ret = VisualServer::get_singleton()->material_get_param(_get_material(), pr); + return true; } } @@ -150,8 +137,6 @@ bool ShaderMaterial::_get(const StringName &p_name, Variant &r_ret) const { void ShaderMaterial::_get_property_list(List<PropertyInfo> *p_list) const { - p_list->push_back(PropertyInfo(Variant::OBJECT, "shader", PROPERTY_HINT_RESOURCE_TYPE, "Shader,ShaderGraph")); - if (!shader.is_null()) { shader->get_param_list(p_list); @@ -192,6 +177,8 @@ void ShaderMaterial::_bind_methods() { 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); + + 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 { @@ -400,10 +387,12 @@ void SpatialMaterial::_update_shader() { if (flags[FLAG_USE_VERTEX_LIGHTING]) { code += ",vertex_lighting"; } - if (flags[FLAG_TRIPLANAR_USE_WORLD] && (flags[FLAG_UV1_USE_TRIPLANAR] || flags[FLAG_UV2_USE_TRIPLANAR])) { code += ",world_vertex_coords"; } + if (flags[FLAG_DONT_RECEIVE_SHADOWS]) { + code += ",shadows_disabled"; + } code += ";\n"; code += "uniform vec4 albedo : hint_color;\n"; @@ -750,15 +739,18 @@ void SpatialMaterial::_update_shader() { } } - if (features[FEATURE_REFRACTION] && !flags[FLAG_UV1_USE_TRIPLANAR]) { //refraction not supported with triplanar + if (features[FEATURE_REFRACTION]) { if (features[FEATURE_NORMAL_MAPPING]) { code += "\tvec3 ref_normal = normalize( mix(NORMAL,TANGENT * NORMALMAP.x + BINORMAL * NORMALMAP.y + NORMAL * NORMALMAP.z,NORMALMAP_DEPTH) );\n"; } else { code += "\tvec3 ref_normal = NORMAL;\n"; } - - code += "\tvec2 ref_ofs = SCREEN_UV - ref_normal.xy * dot(texture(texture_refraction,base_uv),refraction_texture_channel) * refraction;\n"; + if (flags[FLAG_UV1_USE_TRIPLANAR]) { + code += "\tvec2 ref_ofs = SCREEN_UV - ref_normal.xy * dot(triplanar_texture(texture_refraction,uv1_power_normal,uv1_triplanar_pos),refraction_texture_channel) * refraction;\n"; + } else { + code += "\tvec2 ref_ofs = SCREEN_UV - ref_normal.xy * dot(texture(texture_refraction,base_uv),refraction_texture_channel) * refraction;\n"; + } code += "\tfloat ref_amount = 1.0 - albedo.a * albedo_tex.a;\n"; code += "\tEMISSION += textureLod(SCREEN_TEXTURE,ref_ofs,ROUGHNESS * 8.0).rgb * ref_amount;\n"; code += "\tALBEDO *= 1.0 - ref_amount;\n"; @@ -1492,9 +1484,9 @@ bool SpatialMaterial::is_grow_enabled() const { return grow_enabled; } -void SpatialMaterial::set_alpha_scissor_threshold(float p_treshold) { - alpha_scissor_threshold = p_treshold; - VS::get_singleton()->material_set_param(_get_material(), shader_names->alpha_scissor_threshold, p_treshold); +void SpatialMaterial::set_alpha_scissor_threshold(float p_threshold) { + alpha_scissor_threshold = p_threshold; + VS::get_singleton()->material_set_param(_get_material(), shader_names->alpha_scissor_threshold, p_threshold); } float SpatialMaterial::get_alpha_scissor_threshold() const { @@ -1859,6 +1851,7 @@ void SpatialMaterial::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_world_triplanar"), "set_flag", "get_flag", FLAG_TRIPLANAR_USE_WORLD); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_fixed_size"), "set_flag", "get_flag", FLAG_FIXED_SIZE); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_albedo_tex_force_srgb"), "set_flag", "get_flag", FLAG_ALBEDO_TEXTURE_FORCE_SRGB); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_do_not_receive_shadows"), "set_flag", "get_flag", FLAG_DONT_RECEIVE_SHADOWS); ADD_GROUP("Vertex Color", "vertex_color"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "vertex_color_use_as_albedo"), "set_flag", "get_flag", FLAG_ALBEDO_FROM_VERTEX_COLOR); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "vertex_color_is_srgb"), "set_flag", "get_flag", FLAG_SRGB_VERTEX_COLOR); @@ -2048,6 +2041,7 @@ void SpatialMaterial::_bind_methods() { BIND_ENUM_CONSTANT(FLAG_USE_ALPHA_SCISSOR); BIND_ENUM_CONSTANT(FLAG_TRIPLANAR_USE_WORLD); BIND_ENUM_CONSTANT(FLAG_ALBEDO_TEXTURE_FORCE_SRGB); + BIND_ENUM_CONSTANT(FLAG_DONT_RECEIVE_SHADOWS); BIND_ENUM_CONSTANT(FLAG_MAX); BIND_ENUM_CONSTANT(DIFFUSE_BURLEY); @@ -2140,7 +2134,7 @@ SpatialMaterial::SpatialMaterial() : for (int i = 0; i < FLAG_MAX; i++) { flags[i] = 0; } - diffuse_mode = DIFFUSE_LAMBERT; + diffuse_mode = DIFFUSE_BURLEY; specular_mode = SPECULAR_SCHLICK_GGX; for (int i = 0; i < FEATURE_MAX; i++) { diff --git a/scene/resources/material.h b/scene/resources/material.h index 9ce1765e62..ce733bfb8d 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef MATERIAL_H #define MATERIAL_H @@ -187,6 +188,7 @@ public: FLAG_EMISSION_ON_UV2, FLAG_USE_ALPHA_SCISSOR, FLAG_ALBEDO_TEXTURE_FORCE_SRGB, + FLAG_DONT_RECEIVE_SHADOWS, FLAG_MAX }; @@ -235,7 +237,7 @@ private: uint64_t blend_mode : 2; uint64_t depth_draw_mode : 2; uint64_t cull_mode : 2; - uint64_t flags : 14; + uint64_t flags : 15; uint64_t detail_blend_mode : 2; uint64_t diffuse_mode : 3; uint64_t specular_mode : 2; @@ -565,7 +567,7 @@ public: void set_grow(float p_grow); float get_grow() const; - void set_alpha_scissor_threshold(float p_treshold); + void set_alpha_scissor_threshold(float p_threshold); float get_alpha_scissor_threshold() const; void set_on_top_of_alpha(); diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index dc726adf86..d87644381c 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "mesh.h" #include "pair.h" @@ -314,6 +315,8 @@ Ref<Mesh> Mesh::create_outline(float p_margin) const { } } + ERR_FAIL_COND_V(arrays.size() != ARRAY_MAX, Ref<ArrayMesh>()); + { PoolVector<int>::Write ir; PoolVector<int> indices = arrays[ARRAY_INDEX]; @@ -558,12 +561,6 @@ bool ArrayMesh::_set(const StringName &p_name, const Variant &p_value) { return true; } - if (sname == "custom_aabb/custom_aabb") { - - set_custom_aabb(p_value); - return true; - } - if (!sname.begins_with("surfaces")) return false; @@ -672,11 +669,6 @@ bool ArrayMesh::_get(const StringName &p_name, Variant &r_ret) const { else if (what == "name") r_ret = surface_get_name(idx); return true; - } else if (sname == "custom_aabb/custom_aabb") { - - r_ret = custom_aabb; - return true; - } else if (!sname.begins_with("surfaces")) return false; @@ -727,13 +719,13 @@ void ArrayMesh::_get_property_list(List<PropertyInfo> *p_list) const { return; if (blend_shapes.size()) { - p_list->push_back(PropertyInfo(Variant::POOL_STRING_ARRAY, "blend_shape/names", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::POOL_STRING_ARRAY, "blend_shape/names", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); p_list->push_back(PropertyInfo(Variant::INT, "blend_shape/mode", PROPERTY_HINT_ENUM, "Normalized,Relative")); } for (int i = 0; i < surfaces.size(); i++) { - p_list->push_back(PropertyInfo(Variant::DICTIONARY, "surfaces/" + itos(i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::DICTIONARY, "surfaces/" + itos(i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); p_list->push_back(PropertyInfo(Variant::STRING, "surface_" + itos(i + 1) + "/name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); if (surfaces[i].is_2d) { p_list->push_back(PropertyInfo(Variant::OBJECT, "surface_" + itos(i + 1) + "/material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,CanvasItemMaterial", PROPERTY_USAGE_EDITOR)); @@ -741,8 +733,6 @@ void ArrayMesh::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::OBJECT, "surface_" + itos(i + 1) + "/material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,SpatialMaterial", PROPERTY_USAGE_EDITOR)); } } - - p_list->push_back(PropertyInfo(Variant::AABB, "custom_aabb/custom_aabb")); } void ArrayMesh::_recompute_aabb() { @@ -922,6 +912,7 @@ void ArrayMesh::surface_set_material(int p_idx, const Ref<Material> &p_material) VisualServer::get_singleton()->mesh_surface_set_material(mesh, p_idx, p_material.is_null() ? RID() : p_material->get_rid()); _change_notify("material"); + emit_changed(); } void ArrayMesh::surface_set_name(int p_idx, const String &p_name) { @@ -929,6 +920,7 @@ void ArrayMesh::surface_set_name(int p_idx, const String &p_name) { ERR_FAIL_INDEX(p_idx, surfaces.size()); surfaces[p_idx].name = p_name; + emit_changed(); } String ArrayMesh::surface_get_name(int p_idx) const { @@ -941,6 +933,7 @@ void ArrayMesh::surface_update_region(int p_surface, int p_offset, const PoolVec ERR_FAIL_INDEX(p_surface, surfaces.size()); VS::get_singleton()->mesh_surface_update_region(mesh, p_surface, p_offset, p_data); + emit_changed(); } void ArrayMesh::surface_set_custom_aabb(int p_idx, const AABB &p_aabb) { @@ -948,6 +941,7 @@ void ArrayMesh::surface_set_custom_aabb(int p_idx, const AABB &p_aabb) { ERR_FAIL_INDEX(p_idx, surfaces.size()); surfaces[p_idx].aabb = p_aabb; // set custom aabb too? + emit_changed(); } Ref<Material> ArrayMesh::surface_get_material(int p_idx) const { @@ -996,6 +990,7 @@ void ArrayMesh::set_custom_aabb(const AABB &p_custom) { custom_aabb = p_custom; VS::get_singleton()->mesh_set_custom_aabb(mesh, custom_aabb); + emit_changed(); } AABB ArrayMesh::get_custom_aabb() const { @@ -1279,12 +1274,17 @@ void ArrayMesh::_bind_methods() { ClassDB::set_method_flags(get_class_static(), _scs_create("center_geometry"), METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR); ClassDB::bind_method(D_METHOD("regen_normalmaps"), &ArrayMesh::regen_normalmaps); ClassDB::set_method_flags(get_class_static(), _scs_create("regen_normalmaps"), METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR); + ClassDB::bind_method(D_METHOD("lightmap_unwrap"), &ArrayMesh::lightmap_unwrap); + ClassDB::set_method_flags(get_class_static(), _scs_create("lightmap_unwrap"), METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR); ClassDB::bind_method(D_METHOD("get_faces"), &ArrayMesh::get_faces); ClassDB::bind_method(D_METHOD("generate_triangle_mesh"), &ArrayMesh::generate_triangle_mesh); ClassDB::bind_method(D_METHOD("set_custom_aabb", "aabb"), &ArrayMesh::set_custom_aabb); ClassDB::bind_method(D_METHOD("get_custom_aabb"), &ArrayMesh::get_custom_aabb); + ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_shape_mode", PROPERTY_HINT_ENUM, "Normalized,Relative", PROPERTY_USAGE_NOEDITOR), "set_blend_shape_mode", "get_blend_shape_mode"); + ADD_PROPERTY(PropertyInfo(Variant::AABB, "custom_aabb", PROPERTY_HINT_NONE, ""), "set_custom_aabb", "get_custom_aabb"); + BIND_CONSTANT(NO_INDEX_ARRAY); BIND_CONSTANT(ARRAY_WEIGHTS_SIZE); diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h index 98d9edd48b..e8b7ecaf9a 100644 --- a/scene/resources/mesh.h +++ b/scene/resources/mesh.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef MESH_H #define MESH_H diff --git a/scene/resources/mesh_data_tool.cpp b/scene/resources/mesh_data_tool.cpp index 2854d45b52..8639b325c3 100644 --- a/scene/resources/mesh_data_tool.cpp +++ b/scene/resources/mesh_data_tool.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "mesh_data_tool.h" void MeshDataTool::clear() { diff --git a/scene/resources/mesh_data_tool.h b/scene/resources/mesh_data_tool.h index fde6e5a46e..f614b80c3b 100644 --- a/scene/resources/mesh_data_tool.h +++ b/scene/resources/mesh_data_tool.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef MESH_DATA_TOOL_H #define MESH_DATA_TOOL_H diff --git a/scene/resources/mesh_library.cpp b/scene/resources/mesh_library.cpp index adf646f2d9..e1d3540fd1 100644 --- a/scene/resources/mesh_library.cpp +++ b/scene/resources/mesh_library.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "mesh_library.h" bool MeshLibrary::_set(const StringName &p_name, const Variant &p_value) { diff --git a/scene/resources/mesh_library.h b/scene/resources/mesh_library.h index a0bcfec2b6..69719960e2 100644 --- a/scene/resources/mesh_library.h +++ b/scene/resources/mesh_library.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef GRID_THEME_H #define GRID_THEME_H diff --git a/scene/resources/multimesh.cpp b/scene/resources/multimesh.cpp index a89b1c7342..4d0a14e3aa 100644 --- a/scene/resources/multimesh.cpp +++ b/scene/resources/multimesh.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "multimesh.h" #include "servers/visual_server.h" @@ -210,8 +211,8 @@ void MultiMesh::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "transform_format", PROPERTY_HINT_ENUM, "2D,3D"), "set_transform_format", "get_transform_format"); ADD_PROPERTY(PropertyInfo(Variant::INT, "instance_count", PROPERTY_HINT_RANGE, "0,16384,1"), "set_instance_count", "get_instance_count"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh"); - ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR3_ARRAY, "transform_array", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_transform_array", "_get_transform_array"); - ADD_PROPERTY(PropertyInfo(Variant::POOL_COLOR_ARRAY, "color_array", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_color_array", "_get_color_array"); + ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR3_ARRAY, "transform_array", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_transform_array", "_get_transform_array"); + ADD_PROPERTY(PropertyInfo(Variant::POOL_COLOR_ARRAY, "color_array", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_color_array", "_get_color_array"); BIND_ENUM_CONSTANT(TRANSFORM_2D); BIND_ENUM_CONSTANT(TRANSFORM_3D); diff --git a/scene/resources/multimesh.h b/scene/resources/multimesh.h index f84d0c77c6..0875d6d06d 100644 --- a/scene/resources/multimesh.h +++ b/scene/resources/multimesh.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef MULTIMESH_H #define MULTIMESH_H diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index 9dc29151d5..846f6e356e 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "packed_scene.h" #include "core/core_string_names.h" @@ -234,6 +235,7 @@ Node *SceneState::instance(GenEditState p_edit_state) const { 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 @@ -243,13 +245,12 @@ Node *SceneState::instance(GenEditState p_edit_state) const { res = local_dupe; value = local_dupe; } - - //this here may reference nodes not iniialized so this line is commented and used after loading all nodes - //res->setup_local_to_scene(); } //must make a copy, because this res is local to scene } } + } else if (p_edit_state == GEN_EDIT_STATE_INSTANCE) { + value = value.duplicate(true); // Duplicate arrays and dictionaries for the editor } node->set(snames[nprops[j].name], value, &valid); } @@ -1120,7 +1121,7 @@ void SceneState::set_bundled_scene(const Dictionary &p_dictionary) { uint32_t name_index = r[idx++]; nd.name = name_index & ((1 << NAME_INDEX_BITS) - 1); nd.index = (name_index >> NAME_INDEX_BITS); - nd.index--; //0 is invaild, stored as 1 + nd.index--; //0 is invalid, stored as 1 nd.instance = r[idx++]; nd.properties.resize(r[idx++]); for (int j = 0; j < nd.properties.size(); j++) { @@ -1213,7 +1214,7 @@ Dictionary SceneState::get_bundled_scene() const { rnodes.push_back(nd.owner); rnodes.push_back(nd.type); uint32_t name_index = nd.name; - if (nd.index < (1 << (32 - NAME_INDEX_BITS)) - 1) { //save if less than 16k childs + if (nd.index < (1 << (32 - NAME_INDEX_BITS)) - 1) { //save if less than 16k children name_index |= uint32_t(nd.index + 1) << NAME_INDEX_BITS; //for backwards compatibility, index 0 is no index } rnodes.push_back(name_index); diff --git a/scene/resources/packed_scene.h b/scene/resources/packed_scene.h index 01035863d9..278986eb62 100644 --- a/scene/resources/packed_scene.h +++ b/scene/resources/packed_scene.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef PACKED_SCENE_H #define PACKED_SCENE_H diff --git a/scene/resources/plane_shape.cpp b/scene/resources/plane_shape.cpp index 881ecc616e..4d38ebe090 100644 --- a/scene/resources/plane_shape.cpp +++ b/scene/resources/plane_shape.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "plane_shape.h" #include "servers/physics_server.h" diff --git a/scene/resources/plane_shape.h b/scene/resources/plane_shape.h index 3225a614e5..c24c9474fb 100644 --- a/scene/resources/plane_shape.h +++ b/scene/resources/plane_shape.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef PLANE_SHAPE_H #define PLANE_SHAPE_H diff --git a/scene/resources/polygon_path_finder.cpp b/scene/resources/polygon_path_finder.cpp index f26ec5e0a8..6fea2e1a8e 100644 --- a/scene/resources/polygon_path_finder.cpp +++ b/scene/resources/polygon_path_finder.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "polygon_path_finder.h" #include "geometry.h" @@ -588,7 +589,7 @@ void PolygonPathFinder::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_data"), &PolygonPathFinder::_set_data); ClassDB::bind_method(D_METHOD("_get_data"), &PolygonPathFinder::_get_data); - ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_data", "_get_data"); + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); } PolygonPathFinder::PolygonPathFinder() { diff --git a/scene/resources/polygon_path_finder.h b/scene/resources/polygon_path_finder.h index 960e6739d6..19761c274c 100644 --- a/scene/resources/polygon_path_finder.h +++ b/scene/resources/polygon_path_finder.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef POLYGON_PATH_FINDER_H #define POLYGON_PATH_FINDER_H diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp index 94ce3590d7..e0562d9e4a 100644 --- a/scene/resources/primitive_meshes.cpp +++ b/scene/resources/primitive_meshes.cpp @@ -57,6 +57,31 @@ void PrimitiveMesh::_update() const { } } + if (flip_faces) { + PoolVector<Vector3> normals = arr[VS::ARRAY_NORMAL]; + PoolVector<int> indices = arr[VS::ARRAY_INDEX]; + if (normals.size() && indices.size()) { + + { + int nc = normals.size(); + PoolVector<Vector3>::Write w = normals.write(); + for (int i = 0; i < nc; i++) { + w[i] = -w[i]; + } + } + + { + int ic = indices.size(); + PoolVector<int>::Write w = indices.write(); + for (int i = 0; i < ic; i += 3) { + SWAP(w[i + 0], w[i + 1]); + } + } + arr[VS::ARRAY_NORMAL] = normals; + arr[VS::ARRAY_INDEX] = indices; + } + } + // in with the new VisualServer::get_singleton()->mesh_clear(mesh); VisualServer::get_singleton()->mesh_add_surface_from_arrays(mesh, (VisualServer::PrimitiveType)primitive_type, arr); @@ -65,6 +90,8 @@ void PrimitiveMesh::_update() const { pending_request = false; _clear_triangle_mesh(); + + const_cast<PrimitiveMesh *>(this)->emit_changed(); } void PrimitiveMesh::_request_update() { @@ -164,7 +191,15 @@ void PrimitiveMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("get_mesh_arrays"), &PrimitiveMesh::get_mesh_arrays); + ClassDB::bind_method(D_METHOD("set_custom_aabb", "aabb"), &PrimitiveMesh::set_custom_aabb); + ClassDB::bind_method(D_METHOD("get_custom_aabb"), &PrimitiveMesh::get_custom_aabb); + + ClassDB::bind_method(D_METHOD("set_flip_faces", "flip_faces"), &PrimitiveMesh::set_flip_faces); + ClassDB::bind_method(D_METHOD("get_flip_faces"), &PrimitiveMesh::get_flip_faces); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "SpatialMaterial,ShaderMaterial"), "set_material", "get_material"); + ADD_PROPERTY(PropertyInfo(Variant::AABB, "custom_aabb", PROPERTY_HINT_NONE, ""), "set_custom_aabb", "get_custom_aabb"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_faces"), "set_flip_faces", "get_flip_faces"); } void PrimitiveMesh::set_material(const Ref<Material> &p_material) { @@ -185,7 +220,30 @@ Array PrimitiveMesh::get_mesh_arrays() const { return surface_get_arrays(0); } +void PrimitiveMesh::set_custom_aabb(const AABB &p_custom) { + + custom_aabb = p_custom; + VS::get_singleton()->mesh_set_custom_aabb(mesh, custom_aabb); + emit_changed(); +} + +AABB PrimitiveMesh::get_custom_aabb() const { + + return custom_aabb; +} + +void PrimitiveMesh::set_flip_faces(bool p_enable) { + flip_faces = p_enable; + _request_update(); +} + +bool PrimitiveMesh::get_flip_faces() const { + return flip_faces; +} + PrimitiveMesh::PrimitiveMesh() { + + flip_faces = false; // defaults mesh = VisualServer::get_singleton()->mesh_create(); @@ -361,8 +419,8 @@ void CapsuleMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_rings", "rings"), &CapsuleMesh::set_rings); ClassDB::bind_method(D_METHOD("get_rings"), &CapsuleMesh::get_rings); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.1,100.0,0.1"), "set_radius", "get_radius"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "mid_height", PROPERTY_HINT_RANGE, "0.1,100.0,0.1"), "set_mid_height", "get_mid_height"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001"), "set_radius", "get_radius"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "mid_height", PROPERTY_HINT_RANGE, "0.001,100.0,0.001"), "set_mid_height", "get_mid_height"); ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1"), "set_radial_segments", "get_radial_segments"); ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "1,100,1"), "set_rings", "get_rings"); } @@ -823,9 +881,9 @@ void CylinderMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_rings", "rings"), &CylinderMesh::set_rings); ClassDB::bind_method(D_METHOD("get_rings"), &CylinderMesh::get_rings); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "top_radius", PROPERTY_HINT_RANGE, "0.1,100.0,0.1"), "set_top_radius", "get_top_radius"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "bottom_radius", PROPERTY_HINT_RANGE, "0.1,100.0,0.1"), "set_bottom_radius", "get_bottom_radius"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "height", PROPERTY_HINT_RANGE, "0.1,100.0,0.1"), "set_height", "get_height"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "top_radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001"), "set_top_radius", "get_top_radius"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "bottom_radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001"), "set_bottom_radius", "get_bottom_radius"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "height", PROPERTY_HINT_RANGE, "0.001,100.0,0.001"), "set_height", "get_height"); ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1"), "set_radial_segments", "get_radial_segments"); ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "1,100,1"), "set_rings", "get_rings"); } @@ -1224,7 +1282,7 @@ void PrismMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("get_subdivide_depth"), &PrismMesh::get_subdivide_depth); ADD_PROPERTY(PropertyInfo(Variant::REAL, "left_to_right", PROPERTY_HINT_RANGE, "-2.0,2.0,0.1"), "set_left_to_right", "get_left_to_right"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size", PROPERTY_HINT_RANGE, "0.1,100.0,0.1"), "set_size", "get_size"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size"); ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_width", PROPERTY_HINT_RANGE, "0,100,1"), "set_subdivide_width", "get_subdivide_width"); ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_height", PROPERTY_HINT_RANGE, "0,100,1"), "set_subdivide_height", "get_subdivide_height"); ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_depth", PROPERTY_HINT_RANGE, "0,100,1"), "set_subdivide_depth", "get_subdivide_depth"); @@ -1441,8 +1499,8 @@ void SphereMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_is_hemisphere", "is_hemisphere"), &SphereMesh::set_is_hemisphere); ClassDB::bind_method(D_METHOD("get_is_hemisphere"), &SphereMesh::get_is_hemisphere); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.1,100.0,0.1"), "set_radius", "get_radius"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "height", PROPERTY_HINT_RANGE, "0.1,100.0,0.1"), "set_height", "get_height"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001"), "set_radius", "get_radius"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "height", PROPERTY_HINT_RANGE, "0.001,100.0,0.001"), "set_height", "get_height"); ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1"), "set_radial_segments", "get_radial_segments"); ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "1,100,1"), "set_rings", "get_rings"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "is_hemisphere"), "set_is_hemisphere", "get_is_hemisphere"); diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h index 2b7294141a..a91aa09ffe 100644 --- a/scene/resources/primitive_meshes.h +++ b/scene/resources/primitive_meshes.h @@ -39,7 +39,7 @@ @author Bastiaan Olij <mux213@gmail.com> Base class for all the classes in this file, handles a number of code functions that are shared among all meshes. - This class is set appart that it assumes a single surface is always generated for our mesh. + This class is set apart that it assumes a single surface is always generated for our mesh. */ class PrimitiveMesh : public Mesh { @@ -48,8 +48,10 @@ class PrimitiveMesh : public Mesh { private: RID mesh; mutable AABB aabb; + AABB custom_aabb; Ref<Material> material; + bool flip_faces; mutable bool pending_request; void _update() const; @@ -81,6 +83,12 @@ public: Array get_mesh_arrays() const; + void set_custom_aabb(const AABB &p_custom); + AABB get_custom_aabb() const; + + void set_flip_faces(bool p_enable); + bool get_flip_faces() const; + PrimitiveMesh(); ~PrimitiveMesh(); }; @@ -189,7 +197,7 @@ public: }; /** - Similar to quadmesh but with tesselation support + Similar to quadmesh but with tessellation support */ class PlaneMesh : public PrimitiveMesh { diff --git a/scene/resources/ray_shape.cpp b/scene/resources/ray_shape.cpp index dfd7aee23a..a9dec3e87c 100644 --- a/scene/resources/ray_shape.cpp +++ b/scene/resources/ray_shape.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "ray_shape.h" #include "servers/physics_server.h" @@ -42,7 +43,10 @@ Vector<Vector3> RayShape::_gen_debug_mesh_lines() { void RayShape::_update_shape() { - PhysicsServer::get_singleton()->shape_set_data(get_shape(), length); + Dictionary d; + d["length"] = length; + d["slips_on_slope"] = slips_on_slope; + PhysicsServer::get_singleton()->shape_set_data(get_shape(), d); emit_changed(); } @@ -51,6 +55,7 @@ void RayShape::set_length(float p_length) { length = p_length; _update_shape(); notify_change_to_owners(); + _change_notify("length"); } float RayShape::get_length() const { @@ -58,16 +63,33 @@ float RayShape::get_length() const { return length; } +void RayShape::set_slips_on_slope(bool p_active) { + + slips_on_slope = p_active; + _update_shape(); + notify_change_to_owners(); + _change_notify("slips_on_slope"); +} + +bool RayShape::get_slips_on_slope() const { + return slips_on_slope; +} + void RayShape::_bind_methods() { ClassDB::bind_method(D_METHOD("set_length", "length"), &RayShape::set_length); ClassDB::bind_method(D_METHOD("get_length"), &RayShape::get_length); + ClassDB::bind_method(D_METHOD("set_slips_on_slope", "active"), &RayShape::set_slips_on_slope); + ClassDB::bind_method(D_METHOD("get_slips_on_slope"), &RayShape::get_slips_on_slope); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "length", PROPERTY_HINT_RANGE, "0,4096,0.01"), "set_length", "get_length"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slips_on_slope"), "set_slips_on_slope", "get_slips_on_slope"); } RayShape::RayShape() : Shape(PhysicsServer::get_singleton()->shape_create(PhysicsServer::SHAPE_RAY)) { set_length(1.0); + set_slips_on_slope(false); } diff --git a/scene/resources/ray_shape.h b/scene/resources/ray_shape.h index 45fd8ea603..f11b2e7c3c 100644 --- a/scene/resources/ray_shape.h +++ b/scene/resources/ray_shape.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef RAY_SHAPE_H #define RAY_SHAPE_H #include "scene/resources/shape.h" @@ -35,6 +36,7 @@ class RayShape : public Shape { GDCLASS(RayShape, Shape); float length; + bool slips_on_slope; protected: static void _bind_methods(); @@ -45,6 +47,9 @@ public: void set_length(float p_length); float get_length() const; + void set_slips_on_slope(bool p_active); + bool get_slips_on_slope() const; + RayShape(); }; #endif // RAY_SHAPE_H diff --git a/scene/resources/rectangle_shape_2d.cpp b/scene/resources/rectangle_shape_2d.cpp index 5c4c838e29..aeb22f6d0a 100644 --- a/scene/resources/rectangle_shape_2d.cpp +++ b/scene/resources/rectangle_shape_2d.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "rectangle_shape_2d.h" #include "servers/physics_2d_server.h" diff --git a/scene/resources/rectangle_shape_2d.h b/scene/resources/rectangle_shape_2d.h index 1e2a40d695..2d66d328fd 100644 --- a/scene/resources/rectangle_shape_2d.h +++ b/scene/resources/rectangle_shape_2d.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef RECTANGLE_SHAPE_2D_H #define RECTANGLE_SHAPE_2D_H diff --git a/scene/resources/room.cpp b/scene/resources/room.cpp index f46b074225..9493f8fbe6 100644 --- a/scene/resources/room.cpp +++ b/scene/resources/room.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "room.h" #include "servers/visual_server.h" diff --git a/scene/resources/room.h b/scene/resources/room.h index 367757fdb9..359d918665 100644 --- a/scene/resources/room.h +++ b/scene/resources/room.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef ROOM_BOUNDS_H #define ROOM_BOUNDS_H diff --git a/scene/resources/scene_format_text.cpp b/scene/resources/scene_format_text.cpp index 9910fe8dfc..597866eb74 100644 --- a/scene/resources/scene_format_text.cpp +++ b/scene/resources/scene_format_text.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "scene_format_text.h" #include "core/io/resource_format_binary.h" #include "os/dir_access.h" @@ -1311,6 +1312,8 @@ Error ResourceFormatLoaderText::rename_dependencies(const String &p_path, const return ria->rename_dependencies(f, p_path, p_map); } +ResourceFormatLoaderText *ResourceFormatLoaderText::singleton = NULL; + Error ResourceFormatLoaderText::convert_file_to_binary(const String &p_src_path, const String &p_dst_path) { Error err; @@ -1410,7 +1413,7 @@ void ResourceFormatSaverTextInstance::_find_resources(const Variant &p_variant, I = I->next(); } - resource_set.insert(res); //saved after, so the childs it needs are available when loaded + resource_set.insert(res); //saved after, so the children it needs are available when loaded saved_resources.push_back(res); } break; @@ -1442,8 +1445,15 @@ void ResourceFormatSaverTextInstance::_find_resources(const Variant &p_variant, static String _valprop(const String &p_name) { - if (p_name.find("\"") != -1 || p_name.find("=") != -1 || p_name.find(" ") != -1) - return "\"" + p_name.c_escape_multiline() + "\""; + // Escape and quote strings with extended ASCII or further Unicode characters + // as well as '"', '=' or ' ' (32) + const CharType *cstr = p_name.c_str(); + for (int i = 0; cstr[i]; i++) { + if (cstr[i] == '=' || cstr[i] == '"' || cstr[i] < 33 || cstr[i] > 126) { + return "\"" + p_name.c_escape_multiline() + "\""; + } + } + // Keep as is return p_name; } @@ -1503,7 +1513,6 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r title += "load_steps=" + itos(load_steps) + " "; } title += "format=" + itos(FORMAT_VERSION) + ""; - //title+="engine_version=\""+itos(VERSION_MAJOR)+"."+itos(VERSION_MINOR)+"\""; f->store_string(title); f->store_line("]\n"); //one empty line @@ -1663,7 +1672,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r f->store_string(vars); } - f->store_line("]\n"); + f->store_line("]"); for (int j = 0; j < state->get_node_property_count(i); j++) { @@ -1673,10 +1682,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r f->store_string(_valprop(String(state->get_node_property_name(i, j))) + " = " + vars + "\n"); } - if (state->get_node_property_count(i)) { - //add space - f->store_line(String()); - } + f->store_line(String()); } for (int i = 0; i < state->get_connection_count(); i++) { @@ -1699,14 +1705,12 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r f->store_string(" binds= " + vars); } - f->store_line("]\n"); + f->store_line("]"); } - f->store_line(String()); - Vector<NodePath> editable_instances = state->get_editable_instances(); for (int i = 0; i < editable_instances.size(); i++) { - f->store_line("[editable path=\"" + editable_instances[i].operator String() + "\"]"); + f->store_line("\n[editable path=\"" + editable_instances[i].operator String() + "\"]"); } } diff --git a/scene/resources/scene_format_text.h b/scene/resources/scene_format_text.h index 6782aff6fd..c28ded3d77 100644 --- a/scene/resources/scene_format_text.h +++ b/scene/resources/scene_format_text.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef SCENE_FORMAT_TEXT_H #define SCENE_FORMAT_TEXT_H @@ -127,7 +128,9 @@ public: }; class ResourceFormatLoaderText : public ResourceFormatLoader { + public: + static ResourceFormatLoaderText *singleton; virtual Ref<ResourceInteractiveLoader> load_interactive(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); 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; @@ -137,6 +140,8 @@ public: virtual Error rename_dependencies(const String &p_path, const Map<String, String> &p_map); static Error convert_file_to_binary(const String &p_src_path, const String &p_dst_path); + + ResourceFormatLoaderText() { singleton = this; } }; class ResourceFormatSaverTextInstance { diff --git a/scene/resources/segment_shape_2d.cpp b/scene/resources/segment_shape_2d.cpp index 346599b25a..58027c127d 100644 --- a/scene/resources/segment_shape_2d.cpp +++ b/scene/resources/segment_shape_2d.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "segment_shape_2d.h" #include "servers/physics_2d_server.h" @@ -105,7 +106,10 @@ SegmentShape2D::SegmentShape2D() : void RayShape2D::_update_shape() { - Physics2DServer::get_singleton()->shape_set_data(get_rid(), length); + Dictionary d; + d["length"] = length; + d["slips_on_slope"] = slips_on_slope; + Physics2DServer::get_singleton()->shape_set_data(get_rid(), d); emit_changed(); } @@ -139,7 +143,11 @@ void RayShape2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_length", "length"), &RayShape2D::set_length); ClassDB::bind_method(D_METHOD("get_length"), &RayShape2D::get_length); + ClassDB::bind_method(D_METHOD("set_slips_on_slope", "active"), &RayShape2D::set_slips_on_slope); + ClassDB::bind_method(D_METHOD("get_slips_on_slope"), &RayShape2D::get_slips_on_slope); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "length"), "set_length", "get_length"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slips_on_slope"), "set_slips_on_slope", "get_slips_on_slope"); } void RayShape2D::set_length(real_t p_length) { @@ -152,9 +160,20 @@ real_t RayShape2D::get_length() const { return length; } +void RayShape2D::set_slips_on_slope(bool p_active) { + + slips_on_slope = p_active; + _update_shape(); +} + +bool RayShape2D::get_slips_on_slope() const { + return slips_on_slope; +} + RayShape2D::RayShape2D() : Shape2D(Physics2DServer::get_singleton()->ray_shape_create()) { length = 20; + slips_on_slope = false; _update_shape(); } diff --git a/scene/resources/segment_shape_2d.h b/scene/resources/segment_shape_2d.h index 39a71f61d5..700982ac0a 100644 --- a/scene/resources/segment_shape_2d.h +++ b/scene/resources/segment_shape_2d.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef SEGMENT_SHAPE_2D_H #define SEGMENT_SHAPE_2D_H @@ -62,6 +63,7 @@ class RayShape2D : public Shape2D { GDCLASS(RayShape2D, Shape2D); real_t length; + bool slips_on_slope; void _update_shape(); @@ -71,6 +73,10 @@ protected: public: void set_length(real_t p_length); real_t get_length() const; + + void set_slips_on_slope(bool p_active); + bool get_slips_on_slope() const; + virtual void draw(const RID &p_to_rid, const Color &p_color); virtual Rect2 get_rect() const; diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp index c33267d023..36740a307b 100644 --- a/scene/resources/shader.cpp +++ b/scene/resources/shader.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "shader.h" #include "os/file_access.h" #include "scene/scene_string_names.h" diff --git a/scene/resources/shader.h b/scene/resources/shader.h index e9f5873588..248a6f0125 100644 --- a/scene/resources/shader.h +++ b/scene/resources/shader.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef SHADER_H #define SHADER_H diff --git a/scene/resources/shader_graph.cpp b/scene/resources/shader_graph.cpp index 431150d1dd..070cc84863 100644 --- a/scene/resources/shader_graph.cpp +++ b/scene/resources/shader_graph.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "shader_graph.h" #include "scene/scene_string_names.h" @@ -269,7 +270,7 @@ void ShaderGraph::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_data"),&ShaderGraph::_set_data); ClassDB::bind_method(D_METHOD("_get_data"),&ShaderGraph::_get_data); - ADD_PROPERTY( PropertyInfo(Variant::DICTIONARY,"_data",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR), "_set_data","_get_data"); + ADD_PROPERTY( PropertyInfo(Variant::DICTIONARY,"_data",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data","_get_data"); //void get_connections(ShaderType p_which,List<Connection> *p_connections) const; diff --git a/scene/resources/shader_graph.h b/scene/resources/shader_graph.h index f9856158b9..e3a68f8572 100644 --- a/scene/resources/shader_graph.h +++ b/scene/resources/shader_graph.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef SHADER_GRAPH_H #define SHADER_GRAPH_H diff --git a/scene/resources/shape.cpp b/scene/resources/shape.cpp index edec89641f..418d8ce819 100644 --- a/scene/resources/shape.cpp +++ b/scene/resources/shape.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "shape.h" #include "os/os.h" diff --git a/scene/resources/shape.h b/scene/resources/shape.h index bdbc9ba1e4..ad87a69679 100644 --- a/scene/resources/shape.h +++ b/scene/resources/shape.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef SHAPE_H #define SHAPE_H diff --git a/scene/resources/shape_2d.cpp b/scene/resources/shape_2d.cpp index 15c8b6b748..0ca518e5bb 100644 --- a/scene/resources/shape_2d.cpp +++ b/scene/resources/shape_2d.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "shape_2d.h" #include "servers/physics_2d_server.h" RID Shape2D::get_rid() const { diff --git a/scene/resources/shape_2d.h b/scene/resources/shape_2d.h index 877a159aee..7eb0406bd8 100644 --- a/scene/resources/shape_2d.h +++ b/scene/resources/shape_2d.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef SHAPE_2D_H #define SHAPE_2D_H @@ -44,7 +45,7 @@ protected: Shape2D(const RID &p_rid); public: - virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { return true; } + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { return get_rect().has_point(p_point); } void set_custom_solver_bias(real_t p_bias); real_t get_custom_solver_bias() const; diff --git a/scene/resources/shape_line_2d.cpp b/scene/resources/shape_line_2d.cpp index 9548d35370..1a81eea6e5 100644 --- a/scene/resources/shape_line_2d.cpp +++ b/scene/resources/shape_line_2d.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "shape_line_2d.h" #include "servers/physics_2d_server.h" #include "servers/visual_server.h" diff --git a/scene/resources/shape_line_2d.h b/scene/resources/shape_line_2d.h index fe6f2d675e..7d31941558 100644 --- a/scene/resources/shape_line_2d.h +++ b/scene/resources/shape_line_2d.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef SHAPE_LINE_2D_H #define SHAPE_LINE_2D_H diff --git a/scene/resources/sky_box.cpp b/scene/resources/sky_box.cpp index 42ed6f72fa..f2d5cb3516 100644 --- a/scene/resources/sky_box.cpp +++ b/scene/resources/sky_box.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "sky_box.h" #include "io/image_loader.h" diff --git a/scene/resources/sky_box.h b/scene/resources/sky_box.h index b62f9064c5..e561653a9e 100644 --- a/scene/resources/sky_box.h +++ b/scene/resources/sky_box.h @@ -27,8 +27,9 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef Sky_H -#define Sky_H + +#ifndef SKY_BOX_H +#define SKY_BOX_H #include "os/thread.h" #include "scene/resources/texture.h" @@ -195,4 +196,4 @@ public: VARIANT_ENUM_CAST(ProceduralSky::TextureSize) -#endif // Sky_H +#endif // SKY_BOX_H diff --git a/scene/resources/space_2d.cpp b/scene/resources/space_2d.cpp index e2661e83f5..062f4099db 100644 --- a/scene/resources/space_2d.cpp +++ b/scene/resources/space_2d.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "space_2d.h" RID Space2D::get_rid() const { diff --git a/scene/resources/space_2d.h b/scene/resources/space_2d.h index 3ed1e4199a..148285ac6d 100644 --- a/scene/resources/space_2d.h +++ b/scene/resources/space_2d.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef SPACE_2D_H #define SPACE_2D_H diff --git a/scene/resources/sphere_shape.cpp b/scene/resources/sphere_shape.cpp index c61749a598..d8ca1cf3f1 100644 --- a/scene/resources/sphere_shape.cpp +++ b/scene/resources/sphere_shape.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "sphere_shape.h" #include "servers/physics_server.h" diff --git a/scene/resources/sphere_shape.h b/scene/resources/sphere_shape.h index c1e0069941..5dd7daabc5 100644 --- a/scene/resources/sphere_shape.h +++ b/scene/resources/sphere_shape.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef SPHERE_SHAPE_H #define SPHERE_SHAPE_H diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp index 7c8007ff9c..7da65ac984 100644 --- a/scene/resources/style_box.cpp +++ b/scene/resources/style_box.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "style_box.h" #include <limits.h> @@ -100,23 +101,27 @@ StyleBox::StyleBox() { } } -void StyleBoxTexture::set_texture(RES p_texture) { +void StyleBoxTexture::set_texture(Ref<Texture> p_texture) { if (texture == p_texture) return; texture = p_texture; - region_rect = Rect2(Point2(), texture->get_size()); + if (p_texture.is_null()) { + region_rect = Rect2(0, 0, 0, 0); + } else { + region_rect = Rect2(Point2(), texture->get_size()); + } emit_signal("texture_changed"); emit_changed(); _change_notify("texture"); } -RES StyleBoxTexture::get_texture() const { +Ref<Texture> StyleBoxTexture::get_texture() const { return texture; } -void StyleBoxTexture::set_normal_map(RES p_normal_map) { +void StyleBoxTexture::set_normal_map(Ref<Texture> p_normal_map) { if (normal_map == p_normal_map) return; @@ -124,7 +129,7 @@ void StyleBoxTexture::set_normal_map(RES p_normal_map) { emit_changed(); } -RES StyleBoxTexture::get_normal_map() const { +Ref<Texture> StyleBoxTexture::get_normal_map() const { return normal_map; } @@ -181,7 +186,7 @@ Size2 StyleBoxTexture::get_center_size() const { if (texture.is_null()) return Size2(); - return texture->get_size() - get_minimum_size(); + return region_rect.size - get_minimum_size(); } void StyleBoxTexture::set_expand_margin_size(Margin p_expand_margin, float p_size) { @@ -649,14 +654,14 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const { style_rect = style_rect.grow(-((aa_size + 1) / 2)); } - //adapt borders (prevent weired overlapping/glitchy drawings) - int width = style_rect.size.width; - int height = style_rect.size.height; + //adapt borders (prevent weird overlapping/glitchy drawings) + int width = MAX(style_rect.size.width, 0); + int height = MAX(style_rect.size.height, 0); int adapted_border[4] = { INT_MAX, INT_MAX, INT_MAX, INT_MAX }; adapt_values(MARGIN_TOP, MARGIN_BOTTOM, adapted_border, border_width, height, height, height); adapt_values(MARGIN_LEFT, MARGIN_RIGHT, adapted_border, border_width, width, width, width); - //adapt corners (prevent weired overlapping/glitchy drawings) + //adapt corners (prevent weird overlapping/glitchy drawings) int adapted_corner[4] = { INT_MAX, INT_MAX, INT_MAX, INT_MAX }; adapt_values(CORNER_TOP_RIGHT, CORNER_BOTTOM_RIGHT, adapted_corner, corner_radius, height, height - adapted_border[MARGIN_BOTTOM], height - adapted_border[MARGIN_TOP]); adapt_values(CORNER_TOP_LEFT, CORNER_BOTTOM_LEFT, adapted_corner, corner_radius, height, height - adapted_border[MARGIN_BOTTOM], height - adapted_border[MARGIN_TOP]); @@ -911,6 +916,7 @@ void StyleBoxLine::_bind_methods() { ClassDB::bind_method(D_METHOD("is_vertical"), &StyleBoxLine::is_vertical); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "grow", PROPERTY_HINT_RANGE, "-300,300,1"), "set_grow", "get_grow"); ADD_PROPERTY(PropertyInfo(Variant::INT, "thickness", PROPERTY_HINT_RANGE, "0,10"), "set_thickness", "get_thickness"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vertical"), "set_vertical", "is_vertical"); } diff --git a/scene/resources/style_box.h b/scene/resources/style_box.h index bdaa04e492..c1d84fe19f 100644 --- a/scene/resources/style_box.h +++ b/scene/resources/style_box.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef STYLE_BOX_H #define STYLE_BOX_H @@ -111,11 +112,11 @@ public: void set_region_rect(const Rect2 &p_region_rect); Rect2 get_region_rect() const; - void set_texture(RES p_texture); - RES get_texture() const; + void set_texture(Ref<Texture> p_texture); + Ref<Texture> get_texture() const; - void set_normal_map(RES p_normal_map); - RES get_normal_map() const; + void set_normal_map(Ref<Texture> p_normal_map); + Ref<Texture> get_normal_map() const; void set_draw_center(bool p_enabled); bool is_draw_center_enabled() const; diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp index a2535d0ddb..5a42873d79 100644 --- a/scene/resources/surface_tool.cpp +++ b/scene/resources/surface_tool.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "surface_tool.h" #include "method_bind_ext.gen.inc" @@ -860,7 +861,7 @@ void SurfaceTool::generate_tangents() { } } -void SurfaceTool::generate_normals() { +void SurfaceTool::generate_normals(bool p_flip) { ERR_FAIL_COND(primitive != Mesh::PRIMITIVE_TRIANGLES); @@ -886,7 +887,11 @@ void SurfaceTool::generate_normals() { ERR_FAIL_COND(!v[2]); E = v[2]->next(); - Vector3 normal = Plane(v[0]->get().vertex, v[1]->get().vertex, v[2]->get().vertex).normal; + Vector3 normal; + if (!p_flip) + normal = Plane(v[0]->get().vertex, v[1]->get().vertex, v[2]->get().vertex).normal; + else + normal = Plane(v[2]->get().vertex, v[1]->get().vertex, v[0]->get().vertex).normal; if (smooth) { @@ -979,7 +984,7 @@ void SurfaceTool::_bind_methods() { ClassDB::bind_method(D_METHOD("index"), &SurfaceTool::index); ClassDB::bind_method(D_METHOD("deindex"), &SurfaceTool::deindex); - ClassDB::bind_method(D_METHOD("generate_normals"), &SurfaceTool::generate_normals); + ClassDB::bind_method(D_METHOD("generate_normals", "flip"), &SurfaceTool::generate_normals, DEFVAL(false)); ClassDB::bind_method(D_METHOD("generate_tangents"), &SurfaceTool::generate_tangents); ClassDB::bind_method(D_METHOD("add_to_format", "flags"), &SurfaceTool::add_to_format); diff --git a/scene/resources/surface_tool.h b/scene/resources/surface_tool.h index d18c1fc263..459d399380 100644 --- a/scene/resources/surface_tool.h +++ b/scene/resources/surface_tool.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef SURFACE_TOOL_H #define SURFACE_TOOL_H @@ -115,7 +116,7 @@ public: void index(); void deindex(); - void generate_normals(); + void generate_normals(bool p_flip = false); void generate_tangents(); void add_to_format(int p_flags) { format |= p_flags; } diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index ea51a5ba8d..c0f6756fd1 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "texture.h" #include "core/method_bind_ext.gen.inc" #include "core/os/os.h" @@ -75,10 +76,12 @@ void Texture::_bind_methods() { ClassDB::bind_method(D_METHOD("draw_rect_region", "canvas_item", "rect", "src_rect", "modulate", "transpose", "normal_map", "clip_uv"), &Texture::draw_rect_region, DEFVAL(Color(1, 1, 1)), DEFVAL(false), DEFVAL(Variant()), DEFVAL(true)); ClassDB::bind_method(D_METHOD("get_data"), &Texture::get_data); + ADD_PROPERTY(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Mipmaps,Repeat,Filter,Anisotropic Linear,Convert to Linear,Mirrored Repeat,Video Surface"), "set_flags", "get_flags"); + + BIND_ENUM_CONSTANT(FLAGS_DEFAULT); BIND_ENUM_CONSTANT(FLAG_MIPMAPS); BIND_ENUM_CONSTANT(FLAG_REPEAT); BIND_ENUM_CONSTANT(FLAG_FILTER); - BIND_ENUM_CONSTANT(FLAGS_DEFAULT); BIND_ENUM_CONSTANT(FLAG_ANISOTROPIC_FILTER); BIND_ENUM_CONSTANT(FLAG_CONVERT_TO_LINEAR); BIND_ENUM_CONSTANT(FLAG_MIRRORED_REPEAT); @@ -120,10 +123,6 @@ bool ImageTexture::_set(const StringName &p_name, const Variant &p_value) { w = s.width; h = s.height; VisualServer::get_singleton()->texture_set_size_override(texture, w, h); - } else if (p_name == "storage") { - storage = Storage(p_value.operator int()); - } else if (p_name == "lossy_quality") { - lossy_storage_quality = p_value; } else if (p_name == "_data") { _set_data(p_value); } else @@ -142,10 +141,6 @@ bool ImageTexture::_get(const StringName &p_name, Variant &r_ret) const { r_ret = flags; else if (p_name == "size") r_ret = Size2(w, h); - else if (p_name == "storage") - r_ret = storage; - else if (p_name == "lossy_quality") - r_ret = lossy_storage_quality; else return false; @@ -164,8 +159,6 @@ void ImageTexture::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Mipmaps,Repeat,Filter,Anisotropic,sRGB,Mirrored Repeat")); p_list->push_back(PropertyInfo(Variant::OBJECT, "image", PROPERTY_HINT_RESOURCE_TYPE, "Image")); p_list->push_back(PropertyInfo(Variant::VECTOR2, "size", PROPERTY_HINT_NONE, "")); - p_list->push_back(PropertyInfo(Variant::INT, "storage", PROPERTY_HINT_ENUM, "Uncompressed,Compress Lossy,Compress Lossless")); - p_list->push_back(PropertyInfo(Variant::REAL, "lossy_quality", PROPERTY_HINT_RANGE, "0.0,1.0,0.01")); } void ImageTexture::_reload_hook(const RID &p_hook) { @@ -362,6 +355,9 @@ void ImageTexture::_bind_methods() { ClassDB::bind_method(D_METHOD("set_size_override", "size"), &ImageTexture::set_size_override); ClassDB::bind_method(D_METHOD("_reload_hook", "rid"), &ImageTexture::_reload_hook); + ADD_PROPERTY(PropertyInfo(Variant::INT, "storage", PROPERTY_HINT_ENUM, "Uncompressed,Compress Lossy,Compress Lossless"), "set_storage", "get_storage"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "lossy_quality", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_lossy_storage_quality", "get_lossy_storage_quality"); + BIND_ENUM_CONSTANT(STORAGE_RAW); BIND_ENUM_CONSTANT(STORAGE_COMPRESS_LOSSY); BIND_ENUM_CONSTANT(STORAGE_COMPRESS_LOSSLESS); @@ -1132,7 +1128,7 @@ void LargeTexture::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_data", "data"), &LargeTexture::_set_data); ClassDB::bind_method(D_METHOD("_get_data"), &LargeTexture::_get_data); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_data", "_get_data"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); } void LargeTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose, const Ref<Texture> &p_normal_map) const { @@ -1283,8 +1279,6 @@ bool CubeMap::_set(const StringName &p_name, const Variant &p_value) { set_side(SIDE_FRONT, p_value); } else if (p_name == "side/back") { set_side(SIDE_BACK, p_value); - } else if (p_name == "flags") { - set_flags(p_value); } else if (p_name == "storage") { storage = Storage(p_value.operator int()); } else if (p_name == "lossy_quality") { @@ -1309,8 +1303,6 @@ bool CubeMap::_get(const StringName &p_name, Variant &r_ret) const { r_ret = get_side(SIDE_FRONT); } else if (p_name == "side/back") { r_ret = get_side(SIDE_BACK); - } else if (p_name == "flags") { - r_ret = flags; } else if (p_name == "storage") { r_ret = storage; } else if (p_name == "lossy_quality") { @@ -1330,7 +1322,6 @@ void CubeMap::_get_property_list(List<PropertyInfo> *p_list) const { img_hint = PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS; } - p_list->push_back(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Mipmaps,Repeat,Filter")); p_list->push_back(PropertyInfo(Variant::OBJECT, "side/left", PROPERTY_HINT_RESOURCE_TYPE, "Image")); p_list->push_back(PropertyInfo(Variant::OBJECT, "side/right", PROPERTY_HINT_RESOURCE_TYPE, "Image")); p_list->push_back(PropertyInfo(Variant::OBJECT, "side/bottom", PROPERTY_HINT_RESOURCE_TYPE, "Image")); @@ -1352,6 +1343,7 @@ void CubeMap::_bind_methods() { ClassDB::bind_method(D_METHOD("set_lossy_storage_quality", "quality"), &CubeMap::set_lossy_storage_quality); ClassDB::bind_method(D_METHOD("get_lossy_storage_quality"), &CubeMap::get_lossy_storage_quality); + ADD_PROPERTY(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Mipmaps,Repeat,Filter"), "set_flags", "get_flags"); ADD_PROPERTY(PropertyInfo(Variant::INT, "storage_mode", PROPERTY_HINT_ENUM, "Raw,Lossy Compressed,Lossless Compressed"), "set_storage", "get_storage"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "lossy_storage_quality"), "set_lossy_storage_quality", "get_lossy_storage_quality"); diff --git a/scene/resources/texture.h b/scene/resources/texture.h index 1b0754ad3b..93d7ec4ef9 100644 --- a/scene/resources/texture.h +++ b/scene/resources/texture.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef TEXTURE_H #define TEXTURE_H diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp index 7337c5ec4d..f903669fc7 100644 --- a/scene/resources/theme.cpp +++ b/scene/resources/theme.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "theme.h" #include "os/file_access.h" #include "print_string.h" diff --git a/scene/resources/theme.h b/scene/resources/theme.h index 0d15a9a11e..c23f237c75 100644 --- a/scene/resources/theme.h +++ b/scene/resources/theme.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef THEME_H #define THEME_H diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index 89de2de279..42d64376f5 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "tile_set.h" #include "array.h" @@ -56,8 +57,8 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) { tile_set_modulate(id, p_value); else if (what == "region") tile_set_region(id, p_value); - else if (what == "is_autotile") - tile_set_is_autotile(id, p_value); + else if (what == "tile_mode") + tile_set_tile_mode(id, (TileMode)((int)p_value)); else if (what.left(9) == "autotile/") { what = what.right(9); if (what == "bitmask_mode") @@ -141,6 +142,8 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) { tile_set_navigation_polygon(id, p_value); else if (what == "navigation_offset") tile_set_navigation_polygon_offset(id, p_value); + else if (what == "z_index") + tile_set_z_index(id, p_value); else return false; @@ -173,8 +176,8 @@ bool TileSet::_get(const StringName &p_name, Variant &r_ret) const { r_ret = tile_get_modulate(id); else if (what == "region") r_ret = tile_get_region(id); - else if (what == "is_autotile") - r_ret = tile_get_is_autotile(id); + else if (what == "tile_mode") + r_ret = tile_get_tile_mode(id); else if (what.left(9) == "autotile/") { what = what.right(9); if (what == "bitmask_mode") @@ -211,7 +214,7 @@ bool TileSet::_get(const StringName &p_name, Variant &r_ret) const { Vector3 v; for (Map<Vector2, int>::Element *E = tile_map[id].autotile_data.priority_map.front(); E; E = E->next()) { if (E->value() > 1) { - //Dont save default value + //Don't save default value v.x = E->key().x; v.y = E->key().y; v.z = E->value(); @@ -238,6 +241,8 @@ bool TileSet::_get(const StringName &p_name, Variant &r_ret) const { r_ret = tile_get_navigation_polygon(id); else if (what == "navigation_offset") r_ret = tile_get_navigation_polygon_offset(id); + else if (what == "z_index") + r_ret = tile_get_z_index(id); else return false; @@ -257,16 +262,16 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::OBJECT, pre + "material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial")); p_list->push_back(PropertyInfo(Variant::COLOR, pre + "modulate")); p_list->push_back(PropertyInfo(Variant::RECT2, pre + "region")); - p_list->push_back(PropertyInfo(Variant::BOOL, pre + "is_autotile", PROPERTY_HINT_NONE, "")); - if (tile_get_is_autotile(id)) { - p_list->push_back(PropertyInfo(Variant::INT, pre + "autotile/bitmask_mode", PROPERTY_HINT_ENUM, "2X2,3X3", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "autotile/icon_coordinate", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "autotile/tile_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::INT, pre + "autotile/spacing", PROPERTY_HINT_RANGE, "0,256,1", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/bitmask_flags", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/occluder_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/navpoly_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/priority_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::INT, pre + "tile_mode", PROPERTY_HINT_ENUM, "SINGLE_TILE,AUTO_TILE")); + if (tile_get_tile_mode(id) == AUTO_TILE) { + p_list->push_back(PropertyInfo(Variant::INT, pre + "autotile/bitmask_mode", PROPERTY_HINT_ENUM, "2X2,3X3", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/bitmask_flags", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "autotile/icon_coordinate", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "autotile/tile_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::INT, pre + "autotile/spacing", PROPERTY_HINT_RANGE, "0,256,1", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/occluder_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/navpoly_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/priority_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); } p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "occluder_offset")); p_list->push_back(PropertyInfo(Variant::OBJECT, pre + "occluder", PROPERTY_HINT_RESOURCE_TYPE, "OccluderPolygon2D")); @@ -277,11 +282,11 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::OBJECT, pre + "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape2D", PROPERTY_USAGE_EDITOR)); p_list->push_back(PropertyInfo(Variant::BOOL, pre + "shape_one_way", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "shapes", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::INT, pre + "z_index", PROPERTY_HINT_RANGE, itos(VS::CANVAS_ITEM_Z_MIN) + "," + itos(VS::CANVAS_ITEM_Z_MAX) + ",1")); } } void TileSet::create_tile(int p_id) { - ERR_FAIL_COND(tile_map.has(p_id)); tile_map[p_id] = TileData(); tile_map[p_id].autotile_data = AutotileData(); @@ -290,7 +295,6 @@ void TileSet::create_tile(int p_id) { } void TileSet::autotile_set_bitmask_mode(int p_id, BitmaskMode p_mode) { - ERR_FAIL_COND(!tile_map.has(p_id)); tile_map[p_id].autotile_data.bitmask_mode = p_mode; _change_notify(""); @@ -308,6 +312,7 @@ void TileSet::tile_set_texture(int p_id, const Ref<Texture> &p_texture) { ERR_FAIL_COND(!tile_map.has(p_id)); tile_map[p_id].texture = p_texture; emit_changed(); + _change_notify("texture"); } Ref<Texture> TileSet::tile_get_texture(int p_id) const { @@ -347,6 +352,7 @@ void TileSet::tile_set_modulate(int p_id, const Color &p_modulate) { ERR_FAIL_COND(!tile_map.has(p_id)); tile_map[p_id].modulate = p_modulate; emit_changed(); + _change_notify("modulate"); } Color TileSet::tile_get_modulate(int p_id) const { @@ -373,6 +379,7 @@ void TileSet::tile_set_region(int p_id, const Rect2 &p_region) { ERR_FAIL_COND(!tile_map.has(p_id)); tile_map[p_id].region = p_region; emit_changed(); + _change_notify("region"); } Rect2 TileSet::tile_get_region(int p_id) const { @@ -381,18 +388,17 @@ Rect2 TileSet::tile_get_region(int p_id) const { return tile_map[p_id].region; } -void TileSet::tile_set_is_autotile(int p_id, bool p_is_autotile) { - +void TileSet::tile_set_tile_mode(int p_id, TileMode p_tile_mode) { ERR_FAIL_COND(!tile_map.has(p_id)); - tile_map[p_id].is_autotile = p_is_autotile; - _change_notify(""); + tile_map[p_id].tile_mode = p_tile_mode; emit_changed(); + _change_notify("tile_mode"); } -bool TileSet::tile_get_is_autotile(int p_id) const { +TileSet::TileMode TileSet::tile_get_tile_mode(int p_id) const { - ERR_FAIL_COND_V(!tile_map.has(p_id), false); - return tile_map[p_id].is_autotile; + ERR_FAIL_COND_V(!tile_map.has(p_id), SINGLE_TILE); + return tile_map[p_id].tile_mode; } void TileSet::autotile_set_icon_coordinate(int p_id, Vector2 coord) { @@ -532,6 +538,7 @@ void TileSet::tile_set_name(int p_id, const String &p_name) { ERR_FAIL_COND(!tile_map.has(p_id)); tile_map[p_id].name = p_name; emit_changed(); + _change_notify("name"); } String TileSet::tile_get_name(int p_id) const { @@ -746,6 +753,19 @@ Vector<TileSet::ShapeData> TileSet::tile_get_shapes(int p_id) const { return tile_map[p_id].shapes_data; } +int TileSet::tile_get_z_index(int p_id) const { + + ERR_FAIL_COND_V(!tile_map.has(p_id), 0); + return tile_map[p_id].z_index; +} + +void TileSet::tile_set_z_index(int p_id, int p_z_index) { + + ERR_FAIL_COND(!tile_map.has(p_id)); + tile_map[p_id].z_index = p_z_index; + emit_changed(); +} + void TileSet::_tile_set_shapes(int p_id, const Array &p_shapes) { ERR_FAIL_COND(!tile_map.has(p_id)); @@ -917,6 +937,8 @@ void TileSet::_bind_methods() { ClassDB::bind_method(D_METHOD("tile_get_shape_count", "id"), &TileSet::tile_get_shape_count); ClassDB::bind_method(D_METHOD("tile_set_shapes", "id", "shapes"), &TileSet::_tile_set_shapes); ClassDB::bind_method(D_METHOD("tile_get_shapes", "id"), &TileSet::_tile_get_shapes); + ClassDB::bind_method(D_METHOD("tile_set_tile_mode", "id", "tilemode"), &TileSet::tile_set_tile_mode); + ClassDB::bind_method(D_METHOD("tile_get_tile_mode", "id"), &TileSet::tile_get_tile_mode); ClassDB::bind_method(D_METHOD("tile_set_navigation_polygon", "id", "navigation_polygon"), &TileSet::tile_set_navigation_polygon); ClassDB::bind_method(D_METHOD("tile_get_navigation_polygon", "id"), &TileSet::tile_get_navigation_polygon); ClassDB::bind_method(D_METHOD("tile_set_navigation_polygon_offset", "id", "navigation_polygon_offset"), &TileSet::tile_set_navigation_polygon_offset); @@ -925,6 +947,8 @@ void TileSet::_bind_methods() { ClassDB::bind_method(D_METHOD("tile_get_light_occluder", "id"), &TileSet::tile_get_light_occluder); ClassDB::bind_method(D_METHOD("tile_set_occluder_offset", "id", "occluder_offset"), &TileSet::tile_set_occluder_offset); ClassDB::bind_method(D_METHOD("tile_get_occluder_offset", "id"), &TileSet::tile_get_occluder_offset); + ClassDB::bind_method(D_METHOD("tile_set_z_index", "id", "z_index"), &TileSet::tile_set_z_index); + ClassDB::bind_method(D_METHOD("tile_get_z_index", "id"), &TileSet::tile_get_z_index); ClassDB::bind_method(D_METHOD("remove_tile", "id"), &TileSet::remove_tile); ClassDB::bind_method(D_METHOD("clear"), &TileSet::clear); @@ -946,6 +970,10 @@ void TileSet::_bind_methods() { BIND_ENUM_CONSTANT(BIND_BOTTOMLEFT); BIND_ENUM_CONSTANT(BIND_BOTTOM); BIND_ENUM_CONSTANT(BIND_BOTTOMRIGHT); + + BIND_ENUM_CONSTANT(SINGLE_TILE); + BIND_ENUM_CONSTANT(AUTO_TILE); + BIND_ENUM_CONSTANT(ANIMATED_TILE); } TileSet::TileSet() { diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index e7e5c06d34..d5704ac9a0 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef TILE_SET_H #define TILE_SET_H @@ -70,6 +71,12 @@ public: BIND_BOTTOMRIGHT = 256 }; + enum TileMode { + SINGLE_TILE, + AUTO_TILE, + ANIMATED_TILE + }; + struct AutotileData { BitmaskMode bitmask_mode; int spacing; @@ -83,6 +90,7 @@ public: // Default size to prevent invalid value explicit AutotileData() : size(64, 64), + spacing(0), icon_coord(0, 0) { bitmask_mode = BITMASK_2X2; } @@ -103,13 +111,15 @@ private: Ref<NavigationPolygon> navigation_polygon; Ref<ShaderMaterial> material; Color modulate; - bool is_autotile; + TileMode tile_mode; AutotileData autotile_data; + int z_index; // Default modulate for back-compat explicit TileData() : + tile_mode(SINGLE_TILE), modulate(1, 1, 1), - is_autotile(false) {} + z_index(0) {} }; Map<int, TileData> tile_map; @@ -145,8 +155,8 @@ public: void tile_set_region(int p_id, const Rect2 &p_region); Rect2 tile_get_region(int p_id) const; - void tile_set_is_autotile(int p_id, bool p_is_autotile); - bool tile_get_is_autotile(int p_id) const; + void tile_set_tile_mode(int p_id, TileMode p_tile_mode); + TileMode tile_get_tile_mode(int p_id) const; void autotile_set_icon_coordinate(int p_id, Vector2 coord); Vector2 autotile_get_icon_coordinate(int p_id) const; @@ -212,6 +222,9 @@ public: Ref<NavigationPolygon> autotile_get_navigation_polygon(int p_id, const Vector2 &p_coord) const; const Map<Vector2, Ref<NavigationPolygon> > &autotile_get_navigation_map(int p_id) const; + void tile_set_z_index(int p_id, int p_z_index); + int tile_get_z_index(int p_id) const; + void remove_tile(int p_id); bool has_tile(int p_id) const; @@ -230,5 +243,6 @@ public: VARIANT_ENUM_CAST(TileSet::AutotileBindings); VARIANT_ENUM_CAST(TileSet::BitmaskMode); +VARIANT_ENUM_CAST(TileSet::TileMode); #endif // TILE_SET_H diff --git a/scene/resources/video_stream.cpp b/scene/resources/video_stream.cpp index ddedc1a37e..3cd8cbae77 100644 --- a/scene/resources/video_stream.cpp +++ b/scene/resources/video_stream.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "video_stream.h" void VideoStreamPlayback::_bind_methods(){ diff --git a/scene/resources/video_stream.h b/scene/resources/video_stream.h index bd5bf54d0a..0d25d9d687 100644 --- a/scene/resources/video_stream.h +++ b/scene/resources/video_stream.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef VIDEO_STREAM_H #define VIDEO_STREAM_H diff --git a/scene/resources/world.cpp b/scene/resources/world.cpp index 57f567bc3c..82183d24e7 100644 --- a/scene/resources/world.cpp +++ b/scene/resources/world.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "world.h" #include "camera_matrix.h" @@ -317,6 +318,9 @@ void World::_bind_methods() { ClassDB::bind_method(D_METHOD("get_direct_space_state"), &World::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::_RID, "space", PROPERTY_HINT_NONE, "", 0), "", "get_space"); + ADD_PROPERTY(PropertyInfo(Variant::_RID, "scenario", PROPERTY_HINT_NONE, "", 0), "", "get_scenario"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "direct_space_state", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsDirectSpaceState", 0), "", "get_direct_space_state"); } World::World() { diff --git a/scene/resources/world.h b/scene/resources/world.h index 19b8bd03a6..54bdf25784 100644 --- a/scene/resources/world.h +++ b/scene/resources/world.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef WORLD_H #define WORLD_H diff --git a/scene/resources/world_2d.cpp b/scene/resources/world_2d.cpp index 1f75c43d75..bed6ffd1bd 100644 --- a/scene/resources/world_2d.cpp +++ b/scene/resources/world_2d.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "world_2d.h" #include "servers/physics_2d_server.h" #include "servers/visual_server.h" @@ -372,6 +373,10 @@ void World2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_space"), &World2D::get_space); ClassDB::bind_method(D_METHOD("get_direct_space_state"), &World2D::get_direct_space_state); + + ADD_PROPERTY(PropertyInfo(Variant::_RID, "canvas", PROPERTY_HINT_NONE, "", 0), "", "get_canvas"); + ADD_PROPERTY(PropertyInfo(Variant::_RID, "space", PROPERTY_HINT_NONE, "", 0), "", "get_space"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "direct_space_state", PROPERTY_HINT_RESOURCE_TYPE, "Physics2DDirectSpaceState", 0), "", "get_direct_space_state"); } Physics2DDirectSpaceState *World2D::get_direct_space_state() { diff --git a/scene/resources/world_2d.h b/scene/resources/world_2d.h index 84fa00e6cd..59f34e32f2 100644 --- a/scene/resources/world_2d.h +++ b/scene/resources/world_2d.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef WORLD_2D_H #define WORLD_2D_H diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp index e72bc63c53..2dc32b893d 100644 --- a/scene/scene_string_names.cpp +++ b/scene/scene_string_names.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "scene_string_names.h" SceneStringNames *SceneStringNames::singleton = NULL; @@ -48,7 +49,9 @@ SceneStringNames::SceneStringNames() { shader_unshaded = StaticCString::create("shader/unshaded"); shading_mode = StaticCString::create("shader/shading_mode"); tree_entered = StaticCString::create("tree_entered"); + tree_exiting = StaticCString::create("tree_exiting"); tree_exited = StaticCString::create("tree_exited"); + ready = StaticCString::create("ready"); item_rect_changed = StaticCString::create("item_rect_changed"); size_flags_changed = StaticCString::create("size_flags_changed"); minimum_size_changed = StaticCString::create("minimum_size_changed"); diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h index 8e7a0c0075..2e6da26d68 100644 --- a/scene/scene_string_names.h +++ b/scene/scene_string_names.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef SCENE_STRING_NAMES_H #define SCENE_STRING_NAMES_H @@ -68,7 +69,9 @@ public: StringName shader_unshaded; StringName shading_mode; StringName tree_entered; + StringName tree_exiting; StringName tree_exited; + StringName ready; StringName size_flags_changed; StringName minimum_size_changed; StringName sleeping_state_changed; |