diff options
Diffstat (limited to 'scene')
375 files changed, 12567 insertions, 5651 deletions
diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp index de28fef929..54194ff543 100644 --- a/scene/2d/animated_sprite.cpp +++ b/scene/2d/animated_sprite.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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 @@ -326,7 +361,7 @@ void AnimatedSprite::_notification(int p_what) { if (timeout <= 0) { - timeout = 1.0 / speed; + timeout = _get_frame_duration(); int fc = frames->get_frame_count(animation); if (frame >= fc - 1) { @@ -446,6 +481,22 @@ int AnimatedSprite::get_frame() const { return frame; } +void AnimatedSprite::set_speed_scale(float p_speed_scale) { + + float elapsed = _get_frame_duration() - timeout; + + speed_scale = MAX(p_speed_scale, 0.0f); + + // We adapt the timeout so that the animation speed adapts as soon as the speed scale is changed + _reset_timeout(); + timeout -= elapsed; +} + +float AnimatedSprite::get_speed_scale() const { + + return speed_scale; +} + void AnimatedSprite::set_centered(bool p_center) { centered = p_center; @@ -490,29 +541,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); @@ -552,21 +580,22 @@ bool AnimatedSprite::is_playing() const { return playing; } +float AnimatedSprite::_get_frame_duration() { + if (frames.is_valid() && frames->has_animation(animation)) { + float speed = frames->get_animation_speed(animation) * speed_scale; + if (speed > 0) { + return 1.0 / speed; + } + } + return 0.0; +} + void AnimatedSprite::_reset_timeout() { if (!playing) return; - if (frames.is_valid() && frames->has_animation(animation)) { - float speed = frames->get_animation_speed(animation); - if (speed > 0) { - timeout = 1.0 / speed; - } else { - timeout = 0; - } - } else { - timeout = 0; - } + timeout = _get_frame_duration(); } void AnimatedSprite::set_animation(const StringName &p_animation) { @@ -624,6 +653,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 +664,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 +679,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 a8d0db021a..be5b1ef6d6 100644 --- a/scene/2d/animated_sprite.h +++ b/scene/2d/animated_sprite.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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; @@ -139,6 +141,7 @@ class AnimatedSprite : public Node2D { void _res_changed(); + float _get_frame_duration(); void _reset_timeout(); void _set_playing(bool p_playing); bool _is_playing() const; @@ -149,9 +152,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 +174,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 +192,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 80f9bf0f9f..c375374dce 100644 --- a/scene/2d/area_2d.cpp +++ b/scene/2d/area_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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); @@ -665,11 +666,11 @@ void Area2D::_bind_methods() { ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "space_override", PROPERTY_HINT_ENUM, "Disabled,Combine,Combine-Replace,Replace,Replace-Combine"), "set_space_override_mode", "get_space_override_mode"); ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "gravity_point"), "set_gravity_is_point", "is_gravity_a_point"); - ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "gravity_distance_scale", PROPERTY_HINT_RANGE, "0,1024,0.001"), "set_gravity_distance_scale", "get_gravity_distance_scale"); + ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "gravity_distance_scale", PROPERTY_HINT_EXP_RANGE, "0,1024,0.001,or_greater"), "set_gravity_distance_scale", "get_gravity_distance_scale"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "gravity_vec"), "set_gravity_vector", "get_gravity_vector"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "gravity", PROPERTY_HINT_RANGE, "-1024,1024,0.001"), "set_gravity", "get_gravity"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "linear_damp", PROPERTY_HINT_RANGE, "0,100,0.01"), "set_linear_damp", "get_linear_damp"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_damp", PROPERTY_HINT_RANGE, "0,100,0.01"), "set_angular_damp", "get_angular_damp"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "linear_damp", PROPERTY_HINT_RANGE, "0,100,0.01,or_greater"), "set_linear_damp", "get_linear_damp"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_damp", PROPERTY_HINT_RANGE, "0,100,0.01,or_greater"), "set_angular_damp", "get_angular_damp"); ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,128,1"), "set_priority", "get_priority"); ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "monitoring"), "set_monitoring", "is_monitoring"); ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "monitorable"), "set_monitorable", "is_monitorable"); diff --git a/scene/2d/area_2d.h b/scene/2d/area_2d.h index 09ccb364e6..0fda9b867d 100644 --- a/scene/2d/area_2d.h +++ b/scene/2d/area_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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..54541293fd 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,10 +458,11 @@ 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"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "attenuation", PROPERTY_HINT_EXP_EASING), "set_attenuation", "get_attenuation"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_distance", PROPERTY_HINT_EXP_RANGE, "1,4096,1,or_greater"), "set_max_distance", "get_max_distance"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "attenuation", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_attenuation", "get_attenuation"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus"); ADD_PROPERTY(PropertyInfo(Variant::INT, "area_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_area_mask", "get_area_mask"); @@ -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 e4f52a227a..caa1adebdb 100644 --- a/scene/2d/back_buffer_copy.cpp +++ b/scene/2d/back_buffer_copy.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 cfd632d755..752d56de2b 100644 --- a/scene/2d/back_buffer_copy.h +++ b/scene/2d/back_buffer_copy.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 3164344d15..3b86ca76ea 100644 --- a/scene/2d/camera_2d.cpp +++ b/scene/2d/camera_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 * zoom.x * drag_margin[MARGIN_LEFT])); + camera_pos.x = MAX(camera_pos.x, (new_camera_pos.x - screen_size.x * 0.5 * zoom.x * 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 * zoom.y * drag_margin[MARGIN_TOP])); + camera_pos.y = MAX(camera_pos.y, (new_camera_pos.y - screen_size.y * 0.5 * zoom.y * 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 13b6be3978..99571500bf 100644 --- a/scene/2d/camera_2d.h +++ b/scene/2d/camera_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 82123d12ac..27bdeda4a8 100644 --- a/scene/2d/canvas_item.cpp +++ b/scene/2d/canvas_item.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 2384c0f370..10d5082dfc 100644 --- a/scene/2d/canvas_item.h +++ b/scene/2d/canvas_item.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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,29 +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); }; - 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; @@ -280,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)); @@ -302,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 05831609fc..2f8568ccbf 100644 --- a/scene/2d/canvas_modulate.cpp +++ b/scene/2d/canvas_modulate.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 4195c1386c..888ed95f6b 100644 --- a/scene/2d/canvas_modulate.h +++ b/scene/2d/canvas_modulate.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 73e5dc6021..d05c818ae1 100644 --- a/scene/2d/collision_object_2d.cpp +++ b/scene/2d/collision_object_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 a828d8d8ea..6da63d1a0b 100644 --- a/scene/2d/collision_object_2d.h +++ b/scene/2d/collision_object_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 92855299ae..9d2a83fda7 100644 --- a/scene/2d/collision_polygon_2d.cpp +++ b/scene/2d/collision_polygon_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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,15 @@ 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)); +} + String CollisionPolygon2D::get_configuration_warning() const { if (!Object::cast_to<CollisionObject2D>(get_parent())) { diff --git a/scene/2d/collision_polygon_2d.h b/scene/2d/collision_polygon_2d.h index 2fb08a4599..412a923292 100644 --- a/scene/2d/collision_polygon_2d.h +++ b/scene/2d/collision_polygon_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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,8 @@ 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 f7cb5473e3..83ef4df8f4 100644 --- a/scene/2d/collision_shape_2d.cpp +++ b/scene/2d/collision_shape_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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,9 +173,12 @@ Ref<Shape2D> CollisionShape2D::get_shape() const { return shape; } -Rect2 CollisionShape2D::_edit_get_rect() const { +bool CollisionShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + + if (!shape.is_valid()) + return false; - return rect; + return shape->_edit_is_selected_on_click(p_point, p_tolerance); } String CollisionShape2D::get_configuration_warning() const { diff --git a/scene/2d/collision_shape_2d.h b/scene/2d/collision_shape_2d.h index 4745c659c8..ed2c09d53d 100644 --- a/scene/2d/collision_shape_2d.h +++ b/scene/2d/collision_shape_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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,14 @@ 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); Ref<Shape2D> get_shape() const; diff --git a/scene/2d/joints_2d.cpp b/scene/2d/joints_2d.cpp index b98cdcc365..329382c034 100644 --- a/scene/2d/joints_2d.cpp +++ b/scene/2d/joints_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 a6292be51c..744994f0a7 100644 --- a/scene/2d/joints_2d.h +++ b/scene/2d/joints_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 d2b987e037..9a44eb31bb 100644 --- a/scene/2d/light_2d.cpp +++ b/scene/2d/light_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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 9b9da8379f..543805e329 100644 --- a/scene/2d/light_2d.h +++ b/scene/2d/light_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 92e0d8a2f6..c9e5d0f1bc 100644 --- a/scene/2d/light_occluder_2d.cpp +++ b/scene/2d/light_occluder_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 cca7bfecd6..d59c9100b0 100644 --- a/scene/2d/light_occluder_2d.h +++ b/scene/2d/light_occluder_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 57a0e15447..3e61dd05f4 100644 --- a/scene/2d/line_2d.cpp +++ b/scene/2d/line_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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" @@ -48,6 +49,36 @@ Line2D::Line2D() : _round_precision = 8; } +Rect2 Line2D::_edit_get_rect() const { + + if (_points.size() == 0) + return Rect2(0, 0, 0, 0); + Vector2 d = Vector2(_width, _width); + Rect2 aabb = Rect2(_points[0] - d, 2 * d); + for (int i = 1; i < _points.size(); i++) { + aabb.expand_to(_points[i] - d); + aabb.expand_to(_points[i] + d); + } + 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; + PoolVector<Vector2>::Read points = _points.read(); + for (int i = 0; i < _points.size() - 1; i++) { + Vector2 p = Geometry::get_closest_point_to_segment_2d(p_point, &points[i]); + if (p.distance_to(p_point) <= d) + return true; + } + + return false; +} + void Line2D::set_points(const PoolVector<Vector2> &p_points) { _points = p_points; update(); @@ -225,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 6426484f02..24c48982cd 100644 --- a/scene/2d/line_2d.h +++ b/scene/2d/line_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 @@ -57,6 +58,10 @@ 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); PoolVector<Vector2> get_points() const; diff --git a/scene/2d/line_builder.cpp b/scene/2d/line_builder.cpp index 4873c47827..845788bada 100644 --- a/scene/2d/line_builder.cpp +++ b/scene/2d/line_builder.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 f12fbf6f82..b1c62f84e2 100644 --- a/scene/2d/line_builder.h +++ b/scene/2d/line_builder.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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..9f21fe1a1f --- /dev/null +++ b/scene/2d/mesh_instance_2d.cpp @@ -0,0 +1,106 @@ +/*************************************************************************/ +/* mesh_instance_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 "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..c9889c1c03 --- /dev/null +++ b/scene/2d/mesh_instance_2d.h @@ -0,0 +1,63 @@ +/*************************************************************************/ +/* mesh_instance_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 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 9eff107827..16e0386952 100644 --- a/scene/2d/navigation2d.cpp +++ b/scene/2d/navigation2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 @@ -205,7 +206,7 @@ void Navigation2D::_navpoly_unlink(int p_id) { nm.linked = false; } -int Navigation2D::navpoly_create(const Ref<NavigationPolygon> &p_mesh, const Transform2D &p_xform, Object *p_owner) { +int Navigation2D::navpoly_add(const Ref<NavigationPolygon> &p_mesh, const Transform2D &p_xform, Object *p_owner) { int id = last_id++; NavMesh nm; @@ -670,7 +671,7 @@ Object *Navigation2D::get_closest_point_owner(const Vector2 &p_point) { if (Geometry::is_point_in_triangle(p_point, _get_vertex(p.edges[0].point), _get_vertex(p.edges[i - 1].point), _get_vertex(p.edges[i].point))) { - E->get().owner; + return E->get().owner; } } } @@ -708,7 +709,7 @@ Object *Navigation2D::get_closest_point_owner(const Vector2 &p_point) { void Navigation2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("navpoly_create", "mesh", "xform", "owner"), &Navigation2D::navpoly_create, DEFVAL(Variant())); + ClassDB::bind_method(D_METHOD("navpoly_add", "mesh", "xform", "owner"), &Navigation2D::navpoly_add, DEFVAL(Variant())); ClassDB::bind_method(D_METHOD("navpoly_set_transform", "id", "xform"), &Navigation2D::navpoly_set_transform); ClassDB::bind_method(D_METHOD("navpoly_remove", "id"), &Navigation2D::navpoly_remove); diff --git a/scene/2d/navigation2d.h b/scene/2d/navigation2d.h index bb97e1a9a9..02c46d5de1 100644 --- a/scene/2d/navigation2d.h +++ b/scene/2d/navigation2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 @@ -159,7 +160,7 @@ protected: public: //API should be as dynamic as possible - int navpoly_create(const Ref<NavigationPolygon> &p_mesh, const Transform2D &p_xform, Object *p_owner = NULL); + int navpoly_add(const Ref<NavigationPolygon> &p_mesh, const Transform2D &p_xform, Object *p_owner = NULL); void navpoly_set_transform(int p_id, const Transform2D &p_xform); void navpoly_remove(int p_id); diff --git a/scene/2d/navigation_polygon.cpp b/scene/2d/navigation_polygon.cpp index c53241e985..6e27bf1c1d 100644 --- a/scene/2d/navigation_polygon.cpp +++ b/scene/2d/navigation_polygon.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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" @@ -35,9 +36,50 @@ #include "thirdparty/misc/triangulator.h" +Rect2 NavigationPolygon::_edit_get_rect() const { + + if (rect_cache_dirty) { + item_rect = Rect2(); + bool first = true; + + for (int i = 0; i < outlines.size(); i++) { + const PoolVector<Vector2> &outline = outlines[i]; + const int outline_size = outline.size(); + if (outline_size < 3) + continue; + PoolVector<Vector2>::Read p = outline.read(); + for (int j = 0; j < outline_size; j++) { + if (first) { + item_rect = Rect2(p[j], Vector2(0, 0)); + first = false; + } else { + item_rect.expand_to(p[j]); + } + } + } + + rect_cache_dirty = false; + } + return item_rect; +} + +bool NavigationPolygon::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + + for (int i = 0; i < outlines.size(); i++) { + const PoolVector<Vector2> &outline = outlines[i]; + const int outline_size = outline.size(); + if (outline_size < 3) + continue; + if (Geometry::is_point_in_polygon(p_point, Variant(outline))) + return true; + } + return false; +} + void NavigationPolygon::set_vertices(const PoolVector<Vector2> &p_vertices) { vertices = p_vertices; + rect_cache_dirty = true; } PoolVector<Vector2> NavigationPolygon::get_vertices() const { @@ -70,6 +112,7 @@ void NavigationPolygon::_set_outlines(const Array &p_array) { for (int i = 0; i < p_array.size(); i++) { outlines[i] = p_array[i]; } + rect_cache_dirty = true; } Array NavigationPolygon::_get_outlines() const { @@ -93,6 +136,7 @@ void NavigationPolygon::add_polygon(const Vector<int> &p_polygon) { void NavigationPolygon::add_outline_at_index(const PoolVector<Vector2> &p_outline, int p_index) { outlines.insert(p_index, p_outline); + rect_cache_dirty = true; } int NavigationPolygon::get_polygon_count() const { @@ -112,6 +156,7 @@ void NavigationPolygon::clear_polygons() { void NavigationPolygon::add_outline(const PoolVector<Vector2> &p_outline) { outlines.push_back(p_outline); + rect_cache_dirty = true; } int NavigationPolygon::get_outline_count() const { @@ -122,12 +167,14 @@ int NavigationPolygon::get_outline_count() const { void NavigationPolygon::set_outline(int p_idx, const PoolVector<Vector2> &p_outline) { ERR_FAIL_INDEX(p_idx, outlines.size()); outlines[p_idx] = p_outline; + rect_cache_dirty = true; } void NavigationPolygon::remove_outline(int p_idx) { ERR_FAIL_INDEX(p_idx, outlines.size()); outlines.remove(p_idx); + rect_cache_dirty = true; } PoolVector<Vector2> NavigationPolygon::get_outline(int p_idx) const { @@ -138,6 +185,7 @@ PoolVector<Vector2> NavigationPolygon::get_outline(int p_idx) const { void NavigationPolygon::clear_outlines() { outlines.clear(); + rect_cache_dirty = true; } void NavigationPolygon::make_polygons_from_outlines() { @@ -264,12 +312,13 @@ 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() { +NavigationPolygon::NavigationPolygon() : + rect_cache_dirty(true) { } void NavigationPolygonInstance::set_enabled(bool p_enabled) { @@ -293,7 +342,7 @@ void NavigationPolygonInstance::set_enabled(bool p_enabled) { if (navpoly.is_valid()) { - nav_id = navigation->navpoly_create(navpoly, get_relative_transform_to_parent(navigation), this); + nav_id = navigation->navpoly_add(navpoly, get_relative_transform_to_parent(navigation), this); } } } @@ -311,6 +360,16 @@ bool NavigationPolygonInstance::is_enabled() const { ///////////////////////////// +Rect2 NavigationPolygonInstance::_edit_get_rect() const { + + return navpoly.is_valid() ? navpoly->_edit_get_rect() : Rect2(); +} + +bool NavigationPolygonInstance::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + + return navpoly.is_valid() ? navpoly->_edit_is_selected_on_click(p_point, p_tolerance) : false; +} + void NavigationPolygonInstance::_notification(int p_what) { switch (p_what) { @@ -324,7 +383,7 @@ void NavigationPolygonInstance::_notification(int p_what) { if (enabled && navpoly.is_valid()) { - nav_id = navigation->navpoly_create(navpoly, get_relative_transform_to_parent(navigation), this); + nav_id = navigation->navpoly_add(navpoly, get_relative_transform_to_parent(navigation), this); } break; } @@ -419,7 +478,7 @@ void NavigationPolygonInstance::set_navigation_polygon(const Ref<NavigationPolyg } if (navigation && navpoly.is_valid() && enabled) { - nav_id = navigation->navpoly_create(navpoly, get_relative_transform_to_parent(navigation), this); + nav_id = navigation->navpoly_add(navpoly, get_relative_transform_to_parent(navigation), this); } //update_gizmo(); _change_notify("navpoly"); diff --git a/scene/2d/navigation_polygon.h b/scene/2d/navigation_polygon.h index 5576a3abd2..d7684c2d94 100644 --- a/scene/2d/navigation_polygon.h +++ b/scene/2d/navigation_polygon.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 @@ -43,6 +44,9 @@ class NavigationPolygon : public Resource { Vector<Polygon> polygons; Vector<PoolVector<Vector2> > outlines; + mutable Rect2 item_rect; + mutable bool rect_cache_dirty; + protected: static void _bind_methods(); @@ -53,6 +57,9 @@ protected: Array _get_outlines() const; public: + Rect2 _edit_get_rect() const; + bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; + void set_vertices(const PoolVector<Vector2> &p_vertices); PoolVector<Vector2> get_vertices() const; @@ -93,6 +100,9 @@ protected: static void _bind_methods(); public: + virtual Rect2 _edit_get_rect() const; + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; + void set_enabled(bool p_enabled); bool is_enabled() const; diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp index 45f780e50e..3813bd96fe 100644 --- a/scene/2d/node_2d.cpp +++ b/scene/2d/node_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 e1e07f2895..725686cdf8 100644 --- a/scene/2d/node_2d.h +++ b/scene/2d/node_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 b9012e37b2..027d64b813 100644 --- a/scene/2d/parallax_background.cpp +++ b/scene/2d/parallax_background.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 e37ec0db99..31d1c553a2 100644 --- a/scene/2d/parallax_background.h +++ b/scene/2d/parallax_background.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 9da27caa4c..2ac6c76032 100644 --- a/scene/2d/parallax_layer.cpp +++ b/scene/2d/parallax_layer.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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); @@ -119,7 +120,6 @@ void ParallaxLayer::set_base_offset_and_scale(const Point2 &p_offset, float p_sc if (mirroring.x) { double den = mirroring.x * p_scale; - double before = new_ofs.x; new_ofs.x -= den * ceil(new_ofs.x / den); } diff --git a/scene/2d/parallax_layer.h b/scene/2d/parallax_layer.h index 6feb1fad67..b2b98b0ef6 100644 --- a/scene/2d/parallax_layer.h +++ b/scene/2d/parallax_layer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 7d53557216..1da1d44b17 100644 --- a/scene/2d/particles_2d.cpp +++ b/scene/2d/particles_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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" @@ -35,8 +36,7 @@ void Particles2D::set_emitting(bool p_emitting) { - emitting = p_emitting; - VS::get_singleton()->particles_set_emitting(particles, emitting); + VS::get_singleton()->particles_set_emitting(particles, p_emitting); } void Particles2D::set_amount(int p_amount) { @@ -56,7 +56,7 @@ void Particles2D::set_one_shot(bool p_enable) { one_shot = p_enable; VS::get_singleton()->particles_set_one_shot(particles, one_shot); - if (!one_shot && emitting) + if (!one_shot && is_emitting()) VisualServer::get_singleton()->particles_restart(particles); } void Particles2D::set_pre_process_time(float p_time) { @@ -134,7 +134,7 @@ void Particles2D::set_speed_scale(float p_scale) { bool Particles2D::is_emitting() const { - return emitting; + return VS::get_singleton()->particles_get_emitting(particles); } int Particles2D::get_amount() const { @@ -367,9 +367,9 @@ void Particles2D::_bind_methods() { ClassDB::bind_method(D_METHOD("restart"), &Particles2D::restart); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,100000,1"), "set_amount", "get_amount"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_EXP_RANGE, "1,1000000,1"), "set_amount", "get_amount"); ADD_GROUP("Time", ""); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01"), "set_lifetime", "get_lifetime"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01,or_greater"), "set_lifetime", "get_lifetime"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "preprocess", PROPERTY_HINT_RANGE, "0.00,600.0,0.01"), "set_pre_process_time", "get_pre_process_time"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "speed_scale", PROPERTY_HINT_RANGE, "0.01,64,0.01"), "set_speed_scale", "get_speed_scale"); diff --git a/scene/2d/particles_2d.h b/scene/2d/particles_2d.h index 6946f2a799..f367095581 100644 --- a/scene/2d/particles_2d.h +++ b/scene/2d/particles_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 @@ -47,7 +48,6 @@ public: private: RID particles; - bool emitting; bool one_shot; int amount; float lifetime; diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp index 4029ef137b..658b998d17 100644 --- a/scene/2d/path_2d.cpp +++ b/scene/2d/path_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,11 +27,61 @@ /* 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" #include "scene/scene_string_names.h" +#ifdef TOOLS_ENABLED +#include "editor/editor_scale.h" +#endif + +Rect2 Path2D::_edit_get_rect() const { + + 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)); + + for (int i = 0; i < curve->get_point_count(); i++) { + + for (int j = 0; j <= 8; j++) { + + real_t frac = j / 8.0; + Vector2 p = curve->interpolate(i, frac); + aabb.expand_to(p); + } + } + + 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++) { + Vector2 s[2]; + s[0] = curve->get_point_position(i); + + for (int j = 1; j <= 8; j++) { + real_t frac = j / 8.0; + s[1] = curve->interpolate(i, frac); + + Vector2 p = Geometry::get_closest_point_to_segment_2d(p_point, s); + if (p.distance_to(p_point) <= p_tolerance) + return true; + + s[0] = s[1]; + } + } + + return false; +} + void Path2D::_notification(int p_what) { if (p_what == NOTIFICATION_DRAW && curve.is_valid()) { @@ -41,6 +91,13 @@ void Path2D::_notification(int p_what) { return; } +#if TOOLS_ENABLED + const float line_width = 2 * EDSCALE; +#else + const float line_width = 2; +#endif + const Color color = Color(1.0, 1.0, 1.0, 1.0); + for (int i = 0; i < curve->get_point_count(); i++) { Vector2 prev_p = curve->get_point_position(i); @@ -49,7 +106,7 @@ void Path2D::_notification(int p_what) { real_t frac = j / 8.0; Vector2 p = curve->interpolate(i, frac); - draw_line(prev_p, p, Color(0.5, 0.6, 1.0, 0.7), 2); + draw_line(prev_p, p, color, line_width); prev_p = p; } } @@ -94,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)); } ///////////////////////////////////////////////////////////////////////////////// @@ -191,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 { @@ -287,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_EXP_RANGE, "0,10000,0.01,or_greater"), "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 88a0abdea9..64696442c3 100644 --- a/scene/2d/path_2d.h +++ b/scene/2d/path_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 @@ -46,6 +47,10 @@ protected: static void _bind_methods(); 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); Ref<Curve2D> get_curve() const; @@ -70,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 13644f71cf..d36a9fb65a 100644 --- a/scene/2d/path_texture.cpp +++ b/scene/2d/path_texture.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 4639a6072b..7a347c0653 100644 --- a/scene/2d/path_texture.h +++ b/scene/2d/path_texture.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 a1a1101b11..feb11089d0 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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 b82771a8f4..0fda3c5c05 100644 --- a/scene/2d/physics_body_2d.h +++ b/scene/2d/physics_body_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 3f2ad19e51..4d6ebc81c3 100644 --- a/scene/2d/polygon_2d.cpp +++ b/scene/2d/polygon_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,10 +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(); @@ -42,25 +67,19 @@ Rect2 Polygon2D::_edit_get_rect() const { else item_rect.expand_to(pos); } - item_rect = item_rect.grow(20); rect_cache_dirty = false; } return item_rect; } -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) { @@ -72,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()); @@ -161,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); @@ -177,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; } @@ -205,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; @@ -318,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 { @@ -325,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); @@ -336,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); @@ -366,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"); @@ -377,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 d09e22f5ff..575f71d74a 100644 --- a/scene/2d/polygon_2d.h +++ b/scene/2d/polygon_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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,13 +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; + 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 1e729bc179..64d23719e7 100644 --- a/scene/2d/position_2d.cpp +++ b/scene/2d/position_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 5961e447df..bff474cccd 100644 --- a/scene/2d/position_2d.h +++ b/scene/2d/position_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 a809023083..255d2d38d5 100644 --- a/scene/2d/ray_cast_2d.cpp +++ b/scene/2d/ray_cast_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 9d60a16c6a..0850cdc7cc 100644 --- a/scene/2d/ray_cast_2d.h +++ b/scene/2d/ray_cast_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 c139573853..da764e032b 100644 --- a/scene/2d/remote_transform_2d.cpp +++ b/scene/2d/remote_transform_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 30d5fd1e7c..3f395f820e 100644 --- a/scene/2d/remote_transform_2d.h +++ b/scene/2d/remote_transform_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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 d5fcda90d5..9480f18176 100644 --- a/scene/2d/screen_button.cpp +++ b/scene/2d/screen_button.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 2e674c20b4..b2fafcc93d 100644 --- a/scene/2d/screen_button.h +++ b/scene/2d/screen_button.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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..0595cc43b8 --- /dev/null +++ b/scene/2d/skeleton_2d.cpp @@ -0,0 +1,308 @@ +/*************************************************************************/ +/* skeleton_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 "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..b86cf3be81 --- /dev/null +++ b/scene/2d/skeleton_2d.h @@ -0,0 +1,109 @@ +/*************************************************************************/ +/* skeleton_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 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 df2265aae9..64d0771fab 100644 --- a/scene/2d/sprite.cpp +++ b/scene/2d/sprite.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,26 +27,79 @@ /* 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 { + + Rect2 base_rect; + + if (region) { + r_filter_clip = region_filter_clip; + base_rect = region_rect; + } else { + r_filter_clip = false; + base_rect = Rect2(0, 0, texture->get_width(), texture->get_height()); + } + + 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) + dest_offset -= frame_size / 2; + if (Engine::get_singleton()->get_use_pixel_snap()) { + dest_offset = dest_offset.floor(); + } + + r_dst_rect = Rect2(dest_offset, frame_size); + + if (hflip) + r_dst_rect.size.x = -r_dst_rect.size.x; + if (vflip) + r_dst_rect.size.y = -r_dst_rect.size.y; +} + void Sprite::_notification(int p_what) { switch (p_what) { @@ -63,38 +116,9 @@ void Sprite::_notification(int p_what) { break; */ - Size2 s; - Rect2 src_rect; - bool filter_clip = false; - - if (region) { - - s = region_rect.size; - src_rect = region_rect; - filter_clip = region_filter_clip; - } else { - s = Size2(texture->get_size()); - s = s / Size2(hframes, vframes); - - src_rect.size = s; - src_rect.position.x += float(frame % hframes) * s.x; - src_rect.position.y += float(frame / hframes) * s.y; - } - - Point2 ofs = offset; - if (centered) - ofs -= s / 2; - if (Engine::get_singleton()->get_use_pixel_snap()) { - ofs = ofs.floor(); - } - - Rect2 dst_rect(ofs, s); - - if (hflip) - dst_rect.size.x = -dst_rect.size.x; - if (vflip) - dst_rect.size.y = -dst_rect.size.y; - + Rect2 src_rect, dst_rect; + bool filter_clip; + _get_rects(src_rect, dst_rect, filter_clip); texture->draw_rect_region(ci, dst_rect, src_rect, Color(1, 1, 1), false, normal_map, filter_clip); } break; @@ -105,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(); @@ -257,7 +289,81 @@ int Sprite::get_hframes() const { return hframes; } -Rect2 Sprite::_edit_get_rect() const { +bool Sprite::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + + if (texture.is_null()) + return false; + + 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; + 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; + } + + 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(); + + return c.a > 0.01; +} + +Rect2 Sprite::get_rect() const { if (texture.is_null()) return Rect2(0, 0, 1, 1); @@ -269,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; @@ -296,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); @@ -334,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")); @@ -368,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 1bef73c0a5..609ad8bb34 100644 --- a/scene/2d/sprite.h +++ b/scene/2d/sprite.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 @@ -54,6 +55,8 @@ class Sprite : public Node2D { int vframes; int hframes; + void _get_rects(Rect2 &r_src_rect, Rect2 &r_dst_rect, bool &r_filter_clip) const; + protected: void _notification(int p_what); @@ -61,11 +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; @@ -103,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 1e34372d1e..d88e148b2c 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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" @@ -60,6 +61,7 @@ void TileMap::_notification(int p_what) { } pending_update = true; + _recreate_quadrants(); _update_dirty_quadrants(); RID space = get_world_2d()->get_space(); _update_quadrant_transform(); @@ -141,16 +143,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 +266,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 +305,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 +326,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 +342,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 +351,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 +368,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 +462,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 +489,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 { @@ -486,17 +502,66 @@ void TileMap::_update_dirty_quadrants() { xform.set_origin(offset.floor() + q.pos); _fix_cell_transform(xform, c, npoly_ofs + center_ofs, s); - int pid = navigation->navpoly_create(navpoly, nav_rel * xform); + int pid = navigation->navpoly_add(navpoly, nav_rel * xform); Quadrant::NavPoly np; 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); @@ -652,7 +717,7 @@ void TileMap::_make_quadrant_dirty(Map<PosKey, Quadrant>::Element *Q) { pending_update = true; if (!is_inside_tree()) return; - call_deferred("_update_dirty_quadrants"); + _update_dirty_quadrants(); } void TileMap::set_cellv(const Vector2 &p_pos, int p_tile, bool p_flip_x, bool p_flip_y, bool p_transpose) { @@ -739,13 +804,33 @@ void TileMap::update_bitmask_area(const Vector2 &p_pos) { } } +void TileMap::update_bitmask_region(const Vector2 &p_start, const Vector2 &p_end) { + + if ((p_end.x < p_start.x || p_end.y < p_start.y) || (p_end.x == p_start.x && p_end.y == p_start.y)) { + int i; + Array a = get_used_cells(); + for (i = 0; i < a.size(); i++) { + // update_bitmask_area() in order to update cells adjacent to the + // current cell, since ordering in array may not be reliable + Vector2 vector = (Vector2)a[i]; + update_bitmask_area(Vector2(vector.x, vector.y)); + } + return; + } + for (int x = p_start.x - 1; x <= p_end.x + 1; x++) { + for (int y = p_start.y - 1; y <= p_end.y + 1; y++) { + update_cell_bitmask(x, y); + } + } +} + void TileMap::update_cell_bitmask(int p_x, int p_y) { PosKey p(p_x, 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))) { @@ -760,16 +845,37 @@ 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 + 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))) { 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))) { - mask |= TileSet::BIND_TOPLEFT; + } else { + if (tile_set->autotile_get_bitmask_mode(id) == TileSet::BITMASK_3X3_MINIMAL) { + 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))) { + mask |= TileSet::BIND_TOPLEFT; + } + 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))) { + mask |= TileSet::BIND_TOPRIGHT; + } + 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))) { + mask |= TileSet::BIND_BOTTOMLEFT; + } + 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))) { + mask |= TileSet::BIND_BOTTOMRIGHT; + } + } else { + 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 + 1, p_y - 1))) { + mask |= TileSet::BIND_TOPRIGHT; + } + 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 + 1, p_y + 1))) { + mask |= TileSet::BIND_BOTTOMRIGHT; + } } 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))) { - mask |= TileSet::BIND_TOPRIGHT; - } if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y))) { mask |= TileSet::BIND_LEFT; } @@ -777,15 +883,9 @@ 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))) { - 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))) { - mask |= TileSet::BIND_BOTTOMRIGHT; - } } Vector2 coord = tile_set->autotile_get_subtile_for_bitmask(id, mask, this, Vector2(p_x, p_y)); E->get().autotile_coord_x = (int)coord.x; @@ -809,6 +909,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); @@ -978,8 +1088,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]); @@ -1029,6 +1139,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; @@ -1291,10 +1405,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); } @@ -1494,6 +1608,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); @@ -1507,6 +1622,9 @@ void TileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("_recreate_quadrants"), &TileMap::_recreate_quadrants); ClassDB::bind_method(D_METHOD("_update_dirty_quadrants"), &TileMap::_update_dirty_quadrants); + ClassDB::bind_method(D_METHOD("update_bitmask_area", "position"), &TileMap::update_bitmask_area); + ClassDB::bind_method(D_METHOD("update_bitmask_region", "start", "end"), &TileMap::update_bitmask_region, DEFVAL(Vector2()), DEFVAL(Vector2())); + ClassDB::bind_method(D_METHOD("_set_tile_data"), &TileMap::_set_tile_data); ClassDB::bind_method(D_METHOD("_get_tile_data"), &TileMap::_get_tile_data); @@ -1549,6 +1667,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; @@ -1577,5 +1701,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 e5608884c4..07947004b3 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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,9 +245,11 @@ 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); + void update_bitmask_region(const Vector2 &p_start = Vector2(), const Vector2 &p_end = Vector2()); void update_cell_bitmask(int p_x, int p_y); void update_dirty_bitmask(); @@ -306,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 298bc2649e..ddca97e60a 100644 --- a/scene/2d/visibility_notifier_2d.cpp +++ b/scene/2d/visibility_notifier_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 6e06833912..c4e12dfa22 100644 --- a/scene/2d/visibility_notifier_2d.h +++ b/scene/2d/visibility_notifier_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 e0fb3f0645..d3997c13c3 100644 --- a/scene/2d/y_sort.cpp +++ b/scene/2d/y_sort.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 de7aa3a317..5ff12aa25a 100644 --- a/scene/2d/y_sort.h +++ b/scene/2d/y_sort.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 422aa556f9..6ea980ec97 100644 --- a/scene/3d/area.cpp +++ b/scene/3d/area.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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); } @@ -710,7 +711,7 @@ void Area::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "space_override", PROPERTY_HINT_ENUM, "Disabled,Combine,Combine-Replace,Replace,Replace-Combine"), "set_space_override_mode", "get_space_override_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gravity_point"), "set_gravity_is_point", "is_gravity_a_point"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "gravity_distance_scale", PROPERTY_HINT_RANGE, "0,1024,0.001"), "set_gravity_distance_scale", "get_gravity_distance_scale"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "gravity_distance_scale", PROPERTY_HINT_EXP_RANGE, "0,1024,0.001,or_greater"), "set_gravity_distance_scale", "get_gravity_distance_scale"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity_vec"), "set_gravity_vector", "get_gravity_vector"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "gravity", PROPERTY_HINT_RANGE, "-1024,1024,0.01"), "set_gravity", "get_gravity"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "linear_damp", PROPERTY_HINT_RANGE, "0,1024,0.001"), "set_linear_damp", "get_linear_damp"); diff --git a/scene/3d/area.h b/scene/3d/area.h index 7ea97d0a8c..e49b7e493b 100644 --- a/scene/3d/area.h +++ b/scene/3d/area.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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/arvr_nodes.cpp b/scene/3d/arvr_nodes.cpp index e1e0b9b1ce..001c58ea76 100644 --- a/scene/3d/arvr_nodes.cpp +++ b/scene/3d/arvr_nodes.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -231,7 +231,7 @@ void ARVRController::_notification(int p_what) { void ARVRController::_bind_methods() { ClassDB::bind_method(D_METHOD("set_controller_id", "controller_id"), &ARVRController::set_controller_id); ClassDB::bind_method(D_METHOD("get_controller_id"), &ARVRController::get_controller_id); - ADD_PROPERTY(PropertyInfo(Variant::INT, "controller_id", PROPERTY_HINT_RANGE, "1,32,1"), "set_controller_id", "get_controller_id"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "controller_id", PROPERTY_HINT_RANGE, "0,32,1"), "set_controller_id", "get_controller_id"); ClassDB::bind_method(D_METHOD("get_controller_name"), &ARVRController::get_controller_name); // passthroughs to information about our related joystick @@ -251,7 +251,8 @@ void ARVRController::_bind_methods() { }; void ARVRController::set_controller_id(int p_controller_id) { - // we don't check any bounds here, this controller may not yet be active and just be a place holder until it is. + // We don't check any bounds here, this controller may not yet be active and just be a place holder until it is. + // Note that setting this to 0 means this node is not bound to a controller yet. controller_id = p_controller_id; }; @@ -420,7 +421,7 @@ void ARVRAnchor::_bind_methods() { ClassDB::bind_method(D_METHOD("set_anchor_id", "anchor_id"), &ARVRAnchor::set_anchor_id); ClassDB::bind_method(D_METHOD("get_anchor_id"), &ARVRAnchor::get_anchor_id); - ADD_PROPERTY(PropertyInfo(Variant::INT, "anchor_id"), "set_anchor_id", "get_anchor_id"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "anchor_id", PROPERTY_HINT_RANGE, "0,32,1"), "set_anchor_id", "get_anchor_id"); ClassDB::bind_method(D_METHOD("get_anchor_name"), &ARVRAnchor::get_anchor_name); ClassDB::bind_method(D_METHOD("get_is_active"), &ARVRAnchor::get_is_active); @@ -430,7 +431,8 @@ void ARVRAnchor::_bind_methods() { }; void ARVRAnchor::set_anchor_id(int p_anchor_id) { - // we don't check any bounds here, this anchor may not yet be active and just be a place holder until it is. + // We don't check any bounds here, this anchor may not yet be active and just be a place holder until it is. + // Note that setting this to 0 means this node is not bound to an anchor yet. anchor_id = p_anchor_id; }; diff --git a/scene/3d/arvr_nodes.h b/scene/3d/arvr_nodes.h index 6e940351f2..67fb658562 100644 --- a/scene/3d/arvr_nodes.h +++ b/scene/3d/arvr_nodes.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp index 6c102e4027..e7b3645001 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,9 +895,10 @@ 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"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_distance", PROPERTY_HINT_EXP_RANGE, "0,4096,1,or_greater"), "set_max_distance", "get_max_distance"); ADD_PROPERTY(PropertyInfo(Variant::INT, "out_of_range_mode", PROPERTY_HINT_ENUM, "Mix,Pause"), "set_out_of_range_mode", "get_out_of_range_mode"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus"); ADD_PROPERTY(PropertyInfo(Variant::INT, "area_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_area_mask", "get_area_mask"); @@ -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 8af8c2f7da..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" @@ -55,12 +85,13 @@ float BakedLightmapData::get_energy() const { return energy; } -void BakedLightmapData::add_user(const NodePath &p_path, const Ref<Texture> &p_lightmap) { +void BakedLightmapData::add_user(const NodePath &p_path, const Ref<Texture> &p_lightmap, int p_instance) { ERR_FAIL_COND(p_lightmap.is_null()); User user; user.path = p_path; user.lightmap = p_lightmap; + user.instance_index = p_instance; users.push_back(user); } @@ -79,16 +110,22 @@ Ref<Texture> BakedLightmapData::get_user_lightmap(int p_user) const { return users[p_user].lightmap; } +int BakedLightmapData::get_user_instance(int p_user) const { + + ERR_FAIL_INDEX_V(p_user, users.size(), -1); + return users[p_user].instance_index; +} + void BakedLightmapData::clear_users() { users.clear(); } void BakedLightmapData::_set_user_data(const Array &p_data) { - ERR_FAIL_COND(p_data.size() & 1); + ERR_FAIL_COND((p_data.size() % 3) != 0); - for (int i = 0; i < p_data.size(); i += 2) { - add_user(p_data[i], p_data[i + 1]); + for (int i = 0; i < p_data.size(); i += 3) { + add_user(p_data[i], p_data[i + 1], p_data[i + 2]); } } @@ -98,6 +135,7 @@ Array BakedLightmapData::_get_user_data() const { for (int i = 0; i < users.size(); i++) { ret.push_back(users[i].path); ret.push_back(users[i].lightmap); + ret.push_back(users[i].instance_index); } return ret; } @@ -125,18 +163,18 @@ void BakedLightmapData::_bind_methods() { ClassDB::bind_method(D_METHOD("set_energy", "energy"), &BakedLightmapData::set_energy); ClassDB::bind_method(D_METHOD("get_energy"), &BakedLightmapData::get_energy); - ClassDB::bind_method(D_METHOD("add_user", "path", "lightmap"), &BakedLightmapData::add_user); + ClassDB::bind_method(D_METHOD("add_user", "path", "lightmap", "instance"), &BakedLightmapData::add_user); ClassDB::bind_method(D_METHOD("get_user_count"), &BakedLightmapData::get_user_count); ClassDB::bind_method(D_METHOD("get_user_path", "user_idx"), &BakedLightmapData::get_user_path); ClassDB::bind_method(D_METHOD("get_user_lightmap", "user_idx"), &BakedLightmapData::get_user_lightmap); 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() { @@ -157,24 +195,25 @@ BakedLightmap::BakeBeginFunc BakedLightmap::bake_begin_function = NULL; BakedLightmap::BakeStepFunc BakedLightmap::bake_step_function = NULL; BakedLightmap::BakeEndFunc BakedLightmap::bake_end_function = NULL; -void BakedLightmap::set_bake_subdiv(Subdiv p_subdiv) { - bake_subdiv = p_subdiv; +void BakedLightmap::set_bake_cell_size(float p_cell_size) { + bake_cell_size = p_cell_size; } -BakedLightmap::Subdiv BakedLightmap::get_bake_subdiv() const { - return bake_subdiv; +float BakedLightmap::get_bake_cell_size() const { + return bake_cell_size; } -void BakedLightmap::set_capture_subdiv(Subdiv p_subdiv) { - capture_subdiv = p_subdiv; +void BakedLightmap::set_capture_cell_size(float p_cell_size) { + capture_cell_size = p_cell_size; } -BakedLightmap::Subdiv BakedLightmap::get_capture_subdiv() const { - return capture_subdiv; +float BakedLightmap::get_capture_cell_size() const { + return capture_cell_size; } void BakedLightmap::set_extents(const Vector3 &p_extents) { extents = p_extents; + update_gizmo(); } Vector3 BakedLightmap::get_extents() const { @@ -208,6 +247,7 @@ void BakedLightmap::_find_meshes_and_lights(Node *p_at_node, List<PlotMesh> &plo pm.local_xform = xf; pm.mesh = mesh; pm.path = get_path_to(mi); + pm.instance_idx = -1; for (int i = 0; i < mesh->get_surface_count(); i++) { pm.instance_materials.push_back(mi->get_surface_material(i)); } @@ -218,6 +258,26 @@ void BakedLightmap::_find_meshes_and_lights(Node *p_at_node, List<PlotMesh> &plo } } + Spatial *s = Object::cast_to<Spatial>(p_at_node); + + if (!mi && s) { + Array meshes = p_at_node->call("get_bake_meshes"); + if (meshes.size() && (meshes.size() & 1) == 0) { + Transform xf = get_global_transform().affine_inverse() * s->get_global_transform(); + for (int i = 0; i < meshes.size(); i += 2) { + PlotMesh pm; + Transform mesh_xf = meshes[i + 1]; + pm.local_xform = xf * mesh_xf; + pm.mesh = meshes[i]; + pm.instance_idx = i / 2; + if (!pm.mesh.is_valid()) + continue; + pm.path = get_path_to(s); + plot_meshes.push_back(pm); + } + } + } + Light *light = Object::cast_to<Light>(p_at_node); if (light && light->get_bake_mode() != Light::BAKE_DISABLED) { @@ -256,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; @@ -297,11 +357,29 @@ BakedLightmap::BakeError BakedLightmap::bake(Node *p_from_node, bool p_create_vi Ref<BakedLightmapData> new_light_data; new_light_data.instance(); - static const int subdiv_value[SUBDIV_MAX] = { 8, 9, 10, 11, 12, 13 }; - VoxelLightBaker baker; - baker.begin_bake(subdiv_value[bake_subdiv], AABB(-extents, extents * 2.0)); + int bake_subdiv; + int capture_subdiv; + AABB bake_bounds; + { + bake_bounds = AABB(-extents, extents * 2.0); + int subdiv = nearest_power_of_2_templated(int(bake_bounds.get_longest_axis_size() / bake_cell_size)); + bake_bounds.size[bake_bounds.get_longest_axis_size()] = subdiv * bake_cell_size; + bake_subdiv = nearest_shift(subdiv) + 1; + + capture_subdiv = bake_subdiv; + float css = bake_cell_size; + while (css < capture_cell_size && capture_subdiv > 2) { + capture_subdiv--; + css *= 2.0; + } + + print_line("bake subdiv: " + itos(bake_subdiv)); + print_line("capture subdiv: " + itos(capture_subdiv)); + } + + baker.begin_bake(bake_subdiv, bake_bounds); List<PlotMesh> mesh_list; List<PlotLight> light_list; @@ -476,23 +554,23 @@ BakedLightmap::BakeError BakedLightmap::bake(Node *p_from_node, bool p_create_vi if (set_path) { tex->set_path(image_path); } - new_light_data->add_user(E->get().path, tex); + new_light_data->add_user(E->get().path, tex, E->get().instance_idx); } } - int csubdiv = subdiv_value[capture_subdiv]; AABB bounds = AABB(-extents, extents * 2); - new_light_data->set_cell_subdiv(csubdiv); + new_light_data->set_cell_subdiv(capture_subdiv); new_light_data->set_bounds(bounds); - new_light_data->set_octree(baker.create_capture_octree(csubdiv)); + new_light_data->set_octree(baker.create_capture_octree(capture_subdiv)); { + float bake_bound_size = bake_bounds.get_longest_axis_size(); Transform to_bounds; - to_bounds.basis.scale(Vector3(bounds.get_longest_axis_size(), bounds.get_longest_axis_size(), bounds.get_longest_axis_size())); + to_bounds.basis.scale(Vector3(bake_bound_size, bake_bound_size, bake_bound_size)); to_bounds.origin = bounds.position; Transform to_grid; - to_grid.basis.scale(Vector3(1 << (csubdiv - 1), 1 << (csubdiv - 1), 1 << (csubdiv - 1))); + to_grid.basis.scale(Vector3(1 << (capture_subdiv - 1), 1 << (capture_subdiv - 1), 1 << (capture_subdiv - 1))); Transform to_cell_space = to_grid * to_bounds.affine_inverse(); new_light_data->set_cell_space_transform(to_cell_space); @@ -546,12 +624,21 @@ void BakedLightmap::_assign_lightmaps() { ERR_FAIL_COND(!light_data.is_valid()); for (int i = 0; i < light_data->get_user_count(); i++) { - Node *node = get_node(light_data->get_user_path(i)); - VisualInstance *vi = Object::cast_to<VisualInstance>(node); - ERR_CONTINUE(!vi); Ref<Texture> lightmap = light_data->get_user_lightmap(i); ERR_CONTINUE(!lightmap.is_valid()); - VS::get_singleton()->instance_set_use_lightmap(vi->get_instance(), get_instance(), lightmap->get_rid()); + + Node *node = get_node(light_data->get_user_path(i)); + int instance_idx = light_data->get_user_instance(i); + if (instance_idx >= 0) { + RID instance = node->call("get_bake_mesh_instance", instance_idx); + if (instance.is_valid()) { + VS::get_singleton()->instance_set_use_lightmap(instance, get_instance(), lightmap->get_rid()); + } + } else { + VisualInstance *vi = Object::cast_to<VisualInstance>(node); + ERR_CONTINUE(!vi); + VS::get_singleton()->instance_set_use_lightmap(vi->get_instance(), get_instance(), lightmap->get_rid()); + } } } @@ -559,9 +646,17 @@ void BakedLightmap::_clear_lightmaps() { ERR_FAIL_COND(!light_data.is_valid()); for (int i = 0; i < light_data->get_user_count(); i++) { Node *node = get_node(light_data->get_user_path(i)); - VisualInstance *vi = Object::cast_to<VisualInstance>(node); - ERR_CONTINUE(!vi); - VS::get_singleton()->instance_set_use_lightmap(vi->get_instance(), RID(), RID()); + int instance_idx = light_data->get_user_instance(i); + if (instance_idx >= 0) { + RID instance = node->call("get_bake_mesh_instance", instance_idx); + if (instance.is_valid()) { + VS::get_singleton()->instance_set_use_lightmap(instance, get_instance(), RID()); + } + } else { + VisualInstance *vi = Object::cast_to<VisualInstance>(node); + ERR_CONTINUE(!vi); + VS::get_singleton()->instance_set_use_lightmap(vi->get_instance(), get_instance(), RID()); + } } } @@ -646,11 +741,11 @@ void BakedLightmap::_bind_methods() { ClassDB::bind_method(D_METHOD("set_light_data", "data"), &BakedLightmap::set_light_data); ClassDB::bind_method(D_METHOD("get_light_data"), &BakedLightmap::get_light_data); - ClassDB::bind_method(D_METHOD("set_bake_subdiv", "bake_subdiv"), &BakedLightmap::set_bake_subdiv); - ClassDB::bind_method(D_METHOD("get_bake_subdiv"), &BakedLightmap::get_bake_subdiv); + ClassDB::bind_method(D_METHOD("set_bake_cell_size", "bake_cell_size"), &BakedLightmap::set_bake_cell_size); + ClassDB::bind_method(D_METHOD("get_bake_cell_size"), &BakedLightmap::get_bake_cell_size); - ClassDB::bind_method(D_METHOD("set_capture_subdiv", "capture_subdiv"), &BakedLightmap::set_capture_subdiv); - ClassDB::bind_method(D_METHOD("get_capture_subdiv"), &BakedLightmap::get_capture_subdiv); + ClassDB::bind_method(D_METHOD("set_capture_cell_size", "capture_cell_size"), &BakedLightmap::set_capture_cell_size); + ClassDB::bind_method(D_METHOD("get_capture_cell_size"), &BakedLightmap::get_capture_cell_size); ClassDB::bind_method(D_METHOD("set_bake_quality", "bake_quality"), &BakedLightmap::set_bake_quality); ClassDB::bind_method(D_METHOD("get_bake_quality"), &BakedLightmap::get_bake_quality); @@ -677,37 +772,39 @@ void BakedLightmap::_bind_methods() { ClassDB::bind_method(D_METHOD("debug_bake"), &BakedLightmap::_debug_bake); ClassDB::set_method_flags(get_class_static(), _scs_create("debug_bake"), METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR); - ADD_PROPERTY(PropertyInfo(Variant::INT, "bake_subdiv", PROPERTY_HINT_ENUM, "128,256,512,1024,2048,4096"), "set_bake_subdiv", "get_bake_subdiv"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "capture_subdiv", PROPERTY_HINT_ENUM, "128,256,512"), "set_capture_subdiv", "get_capture_subdiv"); + ADD_GROUP("Bake", "bake_"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "bake_cell_size", PROPERTY_HINT_RANGE, "0.01,64,0.01"), "set_bake_cell_size", "get_bake_cell_size"); ADD_PROPERTY(PropertyInfo(Variant::INT, "bake_quality", PROPERTY_HINT_ENUM, "Low,Medium,High"), "set_bake_quality", "get_bake_quality"); ADD_PROPERTY(PropertyInfo(Variant::INT, "bake_mode", PROPERTY_HINT_ENUM, "ConeTrace,RayTrace"), "set_bake_mode", "get_bake_mode"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "propagation", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_propagation", "get_propagation"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "energy", PROPERTY_HINT_RANGE, "0,32,0.01"), "set_energy", "get_energy"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hdr"), "set_hdr", "is_hdr"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "bake_propagation", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_propagation", "get_propagation"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "bake_energy", PROPERTY_HINT_RANGE, "0,32,0.01"), "set_energy", "get_energy"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bake_hdr"), "set_hdr", "is_hdr"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "bake_extents"), "set_extents", "get_extents"); + ADD_GROUP("Capture", "capture_"); + 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::VECTOR3, "extents"), "set_extents", "get_extents"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "light_data", PROPERTY_HINT_RESOURCE_TYPE, "BakedIndirectLightData"), "set_light_data", "get_light_data"); - - BIND_ENUM_CONSTANT(SUBDIV_128); - BIND_ENUM_CONSTANT(SUBDIV_256); - BIND_ENUM_CONSTANT(SUBDIV_512); - BIND_ENUM_CONSTANT(SUBDIV_1024); - BIND_ENUM_CONSTANT(SUBDIV_2048); - BIND_ENUM_CONSTANT(SUBDIV_4096); - BIND_ENUM_CONSTANT(SUBDIV_MAX); + 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); BIND_ENUM_CONSTANT(BAKE_QUALITY_HIGH); BIND_ENUM_CONSTANT(BAKE_MODE_CONE_TRACE); BIND_ENUM_CONSTANT(BAKE_MODE_RAY_TRACE); + + BIND_ENUM_CONSTANT(BAKE_ERROR_OK); + BIND_ENUM_CONSTANT(BAKE_ERROR_NO_SAVE_PATH); + BIND_ENUM_CONSTANT(BAKE_ERROR_NO_MESHES); + BIND_ENUM_CONSTANT(BAKE_ERROR_CANT_CREATE_IMAGE); + BIND_ENUM_CONSTANT(BAKE_ERROR_USER_ABORTED); } BakedLightmap::BakedLightmap() { extents = Vector3(10, 10, 10); - bake_subdiv = SUBDIV_256; - capture_subdiv = SUBDIV_128; + bake_cell_size = 0.25; + capture_cell_size = 0.5; + bake_quality = BAKE_QUALITY_MEDIUM; bake_mode = BAKE_MODE_CONE_TRACE; energy = 1; diff --git a/scene/3d/baked_lightmap.h b/scene/3d/baked_lightmap.h index 5595ec1e61..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 @@ -18,6 +48,7 @@ class BakedLightmapData : public Resource { NodePath path; Ref<Texture> lightmap; + int instance_index; }; Vector<User> users; @@ -44,10 +75,11 @@ public: void set_energy(float p_energy); float get_energy() const; - void add_user(const NodePath &p_path, const Ref<Texture> &p_lightmap); + void add_user(const NodePath &p_path, const Ref<Texture> &p_lightmap, int p_instance = -1); int get_user_count() const; NodePath get_user_path(int p_user) const; Ref<Texture> get_user_lightmap(int p_user) const; + int get_user_instance(int p_user) const; void clear_users(); virtual RID get_rid() const; @@ -59,17 +91,6 @@ class BakedLightmap : public VisualInstance { GDCLASS(BakedLightmap, VisualInstance); public: - enum Subdiv { - SUBDIV_128, - SUBDIV_256, - SUBDIV_512, - SUBDIV_1024, - SUBDIV_2048, - SUBDIV_4096, - SUBDIV_MAX - - }; - enum BakeQuality { BAKE_QUALITY_LOW, BAKE_QUALITY_MEDIUM, @@ -95,8 +116,8 @@ public: typedef void (*BakeEndFunc)(); private: - Subdiv bake_subdiv; - Subdiv capture_subdiv; + float bake_cell_size; + float capture_cell_size; Vector3 extents; float propagation; float energy; @@ -113,6 +134,7 @@ private: Ref<Mesh> mesh; Transform local_xform; NodePath path; + int instance_idx; }; struct PlotLight { @@ -147,11 +169,11 @@ public: void set_light_data(const Ref<BakedLightmapData> &p_data); Ref<BakedLightmapData> get_light_data() const; - void set_bake_subdiv(Subdiv p_subdiv); - Subdiv get_bake_subdiv() const; + void set_bake_cell_size(float p_cell_size); + float get_bake_cell_size() const; - void set_capture_subdiv(Subdiv p_subdiv); - Subdiv get_capture_subdiv() const; + void set_capture_cell_size(float p_cell_size); + float get_capture_cell_size() const; void set_extents(const Vector3 &p_extents); Vector3 get_extents() const; @@ -181,7 +203,6 @@ public: BakedLightmap(); }; -VARIANT_ENUM_CAST(BakedLightmap::Subdiv); VARIANT_ENUM_CAST(BakedLightmap::BakeQuality); VARIANT_ENUM_CAST(BakedLightmap::BakeMode); VARIANT_ENUM_CAST(BakedLightmap::BakeError); diff --git a/scene/3d/bone_attachment.cpp b/scene/3d/bone_attachment.cpp index 2580b645e2..a875b65c22 100644 --- a/scene/3d/bone_attachment.cpp +++ b/scene/3d/bone_attachment.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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 159d16157a..81a225015e 100644 --- a/scene/3d/bone_attachment.h +++ b/scene/3d/bone_attachment.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 af210fff1c..e11e8abe5b 100644 --- a/scene/3d/camera.cpp +++ b/scene/3d/camera.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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" @@ -56,130 +57,23 @@ void Camera::_update_camera_mode() { } } -bool Camera::_set(const StringName &p_name, const Variant &p_value) { - - bool changed_all = false; - if (p_name == "projection") { - - int proj = p_value; - if (proj == PROJECTION_PERSPECTIVE) - mode = PROJECTION_PERSPECTIVE; - if (proj == PROJECTION_ORTHOGONAL) - mode = PROJECTION_ORTHOGONAL; - - changed_all = true; - } else if (p_name == "fov" || p_name == "fovy" || p_name == "fovx") - fov = p_value; - else if (p_name == "size" || p_name == "sizex" || p_name == "sizey") - size = p_value; - else if (p_name == "near") - near = p_value; - else if (p_name == "far") - far = p_value; - else if (p_name == "keep_aspect") - set_keep_aspect_mode(KeepAspect(int(p_value))); - else if (p_name == "vaspect") - set_keep_aspect_mode(p_value ? KEEP_WIDTH : KEEP_HEIGHT); - else if (p_name == "h_offset") - h_offset = p_value; - else if (p_name == "v_offset") - v_offset = p_value; - else if (p_name == "current") { - if (p_value.operator bool()) { - make_current(); - } else { - clear_current(); +void Camera::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "fov") { + if (mode == PROJECTION_ORTHOGONAL) { + p_property.usage = PROPERTY_USAGE_NOEDITOR; } - } else if (p_name == "cull_mask") { - set_cull_mask(p_value); - } else if (p_name == "environment") { - set_environment(p_value); - } else if (p_name == "doppler/tracking") { - set_doppler_tracking(DopplerTracking(int(p_value))); - } else - return false; - - _update_camera_mode(); - if (changed_all) - _change_notify(); - return true; -} -bool Camera::_get(const StringName &p_name, Variant &r_ret) const { - - if (p_name == "projection") { - r_ret = mode; - } else if (p_name == "fov" || p_name == "fovy" || p_name == "fovx") - r_ret = fov; - else if (p_name == "size" || p_name == "sizex" || p_name == "sizey") - r_ret = size; - else if (p_name == "near") - r_ret = near; - else if (p_name == "far") - r_ret = far; - else if (p_name == "keep_aspect") - r_ret = int(keep_aspect); - else if (p_name == "current") { - - if (is_inside_tree() && get_tree()->is_node_being_edited(this)) { - r_ret = current; - } else { - r_ret = is_current(); + } else if (p_property.name == "size") { + if (mode == PROJECTION_PERSPECTIVE) { + p_property.usage = PROPERTY_USAGE_NOEDITOR; } - } else if (p_name == "cull_mask") { - r_ret = get_cull_mask(); - } else if (p_name == "h_offset") { - r_ret = get_h_offset(); - } else if (p_name == "v_offset") { - r_ret = get_v_offset(); - } else if (p_name == "environment") { - r_ret = get_environment(); - } else if (p_name == "doppler/tracking") { - r_ret = get_doppler_tracking(); - } else - return false; - - return true; -} - -void Camera::_get_property_list(List<PropertyInfo> *p_list) const { - - p_list->push_back(PropertyInfo(Variant::INT, "projection", PROPERTY_HINT_ENUM, "Perspective,Orthogonal")); - - switch (mode) { - - case PROJECTION_PERSPECTIVE: { - - p_list->push_back(PropertyInfo(Variant::REAL, "fov", PROPERTY_HINT_RANGE, "1,179,0.1", PROPERTY_USAGE_NOEDITOR)); - if (keep_aspect == KEEP_WIDTH) - p_list->push_back(PropertyInfo(Variant::REAL, "fovx", PROPERTY_HINT_RANGE, "1,179,0.1", PROPERTY_USAGE_EDITOR)); - else - p_list->push_back(PropertyInfo(Variant::REAL, "fovy", PROPERTY_HINT_RANGE, "1,179,0.1", PROPERTY_USAGE_EDITOR)); - - } break; - case PROJECTION_ORTHOGONAL: { - - p_list->push_back(PropertyInfo(Variant::REAL, "size", PROPERTY_HINT_RANGE, "1,16384,0.01", PROPERTY_USAGE_NOEDITOR)); - if (keep_aspect == KEEP_WIDTH) - p_list->push_back(PropertyInfo(Variant::REAL, "sizex", PROPERTY_HINT_RANGE, "0.1,16384,0.01", PROPERTY_USAGE_EDITOR)); - else - p_list->push_back(PropertyInfo(Variant::REAL, "sizey", PROPERTY_HINT_RANGE, "0.1,16384,0.01", PROPERTY_USAGE_EDITOR)); - - } break; } - - p_list->push_back(PropertyInfo(Variant::REAL, "near", PROPERTY_HINT_EXP_RANGE, "0.01,4096.0,0.01")); - p_list->push_back(PropertyInfo(Variant::REAL, "far", PROPERTY_HINT_EXP_RANGE, "0.01,4096.0,0.01")); - p_list->push_back(PropertyInfo(Variant::INT, "keep_aspect", PROPERTY_HINT_ENUM, "Keep Width,Keep Height")); - p_list->push_back(PropertyInfo(Variant::BOOL, "current")); - p_list->push_back(PropertyInfo(Variant::INT, "cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER)); - p_list->push_back(PropertyInfo(Variant::OBJECT, "environment", PROPERTY_HINT_RESOURCE_TYPE, "Environment")); - 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, "doppler/tracking", PROPERTY_HINT_ENUM, "Disabled,Idle,Physics")); } void Camera::_update_camera() { + if (!is_inside_tree()) + return; + Transform tr = get_camera_transform(); tr.origin += tr.basis.get_axis(1) * v_offset; tr.origin += tr.basis.get_axis(0) * h_offset; @@ -191,7 +85,7 @@ void Camera::_update_camera() { get_viewport()->_camera_transform_changed_notify(); */ - if (!is_inside_tree() || get_tree()->is_node_being_edited(this) || !is_current()) + if (get_tree()->is_node_being_edited(this) || !is_current()) return; get_viewport()->_camera_transform_changed_notify(); @@ -282,6 +176,14 @@ void Camera::set_orthogonal(float p_size, float p_z_near, float p_z_far) { update_gizmo(); } +void Camera::set_projection(Camera::Projection p_mode) { + if (p_mode == PROJECTION_PERSPECTIVE || p_mode == PROJECTION_ORTHOGONAL) { + mode = p_mode; + _update_camera_mode(); + _change_notify(); + } +} + RID Camera::get_camera() const { return camera; @@ -299,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()) @@ -307,7 +209,18 @@ 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); + } + } +} + +void Camera::set_current(bool p_current) { + if (p_current) { + make_current(); + } else { + clear_current(); } } @@ -481,6 +394,7 @@ void Camera::set_environment(const Ref<Environment> &p_environment) { VS::get_singleton()->camera_set_environment(camera, environment->get_rid()); else VS::get_singleton()->camera_set_environment(camera, RID()); + _update_camera_mode(); } Ref<Environment> Camera::get_environment() const { @@ -489,10 +403,9 @@ Ref<Environment> Camera::get_environment() const { } void Camera::set_keep_aspect_mode(KeepAspect p_aspect) { - keep_aspect = p_aspect; VisualServer::get_singleton()->camera_set_use_vertical_aspect(camera, p_aspect == KEEP_WIDTH); - + _update_camera_mode(); _change_notify(); } @@ -511,6 +424,7 @@ void Camera::set_doppler_tracking(DopplerTracking p_tracking) { velocity_tracker->set_track_physics_step(doppler_tracking == DOPPLER_TRACKING_PHYSICS_STEP); velocity_tracker->reset(get_global_transform().origin); } + _update_camera_mode(); } Camera::DopplerTracking Camera::get_doppler_tracking() const { @@ -528,14 +442,20 @@ 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); ClassDB::bind_method(D_METHOD("get_fov"), &Camera::get_fov); ClassDB::bind_method(D_METHOD("get_size"), &Camera::get_size); ClassDB::bind_method(D_METHOD("get_zfar"), &Camera::get_zfar); ClassDB::bind_method(D_METHOD("get_znear"), &Camera::get_znear); + ClassDB::bind_method(D_METHOD("set_fov"), &Camera::set_fov); + ClassDB::bind_method(D_METHOD("set_size"), &Camera::set_size); + ClassDB::bind_method(D_METHOD("set_zfar"), &Camera::set_zfar); + ClassDB::bind_method(D_METHOD("set_znear"), &Camera::set_znear); ClassDB::bind_method(D_METHOD("get_projection"), &Camera::get_projection); + ClassDB::bind_method(D_METHOD("set_projection"), &Camera::set_projection); ClassDB::bind_method(D_METHOD("set_h_offset", "ofs"), &Camera::set_h_offset); ClassDB::bind_method(D_METHOD("get_h_offset"), &Camera::get_h_offset); ClassDB::bind_method(D_METHOD("set_v_offset", "ofs"), &Camera::set_v_offset); @@ -550,6 +470,19 @@ void Camera::_bind_methods() { ClassDB::bind_method(D_METHOD("get_doppler_tracking"), &Camera::get_doppler_tracking); //ClassDB::bind_method(D_METHOD("_camera_make_current"),&Camera::_camera_make_current ); + ADD_PROPERTY(PropertyInfo(Variant::INT, "keep_aspect", PROPERTY_HINT_ENUM, "Keep Width,Keep Height"), "set_keep_aspect_mode", "get_keep_aspect_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "environment", PROPERTY_HINT_RESOURCE_TYPE, "Environment"), "set_environment", "get_environment"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "h_offset"), "set_h_offset", "get_h_offset"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "v_offset"), "set_v_offset", "get_v_offset"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "doppler_tracking", PROPERTY_HINT_ENUM, "Disabled,Idle,Physics"), "set_doppler_tracking", "get_doppler_tracking"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "projection", PROPERTY_HINT_ENUM, "Perspective,Orthogonal"), "set_projection", "get_projection"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "current"), "set_current", "is_current"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "fov", PROPERTY_HINT_RANGE, "1,179,0.1"), "set_fov", "get_fov"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "size", PROPERTY_HINT_RANGE, "0.1,16384,0.01"), "set_size", "get_size"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "near", PROPERTY_HINT_EXP_RANGE, "0.1,8192,0.1,or_greater"), "set_znear", "get_znear"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "far", PROPERTY_HINT_EXP_RANGE, "0.1,8192,0.1,or_greater"), "set_zfar", "get_zfar"); + BIND_ENUM_CONSTANT(PROJECTION_PERSPECTIVE); BIND_ENUM_CONSTANT(PROJECTION_ORTHOGONAL); @@ -586,10 +519,30 @@ Camera::Projection Camera::get_projection() const { return mode; } -void Camera::set_cull_mask(uint32_t p_layers) { +void Camera::set_fov(float p_fov) { + fov = p_fov; + _update_camera_mode(); +} + +void Camera::set_size(float p_size) { + size = p_size; + _update_camera_mode(); +} + +void Camera::set_znear(float p_znear) { + near = p_znear; + _update_camera_mode(); +} +void Camera::set_zfar(float p_zfar) { + far = p_zfar; + _update_camera_mode(); +} + +void Camera::set_cull_mask(uint32_t p_layers) { layers = p_layers; VisualServer::get_singleton()->camera_set_cull_mask(camera, layers); + _update_camera_mode(); } uint32_t Camera::get_cull_mask() const { diff --git a/scene/3d/camera.h b/scene/3d/camera.h index 73c6844c1a..1b506e0c4f 100644 --- a/scene/3d/camera.h +++ b/scene/3d/camera.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 @@ -95,10 +96,8 @@ protected: virtual void _request_camera_update(); void _update_camera_mode(); - 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); + virtual void _validate_property(PropertyInfo &p_property) const; static void _bind_methods(); @@ -111,9 +110,11 @@ public: void set_perspective(float p_fovy_degrees, float p_z_near, float p_z_far); void set_orthogonal(float p_size, float p_z_near, float p_z_far); + void set_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; RID get_camera() const; @@ -124,11 +125,16 @@ public: float get_znear() const; Projection get_projection() const; + void set_fov(float p_fov); + void set_size(float p_size); + void set_zfar(float p_zfar); + void set_znear(float p_znear); + 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 7b4770e435..1d5d1b2afe 100644 --- a/scene/3d/collision_object.cpp +++ b/scene/3d/collision_object.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 ac35de697c..f31d65e411 100644 --- a/scene/3d/collision_object.h +++ b/scene/3d/collision_object.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 a6d812efec..379dd21c39 100644 --- a/scene/3d/collision_polygon.cpp +++ b/scene/3d/collision_polygon.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 14d8c3aba6..f1f137c9c5 100644 --- a/scene/3d/collision_polygon.h +++ b/scene/3d/collision_polygon.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 f49d89122d..943f4158f7 100644 --- a/scene/3d/collision_shape.cpp +++ b/scene/3d/collision_shape.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 94621177cb..c9c91a5824 100644 --- a/scene/3d/collision_shape.h +++ b/scene/3d/collision_shape.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 9c811a74bf..4ad2eb60ee 100644 --- a/scene/3d/gi_probe.cpp +++ b/scene/3d/gi_probe.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 0858af0001..af4b646590 100644 --- a/scene/3d/gi_probe.h +++ b/scene/3d/gi_probe.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 092ed8f0b2..14a0b0505d 100644 --- a/scene/3d/immediate_geometry.cpp +++ b/scene/3d/immediate_geometry.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 1ff4e05e82..f7201b4bba 100644 --- a/scene/3d/immediate_geometry.h +++ b/scene/3d/immediate_geometry.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 3ff8317732..ffa283f634 100644 --- a/scene/3d/interpolated_camera.cpp +++ b/scene/3d/interpolated_camera.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 26e7278c99..b025112295 100644 --- a/scene/3d/interpolated_camera.h +++ b/scene/3d/interpolated_camera.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 6eb2028d8e..7c42638107 100644 --- a/scene/3d/light.cpp +++ b/scene/3d/light.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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" @@ -374,7 +375,7 @@ void DirectionalLight::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::REAL, "directional_shadow_normal_bias", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_param", "get_param", PARAM_SHADOW_NORMAL_BIAS); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "directional_shadow_bias_split_scale", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_SHADOW_BIAS_SPLIT_SCALE); ADD_PROPERTY(PropertyInfo(Variant::INT, "directional_shadow_depth_range", PROPERTY_HINT_ENUM, "Stable,Optimized"), "set_shadow_depth_range", "get_shadow_depth_range"); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "directional_shadow_max_distance", PROPERTY_HINT_RANGE, "0,65536,0.1"), "set_param", "get_param", PARAM_SHADOW_MAX_DISTANCE); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "directional_shadow_max_distance", PROPERTY_HINT_EXP_RANGE, "0,8192,0.1,or_greater"), "set_param", "get_param", PARAM_SHADOW_MAX_DISTANCE); BIND_ENUM_CONSTANT(SHADOW_ORTHOGONAL); BIND_ENUM_CONSTANT(SHADOW_PARALLEL_2_SPLITS); @@ -427,8 +428,8 @@ void OmniLight::_bind_methods() { ClassDB::bind_method(D_METHOD("get_shadow_detail"), &OmniLight::get_shadow_detail); ADD_GROUP("Omni", "omni_"); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "omni_range", PROPERTY_HINT_RANGE, "0,65536,0.1"), "set_param", "get_param", PARAM_RANGE); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "omni_attenuation", PROPERTY_HINT_EXP_EASING), "set_param", "get_param", PARAM_ATTENUATION); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "omni_range", PROPERTY_HINT_EXP_RANGE, "0,4096,0.1,or_greater"), "set_param", "get_param", PARAM_RANGE); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "omni_attenuation", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_param", "get_param", PARAM_ATTENUATION); ADD_PROPERTY(PropertyInfo(Variant::INT, "omni_shadow_mode", PROPERTY_HINT_ENUM, "Dual Paraboloid,Cube"), "set_shadow_mode", "get_shadow_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "omni_shadow_detail", PROPERTY_HINT_ENUM, "Vertical,Horizontal"), "set_shadow_detail", "get_shadow_detail"); @@ -449,8 +450,8 @@ OmniLight::OmniLight() : void SpotLight::_bind_methods() { ADD_GROUP("Spot", "spot_"); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "spot_range", PROPERTY_HINT_RANGE, "0,65536,0.1"), "set_param", "get_param", PARAM_RANGE); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "spot_attenuation", PROPERTY_HINT_EXP_EASING), "set_param", "get_param", PARAM_ATTENUATION); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "spot_range", PROPERTY_HINT_EXP_RANGE, "0,4096,0.1,or_greater"), "set_param", "get_param", PARAM_RANGE); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "spot_attenuation", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_param", "get_param", PARAM_ATTENUATION); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "spot_angle", PROPERTY_HINT_RANGE, "0,180,0.1"), "set_param", "get_param", PARAM_SPOT_ANGLE); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "spot_angle_attenuation", PROPERTY_HINT_EXP_EASING), "set_param", "get_param", PARAM_SPOT_ATTENUATION); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "spot_angle_attenuation", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_param", "get_param", PARAM_SPOT_ATTENUATION); } diff --git a/scene/3d/light.h b/scene/3d/light.h index 7ba25731d9..b35b397ced 100644 --- a/scene/3d/light.h +++ b/scene/3d/light.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 51cf9fb89b..439c1f8c45 100644 --- a/scene/3d/listener.cpp +++ b/scene/3d/listener.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 2c800443b4..8047971ebd 100644 --- a/scene/3d/listener.h +++ b/scene/3d/listener.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 1e52ccc6e0..80bae911d4 100644 --- a/scene/3d/mesh_instance.cpp +++ b/scene/3d/mesh_instance.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 970a10aaf3..5d359cd4d5 100644 --- a/scene/3d/mesh_instance.h +++ b/scene/3d/mesh_instance.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 ce7b97be7f..4cbd1df64a 100644 --- a/scene/3d/multimesh_instance.cpp +++ b/scene/3d/multimesh_instance.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 9b2b1ff9a7..1cd077a0a5 100644 --- a/scene/3d/multimesh_instance.h +++ b/scene/3d/multimesh_instance.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 b6507aedb3..77bf703706 100644 --- a/scene/3d/navigation.cpp +++ b/scene/3d/navigation.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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(); @@ -202,7 +204,7 @@ void Navigation::_navmesh_unlink(int p_id) { nm.linked = false; } -int Navigation::navmesh_create(const Ref<NavigationMesh> &p_mesh, const Transform &p_xform, Object *p_owner) { +int Navigation::navmesh_add(const Ref<NavigationMesh> &p_mesh, const Transform &p_xform, Object *p_owner) { int id = last_id++; NavMesh nm; @@ -686,7 +688,7 @@ Vector3 Navigation::get_up_vector() const { void Navigation::_bind_methods() { - ClassDB::bind_method(D_METHOD("navmesh_create", "mesh", "xform", "owner"), &Navigation::navmesh_create, DEFVAL(Variant())); + ClassDB::bind_method(D_METHOD("navmesh_add", "mesh", "xform", "owner"), &Navigation::navmesh_add, DEFVAL(Variant())); ClassDB::bind_method(D_METHOD("navmesh_set_transform", "id", "xform"), &Navigation::navmesh_set_transform); ClassDB::bind_method(D_METHOD("navmesh_remove", "id"), &Navigation::navmesh_remove); diff --git a/scene/3d/navigation.h b/scene/3d/navigation.h index d9a38f7b00..5a501039c8 100644 --- a/scene/3d/navigation.h +++ b/scene/3d/navigation.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 @@ -166,7 +167,7 @@ public: Vector3 get_up_vector() const; //API should be as dynamic as possible - int navmesh_create(const Ref<NavigationMesh> &p_mesh, const Transform &p_xform, Object *p_owner = NULL); + int navmesh_add(const Ref<NavigationMesh> &p_mesh, const Transform &p_xform, Object *p_owner = NULL); void navmesh_set_transform(int p_id, const Transform &p_xform); void navmesh_remove(int p_id); diff --git a/scene/3d/navigation_mesh.cpp b/scene/3d/navigation_mesh.cpp index 40750cdfe8..073e56fdb4 100644 --- a/scene/3d/navigation_mesh.cpp +++ b/scene/3d/navigation_mesh.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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"); @@ -471,7 +472,7 @@ void NavigationMeshInstance::set_enabled(bool p_enabled) { if (navmesh.is_valid()) { - nav_id = navigation->navmesh_create(navmesh, get_relative_transform(navigation), this); + nav_id = navigation->navmesh_add(navmesh, get_relative_transform(navigation), this); } } } @@ -508,7 +509,7 @@ void NavigationMeshInstance::_notification(int p_what) { if (enabled && navmesh.is_valid()) { - nav_id = navigation->navmesh_create(navmesh, get_relative_transform(navigation), this); + nav_id = navigation->navmesh_add(navmesh, get_relative_transform(navigation), this); } break; } @@ -568,7 +569,7 @@ void NavigationMeshInstance::set_navigation_mesh(const Ref<NavigationMesh> &p_na navmesh = p_navmesh; if (navigation && navmesh.is_valid() && enabled) { - nav_id = navigation->navmesh_create(navmesh, get_relative_transform(navigation), this); + nav_id = navigation->navmesh_add(navmesh, get_relative_transform(navigation), this); } if (debug_view && navmesh.is_valid()) { diff --git a/scene/3d/navigation_mesh.h b/scene/3d/navigation_mesh.h index dd5ed79500..753ad76edc 100644 --- a/scene/3d/navigation_mesh.h +++ b/scene/3d/navigation_mesh.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 821f1a5a78..7b5eb8ebc3 100644 --- a/scene/3d/particles.cpp +++ b/scene/3d/particles.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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" @@ -42,8 +43,7 @@ PoolVector<Face3> Particles::get_faces(uint32_t p_usage_flags) const { void Particles::set_emitting(bool p_emitting) { - emitting = p_emitting; - VS::get_singleton()->particles_set_emitting(particles, emitting); + VS::get_singleton()->particles_set_emitting(particles, p_emitting); } void Particles::set_amount(int p_amount) { @@ -63,7 +63,7 @@ void Particles::set_one_shot(bool p_one_shot) { one_shot = p_one_shot; VS::get_singleton()->particles_set_one_shot(particles, one_shot); - if (!one_shot && emitting) + if (!one_shot && is_emitting()) VisualServer::get_singleton()->particles_restart(particles); } @@ -113,7 +113,7 @@ void Particles::set_speed_scale(float p_scale) { bool Particles::is_emitting() const { - return emitting; + return VS::get_singleton()->particles_get_emitting(particles); } int Particles::get_amount() const { @@ -324,11 +324,11 @@ void Particles::_bind_methods() { ClassDB::bind_method(D_METHOD("capture_aabb"), &Particles::capture_aabb); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,100000,1"), "set_amount", "get_amount"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_EXP_RANGE, "1,1000000,1"), "set_amount", "get_amount"); ADD_GROUP("Time", ""); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01"), "set_lifetime", "get_lifetime"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "lifetime", PROPERTY_HINT_EXP_RANGE, "0.01,600.0,0.01"), "set_lifetime", "get_lifetime"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "preprocess", PROPERTY_HINT_RANGE, "0.00,600.0,0.01"), "set_pre_process_time", "get_pre_process_time"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "preprocess", PROPERTY_HINT_EXP_RANGE, "0.00,600.0,0.01"), "set_pre_process_time", "get_pre_process_time"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "speed_scale", PROPERTY_HINT_RANGE, "0.01,64,0.01"), "set_speed_scale", "get_speed_scale"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness_ratio", "get_explosiveness_ratio"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_randomness_ratio", "get_randomness_ratio"); @@ -598,6 +598,11 @@ void ParticlesMaterial::_update_shader() { code += "}\n"; code += "\n"; + code += "float rand_from_seed_m1_p1(inout uint seed) {\n"; + code += " return rand_from_seed(seed)*2.0-1.0;\n"; + code += "}\n"; + code += "\n"; + //improve seed quality code += "uint hash(uint x) {\n"; code += " x = ((x >> uint(16)) ^ x) * uint(73244475);\n"; @@ -614,6 +619,8 @@ void ParticlesMaterial::_update_shader() { code += " float scale_rand = rand_from_seed(alt_seed);\n"; code += " float hue_rot_rand = rand_from_seed(alt_seed);\n"; code += " float anim_offset_rand = rand_from_seed(alt_seed);\n"; + code += " float pi = 3.14159;\n"; + code += " float degree_to_rad = pi / 180.0;\n"; code += "\n"; if (emission_shape >= EMISSION_SHAPE_POINTS) { @@ -638,23 +645,28 @@ void ParticlesMaterial::_update_shader() { else code += " float tex_anim_offset = 0.0;\n"; + code += " float spread_rad = spread*degree_to_rad;\n"; + if (flags[FLAG_DISABLE_Z]) { - code += " float angle1 = (rand_from_seed(alt_seed)*2.0-1.0)*spread/180.0*3.1416;\n"; - code += " vec3 rot = vec3( cos(angle1), sin(angle1),0.0 );\n"; + code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed)*spread_rad;\n"; + code += " vec3 rot = vec3( cos(angle1_rad), sin(angle1_rad),0.0 );\n"; code += " VELOCITY = rot*initial_linear_velocity*mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n"; } else { //initiate velocity spread in 3D - code += " float angle1 = rand_from_seed(alt_seed)*spread*3.1416;\n"; - code += " float angle2 = rand_from_seed(alt_seed)*20.0*3.1416; // make it more random like\n"; - code += " vec3 rot_xz = vec3( sin(angle1), 0.0, cos(angle1) );\n"; - code += " vec3 rot = vec3( cos(angle2)*rot_xz.x,sin(angle2)*rot_xz.x, rot_xz.z);\n"; - code += " VELOCITY = rot*initial_linear_velocity*mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n"; + code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed)*spread_rad;\n"; + code += " float angle2_rad = rand_from_seed_m1_p1(alt_seed)*spread_rad*(1.0-flatness);\n"; + code += " vec3 direction_xz = vec3( sin(angle1_rad), 0, cos(angle1_rad));\n"; + code += " vec3 direction_yz = vec3( 0, sin(angle2_rad), cos(angle2_rad));\n"; + code += " direction_yz.z = direction_yz.z / sqrt(direction_yz.z); //better uniform distribution\n"; + code += " vec3 direction = vec3(direction_xz.x * direction_yz.z, direction_yz.y, direction_xz.z * direction_yz.z);\n"; + code += " direction = normalize(direction);\n"; + code += " VELOCITY = direction*initial_linear_velocity*mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n"; } code += " float base_angle = (initial_angle+tex_angle)*mix(1.0,angle_rand,initial_angle_random);\n"; - code += " CUSTOM.x = base_angle*3.1416/180.0;\n"; //angle + code += " CUSTOM.x = base_angle*degree_to_rad;\n"; //angle code += " CUSTOM.y = 0.0;\n"; //phase code += " CUSTOM.z = (anim_offset+tex_anim_offset)*mix(1.0,anim_offset_rand,anim_offset_random);\n"; //animation offset (0-1) switch (emission_shape) { @@ -777,7 +789,7 @@ void ParticlesMaterial::_update_shader() { code += " float orbit_amount = (orbit_velocity+tex_orbit_velocity)*mix(1.0,rand_from_seed(alt_seed),orbit_velocity_random);\n"; code += " if (orbit_amount!=0.0) {\n"; - code += " float ang = orbit_amount * DELTA * 3.1416 * 2.0;\n"; + code += " float ang = orbit_amount * DELTA * pi * 2.0;\n"; code += " mat2 rot = mat2(vec2(cos(ang),-sin(ang)),vec2(sin(ang),cos(ang)));\n"; code += " TRANSFORM[3].xy-=diff.xy;\n"; code += " TRANSFORM[3].xy+=rot * diff.xy;\n"; @@ -800,7 +812,7 @@ void ParticlesMaterial::_update_shader() { code += " }\n"; code += " float base_angle = (initial_angle+tex_angle)*mix(1.0,angle_rand,initial_angle_random);\n"; code += " base_angle += CUSTOM.y*LIFETIME*(angular_velocity+tex_angular_velocity)*mix(1.0,rand_from_seed(alt_seed)*2.0-1.0,angular_velocity_random);\n"; - code += " CUSTOM.x = base_angle*3.1416/180.0;\n"; //angle + code += " CUSTOM.x = base_angle*degree_to_rad;\n"; //angle code += " CUSTOM.z = (anim_offset+tex_anim_offset)*mix(1.0,anim_offset_rand,anim_offset_random)+CUSTOM.y*(anim_speed+tex_anim_speed)*mix(1.0,rand_from_seed(alt_seed),anim_speed_random);\n"; //angle if (flags[FLAG_ANIM_LOOP]) { code += " CUSTOM.z = mod(CUSTOM.z,1.0);\n"; //loop @@ -821,7 +833,7 @@ void ParticlesMaterial::_update_shader() { else code += " float tex_hue_variation = 0.0;\n"; - code += " float hue_rot_angle = (hue_variation+tex_hue_variation)*3.1416*2.0*mix(1.0,hue_rot_rand*2.0-1.0,hue_variation_random);\n"; + code += " float hue_rot_angle = (hue_variation+tex_hue_variation)*pi*2.0*mix(1.0,hue_rot_rand*2.0-1.0,hue_variation_random);\n"; code += " float hue_rot_c = cos(hue_rot_angle);\n"; code += " float hue_rot_s = sin(hue_rot_angle);\n"; code += " mat4 hue_rot_mat = mat4( vec4(0.299, 0.587, 0.114, 0.0),\n"; @@ -837,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"; @@ -1016,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 { @@ -1070,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 { @@ -1148,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(); @@ -1221,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(); } @@ -1304,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(); } @@ -1319,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(); } @@ -1469,26 +1460,26 @@ void ParticlesMaterial::_bind_methods() { ADD_GROUP("Gravity", ""); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity"), "set_gravity", "get_gravity"); ADD_GROUP("Initial Velocity", "initial_"); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "initial_velocity", PROPERTY_HINT_RANGE, "0,1000,0.01"), "set_param", "get_param", PARAM_INITIAL_LINEAR_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "initial_velocity", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param", "get_param", PARAM_INITIAL_LINEAR_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "initial_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_INITIAL_LINEAR_VELOCITY); ADD_GROUP("Angular Velocity", "angular_"); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_velocity", PROPERTY_HINT_RANGE, "-360,360,0.01"), "set_param", "get_param", PARAM_ANGULAR_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANGULAR_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angular_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANGULAR_VELOCITY); ADD_GROUP("Orbit Velocity", "orbit_"); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "orbit_velocity", PROPERTY_HINT_RANGE, "-1000,1000,0.01"), "set_param", "get_param", PARAM_ORBIT_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "orbit_velocity", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_ORBIT_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "orbit_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ORBIT_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "orbit_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ORBIT_VELOCITY); ADD_GROUP("Linear Accel", "linear_"); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_accel", PROPERTY_HINT_RANGE, "-100,100,0.01"), "set_param", "get_param", PARAM_LINEAR_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_LINEAR_ACCEL); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_LINEAR_ACCEL); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "linear_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_LINEAR_ACCEL); ADD_GROUP("Radial Accel", "radial_"); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "radial_accel", PROPERTY_HINT_RANGE, "-100,100,0.01"), "set_param", "get_param", PARAM_RADIAL_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "radial_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_RADIAL_ACCEL); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "radial_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_RADIAL_ACCEL); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "radial_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_RADIAL_ACCEL); ADD_GROUP("Tangential Accel", "tangential_"); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "tangential_accel", PROPERTY_HINT_RANGE, "-100,100,0.01"), "set_param", "get_param", PARAM_TANGENTIAL_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "tangential_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_TANGENTIAL_ACCEL); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "tangential_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_TANGENTIAL_ACCEL); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "tangential_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_TANGENTIAL_ACCEL); ADD_GROUP("Damping", ""); @@ -1496,11 +1487,11 @@ void ParticlesMaterial::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::REAL, "damping_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_DAMPING); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "damping_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_DAMPING); ADD_GROUP("Angle", ""); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angle", PROPERTY_HINT_RANGE, "-720,720,0.1"), "set_param", "get_param", PARAM_ANGLE); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angle", PROPERTY_HINT_RANGE, "-720,720,0.1,or_lesser,or_greater"), "set_param", "get_param", PARAM_ANGLE); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angle_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANGLE); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angle_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANGLE); ADD_GROUP("Scale", ""); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "scale", PROPERTY_HINT_RANGE, "0,1000,0.01"), "set_param", "get_param", PARAM_SCALE); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "scale", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param", "get_param", PARAM_SCALE); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "scale_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_SCALE); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "scale_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_SCALE); ADD_GROUP("Color", ""); @@ -1512,7 +1503,7 @@ void ParticlesMaterial::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::REAL, "hue_variation_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_HUE_VARIATION); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "hue_variation_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_HUE_VARIATION); ADD_GROUP("Animation", "anim_"); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_speed", PROPERTY_HINT_RANGE, "0,128,0.01"), "set_param", "get_param", PARAM_ANIM_SPEED); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_speed", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater"), "set_param", "get_param", PARAM_ANIM_SPEED); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_speed_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANIM_SPEED); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_speed_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANIM_SPEED); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_offset", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_ANIM_OFFSET); @@ -1585,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 5b8121e937..17e21c6cee 100644 --- a/scene/3d/particles.h +++ b/scene/3d/particles.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 @@ -57,7 +58,6 @@ public: private: RID particles; - bool emitting; bool one_shot; int amount; float lifetime; diff --git a/scene/3d/path.cpp b/scene/3d/path.cpp index 65f20210e1..154dcb4259 100644 --- a/scene/3d/path.cpp +++ b/scene/3d/path.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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_EXP_RANGE, "0,10000,0.01,or_greater"), "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 52760e0c75..2ed686ac3c 100644 --- a/scene/3d/path.h +++ b/scene/3d/path.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 c5f817d317..5056fb2fe4 100644 --- a/scene/3d/physics_body.cpp +++ b/scene/3d/physics_body.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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,1112 @@ 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,or_lesser,or_greater")); + 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(); + _reset_physics_simulation_state(); + 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("is_static_body"), &PhysicalBone::is_static_body); + + 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::TRANSFORM, "body_offset"), "set_body_offset", "get_body_offset"); + + 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"); + + BIND_ENUM_CONSTANT(JOINT_TYPE_NONE); + BIND_ENUM_CONSTANT(JOINT_TYPE_PIN); + BIND_ENUM_CONSTANT(JOINT_TYPE_CONE); + BIND_ENUM_CONSTANT(JOINT_TYPE_HINGE); + BIND_ENUM_CONSTANT(JOINT_TYPE_SLIDER); + BIND_ENUM_CONSTANT(JOINT_TYPE_6DOF); +} + +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(false), + _internal_simulate_physics(false), + 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 9d9feda0b2..17d2769c79 100644 --- a/scene/3d/physics_body.h +++ b/scene/3d/physics_body.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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 1d779d31fe..b2d10006f7 100644 --- a/scene/3d/physics_joint.cpp +++ b/scene/3d/physics_joint.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 b94297da30..37870d6f30 100644 --- a/scene/3d/physics_joint.h +++ b/scene/3d/physics_joint.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 4fde29aab9..d16d9ed7c5 100644 --- a/scene/3d/portal.cpp +++ b/scene/3d/portal.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 4ea208a718..cb3f208072 100644 --- a/scene/3d/portal.h +++ b/scene/3d/portal.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 @@ -39,7 +40,8 @@ If a portal is placed next (very close to) a similar, opposing portal, they automatically connect, otherwise, a portal connects to the parent room */ -//this will be redone and replaced by area portals, left for reference since a new class with this name will have to exist and want to reuse the gizmos +// FIXME: This will be redone and replaced by area portals, left for reference +// since a new class with this name will have to exist and want to reuse the gizmos #if 0 class Portal : public VisualInstance { diff --git a/scene/3d/position_3d.cpp b/scene/3d/position_3d.cpp index d0df985ac4..3ae04f491f 100644 --- a/scene/3d/position_3d.cpp +++ b/scene/3d/position_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 8083c33196..2bc69eff9b 100644 --- a/scene/3d/position_3d.h +++ b/scene/3d/position_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 2288d8b08b..101d9ed70c 100644 --- a/scene/3d/proximity_group.cpp +++ b/scene/3d/proximity_group.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 d003d2f525..448f30bf80 100644 --- a/scene/3d/proximity_group.h +++ b/scene/3d/proximity_group.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 faeb18691a..7f83e2c3ea 100644 --- a/scene/3d/ray_cast.cpp +++ b/scene/3d/ray_cast.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 9fb1a1be67..20cea80700 100644 --- a/scene/3d/ray_cast.h +++ b/scene/3d/ray_cast.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 0e575ec152..7e3a87cbd4 100644 --- a/scene/3d/reflection_probe.cpp +++ b/scene/3d/reflection_probe.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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; } } } @@ -239,7 +240,7 @@ void ReflectionProbe::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode", PROPERTY_HINT_ENUM, "Once,Always"), "set_update_mode", "get_update_mode"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "intensity", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_intensity", "get_intensity"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_distance", PROPERTY_HINT_RANGE, "0,16384,0.1"), "set_max_distance", "get_max_distance"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_distance", PROPERTY_HINT_EXP_RANGE, "0,16384,0.1,or_greater"), "set_max_distance", "get_max_distance"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "extents"), "set_extents", "get_extents"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "origin_offset"), "set_origin_offset", "get_origin_offset"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "box_projection"), "set_enable_box_projection", "is_box_projection_enabled"); diff --git a/scene/3d/reflection_probe.h b/scene/3d/reflection_probe.h index 26f17fdcf9..13ae1c81f6 100644 --- a/scene/3d/reflection_probe.h +++ b/scene/3d/reflection_probe.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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.cpp b/scene/3d/remote_transform.cpp index 8faf985b11..afb85f7314 100644 --- a/scene/3d/remote_transform.cpp +++ b/scene/3d/remote_transform.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/scene/3d/remote_transform.h b/scene/3d/remote_transform.h index 51a0bf35a3..6b788a2d75 100644 --- a/scene/3d/remote_transform.h +++ b/scene/3d/remote_transform.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 47a7b8bfb9..0d83a9942e 100644 --- a/scene/3d/room_instance.cpp +++ b/scene/3d/room_instance.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 3069ea2eba..ff388e606c 100644 --- a/scene/3d/room_instance.h +++ b/scene/3d/room_instance.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 @@ -44,7 +45,7 @@ */ -//this will be removed, left for reference +// FIXME: this will be removed, left for reference #if 0 class Room : public VisualInstance { diff --git a/scene/3d/scenario_fx.cpp b/scene/3d/scenario_fx.cpp index 8102b3f15c..26cbfc0b11 100644 --- a/scene/3d/scenario_fx.cpp +++ b/scene/3d/scenario_fx.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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,11 @@ String WorldEnvironment::get_configuration_warning() const { return TTR("Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."); } + // Commenting this warning for now, I think it makes no sense. If anyone can figure out what its supposed to do, feedback welcome. Else it should be deprecated. + //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 acf6a18526..7a8e2b548f 100644 --- a/scene/3d/scenario_fx.h +++ b/scene/3d/scenario_fx.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 d0e0937eca..76d90dc6ff 100644 --- a/scene/3d/skeleton.cpp +++ b/scene/3d/skeleton.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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")); } } @@ -153,6 +155,24 @@ void Skeleton::_notification(int p_what) { case NOTIFICATION_EXIT_WORLD: { } break; + case NOTIFICATION_TRANSFORM_CHANGED: { + + if (dirty) + break; //will be eventually updated + + //if moved, just update transforms + VisualServer *vs = VisualServer::get_singleton(); + 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++) { + + const Bone &b = bonesptr[i]; + vs->skeleton_bone_set_transform(skeleton, i, global_transform * (b.transform_final * global_transform_inverse)); + } + } break; case NOTIFICATION_UPDATE_SKELETON: { VisualServer *vs = VisualServer::get_singleton(); @@ -180,6 +200,9 @@ void Skeleton::_notification(int p_what) { rest_global_inverse_dirty = false; } + 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]; @@ -239,7 +262,8 @@ void Skeleton::_notification(int p_what) { } } - vs->skeleton_bone_set_transform(skeleton, i, b.pose_global * b.rest_global_inverse); + b.transform_final = b.pose_global * b.rest_global_inverse; + vs->skeleton_bone_set_transform(skeleton, i, global_transform * (b.transform_final * global_transform_inverse)); for (List<uint32_t>::Element *E = b.nodes_bound.front(); E; E = E->next()) { @@ -306,7 +330,7 @@ void Skeleton::add_bone(const String &p_name) { _make_dirty(); update_gizmo(); } -int Skeleton::find_bone(String p_name) const { +int Skeleton::find_bone(const String &p_name) const { for (int i = 0; i < bones.size(); i++) { @@ -323,6 +347,19 @@ String Skeleton::get_bone_name(int p_bone) const { return bones[p_bone].name; } +bool Skeleton::is_bone_parent_of(int p_bone, int p_parent_bone_id) const { + + int parent_of_bone = get_bone_parent(p_bone); + + if (-1 == parent_of_bone) + return false; + + if (parent_of_bone == p_parent_bone_id) + return true; + + return is_bone_parent_of(parent_of_bone, p_parent_bone_id); +} + int Skeleton::get_bone_count() const { return bones.size(); @@ -354,6 +391,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()); @@ -499,6 +547,150 @@ void Skeleton::localize_rests() { } } +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 _pb_stop_simulation(Node *p_node) { + + for (int i = p_node->get_child_count() - 1; 0 <= i; --i) { + _pb_stop_simulation(p_node->get_child(i)); + } + + PhysicalBone *pb = Object::cast_to<PhysicalBone>(p_node); + if (pb) { + pb->set_simulate_physics(false); + pb->set_static_body(false); + } +} + +void Skeleton::physical_bones_stop_simulation() { + _pb_stop_simulation(this); +} + +void _pb_start_simulation(const Skeleton *p_skeleton, Node *p_node, const Vector<int> &p_sim_bones) { + + for (int i = p_node->get_child_count() - 1; 0 <= i; --i) { + _pb_start_simulation(p_skeleton, p_node->get_child(i), p_sim_bones); + } + + PhysicalBone *pb = Object::cast_to<PhysicalBone>(p_node); + if (pb) { + bool sim = false; + for (int i = p_sim_bones.size() - 1; 0 <= i; --i) { + if (p_sim_bones[i] == pb->get_bone_id() || p_skeleton->is_bone_parent_of(pb->get_bone_id(), p_sim_bones[i])) { + sim = true; + break; + } + } + + pb->set_simulate_physics(true); + if (sim) { + pb->set_static_body(false); + } else { + pb->set_static_body(true); + } + } +} + +void Skeleton::physical_bones_start_simulation_on(const Array &p_bones) { + + Vector<int> sim_bones; + if (p_bones.size() <= 0) { + sim_bones.push_back(0); // if no bones is specified, activate ragdoll on full body + } else { + sim_bones.resize(p_bones.size()); + int c = 0; + for (int i = sim_bones.size() - 1; 0 <= i; --i) { + if (Variant::STRING == p_bones.get(i).get_type()) { + int bone_id = find_bone(p_bones.get(i)); + if (bone_id != -1) + sim_bones[c++] = bone_id; + } + } + sim_bones.resize(c); + } + + _pb_start_simulation(this, this, sim_bones); +} + +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); @@ -535,6 +727,11 @@ 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_stop_simulation"), &Skeleton::physical_bones_stop_simulation); + ClassDB::bind_method(D_METHOD("physical_bones_start_simulation", "bones"), &Skeleton::physical_bones_start_simulation_on, DEFVAL(Array())); + 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); } @@ -543,9 +740,9 @@ Skeleton::Skeleton() { rest_global_inverse_dirty = true; dirty = false; skeleton = VisualServer::get_singleton()->skeleton_create(); + set_notify_transform(true); } Skeleton::~Skeleton() { - VisualServer::get_singleton()->free(skeleton); } diff --git a/scene/3d/skeleton.h b/scene/3d/skeleton.h index fdc1100472..dad11960a5 100644 --- a/scene/3d/skeleton.h +++ b/scene/3d/skeleton.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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; @@ -57,13 +62,21 @@ class Skeleton : public Spatial { bool custom_pose_enable; Transform custom_pose; + 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; } }; @@ -80,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; } @@ -107,14 +120,19 @@ public: // skeleton creation api void add_bone(const String &p_name); - int find_bone(String p_name) const; + int find_bone(const String &p_name) const; String get_bone_name(int p_bone) const; + bool is_bone_parent_of(int p_bone_id, int p_parent_bone_id) const; + void set_bone_parent(int p_bone, int p_parent); int get_bone_parent(int p_bone) const; 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; @@ -146,6 +164,26 @@ 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_stop_simulation(); + void physical_bones_start_simulation_on(const Array &p_bones); + 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 d9f88ac693..748aa8aad4 100644 --- a/scene/3d/spatial.cpp +++ b/scene/3d/spatial.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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; @@ -555,30 +569,36 @@ bool Spatial::is_visible() const { return data.visible; } -void Spatial::rotate(const Vector3 &p_normal, float p_radians) { +void Spatial::rotate_object_local(const Vector3 &p_axis, float p_angle) { + Transform t = get_transform(); + t.basis.rotate_local(p_axis, p_angle); + set_transform(t); +} + +void Spatial::rotate(const Vector3 &p_axis, float p_angle) { Transform t = get_transform(); - t.basis.rotate(p_normal, p_radians); + t.basis.rotate(p_axis, p_angle); set_transform(t); } -void Spatial::rotate_x(float p_radians) { +void Spatial::rotate_x(float p_angle) { Transform t = get_transform(); - t.basis.rotate(Vector3(1, 0, 0), p_radians); + t.basis.rotate(Vector3(1, 0, 0), p_angle); set_transform(t); } -void Spatial::rotate_y(float p_radians) { +void Spatial::rotate_y(float p_angle) { Transform t = get_transform(); - t.basis.rotate(Vector3(0, 1, 0), p_radians); + t.basis.rotate(Vector3(0, 1, 0), p_angle); set_transform(t); } -void Spatial::rotate_z(float p_radians) { +void Spatial::rotate_z(float p_angle) { Transform t = get_transform(); - t.basis.rotate(Vector3(0, 0, 1), p_radians); + t.basis.rotate(Vector3(0, 0, 1), p_angle); set_transform(t); } @@ -589,19 +609,45 @@ void Spatial::translate(const Vector3 &p_offset) { set_transform(t); } +void Spatial::translate_object_local(const Vector3 &p_offset) { + Transform t = get_transform(); + + Transform s; + s.translate(p_offset); + set_transform(t * s); +} + void Spatial::scale(const Vector3 &p_ratio) { Transform t = get_transform(); t.basis.scale(p_ratio); set_transform(t); } -void Spatial::global_rotate(const Vector3 &p_normal, float p_radians) { - Basis rotation(p_normal, p_radians); +void Spatial::scale_object_local(const Vector3 &p_scale) { + Transform t = get_transform(); + t.basis.scale_local(p_scale); + set_transform(t); +} + +void Spatial::global_rotate(const Vector3 &p_axis, float p_angle) { + + Basis rotation(p_axis, p_angle); Transform t = get_global_transform(); t.basis = rotation * t.basis; set_global_transform(t); } + +void Spatial::global_scale(const Vector3 &p_scale) { + + Basis s; + s.set_scale(p_scale); + + Transform t = get_global_transform(); + t.basis = s * t.basis; + set_global_transform(t); +} + void Spatial::global_translate(const Vector3 &p_offset) { Transform t = get_global_transform(); t.origin += p_offset; @@ -620,7 +666,7 @@ void Spatial::set_identity() { set_transform(Transform()); } -void Spatial::look_at(const Vector3 &p_target, const Vector3 &p_up_normal) { +void Spatial::look_at(const Vector3 &p_target, const Vector3 &p_up) { Transform lookat; lookat.origin = get_global_transform().origin; @@ -629,19 +675,19 @@ void Spatial::look_at(const Vector3 &p_target, const Vector3 &p_up_normal) { ERR_FAIL(); } - if (p_up_normal.cross(p_target - lookat.origin) == Vector3()) { + if (p_up.cross(p_target - lookat.origin) == Vector3()) { ERR_EXPLAIN("Up vector and direction between node origin and target are aligned, look_at() failed"); ERR_FAIL(); } - lookat = lookat.looking_at(p_target, p_up_normal); + lookat = lookat.looking_at(p_target, p_up); set_global_transform(lookat); } -void Spatial::look_at_from_position(const Vector3 &p_pos, const Vector3 &p_target, const Vector3 &p_up_normal) { +void Spatial::look_at_from_position(const Vector3 &p_pos, const Vector3 &p_target, const Vector3 &p_up) { Transform lookat; lookat.origin = p_pos; - lookat = lookat.looking_at(p_target, p_up_normal); + lookat = lookat.looking_at(p_target, p_up); set_global_transform(lookat); } @@ -677,9 +723,9 @@ void Spatial::_bind_methods() { ClassDB::bind_method(D_METHOD("get_transform"), &Spatial::get_transform); ClassDB::bind_method(D_METHOD("set_translation", "translation"), &Spatial::set_translation); ClassDB::bind_method(D_METHOD("get_translation"), &Spatial::get_translation); - ClassDB::bind_method(D_METHOD("set_rotation", "radians"), &Spatial::set_rotation); + ClassDB::bind_method(D_METHOD("set_rotation", "euler"), &Spatial::set_rotation); ClassDB::bind_method(D_METHOD("get_rotation"), &Spatial::get_rotation); - ClassDB::bind_method(D_METHOD("set_rotation_degrees", "degrees"), &Spatial::set_rotation_degrees); + ClassDB::bind_method(D_METHOD("set_rotation_degrees", "euler_degrees"), &Spatial::set_rotation_degrees); ClassDB::bind_method(D_METHOD("get_rotation_degrees"), &Spatial::get_rotation_degrees); ClassDB::bind_method(D_METHOD("set_scale", "scale"), &Spatial::set_scale); ClassDB::bind_method(D_METHOD("get_scale"), &Spatial::get_scale); @@ -691,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); @@ -711,22 +755,26 @@ void Spatial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_notify_transform", "enable"), &Spatial::set_notify_transform); ClassDB::bind_method(D_METHOD("is_transform_notification_enabled"), &Spatial::is_transform_notification_enabled); - void rotate(const Vector3 &p_normal, float p_radians); - void rotate_x(float p_radians); - void rotate_y(float p_radians); - void rotate_z(float p_radians); + void rotate(const Vector3 &p_axis, float p_angle); + void rotate_x(float p_angle); + void rotate_y(float p_angle); + void rotate_z(float p_angle); void translate(const Vector3 &p_offset); void scale(const Vector3 &p_ratio); - void global_rotate(const Vector3 &p_normal, float p_radians); + void global_rotate(const Vector3 &p_axis, float p_angle); void global_translate(const Vector3 &p_offset); - ClassDB::bind_method(D_METHOD("rotate", "normal", "radians"), &Spatial::rotate); - ClassDB::bind_method(D_METHOD("global_rotate", "normal", "radians"), &Spatial::global_rotate); - ClassDB::bind_method(D_METHOD("rotate_x", "radians"), &Spatial::rotate_x); - ClassDB::bind_method(D_METHOD("rotate_y", "radians"), &Spatial::rotate_y); - ClassDB::bind_method(D_METHOD("rotate_z", "radians"), &Spatial::rotate_z); - ClassDB::bind_method(D_METHOD("translate", "offset"), &Spatial::translate); + ClassDB::bind_method(D_METHOD("rotate", "axis", "angle"), &Spatial::rotate); + ClassDB::bind_method(D_METHOD("global_rotate", "axis", "angle"), &Spatial::global_rotate); + ClassDB::bind_method(D_METHOD("global_scale", "scale"), &Spatial::global_scale); ClassDB::bind_method(D_METHOD("global_translate", "offset"), &Spatial::global_translate); + ClassDB::bind_method(D_METHOD("rotate_object_local", "axis", "angle"), &Spatial::rotate_object_local); + ClassDB::bind_method(D_METHOD("scale_object_local", "scale"), &Spatial::scale_object_local); + ClassDB::bind_method(D_METHOD("translate_object_local", "offset"), &Spatial::translate_object_local); + ClassDB::bind_method(D_METHOD("rotate_x", "angle"), &Spatial::rotate_x); + ClassDB::bind_method(D_METHOD("rotate_y", "angle"), &Spatial::rotate_y); + ClassDB::bind_method(D_METHOD("rotate_z", "angle"), &Spatial::rotate_z); + ClassDB::bind_method(D_METHOD("translate", "offset"), &Spatial::translate); ClassDB::bind_method(D_METHOD("orthonormalize"), &Spatial::orthonormalize); ClassDB::bind_method(D_METHOD("set_identity"), &Spatial::set_identity); @@ -751,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 8f53b4a066..a43bed3e4a 100644 --- a/scene/3d/spatial.h +++ b/scene/3d/spatial.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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; @@ -158,17 +162,23 @@ public: Transform get_relative_transform(const Node *p_parent) const; - void rotate(const Vector3 &p_normal, float p_radians); - void rotate_x(float p_radians); - void rotate_y(float p_radians); - void rotate_z(float p_radians); + void rotate(const Vector3 &p_axis, float p_angle); + void rotate_x(float p_angle); + void rotate_y(float p_angle); + void rotate_z(float p_angle); void translate(const Vector3 &p_offset); void scale(const Vector3 &p_ratio); - void global_rotate(const Vector3 &p_normal, float p_radians); + + void rotate_object_local(const Vector3 &p_axis, float p_angle); + void scale_object_local(const Vector3 &p_scale); + void translate_object_local(const Vector3 &p_offset); + + void global_rotate(const Vector3 &p_axis, float p_angle); + void global_scale(const Vector3 &p_scale); void global_translate(const Vector3 &p_offset); - void look_at(const Vector3 &p_target, const Vector3 &p_up_normal); - void look_at_from_position(const Vector3 &p_pos, const Vector3 &p_target, const Vector3 &p_up_normal); + void look_at(const Vector3 &p_target, const Vector3 &p_up); + void look_at_from_position(const Vector3 &p_pos, const Vector3 &p_target, const Vector3 &p_up); Vector3 to_local(Vector3 p_global) const; Vector3 to_global(Vector3 p_local) 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 18ebc22c8b..232855c978 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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" @@ -294,6 +295,7 @@ SpriteBase3D::SpriteBase3D() { for (int i = 0; i < FLAG_MAX; i++) flags[i] = i == FLAG_TRANSPARENT || i == FLAG_DOUBLE_SIDED; + alpha_cut = ALPHA_CUT_DISABLED; axis = Vector3::AXIS_Z; pixel_size = 0.01; modulate = Color(1, 1, 1, 1); diff --git a/scene/3d/sprite_3d.h b/scene/3d/sprite_3d.h index d18553a504..23e1d96b4b 100644 --- a/scene/3d/sprite_3d.h +++ b/scene/3d/sprite_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 2e3e179a7b..ee8d249981 100644 --- a/scene/3d/vehicle_body.cpp +++ b/scene/3d/vehicle_body.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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); @@ -667,13 +672,8 @@ void VehicleBody::_update_friction(PhysicsDirectBodyState *s) { m_forwardImpulse.resize(numWheel); m_sideImpulse.resize(numWheel); - int numWheelsOnGround = 0; - //collapse all those loops into one! for (int i = 0; i < wheels.size(); i++) { - VehicleWheel &wheelInfo = *wheels[i]; - if (wheelInfo.m_raycastInfo.m_isInContact) - numWheelsOnGround++; m_sideImpulse[i] = real_t(0.); m_forwardImpulse[i] = real_t(0.); } @@ -703,7 +703,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 +815,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 +845,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 +880,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 +912,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 +923,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, "engine_force", PROPERTY_HINT_RANGE, "0.00,1024.0,0.01,or_greater"), "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 +941,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 c642eb61b8..1ac3693cc4 100644 --- a/scene/3d/vehicle_body.h +++ b/scene/3d/vehicle_body.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 47144c4b78..9d6e4941f3 100644 --- a/scene/3d/visibility_notifier.cpp +++ b/scene/3d/visibility_notifier.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 fc06cf5aec..b1985f4a0c 100644 --- a/scene/3d/visibility_notifier.h +++ b/scene/3d/visibility_notifier.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 b92e7ead04..00541a7d8a 100644 --- a/scene/3d/visual_instance.cpp +++ b/scene/3d/visual_instance.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 5827f1e1fb..8458a343b2 100644 --- a/scene/3d/visual_instance.h +++ b/scene/3d/visual_instance.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 98dc1590d8..670df5cc7f 100644 --- a/scene/3d/voxel_light_baker.cpp +++ b/scene/3d/voxel_light_baker.cpp @@ -1,5 +1,39 @@ +/*************************************************************************/ +/* voxel_light_baker.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 "voxel_light_baker.h" #include "os/os.h" +#include "os/threaded_array_processor.h" + +#include <stdlib.h> + #define FINDMINMAX(x0, x1, x2, min, max) \ min = max = x0; \ if (x1 < min) min = x1; \ @@ -183,14 +217,23 @@ static bool fast_tri_box_overlap(const Vector3 &boxcenter, const Vector3 boxhalf return true; /* box and triangle overlaps */ } -static _FORCE_INLINE_ Vector2 get_uv(const Vector3 &p_pos, const Vector3 *p_vtx, const Vector2 *p_uv) { +static _FORCE_INLINE_ void get_uv_and_normal(const Vector3 &p_pos, const Vector3 *p_vtx, const Vector2 *p_uv, const Vector3 *p_normal, Vector2 &r_uv, Vector3 &r_normal) { - if (p_pos.distance_squared_to(p_vtx[0]) < CMP_EPSILON2) - return p_uv[0]; - if (p_pos.distance_squared_to(p_vtx[1]) < CMP_EPSILON2) - return p_uv[1]; - if (p_pos.distance_squared_to(p_vtx[2]) < CMP_EPSILON2) - return p_uv[2]; + if (p_pos.distance_squared_to(p_vtx[0]) < CMP_EPSILON2) { + r_uv = p_uv[0]; + r_normal = p_normal[0]; + return; + } + if (p_pos.distance_squared_to(p_vtx[1]) < CMP_EPSILON2) { + r_uv = p_uv[1]; + r_normal = p_normal[1]; + return; + } + if (p_pos.distance_squared_to(p_vtx[2]) < CMP_EPSILON2) { + r_uv = p_uv[2]; + r_normal = p_normal[2]; + return; + } Vector3 v0 = p_vtx[1] - p_vtx[0]; Vector3 v1 = p_vtx[2] - p_vtx[0]; @@ -202,16 +245,20 @@ static _FORCE_INLINE_ Vector2 get_uv(const Vector3 &p_pos, const Vector3 *p_vtx, float d20 = v2.dot(v0); float d21 = v2.dot(v1); float denom = (d00 * d11 - d01 * d01); - if (denom == 0) - return p_uv[0]; + if (denom == 0) { + r_uv = p_uv[0]; + r_normal = p_normal[0]; + return; + } float v = (d11 * d20 - d01 * d21) / denom; float w = (d00 * d21 - d01 * d20) / denom; float u = 1.0f - v - w; - return p_uv[0] * u + p_uv[1] * v + p_uv[2] * w; + r_uv = p_uv[0] * u + p_uv[1] * v + p_uv[2] * w; + r_normal = (p_normal[0] * u + p_normal[1] * v + p_normal[2] * w).normalized(); } -void VoxelLightBaker::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, const Vector3 *p_vtx, const Vector2 *p_uv, const MaterialCache &p_material, const AABB &p_aabb) { +void VoxelLightBaker::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, const Vector3 *p_vtx, const Vector3 *p_normal, const Vector2 *p_uv, const MaterialCache &p_material, const AABB &p_aabb) { if (p_level == cell_subdiv - 1) { //plot the face by guessing it's albedo and emission value @@ -289,7 +336,11 @@ void VoxelLightBaker::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p intersection = Face3(p_vtx[0], p_vtx[1], p_vtx[2]).get_closest_point_to(intersection); - Vector2 uv = get_uv(intersection, p_vtx, p_uv); + Vector2 uv; + Vector3 lnormal; + get_uv_and_normal(intersection, p_vtx, p_uv, p_normal, uv, lnormal); + if (lnormal == Vector3()) //just in case normal as nor provided + lnormal = normal; int uv_x = CLAMP(Math::fposmod(uv.x, 1.0f) * bake_texture_size, 0, bake_texture_size - 1); int uv_y = CLAMP(Math::fposmod(uv.y, 1.0f) * bake_texture_size, 0, bake_texture_size - 1); @@ -304,7 +355,7 @@ void VoxelLightBaker::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p emission_accum.g += p_material.emission[ofs].g; emission_accum.b += p_material.emission[ofs].b; - normal_accum += normal; + normal_accum += lnormal; alpha += 1.0; } @@ -316,7 +367,11 @@ void VoxelLightBaker::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p Face3 f(p_vtx[0], p_vtx[1], p_vtx[2]); Vector3 inters = f.get_closest_point_to(p_aabb.position + p_aabb.size * 0.5); - Vector2 uv = get_uv(inters, p_vtx, p_uv); + Vector3 lnormal; + Vector2 uv; + get_uv_and_normal(inters, p_vtx, p_uv, p_normal, uv, normal); + if (lnormal == Vector3()) //just in case normal as nor provided + lnormal = normal; int uv_x = CLAMP(Math::fposmod(uv.x, 1.0f) * bake_texture_size, 0, bake_texture_size - 1); int uv_y = CLAMP(Math::fposmod(uv.y, 1.0f) * bake_texture_size, 0, bake_texture_size - 1); @@ -334,7 +389,7 @@ void VoxelLightBaker::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p emission_accum.g = p_material.emission[ofs].g * alpha; emission_accum.b = p_material.emission[ofs].b * alpha; - normal_accum *= alpha; + normal_accum = lnormal * alpha; } else { @@ -406,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_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); } } } @@ -539,9 +594,12 @@ void VoxelLightBaker::plot_mesh(const Transform &p_xform, Ref<Mesh> &p_mesh, con PoolVector<Vector3>::Read vr = vertices.read(); PoolVector<Vector2> uv = a[Mesh::ARRAY_TEX_UV]; PoolVector<Vector2>::Read uvr; + PoolVector<Vector3> normals = a[Mesh::ARRAY_NORMAL]; + PoolVector<Vector3>::Read nr; PoolVector<int> index = a[Mesh::ARRAY_INDEX]; bool read_uv = false; + bool read_normals = false; if (uv.size()) { @@ -549,6 +607,11 @@ void VoxelLightBaker::plot_mesh(const Transform &p_xform, Ref<Mesh> &p_mesh, con read_uv = true; } + if (normals.size()) { + read_normals = true; + nr = normals.read(); + } + if (index.size()) { int facecount = index.size() / 3; @@ -558,6 +621,7 @@ void VoxelLightBaker::plot_mesh(const Transform &p_xform, Ref<Mesh> &p_mesh, con Vector3 vtxs[3]; Vector2 uvs[3]; + Vector3 normal[3]; for (int k = 0; k < 3; k++) { vtxs[k] = p_xform.xform(vr[ir[j * 3 + k]]); @@ -569,11 +633,17 @@ void VoxelLightBaker::plot_mesh(const Transform &p_xform, Ref<Mesh> &p_mesh, con } } + if (read_normals) { + for (int k = 0; k < 3; k++) { + normal[k] = nr[ir[j * 3 + k]]; + } + } + //test against original bounds if (!fast_tri_box_overlap(original_bounds.position + original_bounds.size * 0.5, original_bounds.size * 0.5, vtxs)) continue; //plot - _plot_face(0, 0, 0, 0, 0, vtxs, uvs, material, po2_bounds); + _plot_face(0, 0, 0, 0, 0, vtxs, normal, uvs, material, po2_bounds); } } else { @@ -584,6 +654,7 @@ void VoxelLightBaker::plot_mesh(const Transform &p_xform, Ref<Mesh> &p_mesh, con Vector3 vtxs[3]; Vector2 uvs[3]; + Vector3 normal[3]; for (int k = 0; k < 3; k++) { vtxs[k] = p_xform.xform(vr[j * 3 + k]); @@ -595,11 +666,17 @@ void VoxelLightBaker::plot_mesh(const Transform &p_xform, Ref<Mesh> &p_mesh, con } } + if (read_normals) { + for (int k = 0; k < 3; k++) { + normal[k] = nr[j * 3 + k]; + } + } + //test against original bounds if (!fast_tri_box_overlap(original_bounds.position + original_bounds.size * 0.5, original_bounds.size * 0.5, vtxs)) continue; //plot face - _plot_face(0, 0, 0, 0, 0, vtxs, uvs, material, po2_bounds); + _plot_face(0, 0, 0, 0, 0, vtxs, normal, uvs, material, po2_bounds); } } } @@ -623,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; @@ -732,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; @@ -833,11 +910,13 @@ void VoxelLightBaker::plot_light_directional(const Vector3 &p_direction, const C } } - for (int i = 0; i < 6; i++) { - float s = MAX(0.0, aniso_normal[i].dot(-light_axis)); //light depending on normal for direct - light->direct_accum[i][0] += light_energy.x * s; - light->direct_accum[i][1] += light_energy.y * s; - light->direct_accum[i][2] += light_energy.z * s; + if (p_direct) { + for (int i = 0; i < 6; i++) { + float s = MAX(0.0, aniso_normal[i].dot(-light_axis)); //light depending on normal for direct + light->direct_accum[i][0] += light_energy.x * s; + light->direct_accum[i][1] += light_energy.y * s; + light->direct_accum[i][2] += light_energy.z * s; + } } success_count++; } @@ -897,17 +976,7 @@ void VoxelLightBaker::plot_light_omni(const Vector3 &p_pos, const Color &p_color float dt = CLAMP((d + distance_adv) / local_radius, 0, 1); att *= powf(1.0 - dt, p_attenutation); } -#if 0 - if (light_cache.type == VS::LIGHT_SPOT) { - - float angle = Math::rad2deg(acos(light_axis.dot(spot_axis))); - if (angle > light_cache.spot_angle) - continue; - float d = CLAMP(angle / light_cache.spot_angle, 1, 0); - att *= powf(1.0 - d, light_cache.spot_attenuation); - } -#endif clip_planes = 0; for (int c = 0; c < 3; c++) { @@ -972,11 +1041,13 @@ void VoxelLightBaker::plot_light_omni(const Vector3 &p_pos, const Color &p_color } } - for (int i = 0; i < 6; i++) { - float s = MAX(0.0, aniso_normal[i].dot(-light_axis)); //light depending on normal for direct - light->direct_accum[i][0] += light_energy.x * s * att; - light->direct_accum[i][1] += light_energy.y * s * att; - light->direct_accum[i][2] += light_energy.z * s * att; + if (p_direct) { + for (int i = 0; i < 6; i++) { + float s = MAX(0.0, aniso_normal[i].dot(-light_axis)); //light depending on normal for direct + light->direct_accum[i][0] += light_energy.x * s * att; + light->direct_accum[i][1] += light_energy.y * s * att; + light->direct_accum[i][2] += light_energy.z * s * att; + } } } @@ -1041,17 +1112,7 @@ void VoxelLightBaker::plot_light_spot(const Vector3 &p_pos, const Vector3 &p_axi float dt = CLAMP((d + distance_adv) / local_radius, 0, 1); att *= powf(1.0 - dt, p_attenutation); } -#if 0 - if (light_cache.type == VS::LIGHT_SPOT) { - - float angle = Math::rad2deg(acos(light_axis.dot(spot_axis))); - if (angle > light_cache.spot_angle) - continue; - float d = CLAMP(angle / light_cache.spot_angle, 1, 0); - att *= powf(1.0 - d, light_cache.spot_attenuation); - } -#endif clip_planes = 0; for (int c = 0; c < 3; c++) { @@ -1115,11 +1176,13 @@ void VoxelLightBaker::plot_light_spot(const Vector3 &p_pos, const Vector3 &p_axi } } - for (int i = 0; i < 6; i++) { - float s = MAX(0.0, aniso_normal[i].dot(-light_axis)); //light depending on normal for direct - light->direct_accum[i][0] += light_energy.x * s * att; - light->direct_accum[i][1] += light_energy.y * s * att; - light->direct_accum[i][2] += light_energy.z * s * att; + if (p_direct) { + for (int i = 0; i < 6; i++) { + float s = MAX(0.0, aniso_normal[i].dot(-light_axis)); //light depending on normal for direct + light->direct_accum[i][0] += light_energy.x * s * att; + light->direct_accum[i][1] += light_energy.y * s * att; + light->direct_accum[i][2] += light_energy.z * s * att; + } } } @@ -1194,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; @@ -1420,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; @@ -1614,6 +1677,16 @@ Vector3 VoxelLightBaker::_compute_pixel_light_at_pos(const Vector3 &p_pos, const return accum; } +_ALWAYS_INLINE_ uint32_t xorshift32(uint32_t *state) { + /* Algorithm "xor" from p. 4 of Marsaglia, "Xorshift RNGs" */ + uint32_t x = *state; + x ^= x << 13; + x ^= x >> 17; + x ^= x << 5; + *state = x; + return x; +} + Vector3 VoxelLightBaker::_compute_ray_trace_at_pos(const Vector3 &p_pos, const Vector3 &p_normal) { int samples_per_quality[3] = { 48, 128, 512 }; @@ -1636,20 +1709,22 @@ Vector3 VoxelLightBaker::_compute_ray_trace_at_pos(const Vector3 &p_pos, const V const Light *light = bake_light.ptr(); const Cell *cells = bake_cells.ptr(); + uint32_t local_rng_state = rand(); //needs to be fixed again + for (int i = 0; i < samples; i++) { - float random_angle1 = (((Math::rand() % 65535) / 65535.0) * 2.0 - 1.0) * spread; + float random_angle1 = (((xorshift32(&local_rng_state) % 65535) / 65535.0) * 2.0 - 1.0) * spread; Vector3 axis(0, sin(random_angle1), cos(random_angle1)); - float random_angle2 = ((Math::rand() % 65535) / 65535.0) * Math_PI * 2.0; + float random_angle2 = ((xorshift32(&local_rng_state) % 65535) / 65535.0) * Math_PI * 2.0; Basis rot(Vector3(0, 0, 1), random_angle2); axis = rot.xform(axis); Vector3 direction = normal_xform.xform(axis).normalized(); - Vector3 pos = p_pos + Vector3(0.5, 0.5, 0.5) + direction * bias; - Vector3 advance = direction * _get_normal_advance(direction); + Vector3 pos = p_pos /*+ Vector3(0.5, 0.5, 0.5)*/ + advance * bias; + uint32_t cell = CHILD_EMPTY; while (cell == CHILD_EMPTY) { @@ -1691,8 +1766,8 @@ Vector3 VoxelLightBaker::_compute_ray_trace_at_pos(const Vector3 &p_pos, const V ofs_z += half; } - cell = bc->childs[child]; - if (cell == CHILD_EMPTY) + cell = bc->children[child]; + if (unlikely(cell == CHILD_EMPTY)) break; half >>= 1; @@ -1701,22 +1776,45 @@ Vector3 VoxelLightBaker::_compute_ray_trace_at_pos(const Vector3 &p_pos, const V pos += advance; } - if (cell != CHILD_EMPTY) { + if (unlikely(cell != CHILD_EMPTY)) { for (int i = 0; i < 6; i++) { //anisotropic read light float amount = direction.dot(aniso_normal[i]); - if (amount < 0) - amount = 0; + if (amount <= 0) + continue; accum.x += light[cell].accum[i][0] * amount; accum.y += light[cell].accum[i][1] * amount; accum.z += light[cell].accum[i][2] * amount; } + accum.x += cells[cell].emission[0]; + accum.y += cells[cell].emission[1]; + accum.z += cells[cell].emission[2]; } } + // Make sure we don't reset this thread's RNG state + return accum / samples; } +void VoxelLightBaker::_lightmap_bake_point(uint32_t p_x, LightMap *p_line) { + + LightMap *pixel = &p_line[p_x]; + if (pixel->pos == Vector3()) + return; + //print_line("pos: " + pixel->pos + " normal " + pixel->normal); + switch (bake_mode) { + case BAKE_MODE_CONE_TRACE: { + pixel->light = _compute_pixel_light_at_pos(pixel->pos, pixel->normal) * energy; + } break; + case BAKE_MODE_RAY_TRACE: { + pixel->light = _compute_ray_trace_at_pos(pixel->pos, pixel->normal) * energy; + } break; + // pixel->light = Vector3(1, 1, 1); + //} + } +} + Error VoxelLightBaker::make_lightmap(const Transform &p_xform, Ref<Mesh> &p_mesh, LightMapData &r_lightmap, bool (*p_bake_time_func)(void *, float, float), void *p_bake_time_ud) { //transfer light information to a lightmap @@ -1760,6 +1858,7 @@ Error VoxelLightBaker::make_lightmap(const Transform &p_xform, Ref<Mesh> &p_mesh Vector3 vertex[3]; Vector3 normal[3]; Vector2 uv[3]; + for (int j = 0; j < 3; j++) { int idx = ic ? ir[i * 3 + j] : i * 3 + j; vertex[j] = xform.xform(vr[idx]); @@ -1770,39 +1869,18 @@ Error VoxelLightBaker::make_lightmap(const Transform &p_xform, Ref<Mesh> &p_mesh _plot_triangle(uv, vertex, normal, lightmap.ptrw(), width, height); } } - //step 3 perform voxel cone trace on lightmap pixels + //step 3 perform voxel cone trace on lightmap pixels { LightMap *lightmap_ptr = lightmap.ptrw(); uint64_t begin_time = OS::get_singleton()->get_ticks_usec(); volatile int lines = 0; + // make sure our OS-level rng is seeded + for (int i = 0; i < height; i++) { - //print_line("bake line " + itos(i) + " / " + itos(height)); -#ifdef _OPENMP -#pragma omp parallel for -#endif - for (int j = 0; j < width; j++) { - - //if (i == 125 && j == 280) { - - LightMap *pixel = &lightmap_ptr[i * width + j]; - if (pixel->pos == Vector3()) - continue; //unused, skipe - - //print_line("pos: " + pixel->pos + " normal " + pixel->normal); - switch (bake_mode) { - case BAKE_MODE_CONE_TRACE: { - pixel->light = _compute_pixel_light_at_pos(pixel->pos, pixel->normal) * energy; - } break; - case BAKE_MODE_RAY_TRACE: { - pixel->light = _compute_ray_trace_at_pos(pixel->pos, pixel->normal) * energy; - } break; - // pixel->light = Vector3(1, 1, 1); - //} - } - } + thread_process_array(width, this, &VoxelLightBaker::_lightmap_bake_point, &lightmap_ptr[i * width]); lines = MAX(lines, i); //for multithread if (p_bake_time_func) { @@ -1850,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++) { @@ -1878,12 +1956,14 @@ Error VoxelLightBaker::make_lightmap(const Transform &p_xform, Ref<Mesh> &p_mesh LightMap *lightmap_ptr = lightmap.ptrw(); const Cell *cells = bake_cells.ptr(); const Light *light = bake_light.ptr(); - +#ifdef _OPENMP +#pragma omp parallel +#endif for (int i = 0; i < height; i++) { //print_line("bake line " + itos(i) + " / " + itos(height)); #ifdef _OPENMP -#pragma omp parallel for +#pragma omp parallel for schedule(dynamic, 1) #endif for (int j = 0; j < width; j++) { @@ -2002,6 +2082,7 @@ Error VoxelLightBaker::make_lightmap(const Transform &p_xform, Ref<Mesh> &p_mesh } } +// Enable for debugging #if 0 { PoolVector<uint8_t> img; @@ -2110,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 @@ -2194,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; @@ -2209,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); } } } @@ -2235,13 +2316,10 @@ Ref<MultiMesh> VoxelLightBaker::create_debug_multimesh(DebugMode p_mode) { PoolVector<Vector3> vertices; PoolVector<Color> colors; - - int vtx_idx = 0; #define ADD_VTX(m_idx) \ ; \ vertices.push_back(face_points[m_idx]); \ - colors.push_back(Color(1, 1, 1, 1)); \ - vtx_idx++; + colors.push_back(Color(1, 1, 1, 1)); for (int i = 0; i < 6; i++) { @@ -2257,9 +2335,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]; } } @@ -2342,7 +2420,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 6dee2ee69b..6a1f1253a3 100644 --- a/scene/3d/voxel_light_baker.h +++ b/scene/3d/voxel_light_baker.h @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* voxel_light_baker.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 VOXEL_LIGHT_BAKER_H #define VOXEL_LIGHT_BAKER_H @@ -30,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]; @@ -40,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++) { @@ -99,7 +129,8 @@ private: Vector<Color> _get_bake_texture(Ref<Image> p_image, const Color &p_color_mul, const Color &p_color_add); MaterialCache _get_material_cache(Ref<Material> p_material); - void _plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, const Vector3 *p_vtx, const Vector2 *p_uv, const MaterialCache &p_material, const AABB &p_aabb); + + void _plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, const Vector3 *p_vtx, const Vector3 *p_normal, const Vector2 *p_uv, const MaterialCache &p_material, const AABB &p_aabb); void _fixup_plot(int p_idx, int p_level); void _debug_mesh(int p_idx, int p_level, const AABB &p_aabb, Ref<MultiMesh> &p_multimesh, int &idx, DebugMode p_mode); void _check_init_light(); @@ -119,6 +150,8 @@ private: _FORCE_INLINE_ Vector3 _compute_pixel_light_at_pos(const Vector3 &p_pos, const Vector3 &p_normal); _FORCE_INLINE_ Vector3 _compute_ray_trace_at_pos(const Vector3 &p_pos, const Vector3 &p_normal); + void _lightmap_bake_point(uint32_t p_x, LightMap *p_line); + public: void begin_bake(int p_subdiv, const AABB &p_bounds); void plot_mesh(const Transform &p_xform, Ref<Mesh> &p_mesh, const Vector<Ref<Material> > &p_materials, const Ref<Material> &p_override_material); diff --git a/scene/animation/animation_cache.cpp b/scene/animation/animation_cache.cpp index fbe2593362..949a0be3bc 100644 --- a/scene/animation/animation_cache.cpp +++ b/scene/animation/animation_cache.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 481de59730..cf28236376 100644 --- a/scene/animation/animation_cache.h +++ b/scene/animation/animation_cache.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 5e776c5a1a..a0e0137863 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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; @@ -527,7 +507,6 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, float void AnimationPlayer::_animation_process_data(PlaybackData &cd, float p_delta, float p_blend) { float delta = p_delta * speed_scale * cd.speed_scale; - bool backwards = delta < 0; float next_pos = cd.pos + delta; float len = cd.from->animation->get_length(); @@ -545,14 +524,18 @@ void AnimationPlayer::_animation_process_data(PlaybackData &cd, float p_delta, f if (&cd == &playback.current) { + bool backwards = delta < 0; + if (!backwards && cd.pos <= len && next_pos == len /*&& playback.blend.empty()*/) { //playback finished - end_notify = true; + end_reached = true; + end_notify = cd.pos < len; // Notify only if not already at the end } if (backwards && cd.pos >= 0 && next_pos == 0 /*&& playback.blend.empty()*/) { //playback finished - end_notify = true; + end_reached = true; + end_notify = cd.pos > 0; // Notify only if not already at the beginning } } @@ -598,20 +581,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); @@ -679,24 +658,26 @@ void AnimationPlayer::_animation_process(float p_delta) { if (playback.current.from) { + end_reached = false; end_notify = false; _animation_process2(p_delta); _animation_update_transforms(); - if (end_notify) { + if (end_reached) { if (queued.size()) { String old = playback.assigned; play(queued.front()->get()); String new_name = playback.assigned; queued.pop_front(); - end_notify = false; - emit_signal(SceneStringNames::get_singleton()->animation_changed, old, new_name); + if (end_notify) + emit_signal(SceneStringNames::get_singleton()->animation_changed, old, new_name); } else { //stop(); playing = false; _set_process(false); - end_notify = false; - emit_signal(SceneStringNames::get_singleton()->animation_finished, playback.assigned); + if (end_notify) + emit_signal(SceneStringNames::get_singleton()->animation_finished, playback.assigned); } + end_reached = false; } } else { @@ -954,7 +935,7 @@ void AnimationPlayer::play(const StringName &p_name, float p_custom_blend, float c.current.speed_scale = p_custom_scale; c.assigned = p_name; - if (!end_notify) + if (!end_reached) queued.clear(); _set_process(true); // always process when starting an animation playing = true; @@ -985,10 +966,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 { @@ -999,9 +997,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) { @@ -1010,6 +1008,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(); @@ -1024,12 +1023,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); } @@ -1042,8 +1050,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); } @@ -1198,7 +1208,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()) { @@ -1301,6 +1311,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); @@ -1309,6 +1321,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); @@ -1329,14 +1342,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); @@ -1348,6 +1369,7 @@ AnimationPlayer::AnimationPlayer() { cache_update_size = 0; cache_update_prop_size = 0; speed_scale = 1; + end_reached = false; end_notify = false; animation_process_mode = ANIMATION_PROCESS_IDLE; processing = false; diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h index e39afcf199..af2022ddac 100644 --- a/scene/animation/animation_player.h +++ b/scene/animation/animation_player.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 @@ -205,6 +206,7 @@ private: List<StringName> queued; + bool end_reached; bool end_notify; String autoplay; @@ -249,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); @@ -281,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; @@ -288,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 32f82fe6b8..ce5b372d72 100644 --- a/scene/animation/animation_tree_player.cpp +++ b/scene/animation/animation_tree_player.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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" @@ -386,8 +387,6 @@ bool AnimationTreePlayer::_get(const StringName &p_name, Variant &r_ret) const { void AnimationTreePlayer::_get_property_list(List<PropertyInfo> *p_list) const { - p_list->push_back(PropertyInfo(Variant::NODE_PATH, "base_path")); - p_list->push_back(PropertyInfo(Variant::NODE_PATH, "master_player")); p_list->push_back(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_NETWORK)); } @@ -558,7 +557,7 @@ float AnimationTreePlayer::_process_node(const StringName &p_node, AnimationNode return _process_node(osn->inputs[0].node, r_prev_anim, p_time, p_seek, p_fallback_weight, p_weights); } - float os_seek = p_seek; + bool os_seek = p_seek; if (p_seek) osn->time = p_time; @@ -814,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 */ @@ -896,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) @@ -1140,6 +1142,9 @@ void AnimationTreePlayer::transition_node_set_input_count(const StringName &p_no n->inputs.resize(p_inputs); n->input_data.resize(p_inputs); + + _clear_cycle_test(); + last_error = _cycle_test(out_name); } void AnimationTreePlayer::transition_node_set_input_auto_advance(const StringName &p_node, int p_input, bool p_auto_advance) { @@ -1360,6 +1365,8 @@ void AnimationTreePlayer::remove_node(const StringName &p_node) { node_map.erase(p_node); + _clear_cycle_test(); + // compute last error again, just in case last_error = _cycle_test(out_name); dirty_caches = true; @@ -1387,6 +1394,14 @@ AnimationTreePlayer::ConnectError AnimationTreePlayer::_cycle_test(const StringN return CONNECT_OK; } +// Use this function to not alter next complete _cycle_test(). +void AnimationTreePlayer::_clear_cycle_test() { + for (Map<StringName, NodeBase *>::Element *E = node_map.front(); E; E = E->next()) { + NodeBase *nb = E->get(); + nb->cycletest = false; + } +} + Error AnimationTreePlayer::connect_nodes(const StringName &p_src_node, const StringName &p_dst_node, int p_dst_input) { ERR_FAIL_COND_V(!node_map.has(p_src_node), ERR_INVALID_PARAMETER); @@ -1411,11 +1426,7 @@ Error AnimationTreePlayer::connect_nodes(const StringName &p_src_node, const Str dst->inputs[p_dst_input].node = p_src_node; - for (Map<StringName, NodeBase *>::Element *E = node_map.front(); E; E = E->next()) { - - NodeBase *nb = E->get(); - nb->cycletest = false; - } + _clear_cycle_test(); last_error = _cycle_test(out_name); if (last_error) { diff --git a/scene/animation/animation_tree_player.h b/scene/animation/animation_tree_player.h index c49b0c4d1b..09d6f6fcb4 100644 --- a/scene/animation/animation_tree_player.h +++ b/scene/animation/animation_tree_player.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 @@ -317,6 +318,7 @@ private: bool reset_request; ConnectError _cycle_test(const StringName &p_at_node); + void _clear_cycle_test(); Track *_find_track(const NodePath &p_path); void _recompute_caches(); diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp index 151632a0cb..49013b160a 100644 --- a/scene/animation/tween.cpp +++ b/scene/animation/tween.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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); @@ -561,57 +564,50 @@ void Tween::_tween_process(float p_delta) { data.finish = true; } - switch (data.type) { - case INTER_PROPERTY: - case INTER_METHOD: { - Variant result = _run_equation(data); - emit_signal("tween_step", object, NodePath(Vector<StringName>(), data.key, false), data.elapsed, result); - _apply_tween_value(data, result); - if (data.finish) - _apply_tween_value(data, data.final_val); - } break; - - case INTER_CALLBACK: - if (data.finish) { - if (data.call_deferred) { - - switch (data.args) { - case 0: - object->call_deferred(data.key[0]); - break; - case 1: - object->call_deferred(data.key[0], data.arg[0]); - break; - case 2: - object->call_deferred(data.key[0], data.arg[0], data.arg[1]); - break; - case 3: - object->call_deferred(data.key[0], data.arg[0], data.arg[1], data.arg[2]); - break; - case 4: - object->call_deferred(data.key[0], data.arg[0], data.arg[1], data.arg[2], data.arg[3]); - break; - case 5: - object->call_deferred(data.key[0], data.arg[0], data.arg[1], data.arg[2], data.arg[3], data.arg[4]); - break; - } - } else { - Variant::CallError error; - Variant *arg[5] = { - &data.arg[0], - &data.arg[1], - &data.arg[2], - &data.arg[3], - &data.arg[4], - }; - object->call(data.key[0], (const Variant **)arg, data.args, error); + if (data.type == INTER_CALLBACK) { + if (data.finish) { + if (data.call_deferred) { + + switch (data.args) { + case 0: + object->call_deferred(data.key[0]); + break; + case 1: + object->call_deferred(data.key[0], data.arg[0]); + break; + case 2: + object->call_deferred(data.key[0], data.arg[0], data.arg[1]); + break; + case 3: + object->call_deferred(data.key[0], data.arg[0], data.arg[1], data.arg[2]); + break; + case 4: + object->call_deferred(data.key[0], data.arg[0], data.arg[1], data.arg[2], data.arg[3]); + break; + case 5: + object->call_deferred(data.key[0], data.arg[0], data.arg[1], data.arg[2], data.arg[3], data.arg[4]); + break; } + } else { + Variant::CallError error; + Variant *arg[5] = { + &data.arg[0], + &data.arg[1], + &data.arg[2], + &data.arg[3], + &data.arg[4], + }; + object->call(data.key[0], (const Variant **)arg, data.args, error); } - break; - default: {} + } + } else { + Variant result = _run_equation(data); + emit_signal("tween_step", object, NodePath(Vector<StringName>(), data.key, false), data.elapsed, result); + _apply_tween_value(data, result); } if (data.finish) { + _apply_tween_value(data, data.final_val); emit_signal("tween_completed", object, NodePath(Vector<StringName>(), data.key, false)); // not repeat mode, remove completed action if (!repeat) diff --git a/scene/animation/tween.h b/scene/animation/tween.h index 44710b25f9..757d80e90a 100644 --- a/scene/animation/tween.h +++ b/scene/animation/tween.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 cbf941f3ed..11f2b0c17f 100644 --- a/scene/animation/tween_interpolaters.cpp +++ b/scene/animation/tween_interpolaters.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 81962901d9..408c00334a 100644 --- a/scene/audio/audio_player.cpp +++ b/scene/audio/audio_player.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 6e9d623433..21189aea6d 100644 --- a/scene/audio/audio_player.h +++ b/scene/audio/audio_player.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 148277f2dd..acdbd9de08 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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; @@ -450,11 +464,11 @@ String BaseButton::get_tooltip(const Point2 &p_pos) const { String tooltip = Control::get_tooltip(p_pos); if (shortcut.is_valid() && shortcut->is_valid()) { - if (tooltip.find("$sc") != -1) { - tooltip = tooltip.replace_first("$sc", "(" + shortcut->get_as_text() + ")"); - } else { - tooltip += " (" + shortcut->get_as_text() + ")"; + String text = shortcut->get_name() + " (" + shortcut->get_as_text() + ")"; + if (shortcut->get_name().nocasecmp_to(tooltip) != 0) { + text += "\n" + tooltip; } + tooltip = text; } return tooltip; } @@ -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 0c08dc53a3..79638bbcce 100644 --- a/scene/gui/base_button.h +++ b/scene/gui/base_button.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 739fd84b38..12b9fe7c03 100644 --- a/scene/gui/box_container.cpp +++ b/scene/gui/box_container.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 ebbbbed1f9..abc228f804 100644 --- a/scene/gui/box_container.h +++ b/scene/gui/box_container.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 47977f0283..03b25a138f 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 35488582de..0b41b14f02 100644 --- a/scene/gui/button.h +++ b/scene/gui/button.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 @@ -56,7 +57,6 @@ private: float _internal_margin[4]; protected: - virtual Size2 get_minimum_size() const; void _set_internal_margin(Margin p_margin, float p_value); void _notification(int p_what); static void _bind_methods(); @@ -64,6 +64,8 @@ protected: public: // + virtual Size2 get_minimum_size() const; + void set_text(const String &p_text); String get_text() const; diff --git a/scene/gui/center_container.cpp b/scene/gui/center_container.cpp index 8c9c9d8720..cf71f89830 100644 --- a/scene/gui/center_container.cpp +++ b/scene/gui/center_container.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 4397539046..519e1493ec 100644 --- a/scene/gui/center_container.h +++ b/scene/gui/center_container.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 bf8156b92b..0790f87ea7 100644 --- a/scene/gui/check_box.cpp +++ b/scene/gui/check_box.cpp @@ -1,12 +1,12 @@ /*************************************************************************/ -/* check_button.cpp */ +/* check_box.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 3d3f170e8c..8375b46ca0 100644 --- a/scene/gui/check_box.h +++ b/scene/gui/check_box.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 641d2d4f01..f9ed0ecdbb 100644 --- a/scene/gui/check_button.cpp +++ b/scene/gui/check_button.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 3103a40d3c..a11749e3f6 100644 --- a/scene/gui/check_button.h +++ b/scene/gui/check_button.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 cb6283507e..34891832e2 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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" @@ -39,33 +40,32 @@ void ColorPicker::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: { - //sample->set_texture(get_icon("color_sample")); + btn_pick->set_icon(get_icon("screen_picker", "ColorPicker")); bt_add_preset->set_icon(get_icon("add_preset")); _update_controls(); } break; - case NOTIFICATION_ENTER_TREE: { + btn_pick->set_icon(get_icon("screen_picker", "ColorPicker")); bt_add_preset->set_icon(get_icon("add_preset")); _update_color(); } break; - case NOTIFICATION_PARENTED: { + for (int i = 0; i < 4; i++) set_margin((Margin)i, get_constant("margin")); } break; - case NOTIFICATION_VISIBILITY_CHANGED: { Popup *p = Object::cast_to<Popup>(get_parent()); if (p) p->set_size(Size2(get_combined_minimum_size().width + get_constant("margin") * 2, get_combined_minimum_size().height + get_constant("margin") * 2)); } break; - case MainLoop::NOTIFICATION_WM_QUIT_REQUEST: { + if (screen != NULL) { if (screen->is_visible()) { screen->hide(); @@ -77,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() { @@ -159,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); } } @@ -210,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 { @@ -241,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) @@ -249,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() { @@ -461,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); @@ -482,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"))); } @@ -523,7 +563,6 @@ ColorPicker::ColorPicker() : add_child(hb_edit); w_edit = memnew(Control); - //w_edit->set_ignore_mouse(false); w_edit->set_custom_minimum_size(Size2(get_constant("h_width"), 0)); w_edit->set_h_size_flags(SIZE_FILL); w_edit->set_v_size_flags(SIZE_EXPAND_FILL); @@ -555,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); @@ -585,11 +627,13 @@ 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); _update_controls(); - //_update_color(); updating = false; set_pick_color(Color(1, 1, 1)); @@ -599,7 +643,6 @@ ColorPicker::ColorPicker() : preset = memnew(TextureRect); bbc->add_child(preset); - //preset->set_ignore_mouse(false); preset->connect("gui_input", this, "_preset_input"); preset->connect("draw", this, "_update_presets"); @@ -613,12 +656,19 @@ ColorPicker::ColorPicker() : void ColorPickerButton::_color_changed(const Color &p_color) { + color = p_color; update(); - emit_signal("color_changed", p_color); + emit_signal("color_changed", color); +} + +void ColorPickerButton::_modal_closed() { + + emit_signal("popup_closed"); } void ColorPickerButton::pressed() { + _update_picker(); popup->set_position(get_global_position() - picker->get_combined_minimum_size()); popup->popup(); picker->set_focus_on_line_edit(); @@ -631,43 +681,65 @@ void ColorPickerButton::_notification(int p_what) { Ref<StyleBox> normal = get_stylebox("normal"); Rect2 r = Rect2(normal->get_offset(), get_size() - normal->get_minimum_size()); draw_texture_rect(Control::get_icon("bg", "ColorPickerButton"), r, true); - draw_rect(r, picker->get_pick_color()); + draw_rect(r, color); } - if (p_what == MainLoop::NOTIFICATION_WM_QUIT_REQUEST) { + if (p_what == MainLoop::NOTIFICATION_WM_QUIT_REQUEST && popup) { popup->hide(); } } void ColorPickerButton::set_pick_color(const Color &p_color) { - picker->set_pick_color(p_color); + color = p_color; + if (picker) { + picker->set_pick_color(p_color); + } + update(); - emit_signal("color_changed", p_color); } Color ColorPickerButton::get_pick_color() const { - return picker->get_pick_color(); + return color; } void ColorPickerButton::set_edit_alpha(bool p_show) { - picker->set_edit_alpha(p_show); + edit_alpha = p_show; + if (picker) { + picker->set_edit_alpha(p_show); + } } bool ColorPickerButton::is_editing_alpha() const { - return picker->is_editing_alpha(); + return edit_alpha; } ColorPicker *ColorPickerButton::get_picker() { + + _update_picker(); return picker; } -PopupPanel *ColorPickerButton::get_popup() { +PopupPanel *ColorPickerButton::get_popup() const { + return popup; } +void ColorPickerButton::_update_picker() { + if (!picker) { + popup = memnew(PopupPanel); + picker = memnew(ColorPicker); + popup->add_child(picker); + add_child(popup); + picker->connect("color_changed", this, "_color_changed"); + popup->connect("modal_closed", this, "_modal_closed"); + picker->set_pick_color(color); + picker->set_edit_alpha(edit_alpha); + } +} + void ColorPickerButton::_bind_methods() { ClassDB::bind_method(D_METHOD("set_pick_color", "color"), &ColorPickerButton::set_pick_color); @@ -677,18 +749,20 @@ 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"); } ColorPickerButton::ColorPickerButton() { - popup = memnew(PopupPanel); - picker = memnew(ColorPicker); - popup->add_child(picker); - - picker->connect("color_changed", this, "_color_changed"); - add_child(popup); + //Initialization is now done deferred + //this improves performance in the inspector as the color picker + //can be expensive to initialize + picker = NULL; + popup = NULL; + edit_alpha = true; } diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h index c02cdc8608..6b63e5fe60 100644 --- a/scene/gui/color_picker.h +++ b/scene/gui/color_picker.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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); @@ -114,10 +118,16 @@ class ColorPickerButton : public Button { PopupPanel *popup; ColorPicker *picker; + Color color; + bool edit_alpha; void _color_changed(const Color &p_color); + void _modal_closed(); + virtual void pressed(); + void _update_picker(); + protected: void _notification(int); static void _bind_methods(); @@ -130,7 +140,7 @@ public: bool is_editing_alpha() const; ColorPicker *get_picker(); - PopupPanel *get_popup(); + PopupPanel *get_popup() const; ColorPickerButton(); }; diff --git a/scene/gui/color_rect.cpp b/scene/gui/color_rect.cpp index 6b5247648e..463f3911dc 100644 --- a/scene/gui/color_rect.cpp +++ b/scene/gui/color_rect.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 382648fddf..a841008f76 100644 --- a/scene/gui/color_rect.h +++ b/scene/gui/color_rect.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 ba5d92620b..7df03bf7c6 100644 --- a/scene/gui/container.cpp +++ b/scene/gui/container.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,15 +27,16 @@ /* 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" void Container::_child_minsize_changed() { - Size2 ms = get_combined_minimum_size(); - if (ms.width > get_size().width || ms.height > get_size().height) - minimum_size_changed(); + //Size2 ms = get_combined_minimum_size(); + //if (ms.width > get_size().width || ms.height > get_size().height) { + minimum_size_changed(); queue_sort(); } @@ -50,6 +51,8 @@ void Container::add_child_notify(Node *p_child) { control->connect("size_flags_changed", this, "queue_sort"); control->connect("minimum_size_changed", this, "_child_minsize_changed"); control->connect("visibility_changed", this, "_child_minsize_changed"); + + minimum_size_changed(); queue_sort(); } @@ -60,6 +63,7 @@ void Container::move_child_notify(Node *p_child) { if (!Object::cast_to<Control>(p_child)) return; + minimum_size_changed(); queue_sort(); } @@ -74,6 +78,8 @@ void Container::remove_child_notify(Node *p_child) { control->disconnect("size_flags_changed", this, "queue_sort"); control->disconnect("minimum_size_changed", this, "_child_minsize_changed"); control->disconnect("visibility_changed", this, "_child_minsize_changed"); + + minimum_size_changed(); queue_sort(); } diff --git a/scene/gui/container.h b/scene/gui/container.h index 60a7a9efcb..c472162f58 100644 --- a/scene/gui/container.h +++ b/scene/gui/container.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 81d2b6731f..d07b5a9f65 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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); } @@ -144,12 +155,21 @@ Size2 Control::get_custom_minimum_size() const { return data.custom_minimum_size; } -Size2 Control::get_combined_minimum_size() const { +void Control::_update_minimum_size_cache() { Size2 minsize = get_minimum_size(); minsize.x = MAX(minsize.x, data.custom_minimum_size.x); minsize.y = MAX(minsize.y, data.custom_minimum_size.y); - return minsize; + data.minimum_size_cache = minsize; + data.minimum_size_valid = true; +} + +Size2 Control::get_combined_minimum_size() const { + + if (!data.minimum_size_valid) { + const_cast<Control *>(this)->_update_minimum_size_cache(); + } + return data.minimum_size_cache; } Size2 Control::_edit_get_minimum_size() const { @@ -248,14 +268,18 @@ void Control::_update_minimum_size() { if (!is_inside_tree()) return; - data.pending_min_size_update = false; Size2 minsize = get_combined_minimum_size(); if (minsize.x > data.size_cache.x || minsize.y > data.size_cache.y) { _size_changed(); } - emit_signal(SceneStringNames::get_singleton()->minimum_size_changed); + data.updating_last_minimum_size = false; + + if (minsize != data.last_minimum_size) { + data.last_minimum_size = minsize; + emit_signal(SceneStringNames::get_singleton()->minimum_size_changed); + } } bool Control::_get(const StringName &p_name, Variant &r_ret) const { @@ -426,8 +450,12 @@ void Control::_notification(int p_notification) { case NOTIFICATION_ENTER_TREE: { - _size_changed(); - + } break; + case NOTIFICATION_POST_ENTER_TREE: { + if (is_visible_in_tree()) { + data.minimum_size_valid = false; + _size_changed(); + } } break; case NOTIFICATION_EXIT_TREE: { @@ -609,13 +637,12 @@ void Control::_notification(int p_notification) { if (is_inside_tree()) { _modal_stack_remove(); - minimum_size_changed(); } //remove key focus //remove modalness } else { - + data.minimum_size_valid = false; _size_changed(); } @@ -860,6 +887,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 +897,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 +1305,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 +1354,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 +1363,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 +1386,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 +2041,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 +2191,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 { @@ -2442,17 +2480,25 @@ void Control::minimum_size_changed() { if (!is_inside_tree() || data.block_minimum_size_adjust) return; - if (data.pending_min_size_update) + Control *invalidate = this; + + //invalidate cache upwards + while (invalidate && invalidate->data.minimum_size_valid) { + invalidate->data.minimum_size_valid = false; + if (invalidate->is_set_as_toplevel()) + break; // do not go further up + invalidate = invalidate->data.parent; + } + + if (!is_visible_in_tree()) return; - data.pending_min_size_update = true; - MessageQueue::get_singleton()->push_call(this, "_update_minimum_size"); + if (data.updating_last_minimum_size) + return; - if (!is_toplevel_control()) { - Control *pc = get_parent_control(); - if (pc) - pc->minimum_size_changed(); - } + data.updating_last_minimum_size = true; + + MessageQueue::get_singleton()->push_call(this, "_update_minimum_size"); } int Control::get_v_size_flags() const { @@ -2737,7 +2783,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 +2795,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 +2872,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_PROPERTYNO(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 +2895,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 +2920,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 +2975,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); @@ -2956,7 +3009,6 @@ Control::Control() { data.h_size_flags = SIZE_FILL; data.v_size_flags = SIZE_FILL; data.expand = 1; - data.pending_min_size_update = false; data.rotation = 0; data.parent_canvas_item = NULL; data.scale = Vector2(1, 1); @@ -2966,6 +3018,8 @@ Control::Control() { data.disable_visibility_clip = false; data.h_grow = GROW_DIRECTION_END; data.v_grow = GROW_DIRECTION_END; + data.minimum_size_valid = false; + data.updating_last_minimum_size = false; data.clip_contents = false; for (int i = 0; i < 4; i++) { diff --git a/scene/gui/control.h b/scene/gui/control.h index 9ac0eb0be3..9124256624 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 { @@ -146,6 +148,11 @@ private: Point2 pos_cache; Size2 size_cache; + Size2 minimum_size_cache; + bool minimum_size_valid; + + Size2 last_minimum_size; + bool updating_last_minimum_size; float margin[4]; float anchor[4]; @@ -162,7 +169,6 @@ private: int h_size_flags; int v_size_flags; float expand; - bool pending_min_size_update; Point2 custom_minimum_size; bool pass_on_modal_close_click; @@ -242,6 +248,8 @@ private: void _modal_stack_remove(); void _modal_set_prev_focus_owner(ObjectID p_prev); + void _update_minimum_size_cache(); + protected: virtual void add_child_notify(Node *p_child); virtual void remove_child_notify(Node *p_child); @@ -270,6 +278,8 @@ public: NOTIFICATION_FOCUS_EXIT = 44, NOTIFICATION_THEME_CHANGED = 45, NOTIFICATION_MODAL_CLOSE = 46, + NOTIFICATION_SCROLL_BEGIN = 47, + NOTIFICATION_SCROLL_END = 48, }; @@ -279,6 +289,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 +329,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 d4912339da..d9737fa21a 100644 --- a/scene/gui/dialogs.cpp +++ b/scene/gui/dialogs.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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" @@ -289,10 +290,17 @@ bool WindowDialog::get_resizable() const { Size2 WindowDialog::get_minimum_size() const { Ref<Font> font = get_font("title_font", "WindowDialog"); - int msx = close_button->get_combined_minimum_size().x; - msx += font->get_string_size(title).x; - return Size2(msx, 1); + const int button_width = close_button->get_combined_minimum_size().x; + const int title_width = font->get_string_size(title).x; + const int padding = button_width / 2; + const int button_area = button_width + padding; + + // as the title gets centered, title_width + close_button_width is not enough. + // we want a width w, such that w / 2 - title_width / 2 >= button_area, i.e. + // w >= 2 * button_area + title_width + + return Size2(2 * button_area + title_width, 1); } TextureButton *WindowDialog::get_close_button() { diff --git a/scene/gui/dialogs.h b/scene/gui/dialogs.h index 54cc290b05..feb080dd06 100644 --- a/scene/gui/dialogs.h +++ b/scene/gui/dialogs.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 6af869c503..4bd92d888d 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 c8c1f23105..ad483d5dab 100644 --- a/scene/gui/file_dialog.h +++ b/scene/gui/file_dialog.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 c9ce5e2143..b5622604e2 100644 --- a/scene/gui/gradient_edit.cpp +++ b/scene/gui/gradient_edit.cpp @@ -1,12 +1,12 @@ /*************************************************************************/ -/* color_ramp_edit.cpp */ +/* gradient_edit.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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" @@ -146,6 +147,7 @@ void GradientEdit::_gui_input(const Ref<InputEvent> &p_event) { grabbed = _get_point_from_pos(x); //grab or select if (grabbed != -1) { + grabbed = false; return; } @@ -366,6 +368,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 026e4f783b..e7834ea0de 100644 --- a/scene/gui/gradient_edit.h +++ b/scene/gui/gradient_edit.h @@ -1,12 +1,12 @@ /*************************************************************************/ -/* color_ramp_edit.h */ +/* gradient_edit.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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 da52fb39e0..38ce91a4df 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 e8e530848d..3bfde44854 100644 --- a/scene/gui/graph_edit.h +++ b/scene/gui/graph_edit.h @@ -1,12 +1,12 @@ /*************************************************************************/ -/* graph_edit.cpp */ +/* graph_edit.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 7655363631..24857d49fa 100644 --- a/scene/gui/graph_node.cpp +++ b/scene/gui/graph_node.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 a0840544dd..20d25a69b0 100644 --- a/scene/gui/graph_node.h +++ b/scene/gui/graph_node.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 5dd5667f46..278e4123d7 100644 --- a/scene/gui/grid_container.cpp +++ b/scene/gui/grid_container.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 45e26ed48e..7e3470dc89 100644 --- a/scene/gui/grid_container.h +++ b/scene/gui/grid_container.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 197e474fd6..57b9a9a11b 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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) { +void ItemList::move_item(int p_from_idx, int p_to_idx) { - ERR_FAIL_INDEX(p_item, items.size()); - ERR_FAIL_INDEX(p_to_pos, items.size() + 1); + ERR_FAIL_INDEX(p_from_idx, items.size()); + ERR_FAIL_INDEX(p_to_idx, items.size()); - Item it = items[p_item]; - items.remove(p_item); - - if (p_to_pos > p_item) { - p_to_pos--; - } - - 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) { @@ -930,6 +934,9 @@ void ItemList::_notification(int p_what) { scroll_bar->hide(); } else { scroll_bar->show(); + + if (do_autoscroll_to_bottom) + scroll_bar->set_value(max); } break; } @@ -957,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; @@ -1031,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; @@ -1090,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]) { @@ -1098,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 @@ -1134,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); } } } @@ -1192,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) { @@ -1237,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; } @@ -1245,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; } @@ -1313,6 +1376,11 @@ Size2 ItemList::get_minimum_size() const { return Size2(); } +void ItemList::set_autoscroll_to_bottom(const bool p_enable) { + + do_autoscroll_to_bottom = p_enable; +} + void ItemList::set_auto_height(bool p_enable) { auto_height = p_enable; @@ -1339,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); @@ -1359,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); @@ -1395,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); @@ -1410,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"); @@ -1423,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); @@ -1466,6 +1548,8 @@ 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; set_clip_contents(true); diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h index b1e1e5eeb0..58771c1777 100644 --- a/scene/gui/item_list.h +++ b/scene/gui/item_list.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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,8 +107,12 @@ private: bool allow_rmb_select; + bool allow_reselect; + real_t icon_scale; + bool do_autoscroll_to_bottom; + Array _get_items() const; void _set_items(const Array &p_items); @@ -130,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; @@ -164,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); @@ -195,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(); @@ -212,6 +224,8 @@ public: Size2 get_minimum_size() const; + void set_autoscroll_to_bottom(const bool p_enable); + VScrollBar *get_v_scroll() { return scroll_bar; } ItemList(); diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index e1f77594da..9af479c1cc 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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())); @@ -101,7 +103,8 @@ void Label::_notification(int p_what) { int lines_visible = (size.y + line_spacing) / font_h; - int space_w = font->get_char_size(' ').width; + // ceiling to ensure autowrapping does not cut text + int space_w = Math::ceil(font->get_char_size(' ').width); int chars_total = 0; int vbegin = 0, vsep = 0; @@ -149,6 +152,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) @@ -207,7 +211,7 @@ void Label::_notification(int p_what) { } break; } - int y_ofs = style->get_offset().y; + float y_ofs = style->get_offset().y; y_ofs += (line - lines_skipped) * font_h + font->get_ascent(); y_ofs += vbegin + line * vsep; @@ -243,11 +247,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 +268,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++; } } @@ -328,7 +332,8 @@ int Label::get_longest_line_width() const { } } else { - int char_width = font->get_char_size(current, xl_text[i + 1]).width; + // ceiling to ensure autowrapping does not cut text + int char_width = Math::ceil(font->get_char_size(current, xl_text[i + 1]).width); line_width += char_width; } } @@ -381,7 +386,8 @@ void Label::regenerate_word_cache() { int word_pos = 0; int line_width = 0; int space_count = 0; - int space_width = font->get_char_size(' ').width; + // ceiling to ensure autowrapping does not cut text + int space_width = Math::ceil(font->get_char_size(' ').width); int line_spacing = get_constant("line_spacing"); line_count = 1; total_char_cache = 0; @@ -443,8 +449,8 @@ void Label::regenerate_word_cache() { if (current_word_size == 0) { word_pos = i; } - - char_width = font->get_char_size(current, xl_text[i + 1]).width; + // ceiling to ensure autowrapping does not cut text + char_width = Math::ceil(font->get_char_size(current, xl_text[i + 1]).width); current_word_size += char_width; line_width += char_width; total_char_cache++; @@ -563,6 +569,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 +590,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 +672,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 7c1905af7d..d5e0b60773 100644 --- a/scene/gui/label.h +++ b/scene/gui/label.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 85ae6d6241..2b644e7f96 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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" @@ -85,7 +87,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { if ((cursor_pos < selection.begin) || (cursor_pos > selection.end) || !selection.enabled) { - selection_clear(); + deselect(); selection.cursor_start = cursor_pos; selection.creating = true; } else if (selection.enabled) { @@ -99,7 +101,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { } else { if ((!selection.creating) && (!selection.doubleclick)) { - selection_clear(); + deselect(); } selection.creating = false; selection.doubleclick = false; @@ -175,7 +177,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { if (editable) { - selection_clear(); + deselect(); text = text.substr(cursor_pos, text.length() - cursor_pos); Ref<Font> font = get_font("font"); @@ -204,7 +206,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { if (editable) { - selection_clear(); + deselect(); text = text.substr(0, cursor_pos); _text_changed(); } @@ -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; } } @@ -368,6 +376,20 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { shift_selection_check_post(k->get_shift()); } break; + 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; case KEY_DELETE: { if (!editable) @@ -586,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) { @@ -599,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; @@ -633,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! @@ -646,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) @@ -660,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++; @@ -668,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! @@ -681,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) { @@ -700,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) @@ -714,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++; @@ -787,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; + } } } @@ -827,7 +856,7 @@ void LineEdit::shift_selection_check_pre(bool p_shift) { selection.cursor_start = cursor_pos; } if (!p_shift) - selection_clear(); + deselect(); } void LineEdit::shift_selection_check_post(bool p_shift) { @@ -860,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; } @@ -880,13 +909,6 @@ void LineEdit::set_cursor_at_pixel_pos(int p_x) { } set_cursor_position(ofs); - - /* - int new_cursor_pos=p_x; - int charwidth=draw_area->get_font_char_width(' ',0); - new_cursor_pos=( ( (new_cursor_pos-2)+ (charwidth/2) ) /charwidth ); - if (new_cursor_pos>(int)text.length()) new_cursor_pos=text.length(); - set_cursor_position(window_pos+new_cursor_pos); */ } bool LineEdit::cursor_get_blink_enabled() const { @@ -941,11 +963,6 @@ void LineEdit::delete_char() { set_cursor_position(get_cursor_position() - 1); - if (cursor_pos == window_pos) { - - //set_window_pos(cursor_pos-get_window_length()); - } - _text_changed(); } @@ -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) { @@ -983,7 +1005,6 @@ void LineEdit::set_text(String p_text) { update(); cursor_pos = 0; window_pos = 0; - _text_changed(); } void LineEdit::clear() { @@ -1000,6 +1021,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(); } @@ -1143,7 +1173,7 @@ Size2 LineEdit::get_minimum_size() const { /* selection */ -void LineEdit::selection_clear() { +void LineEdit::deselect() { selection.begin = 0; selection.end = 0; @@ -1159,7 +1189,7 @@ void LineEdit::selection_delete() { if (selection.enabled) delete_text(selection.begin, selection.end); - selection_clear(); + deselect(); } void LineEdit::set_max_length(int p_max_length) { @@ -1200,6 +1230,7 @@ void LineEdit::select_all() { selection.enabled = true; update(); } + void LineEdit::set_editable(bool p_editable) { editable = p_editable; @@ -1216,15 +1247,31 @@ 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) { - selection_clear(); + deselect(); return; } @@ -1302,12 +1349,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 +1387,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,18 +1420,19 @@ 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); ClassDB::bind_method(D_METHOD("_gui_input"), &LineEdit::_gui_input); ClassDB::bind_method(D_METHOD("clear"), &LineEdit::clear); + ClassDB::bind_method(D_METHOD("select", "from", "to"), &LineEdit::select, DEFVAL(0), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("select_all"), &LineEdit::select_all); + ClassDB::bind_method(D_METHOD("deselect"), &LineEdit::deselect); ClassDB::bind_method(D_METHOD("set_text", "text"), &LineEdit::set_text); ClassDB::bind_method(D_METHOD("get_text"), &LineEdit::get_text); ClassDB::bind_method(D_METHOD("set_placeholder", "text"), &LineEdit::set_placeholder); @@ -1405,14 +1454,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("select", "from", "to"), &LineEdit::select, DEFVAL(0), DEFVAL(-1)); + 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); @@ -1433,15 +1483,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() { @@ -1450,14 +1502,17 @@ 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; - selection_clear(); + deselect(); set_focus_mode(FOCUS_ALL); editable = true; set_default_cursor_shape(CURSOR_IBEAM); @@ -1474,15 +1529,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 c3a299c2f5..e9314ba8dd 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 { @@ -119,9 +123,7 @@ private: void shift_selection_check_pre(bool); void shift_selection_check_post(bool); - void selection_clear(); void selection_fill_at_cursor(); - void selection_delete(); void set_window_pos(int p_pos); void set_cursor_at_pixel_pos(int p_x); @@ -132,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); @@ -155,7 +155,10 @@ public: bool is_context_menu_enabled(); PopupMenu *get_menu() const; + void select(int p_from = 0, int p_to = -1); void select_all(); + void selection_delete(); + void deselect(); void delete_char(); void delete_text(int p_from_column, int p_to_column); @@ -190,7 +193,8 @@ public: void set_secret(bool p_secret); bool is_secret() const; - void select(int p_from = 0, int p_to = -1); + void set_secret_character(const String &p_string); + String get_secret_character() const; virtual Size2 get_minimum_size() const; diff --git a/scene/gui/link_button.cpp b/scene/gui/link_button.cpp index 9fe94fc464..d862e8669c 100644 --- a/scene/gui/link_button.cpp +++ b/scene/gui/link_button.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 386eb452ae..0821ad9c0d 100644 --- a/scene/gui/link_button.h +++ b/scene/gui/link_button.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 14991f5d44..5e1d53fe1d 100644 --- a/scene/gui/margin_container.cpp +++ b/scene/gui/margin_container.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 9703d9ebe0..ea75109248 100644 --- a/scene/gui/margin_container.h +++ b/scene/gui/margin_container.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 d850553957..87cf4dc334 100644 --- a/scene/gui/menu_button.cpp +++ b/scene/gui/menu_button.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,12 +27,16 @@ /* 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" void MenuButton::_unhandled_key_input(Ref<InputEvent> p_event) { + if (disable_shortcuts) + return; + if (p_event->is_pressed() && !p_event->is_echo() && (Object::cast_to<InputEventKey>(p_event.ptr()) || Object::cast_to<InputEventJoypadButton>(p_event.ptr()) || Object::cast_to<InputEventAction>(*p_event))) { if (!get_parent() || !is_visible_in_tree() || is_disabled()) @@ -55,30 +59,14 @@ 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) { - /*if (p_event.type==InputEvent::MOUSE_BUTTON && p_event->get_button_index()==BUTTON_LEFT) { - clicked=p_event->is_pressed(); - } - if (clicked && p_event.type==InputEvent::MOUSE_MOTION && popup->is_visible_in_tree()) { - - Point2 gt = Point2(p_event.mouse_motion.x,p_event.mouse_motion.y); - gt = get_global_transform().xform(gt); - Point2 lt = popup->get_transform().affine_inverse().xform(gt); - if (popup->has_point(lt)) { - //print_line("HAS POINT!!!"); - popup->call_deferred("grab_click_focus"); - } - - }*/ - BaseButton::_gui_input(p_event); } -PopupMenu *MenuButton::get_popup() { +PopupMenu *MenuButton::get_popup() const { return popup; } @@ -98,21 +86,28 @@ void MenuButton::_bind_methods() { ClassDB::bind_method(D_METHOD("_unhandled_key_input"), &MenuButton::_unhandled_key_input); ClassDB::bind_method(D_METHOD("_set_items"), &MenuButton::_set_items); 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")); } + +void MenuButton::set_disable_shortcuts(bool p_disabled) { + + disable_shortcuts = p_disabled; +} + MenuButton::MenuButton() { set_flat(true); + set_disable_shortcuts(false); set_enabled_focus_mode(FOCUS_NONE); popup = memnew(PopupMenu); popup->hide(); 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 c7f1d976ff..2356444ecb 100644 --- a/scene/gui/menu_button.h +++ b/scene/gui/menu_button.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 @@ -40,6 +41,7 @@ class MenuButton : public Button { GDCLASS(MenuButton, Button); bool clicked; + bool disable_shortcuts; PopupMenu *popup; virtual void pressed(); @@ -53,7 +55,9 @@ protected: static void _bind_methods(); public: - PopupMenu *get_popup(); + PopupMenu *get_popup() const; + void set_disable_shortcuts(bool p_disabled); + MenuButton(); ~MenuButton(); }; diff --git a/scene/gui/nine_patch_rect.cpp b/scene/gui/nine_patch_rect.cpp index c02d80bba8..b8f6ffe6d2 100644 --- a/scene/gui/nine_patch_rect.cpp +++ b/scene/gui/nine_patch_rect.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 809daf9db3..b4b4602a7d 100644 --- a/scene/gui/nine_patch_rect.h +++ b/scene/gui/nine_patch_rect.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 70f3d9ca83..c5e4149782 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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" @@ -42,41 +43,42 @@ Size2 OptionButton::get_minimum_size() const { void OptionButton::_notification(int p_what) { - switch (p_what) { - - case NOTIFICATION_DRAW: { - - if (!has_icon("arrow")) - return; - - RID ci = get_canvas_item(); - Ref<Texture> arrow = Control::get_icon("arrow"); - Ref<StyleBox> normal = get_stylebox("normal"); - Color clr = Color(1, 1, 1); - if (get_constant("modulate_arrow")) - switch (get_draw_mode()) { - case DRAW_PRESSED: - clr = get_color("font_color_pressed"); - break; - case DRAW_HOVER: - clr = get_color("font_color_hover"); - break; - case DRAW_DISABLED: - clr = get_color("font_color_disabled"); - break; - default: - clr = get_color("font_color"); - } - - Size2 size = get_size(); - - Point2 ofs(size.width - arrow->get_width() - get_constant("arrow_margin"), int(Math::abs((size.height - arrow->get_height()) / 2))); - arrow->draw(ci, ofs, clr); - - } break; + if (p_what == NOTIFICATION_DRAW) { + + if (!has_icon("arrow")) + return; + + RID ci = get_canvas_item(); + Ref<Texture> arrow = Control::get_icon("arrow"); + Ref<StyleBox> normal = get_stylebox("normal"); + Color clr = Color(1, 1, 1); + if (get_constant("modulate_arrow")) { + switch (get_draw_mode()) { + case DRAW_PRESSED: + clr = get_color("font_color_pressed"); + break; + case DRAW_HOVER: + clr = get_color("font_color_hover"); + break; + case DRAW_DISABLED: + clr = get_color("font_color_disabled"); + break; + default: + clr = get_color("font_color"); + } + } + + Size2 size = get_size(); + + Point2 ofs(size.width - arrow->get_width() - get_constant("arrow_margin"), int(Math::abs((size.height - arrow->get_height()) / 2))); + arrow->draw(ci, ofs, clr); } } +void OptionButton::_focused(int p_which) { + emit_signal("item_focused", p_which); +} + void OptionButton::_selected(int p_which) { int selid = -1; @@ -110,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); } @@ -244,6 +246,11 @@ void OptionButton::remove_item(int p_idx) { popup->remove_item(p_idx); } +PopupMenu *OptionButton::get_popup() const { + + return popup; +} + Array OptionButton::_get_items() const { Array items; @@ -287,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); @@ -310,25 +318,31 @@ void OptionButton::_bind_methods() { ClassDB::bind_method(D_METHOD("remove_item", "idx"), &OptionButton::remove_item); ClassDB::bind_method(D_METHOD("_select_int"), &OptionButton::_select_int); + ClassDB::bind_method(D_METHOD("get_popup"), &OptionButton::get_popup); + 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() { + current = -1; + set_text_align(ALIGN_LEFT); + set_action_mode(ACTION_MODE_BUTTON_PRESS); + popup = memnew(PopupMenu); popup->hide(); + add_child(popup); popup->set_as_toplevel(true); popup->set_pass_on_modal_close_click(false); - add_child(popup); popup->connect("id_pressed", this, "_selected"); - - current = -1; - set_text_align(ALIGN_LEFT); + popup->connect("id_focused", this, "_focused"); } OptionButton::~OptionButton() { diff --git a/scene/gui/option_button.h b/scene/gui/option_button.h index a06c540678..d5f866d806 100644 --- a/scene/gui/option_button.h +++ b/scene/gui/option_button.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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); @@ -85,6 +87,8 @@ public: void remove_item(int p_idx); + PopupMenu *get_popup() const; + virtual void get_translatable_strings(List<String> *p_strings) const; OptionButton(); diff --git a/scene/gui/panel.cpp b/scene/gui/panel.cpp index 536b655f75..4375e03a50 100644 --- a/scene/gui/panel.cpp +++ b/scene/gui/panel.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 cc7ca47319..db8b35372e 100644 --- a/scene/gui/panel.h +++ b/scene/gui/panel.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 a0c75d1330..a778d62659 100644 --- a/scene/gui/panel_container.cpp +++ b/scene/gui/panel_container.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 5d8f1ee64b..267e2b921f 100644 --- a/scene/gui/panel_container.h +++ b/scene/gui/panel_container.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 2110298950..d18a3a3f2f 100644 --- a/scene/gui/popup.cpp +++ b/scene/gui/popup.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 6739c580f2..550e803578 100644 --- a/scene/gui/popup.h +++ b/scene/gui/popup.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 32f889e826..93865cebde 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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" @@ -42,24 +43,6 @@ String PopupMenu::_get_accel_text(int p_item) const { else if (items[p_item].accel) return keycode_get_string(items[p_item].accel); return String(); - - /* - String atxt; - if (p_accel&KEY_MASK_SHIFT) - atxt+="Shift+"; - if (p_accel&KEY_MASK_ALT) - atxt+="Alt+"; - if (p_accel&KEY_MASK_CTRL) - atxt+="Ctrl+"; - if (p_accel&KEY_MASK_META) - atxt+="Meta+"; - - p_accel&=KEY_CODE_MASK; - - atxt+=String::chr(p_accel).to_upper(); - - return atxt; -*/ } Size2 PopupMenu::get_minimum_size() const { @@ -72,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++) { @@ -91,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; } @@ -136,13 +119,11 @@ int PopupMenu::_get_mouse_over(const Point2 &p_over) const { Ref<Font> font = get_font("font"); int vseparation = get_constant("vseparation"); - //int hseparation = get_constant("hseparation"); float font_h = font->get_height(); 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()) { @@ -208,55 +189,91 @@ void PopupMenu::_submenu_timeout() { submenu_over = -1; } +void PopupMenu::_scroll(float p_factor, const Point2 &p_over) { + + const float global_y = get_global_position().y; + + int vseparation = get_constant("vseparation"); + Ref<Font> font = get_font("font"); + + 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)); + + Ref<InputEventMouseMotion> ie; + ie.instance(); + ie->set_position(p_over - Vector2(0, dy)); + _gui_input(ie); +} + void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) { - Ref<InputEventKey> k = p_event; + if (p_event->is_action("ui_down") && p_event->is_pressed()) { - if (k.is_valid()) { + int search_from = mouse_over + 1; + if (search_from >= items.size()) + search_from = 0; - if (!k->is_pressed()) - return; + for (int i = search_from; i < items.size(); i++) { - switch (k->get_scancode()) { + if (i < 0 || i >= items.size()) + continue; - case KEY_DOWN: { + if (!items[i].separator && !items[i].disabled) { - for (int i = mouse_over + 1; i < items.size(); i++) { + mouse_over = i; + emit_signal("id_focused", i); + update(); + accept_event(); + break; + } + } + } else if (p_event->is_action("ui_up") && p_event->is_pressed()) { - if (i < 0 || i >= items.size()) - continue; + int search_from = mouse_over - 1; + if (search_from < 0) + search_from = items.size() - 1; - if (!items[i].separator && !items[i].disabled) { + for (int i = search_from; i >= 0; i--) { - mouse_over = i; - update(); - break; - } - } - } break; - case KEY_UP: { + if (i < 0 || i >= items.size()) + continue; - for (int i = mouse_over - 1; i >= 0; i--) { + if (!items[i].separator && !items[i].disabled) { - if (i < 0 || i >= items.size()) - continue; + mouse_over = i; + emit_signal("id_focused", i); + update(); + accept_event(); + break; + } + } + } else if (p_event->is_action("ui_left") && p_event->is_pressed()) { - if (!items[i].separator && !items[i].disabled) { + 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()) { - mouse_over = i; - update(); - break; - } - } - } break; - case KEY_ENTER: - case KEY_KP_ENTER: { + 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 (mouse_over >= 0 && mouse_over < items.size() && !items[mouse_over].separator) { + 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(); } } @@ -267,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 (items[over].separator || items[over].disabled) - break; + if (invalidated_click) { + invalidated_click = false; + break; + } + if (over < 0) { + if (!was_during_grabbed_click) { + hide(); + } + break; //non-activable + } - if (items[over].submenu != "") { + if (items[over].separator || items[over].disabled) + break; - _activate_submenu(over); - return; - } - activate_item(over); + if (items[over].submenu != "") { - } break; + _activate_submenu(over); + return; + } + activate_item(over); + } + } } //update(); @@ -373,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 { @@ -410,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"); @@ -445,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) { @@ -454,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()) { @@ -500,6 +503,18 @@ void PopupMenu::_notification(int p_what) { } break; case NOTIFICATION_MOUSE_EXIT: { + if (mouse_over >= 0 && (items[mouse_over].submenu == "" || submenu_over != -1)) { + mouse_over = -1; + 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) { mouse_over = -1; update(); @@ -518,6 +533,7 @@ void PopupMenu::add_icon_item(const Ref<Texture> &p_icon, const String &p_label, item.ID = p_ID; items.push_back(item); update(); + minimum_size_changed(); } void PopupMenu::add_item(const String &p_label, int p_ID, uint32_t p_accel) { @@ -528,6 +544,7 @@ void PopupMenu::add_item(const String &p_label, int p_ID, uint32_t p_accel) { item.ID = p_ID; items.push_back(item); update(); + minimum_size_changed(); } void PopupMenu::add_submenu_item(const String &p_label, const String &p_submenu, int p_ID) { @@ -539,6 +556,7 @@ void PopupMenu::add_submenu_item(const String &p_label, const String &p_submenu, item.submenu = p_submenu; items.push_back(item); update(); + minimum_size_changed(); } void PopupMenu::add_icon_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID, uint32_t p_accel) { @@ -549,10 +567,12 @@ 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(); + minimum_size_changed(); } + void PopupMenu::add_check_item(const String &p_label, int p_ID, uint32_t p_accel) { Item item; @@ -560,9 +580,26 @@ 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(); + minimum_size_changed(); +} + +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(); + minimum_size_changed(); +} + +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(); + minimum_size_changed(); } void PopupMenu::add_icon_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) { @@ -578,6 +615,7 @@ void PopupMenu::add_icon_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut item.shortcut_is_global = p_global; items.push_back(item); update(); + minimum_size_changed(); } void PopupMenu::add_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) { @@ -592,7 +630,9 @@ void PopupMenu::add_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bool p_g item.shortcut_is_global = p_global; items.push_back(item); update(); + minimum_size_changed(); } + 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()); @@ -602,11 +642,12 @@ 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); update(); + minimum_size_changed(); } void PopupMenu::add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) { @@ -619,9 +660,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(); + minimum_size_changed(); +} + +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(); + minimum_size_changed(); } void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int p_default_state, int p_ID, uint32_t p_accel) { @@ -631,11 +681,11 @@ 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); update(); + minimum_size_changed(); } void PopupMenu::set_item_text(int p_idx, const String &p_text) { @@ -645,6 +695,7 @@ void PopupMenu::set_item_text(int p_idx, const String &p_text) { items[p_idx].xl_text = tr(p_text); update(); + minimum_size_changed(); } void PopupMenu::set_item_icon(int p_idx, const Ref<Texture> &p_icon) { @@ -652,6 +703,7 @@ void PopupMenu::set_item_icon(int p_idx, const Ref<Texture> &p_icon) { items[p_idx].icon = p_icon; update(); + minimum_size_changed(); } void PopupMenu::set_item_checked(int p_idx, bool p_checked) { @@ -660,6 +712,7 @@ void PopupMenu::set_item_checked(int p_idx, bool p_checked) { items[p_idx].checked = p_checked; update(); + minimum_size_changed(); } void PopupMenu::set_item_id(int p_idx, int p_ID) { @@ -667,6 +720,7 @@ void PopupMenu::set_item_id(int p_idx, int p_ID) { items[p_idx].ID = p_ID; update(); + minimum_size_changed(); } void PopupMenu::set_item_accelerator(int p_idx, uint32_t p_accel) { @@ -675,6 +729,7 @@ void PopupMenu::set_item_accelerator(int p_idx, uint32_t p_accel) { items[p_idx].accel = p_accel; update(); + minimum_size_changed(); } void PopupMenu::set_item_metadata(int p_idx, const Variant &p_meta) { @@ -682,6 +737,7 @@ void PopupMenu::set_item_metadata(int p_idx, const Variant &p_meta) { ERR_FAIL_INDEX(p_idx, items.size()); items[p_idx].metadata = p_meta; update(); + minimum_size_changed(); } void PopupMenu::set_item_disabled(int p_idx, bool p_disabled) { @@ -689,6 +745,7 @@ void PopupMenu::set_item_disabled(int p_idx, bool p_disabled) { ERR_FAIL_INDEX(p_idx, items.size()); items[p_idx].disabled = p_disabled; update(); + minimum_size_changed(); } void PopupMenu::set_item_submenu(int p_idx, const String &p_submenu) { @@ -696,6 +753,7 @@ void PopupMenu::set_item_submenu(int p_idx, const String &p_submenu) { ERR_FAIL_INDEX(p_idx, items.size()); items[p_idx].submenu = p_submenu; update(); + minimum_size_changed(); } void PopupMenu::toggle_item_checked(int p_idx) { @@ -703,6 +761,7 @@ void PopupMenu::toggle_item_checked(int p_idx) { ERR_FAIL_INDEX(p_idx, items.size()); items[p_idx].checked = !items[p_idx].checked; update(); + minimum_size_changed(); } String PopupMenu::get_item_text(int p_idx) const { @@ -806,7 +865,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(); } @@ -837,6 +903,7 @@ void PopupMenu::set_item_h_offset(int p_idx, int p_offset) { ERR_FAIL_INDEX(p_idx, items.size()); items[p_idx].h_ofs = p_offset; update(); + minimum_size_changed(); } void PopupMenu::set_item_multistate(int p_idx, int p_state) { @@ -862,7 +929,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 { @@ -936,7 +1008,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) { @@ -953,7 +1025,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) { @@ -996,6 +1068,7 @@ void PopupMenu::clear() { items.clear(); mouse_over = -1; update(); + minimum_size_changed(); } Array PopupMenu::_get_items() const { @@ -1005,7 +1078,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)); @@ -1048,7 +1123,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]; @@ -1061,7 +1138,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); @@ -1142,12 +1225,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); @@ -1159,6 +1244,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); @@ -1177,6 +1263,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); @@ -1201,22 +1288,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 ee514f4c4b..fde91bd845 100644 --- a/scene/gui/popup_menu.h +++ b/scene/gui/popup_menu.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 6953d03384..37e519e375 100644 --- a/scene/gui/progress_bar.cpp +++ b/scene/gui/progress_bar.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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 c5a9252123..d091c983b1 100644 --- a/scene/gui/progress_bar.h +++ b/scene/gui/progress_bar.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 f8fb786fa7..4062e48640 100644 --- a/scene/gui/range.cpp +++ b/scene/gui/range.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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() { @@ -70,10 +71,10 @@ void Range::set_value(double p_val) { p_val = Math::round(p_val); } - if (p_val > shared->max - shared->page) + if (!shared->allow_greater && p_val > shared->max - shared->page) p_val = shared->max - shared->page; - if (p_val < shared->min) + if (!shared->allow_lesser && p_val < shared->min) p_val = shared->min; if (shared->val == p_val) @@ -135,9 +136,9 @@ void Range::set_as_ratio(double p_value) { double v; - if (shared->exp_ratio && get_min() > 0) { + if (shared->exp_ratio && get_min() >= 0) { - double exp_min = Math::log(get_min()) / Math::log((double)2); + double exp_min = get_min() == 0 ? 0.0 : Math::log(get_min()) / Math::log((double)2); double exp_max = Math::log(get_max()) / Math::log((double)2); v = Math::pow(2, exp_min + (exp_max - exp_min) * p_value); } else { @@ -150,21 +151,24 @@ void Range::set_as_ratio(double p_value) { v = percent + get_min(); } } + v = CLAMP(v, get_min(), get_max()); set_value(v); } double Range::get_as_ratio() const { - if (shared->exp_ratio && get_min() > 0) { + if (shared->exp_ratio && get_min() >= 0) { - double exp_min = Math::log(get_min()) / Math::log((double)2); + double exp_min = get_min() == 0 ? 0.0 : Math::log(get_min()) / Math::log((double)2); double exp_max = Math::log(get_max()) / Math::log((double)2); - double v = Math::log(get_value()) / Math::log((double)2); + float value = CLAMP(get_value(), shared->min, shared->max); + double v = Math::log(value) / Math::log((double)2); return (v - exp_min) / (exp_max - exp_min); } else { - return (get_value() - get_min()) / (get_max() - get_min()); + float value = CLAMP(get_value(), shared->min, shared->max); + return (value - get_min()) / (get_max() - get_min()); } } @@ -192,6 +196,8 @@ void Range::unshare() { nshared->val = shared->val; nshared->step = shared->step; nshared->page = shared->page; + nshared->allow_greater = shared->allow_greater; + nshared->allow_lesser = shared->allow_lesser; _unref_shared(); _ref_shared(nshared); } @@ -235,6 +241,10 @@ void Range::_bind_methods() { ClassDB::bind_method(D_METHOD("is_using_rounded_values"), &Range::is_using_rounded_values); ClassDB::bind_method(D_METHOD("set_exp_ratio", "enabled"), &Range::set_exp_ratio); ClassDB::bind_method(D_METHOD("is_ratio_exp"), &Range::is_ratio_exp); + ClassDB::bind_method(D_METHOD("set_allow_greater", "allow"), &Range::set_allow_greater); + ClassDB::bind_method(D_METHOD("is_greater_allowed"), &Range::is_greater_allowed); + ClassDB::bind_method(D_METHOD("set_allow_lesser", "allow"), &Range::set_allow_lesser); + ClassDB::bind_method(D_METHOD("is_lesser_allowed"), &Range::is_lesser_allowed); ClassDB::bind_method(D_METHOD("share", "with"), &Range::_share); ClassDB::bind_method(D_METHOD("unshare"), &Range::unshare); @@ -247,8 +257,11 @@ 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"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "allow_greater"), "set_allow_greater", "is_greater_allowed"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "allow_lesser"), "set_allow_lesser", "is_lesser_allowed"); } void Range::set_use_rounded_values(bool p_enable) { @@ -271,6 +284,22 @@ bool Range::is_ratio_exp() const { return shared->exp_ratio; } +void Range::set_allow_greater(bool p_allow) { + shared->allow_greater = p_allow; +} + +bool Range::is_greater_allowed() const { + return shared->allow_greater; +} + +void Range::set_allow_lesser(bool p_allow) { + shared->allow_lesser = p_allow; +} + +bool Range::is_lesser_allowed() const { + return shared->allow_lesser; +} + Range::Range() { shared = memnew(Shared); shared->min = 0; @@ -280,6 +309,8 @@ Range::Range() { shared->page = 0; shared->owners.insert(this); shared->exp_ratio = false; + shared->allow_greater = false; + shared->allow_lesser = false; _rounded_values = false; } diff --git a/scene/gui/range.h b/scene/gui/range.h index daa8af824a..125f559248 100644 --- a/scene/gui/range.h +++ b/scene/gui/range.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 @@ -42,6 +43,8 @@ class Range : public Control { double val, min, max; double step, page; bool exp_ratio; + bool allow_greater; + bool allow_lesser; Set<Range *> owners; void emit_value_changed(); void emit_changed(const char *p_what = ""); @@ -85,6 +88,12 @@ public: void set_exp_ratio(bool p_enable); bool is_ratio_exp() const; + void set_allow_greater(bool p_allow); + bool is_greater_allowed() const; + + void set_allow_lesser(bool p_allow); + bool is_lesser_allowed() const; + void share(Range *p_range); void unshare(); diff --git a/scene/gui/reference_rect.cpp b/scene/gui/reference_rect.cpp index e2f4abe04a..5e25f43daf 100644 --- a/scene/gui/reference_rect.cpp +++ b/scene/gui/reference_rect.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 9b826a1145..473e348c85 100644 --- a/scene/gui/reference_rect.h +++ b/scene/gui/reference_rect.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 05c6e9f77e..f34559fc8d 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,10 +27,16 @@ /* 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" #include "scene/scene_string_names.h" + +#ifdef TOOLS_ENABLED +#include "editor/editor_node.h" +#endif + RichTextLabel::Item *RichTextLabel::_get_next_item(Item *p_item, bool p_free) { if (p_free) { @@ -80,11 +86,59 @@ RichTextLabel::Item *RichTextLabel::_get_next_item(Item *p_item, bool p_free) { return NULL; } +RichTextLabel::Item *RichTextLabel::_get_prev_item(Item *p_item, bool p_free) { + if (p_free) { + + if (p_item->subitems.size()) { + + return p_item->subitems.back()->get(); + } else if (!p_item->parent) { + return NULL; + } else if (p_item->E->prev()) { + + return p_item->E->prev()->get(); + } else { + //go back until something with a prev is found + while (p_item->parent && !p_item->E->prev()) { + p_item = p_item->parent; + } + + if (p_item->parent) + return p_item->E->prev()->get(); + else + return NULL; + } + + } else { + if (p_item->subitems.size() && p_item->type != ITEM_TABLE) { + + return p_item->subitems.back()->get(); + } else if (p_item->type == ITEM_FRAME) { + return NULL; + } else if (p_item->E->prev()) { + + return p_item->E->prev()->get(); + } else { + //go back until something with a prev is found + while (p_item->type != ITEM_FRAME && !p_item->E->prev()) { + p_item = p_item->parent; + } + + if (p_item->type != ITEM_FRAME) + return p_item->E->prev()->get(); + else + return NULL; + } + } + + return NULL; +} + 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) @@ -115,8 +169,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; @@ -134,6 +191,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 @@ -163,16 +222,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; \ @@ -184,7 +249,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) { \ @@ -231,6 +297,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) { @@ -246,13 +313,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)) { @@ -264,7 +334,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; @@ -274,8 +344,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; @@ -292,9 +363,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; @@ -342,23 +417,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++; @@ -366,15 +455,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; - VS::get_singleton()->canvas_item_add_line(ci, p_ofs + Point2(align_ofs + pofs, uy), p_ofs + Point2(align_ofs + pofs + cw, uy), uc); - } 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); @@ -432,6 +526,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) { @@ -439,9 +536,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()); @@ -452,8 +552,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++; } @@ -461,24 +562,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()) { @@ -490,7 +625,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 } @@ -523,9 +658,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 } @@ -606,9 +741,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; @@ -697,12 +830,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++; } } @@ -717,6 +856,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; @@ -737,7 +879,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++; @@ -797,16 +939,44 @@ 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(); } } } + } else if (b->is_pressed() && b->is_doubleclick() && selection.enabled) { + + //doubleclick: select word + int line = 0; + Item *item = NULL; + bool outside; + + _find_click(main, b->get_position(), &item, &line, &outside); + + while (item && item->type != ITEM_TEXT) { + + item = _get_next_item(item, true); + } + if (item && item->type == ITEM_TEXT) { + + String itext = static_cast<ItemText *>(item)->text; + + int beg, end; + if (select_word(itext, line, beg, end)) { + + selection.from = item; + selection.to = item; + selection.from_char = beg; + selection.to_char = end - 1; + selection.active = true; + update(); + } + } } else if (!b->is_pressed()) { selection.click = NULL; @@ -1084,13 +1254,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; @@ -1573,7 +1746,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 @@ -1764,7 +1937,7 @@ void RichTextLabel::set_selection_enabled(bool p_enabled) { } } -bool RichTextLabel::search(const String &p_string, bool p_from_selection) { +bool RichTextLabel::search(const String &p_string, bool p_from_selection, bool p_search_previous) { ERR_FAIL_COND_V(!selection.enabled, false); Item *it = main; @@ -1813,7 +1986,10 @@ bool RichTextLabel::search(const String &p_string, bool p_from_selection) { } } - it = _get_next_item(it, true); + if (p_search_previous) + it = _get_prev_item(it, true); + else + it = _get_next_item(it, true); charidx = 0; } @@ -1932,6 +2108,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); @@ -1998,12 +2181,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 b9a719dd10..af368af46a 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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; @@ -186,7 +190,6 @@ private: struct ItemNewline : public Item { - int line; // FIXME: Overriding base's line ? ItemNewline() { type = ITEM_NEWLINE; } }; @@ -196,6 +199,7 @@ private: bool expand; int expand_ratio; int min_width; + int max_width; int width; }; @@ -265,7 +269,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); @@ -280,6 +284,7 @@ private: void _gui_input(Ref<InputEvent> p_event); Item *_get_next_item(Item *p_item, bool p_free = false); + Item *_get_prev_item(Item *p_item, bool p_free = false); Rect2 _get_text_rect(); @@ -329,12 +334,14 @@ public: void set_tab_size(int p_spaces); int get_tab_size() const; - bool search(const String &p_string, bool p_from_selection = false); + bool search(const String &p_string, bool p_from_selection = false, bool p_search_previous = false); void scroll_to_line(int p_line); 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 c5ffec2d5e..6ec67aca6b 100644 --- a/scene/gui/scroll_bar.cpp +++ b/scene/gui/scroll_bar.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 753bd35de7..15e037f8bb 100644 --- a/scene/gui/scroll_bar.h +++ b/scene/gui/scroll_bar.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 ffe0db691f..2dd5c64378 100644 --- a/scene/gui/scroll_container.cpp +++ b/scene/gui/scroll_container.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 { @@ -36,6 +37,7 @@ bool ScrollContainer::clips_input() const { Size2 ScrollContainer::get_minimum_size() const { + Ref<StyleBox> sb = get_stylebox("bg"); Size2 min_size; for (int i = 0; i < get_child_count(); i++) { @@ -63,17 +65,24 @@ Size2 ScrollContainer::get_minimum_size() const { if (v_scroll->is_visible_in_tree()) { min_size.x += v_scroll->get_minimum_size().x; } + min_size += sb->get_minimum_size(); return min_size; -}; +} 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 +130,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 +140,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 +152,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 +169,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; + } } } @@ -223,6 +235,12 @@ void ScrollContainer::_notification(int p_what) { child_max_size = Size2(0, 0); Size2 size = get_size(); + Point2 ofs; + + Ref<StyleBox> sb = get_stylebox("bg"); + size -= sb->get_minimum_size(); + ofs += sb->get_offset(); + if (h_scroll->is_visible_in_tree()) size.y -= h_scroll->get_minimum_size().y; @@ -258,6 +276,7 @@ void ScrollContainer::_notification(int p_what) { else r.size.height = minsize.height; } + r.position += ofs; fit_child_in_rect(c, r); } update(); @@ -265,10 +284,13 @@ void ScrollContainer::_notification(int p_what) { if (p_what == NOTIFICATION_DRAW) { + Ref<StyleBox> sb = get_stylebox("bg"); + draw_style_box(sb, Rect2(Vector2(), get_size())); + update_scrollbars(); } - if (p_what == NOTIFICATION_PHYSICS_PROCESS) { + if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) { if (drag_touching) { @@ -322,9 +344,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 { @@ -345,6 +365,8 @@ void ScrollContainer::_notification(int p_what) { void ScrollContainer::update_scrollbars() { Size2 size = get_size(); + Ref<StyleBox> sb = get_stylebox("bg"); + size -= sb->get_minimum_size(); Size2 hmin = h_scroll->get_combined_minimum_size(); Size2 vmin = v_scroll->get_combined_minimum_size(); @@ -429,6 +451,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 +491,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 +527,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 2c5d60de6c..3fe1ed447a 100644 --- a/scene/gui/scroll_container.h +++ b/scene/gui/scroll_container.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 55d837458a..cd0b06da81 100644 --- a/scene/gui/separator.cpp +++ b/scene/gui/separator.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 be2f1e8e77..7949c7ca9b 100644 --- a/scene/gui/separator.h +++ b/scene/gui/separator.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 f37410e77b..36490cf254 100644 --- a/scene/gui/shortcut.cpp +++ b/scene/gui/shortcut.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 8d85bb920f..f9240642bf 100644 --- a/scene/gui/shortcut.h +++ b/scene/gui/shortcut.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 70b8616af1..46215c9277 100644 --- a/scene/gui/slider.cpp +++ b/scene/gui/slider.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 95ae429d63..e77a0b7423 100644 --- a/scene/gui/slider.h +++ b/scene/gui/slider.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 05f2809bfc..145981d498 100644 --- a/scene/gui/spin_box.cpp +++ b/scene/gui/spin_box.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 0c562bd744..8863f44bef 100644 --- a/scene/gui/spin_box.h +++ b/scene/gui/spin_box.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 4420a936d2..c38c411333 100644 --- a/scene/gui/split_container.cpp +++ b/scene/gui/split_container.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,18 +27,12 @@ /* 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" #include "margin_container.h" -struct _MinSizeCache { - - int min_size; - bool will_stretch; - int final_size; -}; - Control *SplitContainer::_getch(int p_idx) const { int idx = 0; @@ -60,136 +54,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) { + float ratio = first->get_stretch_ratio() / (first->get_stretch_ratio() + second->get_stretch_ratio()); - minimum = ms_first.height + ms_second.height; + 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 { - - minimum = ms_first.width + ms_second.width; + no_offset_middle_sep = ms_first[axis]; } - 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; - - if (expand_ofs < -available) - expand_ofs = -available; - - middle_sep = get_size()[axis] - ms_second[axis] - sep + expand_ofs; - - } else { - - if (expand_ofs < 0) - expand_ofs = 0; - - 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))); @@ -246,10 +173,12 @@ void SplitContainer::_notification(int p_what) { _resort(); } break; case NOTIFICATION_MOUSE_ENTER: { + mouse_inside = true; update(); } break; case NOTIFICATION_MOUSE_EXIT: { + mouse_inside = false; update(); } break; @@ -260,22 +189,17 @@ void SplitContainer::_notification(int p_what) { if (collapsed || (!mouse_inside && get_constant("autohide"))) return; + int sep = dragger_visibility != DRAGGER_HIDDEN_COLLAPSED ? get_constant("separation") : 0; Ref<Texture> tex = get_icon("grabber"); Size2 size = get_size(); - if (vertical) { + if (dragger_visibility == DRAGGER_VISIBLE) { - //draw_style_box( get_stylebox("bg"), Rect2(0,middle_sep,get_size().width,sep)); - if (dragger_visibility == DRAGGER_VISIBLE) + if (vertical) draw_texture(tex, Point2i((size.x - tex->get_width()) / 2, middle_sep + (sep - tex->get_height()) / 2)); - - } else { - - //draw_style_box( get_stylebox("bg"), Rect2(middle_sep,0,sep,get_size().height)); - if (dragger_visibility == DRAGGER_VISIBLE) + else draw_texture(tex, Point2i(middle_sep + (sep - tex->get_width()) / 2, (size.y - tex->get_height()) / 2)); } - } break; } } @@ -292,21 +216,24 @@ void SplitContainer::_gui_input(const Ref<InputEvent> &p_event) { if (mb->get_button_index() == BUTTON_LEFT) { if (mb->is_pressed()) { + int sep = get_constant("separation"); if (vertical) { if (mb->get_position().y > middle_sep && mb->get_position().y < middle_sep + sep) { + dragging = true; drag_from = mb->get_position().y; - drag_ofs = expand_ofs; + drag_ofs = split_offset; } } else { if (mb->get_position().x > middle_sep && mb->get_position().x < middle_sep + sep) { + dragging = true; drag_from = mb->get_position().x; - drag_ofs = expand_ofs; + drag_ofs = split_offset; } } } else { @@ -318,36 +245,31 @@ void SplitContainer::_gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseMotion> mm = p_event; - if (mm.is_valid()) { - - if (dragging) { + if (mm.is_valid() && dragging) { - expand_ofs = drag_ofs + ((vertical ? mm->get_position().y : mm->get_position().x) - drag_from); - queue_sort(); - emit_signal("dragged", get_split_offset()); - } + split_offset = drag_ofs + ((vertical ? mm->get_position().y : mm->get_position().x) - drag_from); + queue_sort(); + emit_signal("dragged", get_split_offset()); } } Control::CursorShape SplitContainer::get_cursor_shape(const Point2 &p_pos) const { - if (collapsed) - return Control::get_cursor_shape(p_pos); - if (dragging) return (vertical ? CURSOR_VSIZE : CURSOR_HSIZE); - int sep = get_constant("separation"); + if (!collapsed && _getch(0) && _getch(1) && dragger_visibility == DRAGGER_VISIBLE) { - if (vertical) { + int sep = get_constant("separation"); - if (p_pos.y > middle_sep && p_pos.y < middle_sep + sep) { - return CURSOR_VSIZE; - } - } else { + if (vertical) { - if (p_pos.x > middle_sep && p_pos.x < middle_sep + sep) { - return CURSOR_HSIZE; + if (p_pos.y > middle_sep && p_pos.y < middle_sep + sep) + return CURSOR_VSIZE; + } else { + + if (p_pos.x > middle_sep && p_pos.x < middle_sep + sep) + return CURSOR_HSIZE; } } @@ -356,21 +278,23 @@ 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) { if (collapsed == p_collapsed) return; + collapsed = p_collapsed; queue_sort(); } @@ -418,7 +342,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 c7a484c4c5..321f7fd3b7 100644 --- a/scene/gui/split_container.h +++ b/scene/gui/split_container.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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; @@ -88,7 +89,7 @@ class HSplitContainer : public SplitContainer { public: HSplitContainer() : - SplitContainer(false) { set_default_cursor_shape(CURSOR_HSPLIT); } + SplitContainer(false) {} }; class VSplitContainer : public SplitContainer { @@ -97,7 +98,7 @@ class VSplitContainer : public SplitContainer { public: VSplitContainer() : - SplitContainer(true) { set_default_cursor_shape(CURSOR_VSPLIT); } + SplitContainer(true) {} }; #endif // SPLIT_CONTAINER_H diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index f2a2d862de..0363dd44c2 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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 a36c4f3790..1afe5f7541 100644 --- a/scene/gui/tab_container.h +++ b/scene/gui/tab_container.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 1fb0f84223..b114264de1 100644 --- a/scene/gui/tabs.cpp +++ b/scene/gui/tabs.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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 4eb6be3435..3b38e7f2cb 100644 --- a/scene/gui/tabs.h +++ b/scene/gui/tabs.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 e5169089f2..55a650ff12 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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" @@ -42,14 +43,14 @@ #define TAB_PIXELS -static bool _is_text_char(CharType c) { +inline bool _is_symbol(CharType c) { - return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'; + return is_symbol(c); } -static bool _is_symbol(CharType c) { +static bool _is_text_char(CharType c) { - return c != '_' && ((c >= '!' && c <= '/') || (c >= ':' && c <= '@') || (c >= '[' && c <= '`') || (c >= '{' && c <= '~') || c == '\t' || c == ' '); + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'; } static bool _is_whitespace(CharType c) { @@ -116,7 +117,6 @@ void TextEdit::Text::set_indent_size(int p_indent_size) { void TextEdit::Text::_update_line_cache(int p_line) const { int w = 0; - int tab_w = font->get_char_size(' ').width * indent_size; int len = text[p_line].data.length(); const CharType *str = text[p_line].data.c_str(); @@ -124,22 +124,13 @@ void TextEdit::Text::_update_line_cache(int p_line) const { //update width for (int i = 0; i < len; i++) { - if (str[i] == '\t') { - - int left = w % tab_w; - if (left == 0) - w += tab_w; - else - w += tab_w - w % tab_w; // is right... - - } else { - - w += font->get_char_size(str[i], str[i + 1]).width; - } + w += get_char_width(str[i], str[i + 1], w); } text[p_line].width_cache = w; + text[p_line].wrap_amount_cache = -1; + //update regions text[p_line].region_info.clear(); @@ -183,6 +174,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 +202,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); @@ -239,10 +232,32 @@ int TextEdit::Text::get_line_width(int p_line) const { return text[p_line].width_cache; } -void TextEdit::Text::clear_caches() { +void TextEdit::Text::set_line_wrap_amount(int p_line, int p_wrap_amount) const { + + ERR_FAIL_INDEX(p_line, text.size()); + + text[p_line].wrap_amount_cache = p_wrap_amount; +} + +int TextEdit::Text::get_line_wrap_amount(int p_line) const { - for (int i = 0; i < text.size(); i++) + ERR_FAIL_INDEX_V(p_line, text.size(), -1); + + return text[p_line].wrap_amount_cache; +} + +void TextEdit::Text::clear_width_cache() { + + for (int i = 0; i < text.size(); i++) { text[i].width_cache = -1; + } +} + +void TextEdit::Text::clear_wrap_cache() { + + for (int i = 0; i < text.size(); i++) { + text[i].wrap_amount_cache = -1; + } } void TextEdit::Text::clear() { @@ -267,6 +282,7 @@ void TextEdit::Text::set(int p_line, const String &p_text) { ERR_FAIL_INDEX(p_line, text.size()); text[p_line].width_cache = -1; + text[p_line].wrap_amount_cache = -1; text[p_line].data = p_text; } @@ -277,6 +293,7 @@ void TextEdit::Text::insert(int p_at, const String &p_text) { line.breakpoint = false; line.hidden = false; line.width_cache = -1; + line.wrap_amount_cache = -1; line.data = p_text; text.insert(p_at, line); } @@ -285,6 +302,25 @@ void TextEdit::Text::remove(int p_at) { text.remove(p_at); } +int TextEdit::Text::get_char_width(CharType c, CharType next_c, int px) const { + + int tab_w = font->get_char_size(' ').width * indent_size; + int w = 0; + + if (c == '\t') { + + int left = px % tab_w; + if (left == 0) + w = tab_w; + else + w = tab_w - px % tab_w; // is right... + } else { + + w = font->get_char_size(c, next_c).width; + } + return w; +} + void TextEdit::_update_scrollbars() { Size2 size = get_size(); @@ -299,9 +335,12 @@ void TextEdit::_update_scrollbars() { int hscroll_rows = ((hmin.height - 1) / get_row_height()) + 1; int visible_rows = get_visible_rows(); - int num_rows = MAX(visible_rows, num_lines_from(CLAMP(cursor.line_ofs, 0, text.size() - 1), MIN(visible_rows, text.size() - 1 - cursor.line_ofs))); - int total_rows = (is_hiding_enabled() ? get_total_unhidden_rows() : text.size()); + int first_vis_line = get_first_visible_line(); + int wi; + int num_rows = MAX(visible_rows, num_lines_from_rows(first_vis_line, cursor.wrap_ofs, visible_rows, wi)); + + int total_rows = get_total_visible_rows(); if (scroll_past_end_of_file_enabled) { total_rows += visible_rows - 1; } @@ -347,28 +386,24 @@ void TextEdit::_update_scrollbars() { if (use_vscroll) { v_scroll->show(); - v_scroll->set_max(total_rows); - v_scroll->set_page(visible_rows); + v_scroll->set_max(total_rows + get_visible_rows_offset()); + v_scroll->set_page(visible_rows + get_visible_rows_offset()); if (smooth_scroll_enabled) { v_scroll->set_step(0.25); } else { v_scroll->set_step(1); } - - update_line_scroll_pos(); - if (fabs(v_scroll->get_value() - get_line_scroll_pos()) >= 1) { - cursor.line_ofs += v_scroll->get_value() - get_line_scroll_pos(); - } + set_v_scroll(get_v_scroll()); } else { cursor.line_ofs = 0; - line_scroll_pos = 0; + cursor.wrap_ofs = 0; v_scroll->set_value(0); v_scroll->hide(); } - if (use_hscroll) { + if (use_hscroll && !is_wrap_enabled()) { h_scroll->show(); h_scroll->set_max(total_width); @@ -419,8 +454,8 @@ void TextEdit::_update_selection_mode_pointer() { select(selection.selecting_line, selection.selecting_column, row, col); - cursor_set_line(row); - cursor_set_column(col); + cursor_set_line(row, false); + cursor_set_column(col, false); update(); click_select_held->start(); @@ -454,7 +489,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; @@ -473,7 +508,7 @@ void TextEdit::_update_selection_mode_word() { cursor_set_column(selection.to_column); } } - cursor_set_line(row); + cursor_set_line(row, false); update(); click_select_held->start(); @@ -488,15 +523,15 @@ void TextEdit::_update_selection_mode_line() { col = 0; if (row < selection.selecting_line) { // cursor is above us - cursor_set_line(row - 1); + cursor_set_line(row - 1, false); selection.selecting_column = text[selection.selecting_line].length(); } else { // cursor is below us - cursor_set_line(row + 1); + cursor_set_line(row + 1, false); selection.selecting_column = 0; col = text[row].length(); } - cursor_set_column(0); + cursor_set_column(0, false); select(selection.selecting_line, selection.selecting_column, row, col); update(); @@ -514,13 +549,13 @@ void TextEdit::_notification(int p_what) { MessageQueue::get_singleton()->push_call(this, "_cursor_changed_emit"); if (text_changed_dirty) MessageQueue::get_singleton()->push_call(this, "_text_changed_emit"); - + update_wrap_at(); } break; case NOTIFICATION_RESIZED: { cache.size = get_size(); - adjust_viewport_to_cursor(); - + _update_scrollbars(); + update_wrap_at(); } break; case NOTIFICATION_THEME_CHANGED: { @@ -536,26 +571,25 @@ void TextEdit::_notification(int p_what) { draw_caret = false; update(); } break; - case NOTIFICATION_PHYSICS_PROCESS: { - if (scrolling && v_scroll->get_value() != target_v_scroll) { - double target_y = target_v_scroll - v_scroll->get_value(); + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { + if (scrolling && get_v_scroll() != target_v_scroll) { + double target_y = target_v_scroll - get_v_scroll(); double dist = sqrt(target_y * target_y); double vel = ((target_y / dist) * v_scroll_speed) * get_physics_process_delta_time(); if (Math::abs(vel) >= dist) { - v_scroll->set_value(target_v_scroll); + set_v_scroll(target_v_scroll); scrolling = false; - set_physics_process(false); + set_physics_process_internal(false); } else { - v_scroll->set_value(v_scroll->get_value() + vel); + set_v_scroll(get_v_scroll() + 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 +652,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 +775,6 @@ void TextEdit::_notification(int p_what) { j--; } if (escaped) { - j--; cc = '\\'; continue; } @@ -804,18 +803,20 @@ void TextEdit::_notification(int p_what) { } } - int deregion = 0; //force it to clear inrgion Point2 cursor_pos; // get the highlighted words String highlighted_text = get_selection_text(); String line_num_padding = line_numbers_zero_padded ? "0" : " "; - update_line_scroll_pos(); - int line = cursor.line_ofs - 1; - // another row may be visible during smooth scrolling + int cursor_wrap_index = get_cursor_wrap_index(); + + FontDrawer drawer(cache.font, Color(1, 1, 1)); + + int line = get_first_visible_line() - 1; int draw_amount = visible_rows + (smooth_scroll_enabled ? 1 : 0); + draw_amount += times_line_wraps(line + 1); for (int i = 0; i < draw_amount; i++) { line++; @@ -833,379 +834,360 @@ void TextEdit::_notification(int p_what) { if (line < 0 || line >= (int)text.size()) continue; - const String &str = text[line]; - - int char_margin = xmargin_beg - cursor.x_ofs; - int char_ofs = 0; + const String &fullstr = text[line]; - int ofs_readonly = 0; - int ofs_x = 0; + Map<int, HighlighterInfo> color_map; + if (syntax_coloring) { + color_map = _get_line_syntax_highlighting(line); + } + // ensure we at least use the font color + Color current_color = cache.font_color; if (readonly) { - ofs_readonly = cache.style_readonly->get_offset().y / 2; - ofs_x = cache.style_readonly->get_offset().x / 2; + current_color.a *= readonly_alpha; } - int ofs_y = (i * get_row_height() + cache.line_spacing / 2) + ofs_readonly; - 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 line_wrap_amount = times_line_wraps(line); + int last_wrap_column = 0; + Vector<String> wrap_rows = get_wrap_rows_text(line); - if (!search_text.empty()) - search_text_col = _get_column_pos_of_word(search_text, str, search_flags, 0); + for (int line_wrap_index = 0; line_wrap_index < line_wrap_amount + 1; line_wrap_index++) { + if (line_wrap_index != 0) { + i++; + if (i >= draw_amount) + break; + } - 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 String &str = wrap_rows[line_wrap_index]; + int indent_px = line_wrap_index != 0 ? get_indent_level(line) * cache.font->get_char_size(' ').width : 0; - const Map<int, Text::ColorRegionInfo> &cri_map = text.get_color_region_info(line); + if (line_wrap_index > 0) + last_wrap_column += wrap_rows[line_wrap_index - 1].length(); - if (text.is_marked(line)) { + int char_margin = xmargin_beg - cursor.x_ofs; + char_margin += indent_px; + int char_ofs = 0; - VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, xmargin_end - xmargin_beg, get_row_height()), cache.mark_color); - } - - if (str.length() == 0) { - // draw line background if empty as we won't loop at at all - if (line == cursor.line && highlight_current_line) { - VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(ofs_x, ofs_y, xmargin_end, get_row_height()), cache.current_line_color); + int ofs_readonly = 0; + int ofs_x = 0; + if (readonly) { + ofs_readonly = cache.style_readonly->get_offset().y / 2; + ofs_x = cache.style_readonly->get_offset().x / 2; } - // give visual indication of empty selected line - if (selection.active && line >= selection.from_line && line <= selection.to_line) { - 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 (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); - } - } + int ofs_y = (i * get_row_height() + cache.line_spacing / 2) + ofs_readonly; + ofs_y -= cursor.wrap_ofs * get_row_height(); + if (smooth_scroll_enabled) + ofs_y += (-get_v_scroll_offset()) * get_row_height(); - if (text.is_breakpoint(line) && !draw_breakpoint_gutter) { -#ifdef TOOLS_ENABLED - VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y + get_row_height() - EDSCALE, xmargin_end - xmargin_beg, EDSCALE), cache.breakpoint_color); -#else - VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, xmargin_end - xmargin_beg, get_row_height()), cache.breakpoint_color); -#endif - } + // check if line contains highlighted word + int highlighted_text_col = -1; + int search_text_col = -1; + int highlighted_word_col = -1; - // draw breakpoint marker - if (text.is_breakpoint(line)) { - if (draw_breakpoint_gutter) { - int vertical_gap = (get_row_height() * 40) / 100; - int horizontal_gap = (cache.breakpoint_gutter_width * 30) / 100; - int marker_height = get_row_height() - (vertical_gap * 2); - int marker_width = cache.breakpoint_gutter_width - (horizontal_gap * 2); - // no transparency on marker - VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cache.style_normal->get_margin(MARGIN_LEFT) + horizontal_gap - 2, ofs_y + vertical_gap, marker_width, marker_height), Color(cache.breakpoint_color.r, cache.breakpoint_color.g, cache.breakpoint_color.b)); - } - } + if (!search_text.empty()) + search_text_col = _get_column_pos_of_word(search_text, str, search_flags, 0); - // draw fold markers - if (draw_fold_gutter) { - int horizontal_gap = (cache.fold_gutter_width * 30) / 100; - int gutter_left = cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + cache.line_number_w; - if (is_folded(line)) { - int xofs = horizontal_gap - (cache.can_fold_icon->get_width()) / 2; - int yofs = (get_row_height() - cache.folded_icon->get_height()) / 2; - cache.folded_icon->draw(ci, Point2(gutter_left + xofs + ofs_x, ofs_y + yofs), cache.code_folding_color); - } else if (can_fold(line)) { - int xofs = -cache.can_fold_icon->get_width() / 2 - horizontal_gap + 3; - int yofs = (get_row_height() - cache.can_fold_icon->get_height()) / 2; - cache.can_fold_icon->draw(ci, Point2(gutter_left + xofs + ofs_x, ofs_y + yofs), cache.code_folding_color); - } - } + 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); - if (cache.line_number_w) { - String fc = String::num(line + 1); - while (fc.length() < line_number_char_count) { - fc = line_num_padding + fc; + if (select_identifiers_enabled && highlighted_word.length() != 0) { + if (_is_char(highlighted_word[0])) { + highlighted_word_col = _get_column_pos_of_word(highlighted_word, fullstr, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, 0); + } } - 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 (text.is_marked(line)) { + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, xmargin_end - xmargin_beg, get_row_height()), cache.mark_color); } - 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 + if (str.length() == 0) { + // draw line background if empty as we won't loop at at all + if (line == cursor.line && cursor_wrap_index == line_wrap_index && highlight_current_line) { + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(ofs_x, ofs_y, xmargin_end, get_row_height()), cache.current_line_color); } - // allow ABCDEF in hex notation - if (is_hex_notation && (_is_hex_symbol(str[j]) || is_number)) { - is_number = true; - } else { - is_hex_notation = false; + // give visual indication of empty selected 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 etc will draw over it, draw the rest of line marker later. + if (line == cursor.line && cursor_wrap_index == line_wrap_index && highlight_current_line) { + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(0, ofs_y, xmargin_beg, get_row_height()), cache.current_line_color); } + } - // 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 (line_wrap_index == 0) { + // only do these if we are on the first wrapped part of a line - if (str[j] == 'x' && str[j - 1] == '0') { - is_hex_notation = true; - } + if (text.is_breakpoint(line) && !draw_breakpoint_gutter) { +#ifdef TOOLS_ENABLED + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y + get_row_height() - EDSCALE, xmargin_end - xmargin_beg, EDSCALE), cache.breakpoint_color); +#else + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, xmargin_end - xmargin_beg, get_row_height()), cache.breakpoint_color); +#endif } - if (!in_word && _is_char(str[j]) && !is_number) { - in_word = true; + // draw breakpoint marker + if (text.is_breakpoint(line)) { + if (draw_breakpoint_gutter) { + int vertical_gap = (get_row_height() * 40) / 100; + int horizontal_gap = (cache.breakpoint_gutter_width * 30) / 100; + int marker_height = get_row_height() - (vertical_gap * 2); + int marker_width = cache.breakpoint_gutter_width - (horizontal_gap * 2); + // no transparency on marker + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cache.style_normal->get_margin(MARGIN_LEFT) + horizontal_gap - 2, ofs_y + vertical_gap, marker_width, marker_height), Color(cache.breakpoint_color.r, cache.breakpoint_color.g, cache.breakpoint_color.b)); + } } - if ((in_keyword || in_word) && !is_hex_notation) { - is_number = false; + // draw fold markers + if (draw_fold_gutter) { + int horizontal_gap = (cache.fold_gutter_width * 30) / 100; + int gutter_left = cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + cache.line_number_w; + if (is_folded(line)) { + int xofs = horizontal_gap - (cache.can_fold_icon->get_width()) / 2; + int yofs = (get_row_height() - cache.folded_icon->get_height()) / 2; + cache.folded_icon->draw(ci, Point2(gutter_left + xofs + ofs_x, ofs_y + yofs), cache.code_folding_color); + } else if (can_fold(line)) { + int xofs = -cache.can_fold_icon->get_width() / 2 - horizontal_gap + 3; + int yofs = (get_row_height() - cache.can_fold_icon->get_height()) / 2; + cache.can_fold_icon->draw(ci, Point2(gutter_left + xofs + ofs_x, ofs_y + yofs), cache.code_folding_color); + } } - if (is_symbol && str[j] != '.' && in_word) { - in_word = false; + // draw line numbers + if (cache.line_number_w) { + String fc = String::num(line + 1); + while (fc.length() < line_number_char_count) { + fc = line_num_padding + fc; + } + + 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); } + } - if (is_symbol && cri_map.has(j)) { + //loop through characters in one line + for (int j = 0; j < str.length(); j++) { - const Text::ColorRegionInfo &cri = cri_map[j]; + if (syntax_coloring) { + if (color_map.has(last_wrap_column + j)) { + current_color = color_map[last_wrap_column + j].color; + if (readonly) { + current_color.a *= readonly_alpha; + } + } + color = current_color; + } - if (in_region == -1) { + int char_w; - if (!cri.end) { + //handle tabulator + char_w = text.get_char_width(str[j], str[j + 1], char_ofs); - in_region = cri.region; - } - } else if (in_region == cri.region && !color_regions[cri.region].line_only) { //ignore otherwise + if ((char_ofs + char_margin) < xmargin_beg) { + char_ofs += char_w; - if (cri.end || color_regions[cri.region].eq) { + // line highlighting handle horizontal clipping + if (line == cursor.line && cursor_wrap_index == line_wrap_index && highlight_current_line) { - deregion = color_regions[cri.region].eq ? color_regions[cri.region].begin_key.length() : color_regions[cri.region].end_key.length(); + 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 - (char_ofs + char_margin + char_w), 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 + ofs_x), get_row_height()), cache.current_line_color); } } + continue; } - if (!is_char) { - in_keyword = false; - underlined = false; + if ((char_ofs + char_margin + char_w) >= xmargin_end) { + if (syntax_coloring) + continue; + else + break; } - if (in_region == -1 && !in_keyword && is_char && !prev_is_char) { + bool in_search_result = false; - int to = j; - while (to < str.length() && _is_text_char(str[to])) - to++; + if (search_text_col != -1) { + // if we are at the end check for new search result on same line + if (j >= search_text_col + search_text.length()) + search_text_col = _get_column_pos_of_word(search_text, str, search_flags, j); - uint32_t hash = String::hash(&str[j], to - j); - StrRange range(&str[j], to - j); + in_search_result = j >= search_text_col && j < search_text_col + search_text.length(); - 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_search_result) { + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin, ofs_y), Size2i(char_w, get_row_height())), cache.search_result_color); } } - if (!in_function_name && in_word && !in_keyword) { + //current line highlighting + bool in_selection = (selection.active && line >= selection.from_line && line <= selection.to_line && (line > selection.from_line || last_wrap_column + j >= selection.from_column) && (line < selection.to_line || last_wrap_column + j < selection.to_column)); - int k = j; - while (k < str.length() && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') { - k++; + if (line == cursor.line && cursor_wrap_index == line_wrap_index && highlight_current_line) { + // draw the wrap indent offset highlight + if (line_wrap_index != 0 && j == 0) { + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(char_ofs + char_margin - indent_px, ofs_y, (char_ofs + char_margin), get_row_height()), cache.current_line_color); } - - // check for space between name and bracket - while (k < str.length() && (str[k] == '\t' || str[k] == ' ')) { - k++; + // if its the last char draw to end of the line + if (j == str.length() - 1) { + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(char_ofs + char_margin + char_w, ofs_y, xmargin_end - (char_ofs + char_margin + char_w), get_row_height()), cache.current_line_color); } - - if (str[k] == '(') { - in_function_name = true; + // actual text + if (!in_selection) { + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(char_w, get_row_height())), cache.current_line_color); } } - 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 (in_selection) { + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(char_w, get_row_height())), cache.selection_color); } - 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; - } - int char_w; - - //handle tabulator + if (in_search_result) { + Color border_color = (line == search_result_line && j >= search_result_col && j < search_result_col + search_text.length()) ? cache.font_color : cache.search_result_border_color; - if (str[j] == '\t') { - int left = char_ofs % tab_w; - if (left == 0) - char_w = tab_w; - else - char_w = tab_w - char_ofs % tab_w; // is right... + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(char_w, 1)), border_color); + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y + get_row_height() - 1), Size2i(char_w, 1)), border_color); - } else { - char_w = cache.font->get_char_size(str[j], str[j + 1]).width; - } - - if ((char_ofs + char_margin) < xmargin_beg) { - char_ofs += char_w; - continue; - } + if (j == search_text_col) + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(1, get_row_height())), border_color); + if (j == search_text_col + search_text.length() - 1) + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + char_w + ofs_x - 1, ofs_y), Size2i(1, get_row_height())), border_color); + } - if ((char_ofs + char_margin + char_w) >= xmargin_end) { - if (syntax_coloring) - continue; - else - break; - } + if (highlight_all_occurrences) { + if (highlighted_text_col != -1) { - bool in_search_result = false; + // if we are at the end check for new word on same line + if (j > highlighted_text_col + highlighted_text.length()) { + highlighted_text_col = _get_column_pos_of_word(highlighted_text, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, j); + } - if (search_text_col != -1) { - // if we are at the end check for new search result on same line - if (j >= search_text_col + search_text.length()) - search_text_col = _get_column_pos_of_word(search_text, str, search_flags, j); + bool in_highlighted_word = (j >= highlighted_text_col && j < highlighted_text_col + highlighted_text.length()); - in_search_result = j >= search_text_col && j < search_text_col + search_text.length(); + // if this is the original highlighted text we don't want to highlight it again + if (cursor.line == line && cursor_wrap_index == line_wrap_index && (cursor.column >= highlighted_text_col && cursor.column <= highlighted_text_col + highlighted_text.length())) { + in_highlighted_word = false; + } - if (in_search_result) { - VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin, ofs_y), Size2i(char_w, get_row_height())), cache.search_result_color); + if (in_highlighted_word) { + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(char_w, get_row_height())), cache.word_highlighted_color); + } + } } - } - //current line highlighting - bool in_selection = (selection.active && line >= selection.from_line && line <= selection.to_line && (line > selection.from_line || j >= selection.from_column) && (line < selection.to_line || j < selection.to_column)); - - if (line == cursor.line && highlight_current_line) { - // if its the last char draw to end of the line - if (j == str.length() - 1) { - VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(char_ofs + char_margin + char_w, ofs_y, xmargin_end - (char_ofs + char_margin + char_w), get_row_height()), cache.current_line_color); - } - // actual text - if (!in_selection) { - VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(char_w, get_row_height())), cache.current_line_color); + if (highlighted_word_col != -1) { + if (j + last_wrap_column > highlighted_word_col + highlighted_word.length()) { + highlighted_word_col = _get_column_pos_of_word(highlighted_word, fullstr, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, j + last_wrap_column); + } + underlined = (j + last_wrap_column >= highlighted_word_col && j + last_wrap_column < highlighted_word_col + highlighted_word.length()); } - } - if (in_selection) { - VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(char_w, get_row_height())), cache.selection_color); - } + if (brace_matching_enabled) { + if ((brace_open_match_line == line && brace_open_match_column == last_wrap_column + j) || + (cursor.column == last_wrap_column + j && cursor.line == line && cursor_wrap_index == line_wrap_index && (brace_open_matching || brace_open_mismatch))) { + + if (brace_open_mismatch) + color = cache.brace_mismatch_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 (in_search_result) { - Color border_color = (line == search_result_line && j >= search_result_col && j < search_result_col + search_text.length()) ? cache.font_color : cache.search_result_border_color; + if ((brace_close_match_line == line && brace_close_match_column == last_wrap_column + j) || + (cursor.column == last_wrap_column + j + 1 && cursor.line == line && cursor_wrap_index == line_wrap_index && (brace_close_matching || brace_close_mismatch))) { - VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(char_w, 1)), border_color); - VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y + get_row_height() - 1), Size2i(char_w, 1)), border_color); + if (brace_close_mismatch) + color = cache.brace_mismatch_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 (j == search_text_col) - VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(1, get_row_height())), border_color); - if (j == search_text_col + search_text.length() - 1) - VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + char_w + ofs_x - 1, ofs_y), Size2i(1, get_row_height())), border_color); - } + if (cursor.column == last_wrap_column + j && cursor.line == line && cursor_wrap_index == line_wrap_index) { - if (highlight_all_occurrences) { - if (highlighted_text_col != -1) { + cursor_pos = Point2i(char_ofs + char_margin + ofs_x, ofs_y); - // if we are at the end check for new word on same line - if (j > highlighted_text_col + highlighted_text.length()) { - highlighted_text_col = _get_column_pos_of_word(highlighted_text, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, j); + if (insert_mode) { + cursor_pos.y += (get_row_height() - 3); } - bool in_highlighted_word = (j >= highlighted_text_col && j < highlighted_text_col + highlighted_text.length()); + int caret_w = (str[j] == '\t') ? cache.font->get_char_size(' ').width : char_w; + if (ime_text.length() > 0) { + int ofs = 0; + while (true) { + if (ofs >= ime_text.length()) + break; + + CharType cchar = ime_text[ofs]; + CharType next = ime_text[ofs + 1]; + int im_char_width = cache.font->get_char_size(cchar, next).width; + + if ((char_ofs + char_margin + im_char_width) >= xmargin_end) + break; + + bool selected = ofs >= ime_selection.x && ofs < ime_selection.x + ime_selection.y; + if (selected) { + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(char_ofs + char_margin, ofs_y + get_row_height()), Size2(im_char_width, 3)), color); + } else { + 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); + } - /* if this is the original highlighted text we don't want to highlight it again */ - if (cursor.line == line && (cursor.column >= highlighted_text_col && cursor.column <= highlighted_text_col + highlighted_text.length())) { - in_highlighted_word = false; - } + drawer.draw_char(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + ascent), cchar, next, color); - if (in_highlighted_word) { - VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(char_w, get_row_height())), cache.word_highlighted_color); + char_ofs += im_char_width; + ofs++; + } + } + if (ime_text.length() == 0) { + if (draw_caret) { + if (insert_mode) { + int caret_h = (block_caret) ? 4 : 1; + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(caret_w, caret_h)), cache.caret_color); + } else { + caret_w = (block_caret) ? caret_w : 1; + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(caret_w, get_row_height())), cache.caret_color); + } + } } } - } - 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 (cursor.column == last_wrap_column + j && cursor.line == line && cursor_wrap_index == line_wrap_index && block_caret && draw_caret && !insert_mode) { + color = cache.caret_background_color; + } else if (!syntax_coloring && block_caret) { + color = cache.font_color; + color.a *= readonly_alpha; + } - 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); + if (str[j] >= 32) { + 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); + } + } else if (draw_tabs && str[j] == '\t') { + int yofs = (get_row_height() - cache.tab_icon->get_height()) / 2; + cache.tab_icon->draw(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + yofs), in_selection && override_selected_font_color ? cache.font_selected_color : color); } - if ( - (brace_close_match_line == line && brace_close_match_column == j) || - (cursor.column == j + 1 && cursor.line == line && (brace_close_matching || brace_close_mismatch))) { + char_ofs += char_w; - 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); + if (line_wrap_index == line_wrap_amount && j == str.length() - 1 && is_folded(line)) { + int yofs = (get_row_height() - cache.folded_eol_icon->get_height()) / 2; + int xofs = cache.folded_eol_icon->get_width() / 2; + Color eol_color = cache.code_folding_color; + eol_color.a = 1; + cache.folded_eol_icon->draw(ci, Point2(char_ofs + char_margin + xofs + ofs_x, ofs_y + yofs), eol_color); } } - if (cursor.column == j && cursor.line == line) { + if (cursor.column == last_wrap_column + str.length() && cursor.line == line && cursor_wrap_index == line_wrap_index && (char_ofs + char_margin) >= xmargin_beg) { cursor_pos = Point2i(char_ofs + char_margin + ofs_x, ofs_y); if (insert_mode) { cursor_pos.y += (get_row_height() - 3); } - - int caret_w = (str[j] == '\t') ? cache.font->get_char_size(' ').width : char_w; if (ime_text.length() > 0) { int ofs = 0; while (true) { @@ -1226,7 +1208,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++; @@ -1235,92 +1217,17 @@ void TextEdit::_notification(int p_what) { if (ime_text.length() == 0) { if (draw_caret) { if (insert_mode) { + int char_w = cache.font->get_char_size(' ').width; int caret_h = (block_caret) ? 4 : 1; - VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(caret_w, caret_h)), cache.caret_color); + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(char_w, caret_h)), cache.caret_color); } else { - caret_w = (block_caret) ? caret_w : 1; + int char_w = cache.font->get_char_size(' ').width; + int caret_w = (block_caret) ? char_w : 1; VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(caret_w, get_row_height())), cache.caret_color); } } } } - - if (cursor.column == j && cursor.line == line && block_caret && draw_caret && !insert_mode) { - color = cache.caret_background_color; - } else if (!syntax_coloring && block_caret) { - color = cache.font_color; - color.a *= readonly_alpha; - } - - 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); - 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); - } - } - - else if (draw_tabs && str[j] == '\t') { - int yofs = (get_row_height() - cache.tab_icon->get_height()) / 2; - cache.tab_icon->draw(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + yofs), in_selection && override_selected_font_color ? cache.font_selected_color : color); - } - - char_ofs += char_w; - - if (j == str.length() - 1 && is_folded(line)) { - int yofs = (get_row_height() - cache.folded_eol_icon->get_height()) / 2; - int xofs = cache.folded_eol_icon->get_width() / 2; - Color eol_color = cache.code_folding_color; - eol_color.a = 1; - cache.folded_eol_icon->draw(ci, Point2(char_ofs + char_margin + xofs + ofs_x, ofs_y + yofs), eol_color); - } - } - - if (cursor.column == str.length() && cursor.line == line && (char_ofs + char_margin) >= xmargin_beg) { - - cursor_pos = Point2i(char_ofs + char_margin + ofs_x, ofs_y); - - if (insert_mode) { - cursor_pos.y += (get_row_height() - 3); - } - if (ime_text.length() > 0) { - int ofs = 0; - while (true) { - if (ofs >= ime_text.length()) - break; - - CharType cchar = ime_text[ofs]; - CharType next = ime_text[ofs + 1]; - int im_char_width = cache.font->get_char_size(cchar, next).width; - - if ((char_ofs + char_margin + im_char_width) >= xmargin_end) - break; - - bool selected = ofs >= ime_selection.x && ofs < ime_selection.x + ime_selection.y; - if (selected) { - VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(char_ofs + char_margin, ofs_y + get_row_height()), Size2(im_char_width, 3)), color); - } else { - 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); - - char_ofs += im_char_width; - ofs++; - } - } - if (ime_text.length() == 0) { - if (draw_caret) { - if (insert_mode) { - int char_w = cache.font->get_char_size(' ').width; - int caret_h = (block_caret) ? 4 : 1; - VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(char_w, caret_h)), cache.caret_color); - } else { - int char_w = cache.font->get_char_size(' ').width; - int caret_w = (block_caret) ? char_w : 1; - VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(caret_w, get_row_height())), cache.caret_color); - } - } - } } } @@ -1484,6 +1391,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 +1406,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 +1419,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; } @@ -1664,17 +1571,22 @@ void TextEdit::backspace_at_cursor() { cursor_set_column(prev_column); } -void TextEdit::indent_selection_right() { +void TextEdit::indent_right() { - if (!is_selection_active()) { - return; - } + int start_line; + int end_line; begin_complex_operation(); - int start_line = get_selection_from_line(); - int end_line = get_selection_to_line(); + + if (is_selection_active()) { + start_line = get_selection_from_line(); + end_line = get_selection_to_line(); + } else { + start_line = cursor.line; + end_line = start_line; + } // ignore if the cursor is not past the first column - if (get_selection_to_column() == 0) { + if (is_selection_active() && get_selection_to_column() == 0) { end_line--; } @@ -1688,23 +1600,31 @@ void TextEdit::indent_selection_right() { set_line(i, line_text); } - // fix selection being off by one on the last line - selection.to_column++; + // fix selection and cursor being off by one on the last line + if (is_selection_active()) { + select(selection.from_line, selection.from_column + 1, selection.to_line, selection.to_column + 1); + } + cursor_set_column(cursor.column + 1, false); end_complex_operation(); update(); } -void TextEdit::indent_selection_left() { +void TextEdit::indent_left() { - if (!is_selection_active()) { - return; - } + int start_line; + int end_line; begin_complex_operation(); - int start_line = get_selection_from_line(); - int end_line = get_selection_to_line(); + + if (is_selection_active()) { + start_line = get_selection_from_line(); + end_line = get_selection_to_line(); + } else { + start_line = cursor.line; + end_line = start_line; + } // ignore if the cursor is not past the first column - if (get_selection_to_column() == 0) { + if (is_selection_active() && get_selection_to_column() == 0) { end_line--; } String last_line_text = get_line(end_line); @@ -1721,10 +1641,11 @@ void TextEdit::indent_selection_left() { } } - // fix selection being off by one on the last line - if (last_line_text != get_line(end_line) && selection.to_column > 0) { - selection.to_column--; + // fix selection and cursor being off by one on the last line + if (is_selection_active() && last_line_text != get_line(end_line)) { + select(selection.from_line, selection.from_column - 1, selection.to_line, selection.to_column - 1); } + cursor_set_column(cursor.column - 1, false); end_complex_operation(); update(); } @@ -1734,14 +1655,16 @@ 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 /= get_row_height(); - int lsp = get_line_scroll_pos(true); - int row = cursor.line_ofs + (rows + (round(v_scroll->get_value()) - lsp)); + rows += get_v_scroll_offset(); + int first_vis_line = get_first_visible_line(); + int row = first_vis_line + Math::floor(rows); + int wrap_index = 0; + + if (is_wrap_enabled() || is_hiding_enabled()) { - 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)); - row = CLAMP(row, 0, text.size() - num_lines_from(text.size() - 1, -1)); + int f_ofs = num_lines_from_rows(first_vis_line, cursor.wrap_ofs, rows + 1, wrap_index) - 1; + row = first_vis_line + f_ofs; + row = CLAMP(row, 0, get_last_visible_line() + 1); } if (row < 0) @@ -1755,9 +1678,19 @@ void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) co col = text[row].size(); } else { - col = p_mouse.x - (cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width); - col += cursor.x_ofs; - col = get_char_pos_for(col, get_line(row)); + int colx = p_mouse.x - (cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width); + colx += cursor.x_ofs; + col = get_char_pos_for_line(colx, row, wrap_index); + if (is_wrap_enabled() && wrap_index < times_line_wraps(row)) { + // move back one if we are at the end of the row + Vector<String> rows = get_wrap_rows_text(row); + int row_end_col = 0; + for (int i = 0; i < wrap_index + 1; i++) { + row_end_col += rows[i].length(); + } + if (col >= row_end_col) + col -= 1; + } } r_row = row; @@ -1808,10 +1741,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())); @@ -1824,7 +1765,6 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { _reset_caret_blink_timer(); int row, col; - update_line_scroll_pos(); _get_mouse_pos(Point2i(mb->get_position().x, mb->get_position().y), row, col); if (mb->get_command() && highlighted_word != String()) { @@ -1942,7 +1882,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { } else if (mb->is_doubleclick() && text[cursor.line].length()) { - //doubleclick select world + //doubleclick select word selection.selecting_mode = Selection::MODE_WORD; _update_selection_mode_word(); last_dblclk = OS::get_singleton()->get_ticks_msec(); @@ -1953,6 +1893,30 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { if (mb->get_button_index() == BUTTON_RIGHT && context_menu_enabled) { + _reset_caret_blink_timer(); + + int row, col; + _get_mouse_pos(Point2i(mb->get_position().x, mb->get_position().y), row, col); + + if (is_right_click_moving_caret()) { + if (is_selection_active()) { + + int from_line = get_selection_from_line(); + int to_line = get_selection_to_line(); + int from_column = get_selection_from_column(); + int to_column = get_selection_to_column(); + + if (row < from_line || row > to_line || (row == from_line && col < from_column) || (row == to_line && col > to_column)) { + // Right click is outside the seleted text + deselect(); + } + } + if (!is_selection_active()) { + cursor_set_line(row, true, false); + cursor_set_column(col); + } + } + menu->set_position(get_global_transform().xform(get_local_mouse_position())); menu->set_size(Vector2(1, 1)); menu->popup(); @@ -2065,9 +2029,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; } @@ -2076,9 +2043,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; } @@ -2216,9 +2186,9 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { case KEY_TAB: { if (k->get_shift()) { - indent_selection_left(); + indent_left(); } else { - indent_selection_right(); + indent_right(); } dobreak = true; accept_event(); @@ -2389,33 +2359,55 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { if (readonly) break; - if (selection.active) { - + if (is_selection_active()) { + if (k->get_shift()) { + indent_left(); + } else { + indent_right(); + } } else { if (k->get_shift()) { //simple unindent int cc = cursor.column; + + const int len = text[cursor.line].length(); + const String &line = text[cursor.line]; + + int left = 0; // number of whitespace chars at beginning of line + while (left < len && (line[left] == '\t' || line[left] == ' ')) + left++; + cc = MIN(cc, left); + + while (cc < indent_size && cc < left && line[cc] == ' ') + cc++; + if (cc > 0 && cc <= text[cursor.line].length()) { - if (text[cursor.line][cursor.column - 1] == '\t') { - backspace_at_cursor(); + if (text[cursor.line][cc - 1] == '\t') { + _remove_text(cursor.line, cc - 1, cursor.line, cc); + if (cursor.column >= left) + cursor_set_column(MAX(0, cursor.column - 1)); + update(); } else { - if (cursor.column - indent_size >= 0) { + int n = 0; - bool unindent = true; - for (int i = 1; i <= indent_size; i++) { - if (text[cursor.line][cursor.column - i] != ' ') { - unindent = false; - break; - } + for (int i = 1; i <= MIN(cc, indent_size); i++) { + if (line[cc - i] != ' ') { + break; } + n++; + } - if (unindent) { - _remove_text(cursor.line, cursor.column - indent_size, cursor.line, cursor.column); - cursor_set_column(cursor.column - indent_size); - } + if (n > 0) { + _remove_text(cursor.line, cc - n, cursor.line, cc); + if (cursor.column > left - n) // inside text? + cursor_set_column(MAX(0, cursor.column - n)); + update(); } } + } else if (cc == 0 && line.length() > 0 && line[0] == '\t') { + _remove_text(cursor.line, 0, cursor.line, 1); + update(); } } else { //simple indent @@ -2478,6 +2470,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); @@ -2514,28 +2512,26 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { break; } else if (k->get_command()) { #endif - bool prev_char = false; int cc = cursor.column; if (cc == 0 && cursor.line > 0) { cursor_set_line(cursor.line - 1); cursor_set_column(text[cursor.line].length()); - break; - } - - while (cc > 0) { + } else { + bool prev_char = false; - bool ischar = _is_text_char(text[cursor.line][cc - 1]); + while (cc > 0) { + bool ischar = _is_text_char(text[cursor.line][cc - 1]); - if (prev_char && !ischar) - break; + if (prev_char && !ischar) + break; - prev_char = ischar; - cc--; + prev_char = ischar; + cc--; + } + cursor_set_column(cc); } - cursor_set_column(cc); - } else if (cursor.column == 0) { if (cursor.line > 0) { @@ -2578,27 +2574,25 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { break; } else if (k->get_command()) { #endif - bool prev_char = false; int cc = cursor.column; if (cc == text[cursor.line].length() && cursor.line < text.size() - 1) { cursor_set_line(cursor.line + 1); cursor_set_column(0); - break; - } - - while (cc < text[cursor.line].length()) { + } else { + bool prev_char = false; - bool ischar = _is_text_char(text[cursor.line][cc]); + while (cc < text[cursor.line].length()) { + bool ischar = _is_text_char(text[cursor.line][cc]); - if (prev_char && !ischar) - break; - prev_char = ischar; - cc++; + if (prev_char && !ischar) + break; + prev_char = ischar; + cc++; + } + cursor_set_column(cc); } - cursor_set_column(cc); - } else if (cursor.column == text[cursor.line].length()) { if (cursor.line < text.size() - 1) { @@ -2639,11 +2633,25 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { break; } - if (k->get_command()) + if (k->get_command()) { cursor_set_line(0); - else + } else #endif - cursor_set_line(cursor_get_line() - num_lines_from(CLAMP(cursor.line - 1, 0, text.size() - 1), -1)); + { + int cur_wrap_index = get_cursor_wrap_index(); + if (cur_wrap_index > 0) { + cursor_set_line(cursor.line, true, false, cur_wrap_index - 1); + } else if (cursor.line == 0) { + cursor_set_column(0); + } else { + int new_line = cursor.line - num_lines_from(cursor.line - 1, -1); + if (line_wraps(new_line)) { + cursor_set_line(new_line, true, false, times_line_wraps(new_line)); + } else { + cursor_set_line(new_line, true, false); + } + } + } if (k->get_shift()) _post_shift_selection(); @@ -2670,24 +2678,34 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { _scroll_lines_down(); break; } + #else if (k->get_command() && k->get_alt()) { _scroll_lines_down(); break; } - if (k->get_command()) - cursor_set_line(text.size() - 1, true, false); - else + if (k->get_command()) { + cursor_set_line(get_last_unhidden_line(), true, false, 9999); + } else #endif - cursor_set_line(cursor_get_line() + num_lines_from(CLAMP(cursor.line + 1, 0, text.size() - 1), 1), true, false); + { + int cur_wrap_index = get_cursor_wrap_index(); + if (cur_wrap_index < times_line_wraps(cursor.line)) { + cursor_set_line(cursor.line, true, false, cur_wrap_index + 1); + } else if (cursor.line == get_last_unhidden_line()) { + cursor_set_column(text[cursor.line].length()); + } else { + int new_line = cursor.line + num_lines_from(CLAMP(cursor.line + 1, 0, text.size() - 1), 1); + cursor_set_line(new_line, true, false, 0); + } + } if (k->get_shift()) _post_shift_selection(); _cancel_code_hint(); } break; - case KEY_DELETE: { if (readonly) @@ -2750,7 +2768,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; } @@ -2790,19 +2812,31 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { cursor_set_line(0); cursor_set_column(0); } else { - // compute whitespace symbols seq length - 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++; + + // move cursor column to start of wrapped row and then to start of text + Vector<String> rows = get_wrap_rows_text(cursor.line); + int wi = get_cursor_wrap_index(); + int row_start_col = 0; + for (int i = 0; i < wi; i++) { + row_start_col += rows[i].length(); } + if (cursor.column == row_start_col || wi == 0) { + // compute whitespace symbols seq length + 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 (cursor_get_column() == current_line_whitespace_len) + cursor_set_column(0); + else + cursor_set_column(current_line_whitespace_len); + } else { + cursor_set_column(row_start_col); + } } if (k->get_shift()) @@ -2827,7 +2861,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { if (k->get_shift()) _pre_shift_selection(); - cursor_set_line(text.size() - 1, true, false); + cursor_set_line(get_last_unhidden_line(), true, false, 9999); if (k->get_shift()) _post_shift_selection(); @@ -2842,8 +2876,20 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { _pre_shift_selection(); if (k->get_command()) - cursor_set_line(text.size() - 1, true, false); - cursor_set_column(text[cursor.line].length()); + cursor_set_line(get_last_unhidden_line(), true, false, 9999); + + // move cursor column to end of wrapped row and then to end of text + Vector<String> rows = get_wrap_rows_text(cursor.line); + int wi = get_cursor_wrap_index(); + int row_end_col = -1; + for (int i = 0; i < wi + 1; i++) { + row_end_col += rows[i].length(); + } + if (wi == rows.size() - 1 || cursor.column == row_end_col) { + cursor_set_column(text[cursor.line].length()); + } else { + cursor_set_column(row_end_col); + } if (k->get_shift()) _post_shift_selection(); @@ -2867,7 +2913,9 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { if (k->get_shift()) _pre_shift_selection(); - cursor_set_line(cursor_get_line() - num_lines_from(cursor.line, -get_visible_rows()), true, false); + int wi; + int n_line = cursor.line - num_lines_from_rows(cursor.line, get_cursor_wrap_index(), -get_visible_rows(), wi) + 1; + cursor_set_line(n_line, true, false, wi); if (k->get_shift()) _post_shift_selection(); @@ -2881,14 +2929,16 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { scancode_handled = false; break; } - // numlock disabled. fallthrough to key_pageup + // numlock disabled. fallthrough to key_pagedown } case KEY_PAGEDOWN: { if (k->get_shift()) _pre_shift_selection(); - cursor_set_line(cursor_get_line() + num_lines_from(cursor.line, get_visible_rows()), true, false); + int wi; + int n_line = cursor.line + num_lines_from_rows(cursor.line, get_cursor_wrap_index(), get_visible_rows(), wi) - 1; + cursor_set_line(n_line, true, false, wi); if (k->get_shift()) _post_shift_selection(); @@ -2899,13 +2949,64 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { } break; case KEY_A: { - if (!k->get_command() || k->get_shift() || k->get_alt()) { +#ifndef APPLE_STYLE_KEYS + if (!k->get_control() || k->get_shift() || k->get_alt()) { scancode_handled = false; break; } - select_all(); +#else + if ((!k->get_command() && !k->get_control())) { + 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) { @@ -3030,7 +3131,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) { @@ -3076,45 +3177,55 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { void TextEdit::_scroll_up(real_t p_delta) { + if (scrolling && smooth_scroll_enabled && SGN(target_v_scroll - v_scroll->get_value()) != SGN(-p_delta)) + scrolling = false; + if (scrolling) { target_v_scroll = (target_v_scroll - p_delta); } else { - target_v_scroll = (v_scroll->get_value() - p_delta); + target_v_scroll = (get_v_scroll() - p_delta); } if (smooth_scroll_enabled) { if (target_v_scroll <= 0) { target_v_scroll = 0; } - scrolling = true; - set_physics_process(true); + if (Math::abs(target_v_scroll - v_scroll->get_value()) < 1.0) { + v_scroll->set_value(target_v_scroll); + } else { + scrolling = true; + set_physics_process_internal(true); + } } else { - v_scroll->set_value(target_v_scroll); + set_v_scroll(target_v_scroll); } } void TextEdit::_scroll_down(real_t p_delta) { + if (scrolling && smooth_scroll_enabled && SGN(target_v_scroll - v_scroll->get_value()) != SGN(p_delta)) + scrolling = false; + if (scrolling) { target_v_scroll = (target_v_scroll + p_delta); } else { - target_v_scroll = (v_scroll->get_value() + p_delta); + target_v_scroll = (get_v_scroll() + p_delta); } if (smooth_scroll_enabled) { - int max_v_scroll = get_total_unhidden_rows(); - if (!scroll_past_end_of_file_enabled) { - max_v_scroll -= get_visible_rows(); - max_v_scroll = CLAMP(max_v_scroll, 0, get_total_unhidden_rows()); - } - + int max_v_scroll = v_scroll->get_max() - v_scroll->get_page(); if (target_v_scroll > max_v_scroll) { target_v_scroll = max_v_scroll; + v_scroll->set_value(target_v_scroll); + } + if (Math::abs(target_v_scroll - v_scroll->get_value()) < 1.0) { + v_scroll->set_value(target_v_scroll); + } else { + scrolling = true; + set_physics_process_internal(true); } - scrolling = true; - set_physics_process(true); } else { - v_scroll->set_value(target_v_scroll); + set_v_scroll(target_v_scroll); } } @@ -3145,35 +3256,37 @@ void TextEdit::_scroll_lines_up() { scrolling = false; // adjust the vertical scroll - if (get_v_scroll() >= 0) { - set_v_scroll(get_v_scroll() - 1); - } + set_v_scroll(get_v_scroll() - 1); + + // adjust the cursor to viewport + if (!selection.active) { + int cur_line = cursor.line; + int cur_wrap = get_cursor_wrap_index(); + int last_vis_line = get_last_visible_line(); + int last_vis_wrap = get_last_visible_line_wrap_index(); - // adjust the cursor - int num_lines = num_lines_from(CLAMP(cursor.line_ofs, 0, text.size() - 1), get_visible_rows()); - if (cursor.line >= cursor.line_ofs + num_lines && !selection.active) { - cursor_set_line(cursor.line_ofs + num_lines, false, false); + if (cur_line > last_vis_line || (cur_line == last_vis_line && cur_wrap > last_vis_wrap)) { + cursor_set_line(last_vis_line, false, false, last_vis_wrap); + } } } void TextEdit::_scroll_lines_down() { scrolling = false; - // calculate the maximum vertical scroll position - int max_v_scroll = get_total_unhidden_rows(); - if (!scroll_past_end_of_file_enabled) { - max_v_scroll -= get_visible_rows(); - max_v_scroll = CLAMP(max_v_scroll, 0, get_total_unhidden_rows()); - } - // adjust the vertical scroll - if (get_v_scroll() < max_v_scroll) { - set_v_scroll(get_v_scroll() + 1); - } + set_v_scroll(get_v_scroll() + 1); + + // adjust the cursor to viewport + if (!selection.active) { + int cur_line = cursor.line; + int cur_wrap = get_cursor_wrap_index(); + int first_vis_line = get_first_visible_line(); + int first_vis_wrap = cursor.wrap_ofs; - // adjust the cursor - if (cursor.line <= cursor.line_ofs - 1 && !selection.active) { - cursor_set_line(cursor.line_ofs, false, false); + if (cur_line < first_vis_line || (cur_line == first_vis_line && cur_wrap < first_vis_wrap)) { + cursor_set_line(first_vis_line, false, false, first_vis_wrap); + } } } @@ -3227,6 +3340,8 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i text.set_hidden(p_line, false); } + text.set_line_wrap_amount(p_line, -1); + r_end_line = p_line + substrings.size() - 1; r_end_column = text[r_end_line].length() - postinsert_text.length(); @@ -3235,6 +3350,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 { @@ -3280,11 +3396,14 @@ void TextEdit::_base_remove_text(int p_from_line, int p_from_column, int p_to_li text.set(p_from_line, pre_text + post_text); + text.set_line_wrap_amount(p_from_line, -1); + if (!text_changed_dirty && !setting_text) { if (is_inside_tree()) 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) { @@ -3407,6 +3526,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; @@ -3430,61 +3556,63 @@ int TextEdit::get_visible_rows() const { int total = cache.size.height; total -= cache.style_normal->get_minimum_size().height; + if (h_scroll->is_visible_in_tree()) + total -= h_scroll->get_size().height; total /= get_row_height(); return total; } -int TextEdit::get_total_unhidden_rows() const { - if (!is_hiding_enabled()) +int TextEdit::get_total_visible_rows() const { + + // returns the total amount of rows we need in the editor. + // This skips hidden lines and counts each wrapping of a line. + if (!is_hiding_enabled() && !is_wrap_enabled()) return text.size(); - int total_unhidden = 0; + int total_rows = 0; for (int i = 0; i < text.size(); i++) { - if (!text.is_hidden(i)) - total_unhidden++; + if (!text.is_hidden(i)) { + total_rows++; + total_rows += times_line_wraps(i); + } } - return total_unhidden; + return total_rows; } -double TextEdit::get_line_scroll_pos(bool p_recalculate) const { +void TextEdit::update_wrap_at() { - if (!is_hiding_enabled()) - return cursor.line_ofs; - if (!p_recalculate) - return line_scroll_pos; + wrap_at = cache.size.width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width - wrap_right_offset; + update_cursor_wrap_offset(); + text.clear_wrap_cache(); - // count num unhidden lines to the cursor line ofs - double new_line_scroll_pos = 0; - int to = CLAMP(cursor.line_ofs, 0, text.size() - 1); - for (int i = 0; i < to; i++) { - if (!text.is_hidden(i)) - new_line_scroll_pos++; + for (int i = 0; i < text.size(); i++) { + // update all values that wrap + if (!line_wraps(i)) + continue; + Vector<String> rows = get_wrap_rows_text(i); + text.set_line_wrap_amount(i, rows.size() - 1); } - return new_line_scroll_pos; } -void TextEdit::update_line_scroll_pos() { +void TextEdit::adjust_viewport_to_cursor() { - if (!is_hiding_enabled()) { - line_scroll_pos = cursor.line_ofs; - return; - } + // make sure cursor is visible on the screen + scrolling = false; - // count num unhidden lines to the cursor line ofs - double new_line_scroll_pos = 0; - int to = CLAMP(cursor.line_ofs, 0, text.size() - 1); - for (int i = 0; i < to; i++) { - if (!text.is_hidden(i)) - new_line_scroll_pos++; - } - line_scroll_pos = new_line_scroll_pos; -} + int cur_line = cursor.line; + int cur_wrap = get_cursor_wrap_index(); -void TextEdit::adjust_viewport_to_cursor() { - scrolling = false; + int first_vis_line = get_first_visible_line(); + int first_vis_wrap = cursor.wrap_ofs; + int last_vis_line = get_last_visible_line(); + int last_vis_wrap = get_last_visible_line_wrap_index(); - if (cursor.line_ofs > cursor.line) { - cursor.line_ofs = cursor.line; + if (cur_line < first_vis_line || (cur_line == first_vis_line && cur_wrap < first_vis_wrap)) { + // cursor is above screen + set_line_as_first_visible(cur_line, cur_wrap); + } else if (cur_line > last_vis_line || (cur_line == last_vis_line && cur_wrap > last_vis_wrap)) { + // cursor is below screen + set_line_as_last_visible(cur_line, cur_wrap); } int visible_width = cache.size.width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width; @@ -3492,90 +3620,174 @@ void TextEdit::adjust_viewport_to_cursor() { visible_width -= v_scroll->get_combined_minimum_size().width; visible_width -= 20; // give it a little more space - int visible_rows = get_visible_rows(); - if (h_scroll->is_visible_in_tree() && !scroll_past_end_of_file_enabled) - visible_rows -= ((h_scroll->get_combined_minimum_size().height - 1) / get_row_height()); - int num_rows = num_lines_from(CLAMP(cursor.line_ofs, 0, text.size() - 1), MIN(visible_rows, text.size() - 1 - cursor.line_ofs)); - - // make sure the cursor is on the screen - // above the caret - if (cursor.line > (cursor.line_ofs + MAX(num_rows, visible_rows))) { - cursor.line_ofs = cursor.line - num_lines_from(cursor.line, -visible_rows) + 1; - } - // below the caret - if (cursor.line_ofs == cursor.line) { - cursor.line_ofs = cursor.line - 2; - } - int line_ofs_max = text.size() - 1; - if (!scroll_past_end_of_file_enabled) { - line_ofs_max -= num_lines_from(text.size() - 1, -visible_rows) - 1; - line_ofs_max += (h_scroll->is_visible_in_tree() ? 1 : 0); - line_ofs_max += (cursor.line == text.size() - 1 ? 1 : 0); - } - line_ofs_max = MAX(line_ofs_max, 0); - cursor.line_ofs = CLAMP(cursor.line_ofs, 0, line_ofs_max); - - // adjust x offset - int cursor_x = get_column_x_offset(cursor.column, text[cursor.line]); - - if (cursor_x > (cursor.x_ofs + visible_width)) - cursor.x_ofs = cursor_x - visible_width + 1; + if (!is_wrap_enabled()) { + // adjust x offset + int cursor_x = get_column_x_offset(cursor.column, text[cursor.line]); - if (cursor_x < cursor.x_ofs) - cursor.x_ofs = cursor_x; + if (cursor_x > (cursor.x_ofs + visible_width)) + cursor.x_ofs = cursor_x - visible_width + 1; - updating_scrolls = true; - h_scroll->set_value(cursor.x_ofs); - update_line_scroll_pos(); - double new_v_scroll = get_line_scroll_pos(); - // keep offset if smooth scroll is enabled - if (smooth_scroll_enabled) { - new_v_scroll += fmod(v_scroll->get_value(), 1.0); + if (cursor_x < cursor.x_ofs) + cursor.x_ofs = cursor_x; + } else { + cursor.x_ofs = 0; } - v_scroll->set_value(new_v_scroll); - updating_scrolls = false; + h_scroll->set_value(cursor.x_ofs); + update(); } void TextEdit::center_viewport_to_cursor() { - scrolling = false; - if (cursor.line_ofs > cursor.line) - cursor.line_ofs = cursor.line; + // move viewport so the cursor is in the center of the screen + scrolling = false; if (is_line_hidden(cursor.line)) unfold_line(cursor.line); + set_line_as_center_visible(cursor.line, get_cursor_wrap_index()); int visible_width = cache.size.width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width; if (v_scroll->is_visible_in_tree()) visible_width -= v_scroll->get_combined_minimum_size().width; visible_width -= 20; // give it a little more space - 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()); + if (is_wrap_enabled()) { + // center x offset + int cursor_x = get_column_x_offset_for_line(cursor.column, cursor.line); + + if (cursor_x > (cursor.x_ofs + visible_width)) + cursor.x_ofs = cursor_x - visible_width + 1; - 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); - int cursor_x = get_column_x_offset(cursor.column, text[cursor.line]); + if (cursor_x < cursor.x_ofs) + cursor.x_ofs = cursor_x; + } else { + cursor.x_ofs = 0; + } + h_scroll->set_value(cursor.x_ofs); - if (cursor_x > (cursor.x_ofs + visible_width)) - cursor.x_ofs = cursor_x - visible_width + 1; + update(); +} - if (cursor_x < cursor.x_ofs) - cursor.x_ofs = cursor_x; +void TextEdit::update_cursor_wrap_offset() { + int first_vis_line = get_first_visible_line(); + if (line_wraps(first_vis_line)) { + cursor.wrap_ofs = MIN(cursor.wrap_ofs, times_line_wraps(first_vis_line)); + } else { + cursor.wrap_ofs = 0; + } + set_line_as_first_visible(cursor.line_ofs, cursor.wrap_ofs); +} - updating_scrolls = true; - h_scroll->set_value(cursor.x_ofs); - update_line_scroll_pos(); - double new_v_scroll = get_line_scroll_pos(); - // keep offset if smooth scroll is enabled - if (smooth_scroll_enabled) { - new_v_scroll += fmod(v_scroll->get_value(), 1.0); +bool TextEdit::line_wraps(int line) const { + + ERR_FAIL_INDEX_V(line, text.size(), 0); + if (!is_wrap_enabled()) + return false; + return text.get_line_width(line) > wrap_at; +} + +int TextEdit::times_line_wraps(int line) const { + + ERR_FAIL_INDEX_V(line, text.size(), 0); + if (!line_wraps(line)) + return 0; + + int wrap_amount = text.get_line_wrap_amount(line); + if (wrap_amount == -1) { + // update the value + Vector<String> rows = get_wrap_rows_text(line); + wrap_amount = rows.size() - 1; + text.set_line_wrap_amount(line, wrap_amount); } - v_scroll->set_value(new_v_scroll); - updating_scrolls = false; - update(); + + return wrap_amount; +} + +Vector<String> TextEdit::get_wrap_rows_text(int p_line) const { + + ERR_FAIL_INDEX_V(p_line, text.size(), Vector<String>()); + + Vector<String> lines; + if (!line_wraps(p_line)) { + lines.push_back(text[p_line]); + return lines; + } + + int px = 0; + int col = 0; + String line_text = text[p_line]; + String wrap_substring = ""; + + int word_px = 0; + String word_str = ""; + int cur_wrap_index = 0; + + int tab_offset_px = get_indent_level(p_line) * cache.font->get_char_size(' ').width; + + while (col < line_text.length()) { + char c = line_text[col]; + int w = text.get_char_width(c, line_text[col + 1], px + word_px); + + int indent_ofs = (cur_wrap_index != 0 ? tab_offset_px : 0); + + word_str += c; + word_px += w; + if (c == ' ') { + // end of a word; add this word to the substring + wrap_substring += word_str; + px += word_px; + word_str = ""; + word_px = 0; + } + + if ((indent_ofs + px + word_px) > wrap_at) { + // do not want to add this word + if (indent_ofs + word_px > wrap_at) { + // not enough space; add it anyway + wrap_substring += word_str; + px += word_px; + word_str = ""; + word_px = 0; + } + lines.push_back(wrap_substring); + // reset for next wrap + cur_wrap_index++; + wrap_substring = ""; + px = 0; + } + col++; + } + // line ends before hit wrap_at; add this word to the substring + wrap_substring += word_str; + px += word_px; + lines.push_back(wrap_substring); + return lines; +} + +int TextEdit::get_cursor_wrap_index() const { + + return get_line_wrap_index_at_col(cursor.line, cursor.column); +} + +int TextEdit::get_line_wrap_index_at_col(int p_line, int p_column) const { + + ERR_FAIL_INDEX_V(p_line, text.size(), 0); + + if (!line_wraps(p_line)) + return 0; + + // loop through wraps in the line text until we get to the column + int wrap_index = 0; + int col = 0; + Vector<String> rows = get_wrap_rows_text(p_line); + for (int i = 0; i < rows.size(); i++) { + wrap_index = i; + String s = rows[wrap_index]; + col += s.length(); + if (col > p_column) + break; + } + return wrap_index; } void TextEdit::cursor_set_column(int p_col, bool p_adjust_viewport) { @@ -3587,7 +3799,7 @@ void TextEdit::cursor_set_column(int p_col, bool p_adjust_viewport) { if (cursor.column > get_line(cursor.line).length()) cursor.column = get_line(cursor.line).length(); - cursor.last_fit_x = get_column_x_offset(cursor.column, get_line(cursor.line)); + cursor.last_fit_x = get_column_x_offset_for_line(cursor.column, cursor.line); if (p_adjust_viewport) adjust_viewport_to_cursor(); @@ -3599,7 +3811,7 @@ void TextEdit::cursor_set_column(int p_col, bool p_adjust_viewport) { } } -void TextEdit::cursor_set_line(int p_row, bool p_adjust_viewport, bool p_can_be_hidden) { +void TextEdit::cursor_set_line(int p_row, bool p_adjust_viewport, bool p_can_be_hidden, int p_wrap_index) { if (setting_row) return; @@ -3608,8 +3820,8 @@ void TextEdit::cursor_set_line(int p_row, bool p_adjust_viewport, bool p_can_be_ if (p_row < 0) p_row = 0; - if (p_row >= (int)text.size()) - p_row = (int)text.size() - 1; + if (p_row >= text.size()) + p_row = text.size() - 1; if (!p_can_be_hidden) { if (is_line_hidden(CLAMP(p_row, 0, text.size() - 1))) { @@ -3627,7 +3839,18 @@ void TextEdit::cursor_set_line(int p_row, bool p_adjust_viewport, bool p_can_be_ } } cursor.line = p_row; - cursor.column = get_char_pos_for(cursor.last_fit_x, get_line(cursor.line)); + + int n_col = get_char_pos_for_line(cursor.last_fit_x, p_row, p_wrap_index); + if (is_wrap_enabled() && p_wrap_index < times_line_wraps(p_row)) { + Vector<String> rows = get_wrap_rows_text(p_row); + int row_end_col = 0; + for (int i = 0; i < p_wrap_index + 1; i++) { + row_end_col += rows[i].length(); + } + if (n_col >= row_end_col) + n_col -= 1; + } + cursor.column = n_col; if (p_adjust_viewport) adjust_viewport_to_cursor(); @@ -3684,6 +3907,14 @@ bool TextEdit::cursor_is_block_mode() const { return block_caret; } +void TextEdit::set_right_click_moves_caret(bool p_enable) { + right_click_moves_caret = p_enable; +} + +bool TextEdit::is_right_click_moving_caret() const { + return right_click_moves_caret; +} + void TextEdit::_v_scroll_input() { scrolling = false; } @@ -3696,9 +3927,25 @@ void TextEdit::_scroll_moved(double p_to_val) { if (h_scroll->is_visible_in_tree()) cursor.x_ofs = h_scroll->get_value(); if (v_scroll->is_visible_in_tree()) { - double val = v_scroll->get_value(); - cursor.line_ofs = num_lines_from(0, (int)floor(val)); - line_scroll_pos = (int)floor(val); + + // set line ofs and wrap ofs + int v_scroll_i = floor(get_v_scroll()); + int sc = 0; + int n_line; + for (n_line = 0; n_line < text.size(); n_line++) { + if (!is_line_hidden(n_line)) { + sc++; + sc += times_line_wraps(n_line); + if (sc > v_scroll_i) + break; + } + } + int line_wrap_amount = times_line_wraps(n_line); + int wi = line_wrap_amount - (sc - v_scroll_i - 1); + wi = CLAMP(wi, 0, line_wrap_amount); + + cursor.line_ofs = n_line; + cursor.wrap_ofs = wi; } update(); } @@ -3708,29 +3955,73 @@ int TextEdit::get_row_height() const { return cache.font->get_height() + cache.line_spacing; } -int TextEdit::get_char_pos_for(int p_px, String p_str) const { +int TextEdit::get_char_pos_for_line(int p_px, int p_line, int p_wrap_index) const { - int px = 0; - int c = 0; + ERR_FAIL_INDEX_V(p_line, text.size(), 0); - int tab_w = cache.font->get_char_size(' ').width * indent_size; + if (line_wraps(p_line)) { - while (c < p_str.length()) { + int line_wrap_amount = times_line_wraps(p_line); + int wrap_offset_px = get_indent_level(p_line) * cache.font->get_char_size(' ').width; + if (p_wrap_index > line_wrap_amount) + p_wrap_index = line_wrap_amount; + if (p_wrap_index > 0) + p_px -= wrap_offset_px; + else + p_wrap_index = 0; + Vector<String> rows = get_wrap_rows_text(p_line); + int c_pos = get_char_pos_for(p_px, rows[p_wrap_index]); + for (int i = 0; i < p_wrap_index; i++) { + String s = rows[i]; + c_pos += s.length(); + } - int w = 0; + return c_pos; + } else { - if (p_str[c] == '\t') { + return get_char_pos_for(p_px, text[p_line]); + } +} - int left = px % tab_w; - if (left == 0) - w = tab_w; - else - w = tab_w - px % tab_w; // is right... +int TextEdit::get_column_x_offset_for_line(int p_char, int p_line) const { - } else { + ERR_FAIL_INDEX_V(p_line, text.size(), 0); - w = cache.font->get_char_size(p_str[c], p_str[c + 1]).width; + if (line_wraps(p_line)) { + + int n_char = p_char; + int col = 0; + Vector<String> rows = get_wrap_rows_text(p_line); + int wrap_index = 0; + for (int i = 0; i < rows.size(); i++) { + wrap_index = i; + String s = rows[wrap_index]; + col += s.length(); + if (col > p_char) + break; + n_char -= s.length(); } + int px = get_column_x_offset(n_char, rows[wrap_index]); + + int wrap_offset_px = get_indent_level(p_line) * cache.font->get_char_size(' ').width; + if (wrap_index != 0) + px += wrap_offset_px; + + return px; + } else { + + return get_column_x_offset(p_char, text[p_line]); + } +} + +int TextEdit::get_char_pos_for(int p_px, String p_str) const { + + int px = 0; + int c = 0; + + while (c < p_str.length()) { + + int w = text.get_char_width(p_str[c], p_str[c + 1], px); if (p_px < (px + w / 2)) break; @@ -3741,28 +4032,16 @@ int TextEdit::get_char_pos_for(int p_px, String p_str) const { return c; } -int TextEdit::get_column_x_offset(int p_char, String p_str) { +int TextEdit::get_column_x_offset(int p_char, String p_str) const { int px = 0; - int tab_w = cache.font->get_char_size(' ').width * indent_size; - for (int i = 0; i < p_char; i++) { if (i >= p_str.length()) break; - if (p_str[i] == '\t') { - - int left = px % tab_w; - if (left == 0) - px += tab_w; - else - px += tab_w - px % tab_w; // is right... - - } else { - px += cache.font->get_char_size(p_str[i], p_str[i + 1]).width; - } + px += text.get_char_width(p_str[i], p_str[i + 1], px); } return px; @@ -3831,20 +4110,20 @@ Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const { void TextEdit::set_text(String p_text) { setting_text = true; - clear(); + _clear(); _insert_text_at_cursor(p_text); clear_undo_history(); cursor.column = 0; cursor.line = 0; cursor.x_ofs = 0; cursor.line_ofs = 0; - line_scroll_pos = 0; + cursor.wrap_ofs = 0; cursor.last_fit_x = 0; cursor_set_line(0); cursor_set_column(0); update(); setting_text = false; - _text_changed_emit(); + //get_range()->set(0); }; @@ -3924,7 +4203,7 @@ void TextEdit::_clear() { cursor.line = 0; cursor.x_ofs = 0; cursor.line_ofs = 0; - line_scroll_pos = 0; + cursor.wrap_ofs = 0; cursor.last_fit_x = 0; } @@ -3946,9 +4225,14 @@ bool TextEdit::is_readonly() const { return readonly; } -void TextEdit::set_wrap(bool p_wrap) { +void TextEdit::set_wrap_enabled(bool p_wrap_enabled) { + + wrap_enabled = p_wrap_enabled; +} + +bool TextEdit::is_wrap_enabled() const { - wrap = p_wrap; + return wrap_enabled; } void TextEdit::set_max_chars(int p_max_chars) { @@ -3956,6 +4240,11 @@ 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(); @@ -4010,13 +4299,89 @@ 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(); - text.clear_caches(); + color_region_cache.clear(); + text.clear_width_cache(); } void TextEdit::add_keyword_color(const String &p_keyword, const Color &p_color) { @@ -4025,10 +4390,36 @@ 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)); - text.clear_caches(); + text.clear_width_cache(); + 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(); } @@ -4060,7 +4451,7 @@ void TextEdit::cut() { backspace_at_cursor(); update(); cursor_set_line(cursor.line + 1); - cut_copy_line = true; + cut_copy_line = clipboard; } else { @@ -4074,7 +4465,7 @@ void TextEdit::cut() { selection.active = false; selection.selecting_mode = Selection::MODE_NONE; update(); - cut_copy_line = false; + cut_copy_line = ""; } } @@ -4083,11 +4474,11 @@ void TextEdit::copy() { if (!selection.active) { String clipboard = _base_get_text(cursor.line, 0, cursor.line, text[cursor.line].length()); OS::get_singleton()->set_clipboard(clipboard); - cut_copy_line = true; + cut_copy_line = clipboard; } else { String clipboard = _base_get_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column); OS::get_singleton()->set_clipboard(clipboard); - cut_copy_line = false; + cut_copy_line = ""; } } @@ -4095,6 +4486,7 @@ void TextEdit::paste() { String clipboard = OS::get_singleton()->get_clipboard(); + begin_complex_operation(); if (selection.active) { selection.active = false; @@ -4103,7 +4495,7 @@ void TextEdit::paste() { cursor_set_line(selection.from_line); cursor_set_column(selection.from_column); - } else if (cut_copy_line) { + } else if (!cut_copy_line.empty() && cut_copy_line == clipboard) { cursor_set_column(0); String ins = "\n"; @@ -4111,6 +4503,8 @@ void TextEdit::paste() { } _insert_text_at_cursor(clipboard); + end_complex_operation(); + update(); } @@ -4144,11 +4538,15 @@ void TextEdit::select(int p_from_line, int p_from_column, int p_to_line, int p_t p_from_line = text.size() - 1; if (p_from_column >= text[p_from_line].length()) p_from_column = text[p_from_line].length(); + if (p_from_column < 0) + p_from_column = 0; if (p_to_line >= text.size()) p_to_line = text.size() - 1; if (p_to_column >= text[p_to_line].length()) p_to_column = text[p_to_line].length(); + if (p_to_column < 0) + p_to_column = 0; selection.from_line = p_from_line; selection.from_column = p_from_column; @@ -4322,7 +4720,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; @@ -4367,31 +4765,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) @@ -4487,36 +4898,101 @@ void TextEdit::unhide_all_lines() { update(); } -int TextEdit::num_lines_from(int p_line_from, int unhidden_amount) const { +int TextEdit::num_lines_from(int p_line_from, int visible_amount) const { - // returns the number of hidden and unhidden lines from p_line_from to p_line_from + amount of visible lines - ERR_FAIL_INDEX_V(p_line_from, text.size(), ABS(unhidden_amount)); + // returns the number of lines (hidden and unhidden) from p_line_from to (p_line_from + visible_amount of unhidden lines) + ERR_FAIL_INDEX_V(p_line_from, text.size(), ABS(visible_amount)); if (!is_hiding_enabled()) - return ABS(unhidden_amount); + return ABS(visible_amount); + int num_visible = 0; int num_total = 0; - if (unhidden_amount >= 0) { + if (visible_amount >= 0) { for (int i = p_line_from; i < text.size(); i++) { num_total++; - if (!is_line_hidden(i)) + if (!is_line_hidden(i)) { num_visible++; - if (num_visible >= unhidden_amount) + } + if (num_visible >= visible_amount) break; } } else { - unhidden_amount = ABS(unhidden_amount); + visible_amount = ABS(visible_amount); for (int i = p_line_from; i >= 0; i--) { num_total++; - if (!is_line_hidden(i)) + if (!is_line_hidden(i)) { num_visible++; - if (num_visible >= unhidden_amount) + } + if (num_visible >= visible_amount) break; } } return num_total; } +int TextEdit::num_lines_from_rows(int p_line_from, int p_wrap_index_from, int visible_amount, int &wrap_index) const { + + // returns the number of lines (hidden and unhidden) from (p_line_from + p_wrap_index_from) row to (p_line_from + visible_amount of unhidden and wrapped rows) + // wrap index is set to the wrap index of the last line + wrap_index = 0; + ERR_FAIL_INDEX_V(p_line_from, text.size(), ABS(visible_amount)); + + if (!is_hiding_enabled() && !is_wrap_enabled()) + return ABS(visible_amount); + + int num_visible = 0; + int num_total = 0; + if (visible_amount == 0) { + num_total = 0; + wrap_index = 0; + } else if (visible_amount > 0) { + int i; + num_visible -= p_wrap_index_from; + for (i = p_line_from; i < text.size(); i++) { + num_total++; + if (!is_line_hidden(i)) { + num_visible++; + num_visible += times_line_wraps(i); + } + if (num_visible >= visible_amount) + break; + } + wrap_index = times_line_wraps(MIN(i, text.size() - 1)) - (num_visible - visible_amount); + } else { + visible_amount = ABS(visible_amount); + int i; + num_visible -= times_line_wraps(p_line_from) - p_wrap_index_from; + for (i = p_line_from; i >= 0; i--) { + num_total++; + if (!is_line_hidden(i)) { + num_visible++; + num_visible += times_line_wraps(i); + } + if (num_visible >= visible_amount) + break; + } + wrap_index = (num_visible - visible_amount); + } + wrap_index = MAX(wrap_index, 0); + return num_total; +} + +int TextEdit::get_last_unhidden_line() const { + + // returns the last line in the text that is not hidden + if (!is_hiding_enabled()) + return text.size() - 1; + + int last_line; + for (last_line = text.size() - 1; last_line > 0; last_line--) { + if (!is_line_hidden(last_line)) { + break; + } + } + return last_line; +} + int TextEdit::get_indent_level(int p_line) const { ERR_FAIL_INDEX_V(p_line, text.size(), 0); @@ -4530,13 +5006,36 @@ 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] == '#') { + } else { break; + } + } + return tab_count * indent_size + whitespace_count; +} + +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 tab_count + whitespace_count / indent_size; + return false; } bool TextEdit::can_fold(int p_line) const { @@ -4552,6 +5051,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); @@ -4559,10 +5060,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; @@ -4591,7 +5095,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; @@ -4712,6 +5218,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; @@ -4746,6 +5254,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; @@ -4830,6 +5340,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; @@ -4843,6 +5358,7 @@ bool TextEdit::is_drawing_tabs() const { void TextEdit::set_override_selected_font_color(bool p_override_selected_font_color) { override_selected_font_color = p_override_selected_font_color; } + bool TextEdit::is_overriding_selected_font_color() const { return override_selected_font_color; } @@ -4863,58 +5379,143 @@ bool TextEdit::is_insert_text_operation() { uint32_t TextEdit::get_version() const { return current_op.version; } + uint32_t TextEdit::get_saved_version() const { return saved_version; } + void TextEdit::tag_saved_version() { saved_version = get_version(); } -int TextEdit::get_v_scroll() const { +double TextEdit::get_scroll_pos_for_line(int p_line, int p_wrap_index) const { - return v_scroll->get_value(); -} -void TextEdit::set_v_scroll(int p_scroll) { + if (!is_wrap_enabled() && !is_hiding_enabled()) + return p_line; - if (p_scroll < 0) { - p_scroll = 0; - } - if (!scroll_past_end_of_file_enabled) { - if (p_scroll + get_visible_rows() > get_total_unhidden_rows()) { - int num_rows = num_lines_from(CLAMP(p_scroll, 0, text.size() - 1), MIN(get_visible_rows(), text.size() - 1 - p_scroll)); - p_scroll = text.size() - num_rows; + // count the number of visible lines up to this line + double new_line_scroll_pos = 0; + int to = CLAMP(p_line, 0, text.size() - 1); + for (int i = 0; i < to; i++) { + if (!text.is_hidden(i)) { + new_line_scroll_pos++; + new_line_scroll_pos += times_line_wraps(i); } } + new_line_scroll_pos += p_wrap_index; + return new_line_scroll_pos; +} + +void TextEdit::set_line_as_first_visible(int p_line, int p_wrap_index) { + + set_v_scroll(get_scroll_pos_for_line(p_line, p_wrap_index)); +} + +void TextEdit::set_line_as_center_visible(int p_line, int p_wrap_index) { + + int visible_rows = get_visible_rows(); + int wi; + int first_line = p_line - num_lines_from_rows(p_line, p_wrap_index, -visible_rows / 2, wi) + 1; + + set_v_scroll(get_scroll_pos_for_line(first_line, wi)); +} + +void TextEdit::set_line_as_last_visible(int p_line, int p_wrap_index) { + + int wi; + int first_line = p_line - num_lines_from_rows(p_line, p_wrap_index, -get_visible_rows() - 1, wi) + 1; + + set_v_scroll(get_scroll_pos_for_line(first_line, wi) + get_visible_rows_offset()); +} + +int TextEdit::get_first_visible_line() const { + + return CLAMP(cursor.line_ofs, 0, text.size() - 1); +} + +int TextEdit::get_last_visible_line() const { + + int first_vis_line = get_first_visible_line(); + int last_vis_line = 0; + int wi; + last_vis_line = first_vis_line + num_lines_from_rows(first_vis_line, cursor.wrap_ofs, get_visible_rows() + 1, wi) - 1; + last_vis_line = CLAMP(last_vis_line, 0, text.size() - 1); + return last_vis_line; +} + +int TextEdit::get_last_visible_line_wrap_index() const { + + int first_vis_line = get_first_visible_line(); + int last_vis_line = 0; + int wi; + last_vis_line = first_vis_line + num_lines_from_rows(first_vis_line, cursor.wrap_ofs, get_visible_rows() + 1, wi) - 1; + return wi; +} + +double TextEdit::get_visible_rows_offset() const { + + double total = cache.size.height; + total -= cache.style_normal->get_minimum_size().height; + if (h_scroll->is_visible_in_tree()) + total -= h_scroll->get_size().height; + total /= (double)get_row_height(); + total = total - floor(total); + total = -CLAMP(total, 0.001, 1) + 1; + return total; +} + +double TextEdit::get_v_scroll_offset() const { + + double val = get_v_scroll() - floor(get_v_scroll()); + return CLAMP(val, 0, 1); +} + +double TextEdit::get_v_scroll() const { + + return v_scroll->get_value(); +} + +void TextEdit::set_v_scroll(double p_scroll) { + v_scroll->set_value(p_scroll); - cursor.line_ofs = num_lines_from(0, p_scroll); - line_scroll_pos = p_scroll; + int max_v_scroll = v_scroll->get_max() - v_scroll->get_page(); + if (p_scroll >= max_v_scroll - 1.0) + _scroll_moved(v_scroll->get_value()); } int TextEdit::get_h_scroll() const { return h_scroll->get_value(); } + void TextEdit::set_h_scroll(int p_scroll) { + if (p_scroll < 0) { + p_scroll = 0; + } h_scroll->set_value(p_scroll); } void TextEdit::set_smooth_scroll_enabled(bool p_enable) { + v_scroll->set_smooth_scroll_enabled(p_enable); smooth_scroll_enabled = p_enable; } bool TextEdit::is_smooth_scroll_enabled() const { + return smooth_scroll_enabled; } void TextEdit::set_v_scroll_speed(float p_speed) { + v_scroll_speed = p_speed; } float TextEdit::get_v_scroll_speed() const { + return v_scroll_speed; } @@ -4946,7 +5547,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(); @@ -4954,7 +5555,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; @@ -5019,7 +5620,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; @@ -5129,7 +5730,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; @@ -5138,7 +5739,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; @@ -5157,12 +5758,8 @@ String TextEdit::get_word_at_pos(const Vector2 &p_pos) const { String s = text[row]; if (s.length() == 0) return ""; - int beg = CLAMP(col, 0, s.length()); - int end = beg; - - if (s[beg] > 32 || beg == s.length()) { - - bool symbol = beg < s.length() && _is_symbol(s[beg]); //not sure if right but most editors behave like this + int beg, end; + if (select_word(s, col, beg, end)) { bool inside_quotes = false; int qbegin = 0, qend = 0; @@ -5181,16 +5778,6 @@ String TextEdit::get_word_at_pos(const Vector2 &p_pos) const { } } - while (beg > 0 && s[beg - 1] > 32 && (symbol == _is_symbol(s[beg - 1]))) { - beg--; - } - while (end < s.length() && s[end + 1] > 32 && (symbol == _is_symbol(s[end + 1]))) { - end++; - } - - if (end < s.length()) - end += 1; - return s.substr(beg, end - beg); } @@ -5207,22 +5794,8 @@ String TextEdit::get_tooltip(const Point2 &p_pos) const { String s = text[row]; if (s.length() == 0) return Control::get_tooltip(p_pos); - int beg = CLAMP(col, 0, s.length()); - int end = beg; - - if (s[beg] > 32 || beg == s.length()) { - - bool symbol = beg < s.length() && _is_symbol(s[beg]); //not sure if right but most editors behave like this - - while (beg > 0 && s[beg - 1] > 32 && (symbol == _is_symbol(s[beg - 1]))) { - beg--; - } - while (end < s.length() && s[end + 1] > 32 && (symbol == _is_symbol(s[end + 1]))) { - end++; - } - - if (end < s.length()) - end += 1; + int beg, end; + if (select_word(s, col, beg, end)) { String tt = tooltip_obj->call(tooltip_func, s.substr(beg, end - beg), tooltip_ud); @@ -5422,7 +5995,7 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("get_line", "line"), &TextEdit::get_line); ClassDB::bind_method(D_METHOD("cursor_set_column", "column", "adjust_viewport"), &TextEdit::cursor_set_column, DEFVAL(true)); - ClassDB::bind_method(D_METHOD("cursor_set_line", "line", "adjust_viewport", "can_be_hidden"), &TextEdit::cursor_set_line, DEFVAL(true), DEFVAL(true)); + ClassDB::bind_method(D_METHOD("cursor_set_line", "line", "adjust_viewport", "can_be_hidden", "wrap_index"), &TextEdit::cursor_set_line, DEFVAL(true), DEFVAL(true), DEFVAL(0)); ClassDB::bind_method(D_METHOD("cursor_get_column"), &TextEdit::cursor_get_column); ClassDB::bind_method(D_METHOD("cursor_get_line"), &TextEdit::cursor_get_line); @@ -5433,19 +6006,26 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("cursor_set_block_mode", "enable"), &TextEdit::cursor_set_block_mode); ClassDB::bind_method(D_METHOD("cursor_is_block_mode"), &TextEdit::cursor_is_block_mode); + ClassDB::bind_method(D_METHOD("set_right_click_moves_caret", "enable"), &TextEdit::set_right_click_moves_caret); + ClassDB::bind_method(D_METHOD("is_right_click_moving_caret"), &TextEdit::is_right_click_moving_caret); + ClassDB::bind_method(D_METHOD("set_readonly", "enable"), &TextEdit::set_readonly); 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("set_wrap_enabled", "enable"), &TextEdit::set_wrap_enabled); + ClassDB::bind_method(D_METHOD("is_wrap_enabled"), &TextEdit::is_wrap_enabled); + // 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); ClassDB::bind_method(D_METHOD("cut"), &TextEdit::cut); ClassDB::bind_method(D_METHOD("copy"), &TextEdit::copy); ClassDB::bind_method(D_METHOD("paste"), &TextEdit::paste); - ClassDB::bind_method(D_METHOD("select_all"), &TextEdit::select_all); + ClassDB::bind_method(D_METHOD("select", "from_line", "from_column", "to_line", "to_column"), &TextEdit::select); + ClassDB::bind_method(D_METHOD("select_all"), &TextEdit::select_all); + ClassDB::bind_method(D_METHOD("deselect"), &TextEdit::deselect); ClassDB::bind_method(D_METHOD("is_selection_active"), &TextEdit::is_selection_active); ClassDB::bind_method(D_METHOD("get_selection_from_line"), &TextEdit::get_selection_from_line); @@ -5466,7 +6046,7 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("set_hiding_enabled", "enable"), &TextEdit::set_hiding_enabled); ClassDB::bind_method(D_METHOD("is_hiding_enabled"), &TextEdit::is_hiding_enabled); ClassDB::bind_method(D_METHOD("set_line_as_hidden", "line", "enable"), &TextEdit::set_line_as_hidden); - ClassDB::bind_method(D_METHOD("is_line_hidden"), &TextEdit::is_line_hidden); + ClassDB::bind_method(D_METHOD("is_line_hidden", "line"), &TextEdit::is_line_hidden); ClassDB::bind_method(D_METHOD("fold_all_lines"), &TextEdit::fold_all_lines); ClassDB::bind_method(D_METHOD("unhide_all_lines"), &TextEdit::unhide_all_lines); ClassDB::bind_method(D_METHOD("fold_line", "line"), &TextEdit::fold_line); @@ -5493,6 +6073,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); @@ -5509,11 +6091,14 @@ 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_enabled"), "set_wrap_enabled", "is_wrap_enabled"); + // 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")); ADD_SIGNAL(MethodInfo("text_changed")); @@ -5541,8 +6126,10 @@ TextEdit::TextEdit() { draw_caret = true; max_chars = 0; clear(); - wrap = false; + wrap_enabled = false; + wrap_right_offset = 10; set_focus_mode(FOCUS_ALL); + syntax_highlighter = NULL; _update_caches(); cache.size = Size2(1, 1); cache.row_height = 1; @@ -5556,7 +6143,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); @@ -5591,6 +6178,7 @@ TextEdit::TextEdit() { caret_blink_timer->set_wait_time(0.65); caret_blink_timer->connect("timeout", this, "_toggle_draw_caret"); cursor_set_blink_enabled(false); + right_click_moves_caret = true; idle_detect = memnew(Timer); add_child(idle_detect); @@ -5645,16 +6233,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 dd305d5822..5c82d1ac20 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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,14 +36,91 @@ #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; + int wrap_amount_cache : 24; + 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; + int get_char_width(CharType c, CharType next_c, int px) const; + void set_line_wrap_amount(int p_line, int p_wrap_amount) const; + int get_line_wrap_amount(int p_line) 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_width_cache(); + void clear_wrap_cache(); + _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 - int x_ofs, line_ofs; + int x_ofs, line_ofs, wrap_ofs; } cursor; struct Selection { @@ -114,69 +192,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 +224,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; @@ -246,9 +266,13 @@ class TextEdit : public Control { bool draw_caret; bool window_has_focus; bool block_caret; + bool right_click_moves_caret; + + bool wrap_enabled; + int wrap_at; + int wrap_right_offset; bool setting_row; - bool wrap; bool draw_tabs; bool override_selected_font_color; bool cursor_changed_dirty; @@ -270,7 +294,7 @@ class TextEdit : public Control { bool brace_matching_enabled; bool highlight_current_line; bool auto_indent; - bool cut_copy_line; + String cut_copy_line; bool insert_mode; bool select_identifiers_enabled; @@ -305,19 +329,34 @@ class TextEdit : public Control { int search_result_line; int search_result_col; - double line_scroll_pos; - bool context_menu_enabled; int get_visible_rows() const; - int get_total_unhidden_rows() const; - double get_line_scroll_pos(bool p_recalculate = false) const; - void update_line_scroll_pos(); - + int get_total_visible_rows() const; + + void update_cursor_wrap_offset(); + void update_wrap_at(); + bool line_wraps(int line) const; + int times_line_wraps(int line) const; + Vector<String> get_wrap_rows_text(int p_line) const; + int get_cursor_wrap_index() const; + int get_line_wrap_index_at_col(int p_line, int p_column) const; int get_char_count(); + double get_scroll_pos_for_line(int p_line, int p_wrap_index = 0) const; + void set_line_as_first_visible(int p_line, int p_wrap_index = 0); + void set_line_as_center_visible(int p_line, int p_wrap_index = 0); + void set_line_as_last_visible(int p_line, int p_wrap_index = 0); + int get_first_visible_line() const; + int get_last_visible_line() const; + int get_last_visible_line_wrap_index() const; + double get_visible_rows_offset() const; + double get_v_scroll_offset() const; + + int get_char_pos_for_line(int p_px, int p_line, int p_wrap_index = 0) const; + int get_column_x_offset_for_line(int p_char, int p_line) const; int get_char_pos_for(int p_px, String p_str) const; - int get_column_x_offset(int p_char, String p_str); + int get_column_x_offset(int p_char, String p_str) const; void adjust_viewport_to_cursor(); double get_scroll_line_diff() const; @@ -352,6 +391,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(); @@ -388,6 +428,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, @@ -431,7 +478,10 @@ public: bool is_line_hidden(int p_line) const; void fold_all_lines(); void unhide_all_lines(); - int num_lines_from(int p_line_from, int unhidden_amount) const; + int num_lines_from(int p_line_from, int visible_amount) const; + int num_lines_from_rows(int p_line_from, int p_wrap_index_from, int visible_amount, int &wrap_index) const; + int get_last_unhidden_line() const; + bool can_fold(int p_line) const; bool is_folded(int p_line) const; void fold_line(int p_line); @@ -443,9 +493,10 @@ public: void set_line(int line, String new_text); void backspace_at_cursor(); - void indent_selection_left(); - void indent_selection_right(); + 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; @@ -467,7 +518,7 @@ public: void center_viewport_to_cursor(); void cursor_set_column(int p_col, bool p_adjust_viewport = true); - void cursor_set_line(int p_row, bool p_adjust_viewport = true, bool p_can_be_hidden = true); + void cursor_set_line(int p_row, bool p_adjust_viewport = true, bool p_can_be_hidden = true, int p_wrap_index = 0); int cursor_get_column() const; int cursor_get_line() const; @@ -481,11 +532,17 @@ public: void cursor_set_block_mode(const bool p_enable); bool cursor_is_block_mode() const; + void set_right_click_moves_caret(bool p_enable); + bool is_right_click_moving_caret() const; + void set_readonly(bool p_readonly); bool is_readonly() const; void set_max_chars(int p_max_chars); - void set_wrap(bool p_wrap); + int get_max_chars() const; + + void set_wrap_enabled(bool p_wrap_enabled); + bool is_wrap_enabled() const; void clear(); @@ -525,6 +582,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); @@ -534,11 +592,19 @@ 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(); - int get_v_scroll() const; - void set_v_scroll(int p_scroll); + 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(); + + double get_v_scroll() const; + void set_v_scroll(double p_scroll); int get_h_scroll() const; void set_h_scroll(int p_scroll); @@ -607,4 +673,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 77bc876201..6bd3b26280 100644 --- a/scene/gui/texture_button.cpp +++ b/scene/gui/texture_button.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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 94f372decf..d42df390e8 100644 --- a/scene/gui/texture_button.h +++ b/scene/gui/texture_button.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 aad7c6b96b..82d983184b 100644 --- a/scene/gui/texture_progress.cpp +++ b/scene/gui/texture_progress.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 04096d35e3..34158b5db5 100644 --- a/scene/gui/texture_progress.h +++ b/scene/gui/texture_progress.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 38f90fe7b5..f4285525f6 100644 --- a/scene/gui/texture_rect.cpp +++ b/scene/gui/texture_rect.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 8a2b715488..b684ac816c 100644 --- a/scene/gui/texture_rect.h +++ b/scene/gui/texture_rect.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 4dfa3d8f37..4220a6b5ce 100644 --- a/scene/gui/tool_button.cpp +++ b/scene/gui/tool_button.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 4c5ea685de..b8be18e560 100644 --- a/scene/gui/tool_button.h +++ b/scene/gui/tool_button.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 ab12d123ba..1d27612766 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,9 +27,11 @@ /* 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> +#include "math_funcs.h" #include "os/input.h" #include "os/keyboard.h" #include "os/os.h" @@ -37,14 +39,18 @@ #include "project_settings.h" #include "scene/main/viewport.h" +#ifdef TOOLS_ENABLED +#include "editor/editor_node.h" +#endif + 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() { @@ -59,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; @@ -362,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; @@ -379,7 +385,7 @@ TreeItem *TreeItem::get_parent() { TreeItem *TreeItem::get_children() { - return childs; + return children; } TreeItem *TreeItem::get_prev_visible() { @@ -396,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; } @@ -412,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) { @@ -438,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) { @@ -778,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); @@ -792,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) { @@ -813,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() { @@ -910,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++) { @@ -966,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) { @@ -983,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; @@ -1004,10 +1017,10 @@ void Tree::draw_item_rect(const TreeItem::Cell &p_cell, const Rect2i &p_rect, co case TreeItem::ALIGN_LEFT: break; //do none case TreeItem::ALIGN_CENTER: - rect.position.x = MAX(0, (rect.size.width - w) / 2); + rect.position.x += MAX(0, (rect.size.width - w) / 2); break; //do none case TreeItem::ALIGN_RIGHT: - rect.position.x = MAX(0, (rect.size.width - w)); + rect.position.x += MAX(0, (rect.size.width - w)); break; //do none } @@ -1052,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(); @@ -1372,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; @@ -1399,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) { @@ -1412,18 +1426,39 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 if (c->get_children() != NULL) root_pos -= Point2i(cache.arrow->get_width(), 0); + float line_width = 1.0; +#ifdef TOOLS_ENABLED + line_width *= EDSCALE; +#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, root_pos.y), cache.relationship_line_color); - VisualServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y), parent_pos, cache.relationship_line_color); + + 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; } } @@ -1534,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) { @@ -1600,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! @@ -1765,7 +1800,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool case TreeItem::CELL_MODE_STRING: { //nothing in particular - if (select_mode == SELECT_MULTI && (get_tree()->get_last_event_id() == focus_in_id || !already_cursor)) { + if (select_mode == SELECT_MULTI && (get_tree()->get_event_count() == focus_in_id || !already_cursor)) { bring_up_editor = false; } @@ -1853,7 +1888,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool } else { editor_text = String::num(p_item->cells[col].val, Math::step_decimals(p_item->cells[col].step)); - if (select_mode == SELECT_MULTI && get_tree()->get_last_event_id() == focus_in_id) + if (select_mode == SELECT_MULTI && get_tree()->get_event_count() == focus_in_id) bring_up_editor = false; } } @@ -1907,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) { @@ -2026,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++) { - if (select_mode == SELECT_MULTI) { + TreeItem *_n = prev->get_prev_visible(); + if (_n) { + prev = _n; + } else { - selected_item = prev; - emit_signal("cell_selected"); - update(); - } else { + return; + } + } + if (prev == selected_item) + return; - while (prev && !prev->cells[selected_col].selectable) - prev = prev->get_prev_visible(); - if (!prev) - EXIT_BREAK; // do nothing.. - prev->select(selected_col); - } + if (select_mode == SELECT_MULTI) { - ensure_cursor_is_visible(); + selected_item = prev; + emit_signal("cell_selected"); + update(); + } else { - } 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(); + 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_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 (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; - default: { + 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(); + } - if (k->get_unicode() > 0) { + if (k.is_valid()) { // Incremental search - _do_incr_search(String::chr(k->get_unicode())); - accept_event(); + if (!k->is_pressed()) + return; + if (k->get_command() || (k->get_shift() && k->get_unicode() == 0) || k->get_metakey()) + return; + if (!root) + return; - return; - } else { - if (k->get_scancode() != KEY_SHIFT) - last_keypress = 0; - } - } break; + if (hide_root && !root->get_next_visible()) + return; + + 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; } } @@ -2351,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; @@ -2450,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()) { @@ -2516,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; @@ -2582,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; @@ -2597,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) { @@ -2769,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; } @@ -2776,7 +2806,7 @@ void Tree::_notification(int p_what) { if (p_what == NOTIFICATION_FOCUS_ENTER) { - focus_in_id = get_tree()->get_last_event_id(); + focus_in_id = get_tree()->get_event_count(); } if (p_what == NOTIFICATION_MOUSE_EXIT) { @@ -2799,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) { @@ -2807,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) { @@ -2823,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; } @@ -2843,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; } @@ -2940,43 +2970,51 @@ Size2 Tree::get_minimum_size() const { return Size2(1, 1); } -TreeItem *Tree::create_item(TreeItem *p_parent) { +TreeItem *Tree::create_item(TreeItem *p_parent, int p_idx) { ERR_FAIL_COND_V(blocked > 0, NULL); - TreeItem *ti = memnew(TreeItem(this)); - - ERR_FAIL_COND_V(!ti, NULL); - ti->cells.resize(columns.size()); + TreeItem *ti = NULL; if (p_parent) { - /* Always append at the end */ + // Append or insert a new item to the given parent. + ti = memnew(TreeItem(this)); + ERR_FAIL_COND_V(!ti, NULL); + ti->cells.resize(columns.size()); - TreeItem *last = 0; - TreeItem *c = p_parent->childs; + TreeItem *prev = NULL; + TreeItem *c = p_parent->children; + int idx = 0; while (c) { - - last = c; + if (idx++ == p_idx) { + ti->next = c; + break; + } + prev = c; c = c->next; } - if (last) { - - last->next = ti; - } else { - - p_parent->childs = ti; - } + if (prev) + prev->next = ti; + else + p_parent->children = ti; ti->parent = p_parent; } else { - if (root) - ti->childs = root; + if (!root) { + // No root exists, make the given item the new root. + ti = memnew(TreeItem(this)); + ERR_FAIL_COND_V(!ti, NULL); + ti->cells.resize(columns.size()); - root = ti; + root = ti; + } else { + // Root exists, append or insert to root. + ti = create_item(root, p_idx); + } } return ti; @@ -2994,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; } @@ -3028,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); @@ -3048,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; @@ -3101,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()); @@ -3153,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) { @@ -3270,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) { @@ -3493,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) { @@ -3713,7 +3764,7 @@ void Tree::_bind_methods() { ClassDB::bind_method(D_METHOD("_scroll_moved"), &Tree::_scroll_moved); ClassDB::bind_method(D_METHOD("clear"), &Tree::clear); - ClassDB::bind_method(D_METHOD("create_item", "parent"), &Tree::_create_item, DEFVAL(Variant())); + ClassDB::bind_method(D_METHOD("create_item", "parent", "idx"), &Tree::_create_item, DEFVAL(Variant()), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("get_root"), &Tree::get_root); ClassDB::bind_method(D_METHOD("set_column_min_width", "column", "min_width"), &Tree::set_column_min_width); @@ -3721,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); @@ -3759,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"))); @@ -3846,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 112de3165f..5af66c5faa 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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); @@ -359,7 +360,7 @@ private: LineEdit *text_editor; HSlider *value_editor; bool updating_value_editor; - uint32_t focus_in_id; + int64_t focus_in_id; PopupMenu *popup_menu; Vector<ColumnInfo> columns; @@ -506,13 +507,17 @@ 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(); //bind helpers - Object *_create_item(Object *p_parent) { - return create_item(Object::cast_to<TreeItem>(p_parent)); + Object *_create_item(Object *p_parent, int p_idx = -1) { + return create_item(Object::cast_to<TreeItem>(p_parent), p_idx); } TreeItem *_get_next_selected(Object *p_item) { @@ -532,7 +537,7 @@ public: void clear(); - TreeItem *create_item(TreeItem *p_parent = 0); + TreeItem *create_item(TreeItem *p_parent = 0, int p_idx = -1); TreeItem *get_root(); TreeItem *get_last_item(); @@ -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 8f567f9796..88e1847533 100644 --- a/scene/gui/video_player.cpp +++ b/scene/gui/video_player.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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 74e2f14e58..5c379b5620 100644 --- a/scene/gui/video_player.h +++ b/scene/gui/video_player.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 9244d8de2f..ac5e6020eb 100644 --- a/scene/gui/viewport_container.cpp +++ b/scene/gui/viewport_container.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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 ebf5869ed9..45c4cd03a1 100644 --- a/scene/gui/viewport_container.h +++ b/scene/gui/viewport_container.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 5a3814ef35..8414210952 100644 --- a/scene/main/canvas_layer.cpp +++ b/scene/main/canvas_layer.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 db2c8e1273..aae23fbb12 100644 --- a/scene/main/canvas_layer.h +++ b/scene/main/canvas_layer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 4afdb56f86..ae21775c55 100644 --- a/scene/main/http_request.cpp +++ b/scene/main/http_request.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 ab5a79c40d..eb5d020bc5 100644 --- a/scene/main/http_request.h +++ b/scene/main/http_request.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 cbe30e05dd..1443d5efbf 100644 --- a/scene/main/instance_placeholder.cpp +++ b/scene/main/instance_placeholder.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 375c87a035..2158257c93 100644 --- a/scene/main/instance_placeholder.h +++ b/scene/main/instance_placeholder.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 efc5d269a6..3643aedb85 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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: { @@ -176,14 +176,18 @@ void Node::_propagate_ready() { data.children[i]->_propagate_ready(); } data.blocked--; + + notification(NOTIFICATION_POST_ENTER_TREE); + 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 +278,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 +300,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) { @@ -340,7 +346,8 @@ void Node::move_child(Node *p_child, int p_pos) { data.children[i]->notification(NOTIFICATION_MOVED_IN_PARENT); } for (const Map<StringName, GroupData>::Element *E = p_child->data.grouped.front(); E; E = E->next()) { - E->get().group->changed = true; + if (E->get().group) + E->get().group->changed = true; } data.blocked--; @@ -473,7 +480,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()->get_network_unique_id() == data.network_master; } /***** RPC CONFIG ********/ @@ -663,200 +670,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()->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()->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); @@ -878,6 +701,30 @@ void Node::rset_unreliable_id(int p_peer_id, const StringName &p_property, const } //////////// end of rpc +Ref<MultiplayerAPI> Node::get_multiplayer() const { + if (multiplayer.is_valid()) + return multiplayer; + if (!is_inside_tree()) + return Ref<MultiplayerAPI>(); + return get_tree()->get_multiplayer(); +} + +Ref<MultiplayerAPI> Node::get_custom_multiplayer() const { + return multiplayer; +} + +void Node::set_custom_multiplayer(Ref<MultiplayerAPI> p_multiplayer) { + + multiplayer = p_multiplayer; +} + +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 { @@ -1135,9 +982,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; @@ -1198,13 +1059,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; } @@ -1307,7 +1168,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); } @@ -1864,11 +1725,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() { @@ -1876,6 +1744,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++; @@ -2006,7 +1880,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; @@ -2110,6 +1984,7 @@ Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const StringName script_property_name = CoreStringNames::get_singleton()->_script; + List<const Node *> hidden_roots; List<const Node *> node_tree; node_tree.push_front(this); @@ -2120,11 +1995,16 @@ Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const for (List<const Node *>::Element *N = node_tree.front(); N; N = N->next()) { for (int i = 0; i < N->get()->get_child_count(); ++i) { + Node *descendant = N->get()->get_child(i); // Skip nodes not really belonging to the instanced hierarchy; they'll be processed normally later - if (N->get()->get_child(i)->data.owner != this) + // but remember non-instanced nodes that are hidden below instanced ones + if (descendant->data.owner != this) { + if (descendant->get_parent() && descendant->get_parent() != this && descendant->get_parent()->data.owner == this) + hidden_roots.push_back(descendant); continue; + } - node_tree.push_back(N->get()->get_child(i)); + node_tree.push_back(descendant); } } } @@ -2153,15 +2033,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).copy(); - } else if (value.get_type() == Variant::ARRAY) { - value = Array(value).duplicate(); - } + Variant value = N->get()->get(name).duplicate(true); + + if (E->get().usage & PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE) { - current_node->set(name, value); + 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); + } } } @@ -2201,6 +2084,34 @@ Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const } node->add_child(dup); + if (i < node->get_child_count() - 1) { + node->move_child(dup, i); + } + } + + for (List<const Node *>::Element *E = hidden_roots.front(); E; E = E->next()) { + + Node *parent = node->get_node(get_path_to(E->get()->data.parent)); + if (!parent) { + + memdelete(node); + return NULL; + } + + Node *dup = E->get()->_duplicate(p_flags, r_duplimap); + if (!dup) { + + memdelete(node); + return NULL; + } + + parent->add_child(dup); + int pos = E->get()->get_position_in_parent(); + + if (pos < parent->get_child_count() - 1) { + + parent->move_child(dup, pos); + } } return node; @@ -2266,13 +2177,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).copy(); - } else if (value.get_type() == Variant::ARRAY) { - value = Array(value).duplicate(); - } + Variant value = get(name).duplicate(true); node->set(name, value); } @@ -2455,7 +2360,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); @@ -2676,18 +2584,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) { @@ -2790,6 +2701,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); @@ -2839,15 +2751,15 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("is_network_master"), &Node::is_network_master); + ClassDB::bind_method(D_METHOD("get_multiplayer"), &Node::get_multiplayer); + ClassDB::bind_method(D_METHOD("get_custom_multiplayer"), &Node::get_custom_multiplayer); + ClassDB::bind_method(D_METHOD("set_custom_multiplayer", "api"), &Node::set_custom_multiplayer); 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; @@ -2905,8 +2817,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") ; @@ -2915,7 +2829,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", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", 0), "", "get_multiplayer"); + ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "custom_multiplayer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", 0), "set_custom_multiplayer", "get_custom_multiplayer"); 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 2b71b71c8d..540f34cba7 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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; + + 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--; } @@ -227,6 +237,7 @@ public: NOTIFICATION_TRANSLATION_CHANGED = 24, NOTIFICATION_INTERNAL_PROCESS = 25, NOTIFICATION_INTERNAL_PHYSICS_PROCESS = 26, + NOTIFICATION_POST_ENTER_TREE = 27, }; @@ -286,6 +297,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 +375,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 +412,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() const; + Ref<MultiplayerAPI> get_custom_multiplayer() const; + void set_custom_multiplayer(Ref<MultiplayerAPI> p_multiplayer); + 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 35bb6ef7e8..dbe7daa604 100644 --- a/scene/main/resource_preloader.cpp +++ b/scene/main/resource_preloader.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 40e900a492..98c7b21f37 100644 --- a/scene/main/resource_preloader.h +++ b/scene/main/resource_preloader.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 deb40800bc..607dbebf6c 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,17 +27,20 @@ /* 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" #include "os/os.h" #include "print_string.h" #include "project_settings.h" +#include "scene/resources/dynamic_font.h" #include "scene/resources/material.h" #include "scene/resources/mesh.h" #include "scene/resources/packed_scene.h" @@ -53,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")); } @@ -390,13 +395,12 @@ void SceneTree::input_event(const Ref<InputEvent> &p_event) { if (Engine::get_singleton()->is_editor_hint() && (Object::cast_to<InputEventJoypadButton>(p_event.ptr()) || Object::cast_to<InputEventJoypadMotion>(*p_event))) return; //avoid joy input on editor + current_event++; root_lock++; - //last_id=p_event.ID; input_handled = false; Ref<InputEvent> ev = p_event; - ev->set_id(++last_id); //this should work better MainLoop::input_event(ev); @@ -481,7 +485,7 @@ bool SceneTree::idle(float p_time) { idle_process_time = p_time; - _network_poll(); + multiplayer->poll(); emit_signal("idle_frame"); @@ -498,6 +502,11 @@ bool SceneTree::idle(float p_time) { 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(); + } + emit_signal("screen_resized"); } @@ -607,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: { @@ -935,11 +953,6 @@ void SceneMainLoop::_update_listener_2d() { } */ -uint32_t SceneTree::get_last_event_id() const { - - return last_id; -} - Variant SceneTree::_call_group_flags(const Variant **p_args, int p_argcount, Variant::CallError &r_error) { r_error.error = Variant::CallError::CALL_OK; @@ -988,6 +1001,10 @@ int64_t SceneTree::get_frame() const { return current_frame; } +int64_t SceneTree::get_event_count() const { + + return current_event; +} Array SceneTree::_get_nodes_in_group(const StringName &p_group) { @@ -1188,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) { @@ -1624,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); } @@ -1652,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() const { + return multiplayer; +} + +void SceneTree::set_multiplayer(Ref<MultiplayerAPI> p_multiplayer) { + ERR_FAIL_COND(!p_multiplayer.is_valid()); + + if (multiplayer.is_valid()) { + multiplayer->disconnect("network_peer_connected", this, "_network_peer_connected"); + multiplayer->disconnect("network_peer_disconnected", this, "_network_peer_disconnected"); + multiplayer->disconnect("connected_to_server", this, "_connected_to_server"); + multiplayer->disconnect("connection_failed", this, "_connection_failed"); + multiplayer->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 = p_multiplayer; + multiplayer->set_root_node(root); - network_peer = p_network_peer; + multiplayer->connect("network_peer_connected", this, "_network_peer_connected"); + multiplayer->connect("network_peer_disconnected", this, "_network_peer_disconnected"); + multiplayer->connect("connected_to_server", this, "_connected_to_server"); + multiplayer->connect("connection_failed", this, "_connection_failed"); + multiplayer->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->set_network_peer(p_network_peer); +} + +Ref<NetworkedMultiplayerPeer> SceneTree::get_network_peer() const { + + return multiplayer->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->is_network_server(); } bool SceneTree::has_network_peer() const { - return network_peer.is_valid(); + return multiplayer->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->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->get_network_connected_peers(); } int SceneTree::get_rpc_sender_id() const { - return rpc_sender_id; + return multiplayer->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->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->is_refusing_new_network_connections(); } void SceneTree::_bind_methods() { @@ -2122,16 +1742,15 @@ void SceneTree::_bind_methods() { ClassDB::bind_method(D_METHOD("has_group", "name"), &SceneTree::has_group); ClassDB::bind_method(D_METHOD("set_auto_accept_quit", "enabled"), &SceneTree::set_auto_accept_quit); + ClassDB::bind_method(D_METHOD("set_quit_on_go_back", "enabled"), &SceneTree::set_quit_on_go_back); ClassDB::bind_method(D_METHOD("set_debug_collisions_hint", "enable"), &SceneTree::set_debug_collisions_hint); ClassDB::bind_method(D_METHOD("is_debugging_collisions_hint"), &SceneTree::is_debugging_collisions_hint); 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); @@ -2181,7 +1800,10 @@ void SceneTree::_bind_methods() { ClassDB::bind_method(D_METHOD("_change_scene"), &SceneTree::_change_scene); + ClassDB::bind_method(D_METHOD("set_multiplayer", "multiplayer"), &SceneTree::set_multiplayer); + ClassDB::bind_method(D_METHOD("get_multiplayer"), &SceneTree::get_multiplayer); 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); @@ -2195,6 +1817,20 @@ void SceneTree::_bind_methods() { ClassDB::bind_method(D_METHOD("_connection_failed"), &SceneTree::_connection_failed); ClassDB::bind_method(D_METHOD("_server_disconnected"), &SceneTree::_server_disconnected); + 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", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", 0), "set_multiplayer", "get_multiplayer"); + ADD_SIGNAL(MethodInfo("tree_changed")); ADD_SIGNAL(MethodInfo("node_added", PropertyInfo(Variant::OBJECT, "node"))); ADD_SIGNAL(MethodInfo("node_removed", PropertyInfo(Variant::OBJECT, "node"))); @@ -2244,6 +1880,20 @@ void SceneTree::add_idle_callback(IdleCallback p_callback) { idle_callbacks[idle_callback_count++] = p_callback; } +void SceneTree::set_use_font_oversampling(bool p_oversampling) { + + use_font_oversampling = p_oversampling; + if (use_font_oversampling) { + DynamicFontAtSize::font_oversampling = OS::get_singleton()->get_window_size().width / root->get_visible_rect().size.width; + } else { + DynamicFontAtSize::font_oversampling = 1.0; + } +} + +bool SceneTree::is_using_font_oversampling() const { + return use_font_oversampling; +} + SceneTree::SceneTree() { singleton = this; @@ -2264,9 +1914,10 @@ SceneTree::SceneTree() { tree_version = 1; physics_process_time = 1; idle_process_time = 1; - last_id = 1; + root = NULL; current_frame = 0; + current_event = 0; tree_changed_name = "tree_changed"; node_added_name = "node_added"; node_removed_name = "node_removed"; @@ -2274,7 +1925,6 @@ SceneTree::SceneTree() { call_lock = 0; root_lock = 0; node_count = 0; - rpc_sender_id = 0; //create with mainloop @@ -2283,6 +1933,9 @@ SceneTree::SceneTree() { if (!root->get_world().is_valid()) root->set_world(Ref<World>(memnew(World))); + // Initialize network state + set_multiplayer(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); @@ -2327,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 -> Viewport -> Default Environment) could not be loaded.")); + ERR_PRINTS(RTR("Default Environment as specified in Project Settings (Rendering -> Environment -> Default Environment) could not be loaded.")); } } } @@ -2377,9 +2030,9 @@ SceneTree::SceneTree() { live_edit_root = NodePath("/root"); - last_send_cache_id = 1; - #endif + + use_font_oversampling = false; } SceneTree::~SceneTree() { diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h index 244fc8da62..6e0156546e 100644 --- a/scene/main/scene_tree.h +++ b/scene/main/scene_tree.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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" @@ -109,7 +110,6 @@ private: float idle_process_time; bool accept_quit; bool quit_on_go_back; - uint32_t last_id; #ifdef DEBUG_ENABLED bool debug_collisions_hint; @@ -122,12 +122,15 @@ private: bool _quit; bool initialized; bool input_handled; + Size2 last_screen_size; StringName tree_changed_name; StringName node_added_name; StringName node_removed_name; + bool use_font_oversampling; int64_t current_frame; + int64_t current_event; int node_count; #ifdef TOOLS_ENABLED @@ -182,16 +185,8 @@ private: ///network/// - enum NetworkCommands { - NETWORK_COMMAND_REMOTE_CALL, - NETWORK_COMMAND_REMOTE_SET, - NETWORK_COMMAND_SIMPLIFY_PATH, - NETWORK_COMMAND_CONFIRM_PATH, - }; + Ref<MultiplayerAPI> multiplayer; - Ref<NetworkedMultiplayerPeer> network_peer; - - Set<int> connected_peers; void _network_peer_connected(int p_id); void _network_peer_disconnected(int p_id); @@ -199,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); @@ -323,7 +288,7 @@ public: NOTIFICATION_TRANSFORM_CHANGED = 29 }; - enum CallGroupFlags { + enum GroupCallFlags { GROUP_CALL_DEFAULT = 0, GROUP_CALL_REVERSE = 1, GROUP_CALL_REALTIME = 2, @@ -333,8 +298,6 @@ public: _FORCE_INLINE_ Viewport *get_root() const { return root; } - uint32_t get_last_event_id() const; - void call_group_flags(uint32_t p_call_flags, const StringName &p_group, const StringName &p_function, VARIANT_ARG_LIST); void notify_group_flags(uint32_t p_call_flags, const StringName &p_group, int p_notification); void set_group_flags(uint32_t p_call_flags, const StringName &p_group, const String &p_name, const Variant &p_value); @@ -410,6 +373,7 @@ public: int get_collision_debug_contact_count() { return collision_debug_contacts; } int64_t get_frame() const; + int64_t get_event_count() const; int get_node_count() const; @@ -420,13 +384,14 @@ public: void set_screen_stretch(StretchMode p_mode, StretchAspect p_aspect, const Size2 p_minsize, real_t p_shrink = 1); + void set_use_font_oversampling(bool p_oversampling); + bool is_using_font_oversampling() const; + //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; @@ -445,7 +410,10 @@ public: //network API + Ref<MultiplayerAPI> get_multiplayer() const; + void set_multiplayer(Ref<MultiplayerAPI> p_multiplayer); 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; @@ -462,6 +430,6 @@ public: VARIANT_ENUM_CAST(SceneTree::StretchMode); VARIANT_ENUM_CAST(SceneTree::StretchAspect); -VARIANT_ENUM_CAST(SceneTree::CallGroupFlags); +VARIANT_ENUM_CAST(SceneTree::GroupCallFlags); #endif diff --git a/scene/main/timer.cpp b/scene/main/timer.cpp index e0c6f93f25..c285694dfa 100755 --- a/scene/main/timer.cpp +++ b/scene/main/timer.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 a02adbb0a3..2f42252a7e 100755 --- a/scene/main/timer.h +++ b/scene/main/timer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 4635de81e8..f631fd6f3a 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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" @@ -71,6 +72,9 @@ void ViewportTexture::setup_local_to_scene() { vp->viewport_textures.insert(this); VS::get_singleton()->texture_set_proxy(proxy, vp->texture_rid); + + vp->texture_flags = flags; + VS::get_singleton()->texture_set_flags(vp->texture_rid, flags); } void ViewportTexture::set_viewport_path_in_scene(const NodePath &p_path) { @@ -121,20 +125,18 @@ Ref<Image> ViewportTexture::get_data() const { return VS::get_singleton()->texture_get_data(vp->texture_rid); } void ViewportTexture::set_flags(uint32_t p_flags) { + flags = p_flags; if (!vp) return; - vp->texture_flags = p_flags; - VS::get_singleton()->texture_set_flags(vp->texture_rid, p_flags); + vp->texture_flags = flags; + VS::get_singleton()->texture_set_flags(vp->texture_rid, flags); } uint32_t ViewportTexture::get_flags() const { - if (!vp) - return 0; - - return vp->texture_flags; + return flags; } void ViewportTexture::_bind_methods() { @@ -148,6 +150,7 @@ void ViewportTexture::_bind_methods() { ViewportTexture::ViewportTexture() { vp = NULL; + flags = 0; set_local_to_scene(true); proxy = VS::get_singleton()->texture_create(); } @@ -180,24 +183,24 @@ public: Viewport::GUI::GUI() { mouse_focus = NULL; + mouse_click_grabber = NULL; mouse_focus_button = -1; key_focus = NULL; mouse_over = NULL; - cancelled_input_ID = 0; 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); @@ -211,114 +214,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()) @@ -376,7 +271,6 @@ void Viewport::_notification(int p_what) { _update_listener(); _update_listener_2d(); - _update_rect(); find_world_2d()->_register_viewport(this, Rect2()); @@ -429,6 +323,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: { @@ -436,11 +335,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); @@ -461,15 +355,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); @@ -679,6 +576,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; } } @@ -704,7 +615,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"); @@ -1153,7 +1063,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"); } @@ -1172,9 +1082,6 @@ void Viewport::set_size_override_stretch(bool p_enable) { return; size_override_stretch = p_enable; - if (size_override) { - _update_rect(); - } _update_stretch_transform(); } @@ -1347,6 +1254,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) @@ -1421,8 +1346,8 @@ void Viewport::_gui_show_tooltip() { gui.tooltip_label->set_anchor_and_margin(MARGIN_TOP, Control::ANCHOR_BEGIN, ttp->get_margin(MARGIN_TOP)); gui.tooltip_label->set_anchor_and_margin(MARGIN_RIGHT, Control::ANCHOR_END, -ttp->get_margin(MARGIN_RIGHT)); gui.tooltip_label->set_anchor_and_margin(MARGIN_BOTTOM, Control::ANCHOR_END, -ttp->get_margin(MARGIN_BOTTOM)); - gui.tooltip_label->set_text(tooltip); - Rect2 r(gui.tooltip_pos + Point2(10, 10), gui.tooltip_label->get_combined_minimum_size() + ttp->get_minimum_size()); + gui.tooltip_label->set_text(tooltip.strip_edges()); + Rect2 r(gui.tooltip_pos + Point2(10, 10), gui.tooltip_label->get_minimum_size() + ttp->get_minimum_size()); Rect2 vr = gui.tooltip_label->get_viewport_rect(); if (r.size.x + r.position.x > vr.size.x) r.position.x = vr.size.x - r.size.x; @@ -1489,7 +1414,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()) { @@ -1620,9 +1545,6 @@ bool Viewport::_gui_drop(Control *p_at_control, Point2 p_at_pos, bool p_just_che void Viewport::_gui_input_event(Ref<InputEvent> p_event) { - if (p_event->get_id() == gui.cancelled_input_ID) { - return; - } //? /* if (!is_visible()) { @@ -1640,7 +1562,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { if (mb->is_pressed()) { Size2 pos = mpos; - if (gui.mouse_focus && mb->get_button_index() != gui.mouse_focus_button) { + if (gui.mouse_focus && mb->get_button_index() != gui.mouse_focus_button && mb->get_button_index() == BUTTON_LEFT) { //do not steal mouse focus and stuff @@ -1659,6 +1581,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { //cancel event, sorry, modal exclusive EATS UP ALL //alternative, you can't pop out a window the same frame it was made modal (fixes many issues) get_tree()->set_input_as_handled(); + return; // no one gets the event if exclusive NO ONE } @@ -1751,7 +1674,6 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { _gui_call_input(gui.mouse_focus, mb); } - get_tree()->call_group_flags(SceneTree::GROUP_CALL_REALTIME, "windows", "_cancel_input_ID", mb->get_id()); get_tree()->set_input_as_handled(); if (gui.drag_data.get_type() != Variant::NIL && mb->get_button_index() == BUTTON_LEFT) { @@ -1807,21 +1729,23 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { pos = gui.focus_inv_xform.xform(pos); mb->set_position(pos); - if (gui.mouse_focus->can_process()) { - _gui_call_input(gui.mouse_focus, mb); - } + Control *mouse_focus = gui.mouse_focus; + //disable mouse focus if needed before calling input, this makes popups on mouse press event work better, as the release will never be received otherwise if (mb->get_button_index() == gui.mouse_focus_button) { gui.mouse_focus = NULL; gui.mouse_focus_button = -1; } + if (mouse_focus->can_process()) { + _gui_call_input(mouse_focus, mb); + } + /*if (gui.drag_data.get_type()!=Variant::NIL && mb->get_button_index()==BUTTON_LEFT) { _propagate_viewport_notification(this,NOTIFICATION_DRAG_END); gui.drag_data=Variant(); //always clear }*/ - get_tree()->call_group_flags(SceneTree::GROUP_CALL_REALTIME, "windows", "_cancel_input_ID", mb->get_id()); get_tree()->set_input_as_handled(); } } @@ -1911,7 +1835,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; } @@ -2032,6 +1956,10 @@ 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(); Control *over = _gui_find_control(pos); @@ -2122,6 +2050,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 +2114,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 +2195,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 +2206,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 +2243,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) @@ -2348,7 +2293,6 @@ void Viewport::_gui_control_grab_focus(Control *p_control) { //no need for change if (gui.key_focus && gui.key_focus == p_control) return; - get_tree()->call_group_flags(SceneTree::GROUP_CALL_REALTIME, "_viewports", "_gui_remove_focus"); gui.key_focus = p_control; p_control->notification(Control::NOTIFICATION_FOCUS_ENTER); @@ -2370,6 +2314,18 @@ 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) && !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); + Control *c = gui.mouse_focus; + gui.mouse_focus = NULL; + c->call_multilevel(SceneStringNames::get_singleton()->_gui_input, mb); + } + return gui.modal_stack.back(); } @@ -2380,9 +2336,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(); @@ -2393,9 +2362,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); @@ -2498,9 +2467,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 { @@ -2514,11 +2488,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(); @@ -2543,6 +2512,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; } @@ -2658,9 +2637,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); @@ -2729,8 +2705,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); @@ -2741,17 +2721,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_"); @@ -2772,6 +2755,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")); @@ -2818,6 +2803,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)); @@ -2872,6 +2864,7 @@ Viewport::Viewport() { disable_input = false; disable_3d = false; + keep_3d_linear = false; //window tooltip gui.tooltip_timer = -1; @@ -2884,9 +2877,10 @@ Viewport::Viewport() { gui.drag_preview = NULL; gui.drag_attempted = false; gui.canvas_sort_index = 0; + gui.roots_order_dirty = false; msaa = MSAA_DISABLED; - hdr = false; + hdr = true; usage = USAGE_3D; debug_draw = DEBUG_DRAW_DISABLED; diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 0835e3f69a..c1ef58de69 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -1,4 +1,3 @@ - /*************************************************************************/ /* viewport.h */ /*************************************************************************/ @@ -6,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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 @@ -58,6 +58,7 @@ class ViewportTexture : public Texture { friend class Viewport; Viewport *vp; + uint32_t flags; RID proxy; @@ -206,12 +207,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 +227,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 +250,7 @@ private: bool key_event_accepted; Control *mouse_focus; + Control *mouse_click_grabber; int mouse_focus_button; Control *key_focus; Control *mouse_over; @@ -269,10 +266,11 @@ private: float tooltip_timer; float tooltip_delay; List<Control *> modal_stack; - unsigned int cancelled_input_ID; 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 @@ -283,6 +281,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(); @@ -295,9 +294,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); @@ -333,6 +329,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(); @@ -441,6 +438,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; @@ -471,6 +471,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 9715e1d6a0..7533fa5f6c 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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,11 @@ 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; + void register_scene_types() { SceneStringNames::create(); @@ -217,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 @@ -268,11 +265,11 @@ void register_scene_types() { ClassDB::register_class<Control>(); ClassDB::register_class<Button>(); ClassDB::register_class<Label>(); - ClassDB::register_class<ScrollBar>(); + ClassDB::register_virtual_class<ScrollBar>(); ClassDB::register_class<HScrollBar>(); ClassDB::register_class<VScrollBar>(); ClassDB::register_class<ProgressBar>(); - ClassDB::register_class<Slider>(); + ClassDB::register_virtual_class<Slider>(); ClassDB::register_class<HSlider>(); ClassDB::register_class<VSlider>(); ClassDB::register_class<Popup>(); @@ -283,7 +280,7 @@ void register_scene_types() { ClassDB::register_class<ToolButton>(); ClassDB::register_class<LinkButton>(); ClassDB::register_class<Panel>(); - ClassDB::register_class<Range>(); + ClassDB::register_virtual_class<Range>(); OS::get_singleton()->yield(); //may take time to init @@ -393,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>(); @@ -439,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>(); @@ -452,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>(); @@ -542,6 +543,8 @@ void register_scene_types() { ClassDB::register_class<DynamicFontData>(); ClassDB::register_class<DynamicFont>(); + DynamicFont::initialize_dynamic_fonts(); + ClassDB::register_virtual_class<StyleBox>(); ClassDB::register_class<StyleBoxEmpty>(); ClassDB::register_class<StyleBoxTexture>(); @@ -599,18 +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); - 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() { @@ -621,6 +648,8 @@ void unregister_scene_types() { memdelete(resource_loader_stream_texture); memdelete(resource_loader_theme); + DynamicFont::finish_dynamic_fonts(); + if (resource_saver_text) { memdelete(resource_saver_text); } @@ -628,6 +657,16 @@ void unregister_scene_types() { memdelete(resource_loader_text); } + if (resource_saver_shader) { + memdelete(resource_saver_shader); + } + if (resource_loader_shader) { + memdelete(resource_loader_shader); + } + if (resource_loader_bmfont) { + memdelete(resource_loader_bmfont); + } + SpatialMaterial::finish_shaders(); ParticlesMaterial::finish_shaders(); CanvasItemMaterial::finish_shaders(); diff --git a/scene/register_scene_types.h b/scene/register_scene_types.h index 956b12a7cd..9121c015fd 100644 --- a/scene/register_scene_types.h +++ b/scene/register_scene_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 4544549f94..7a1fffaa26 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 1f468b29b5..73691a69f2 100644 --- a/scene/resources/animation.h +++ b/scene/resources/animation.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 f81f460521..b77143cd9d 100644 --- a/scene/resources/audio_stream_sample.cpp +++ b/scene/resources/audio_stream_sample.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 fbb8010a9c..5fe65c194e 100644 --- a/scene/resources/audio_stream_sample.h +++ b/scene/resources/audio_stream_sample.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 eebc872dfb..29ffefd9d6 100644 --- a/scene/resources/bit_mask.cpp +++ b/scene/resources/bit_mask.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 72090f2ebe..dcd5edb4fb 100644 --- a/scene/resources/bit_mask.h +++ b/scene/resources/bit_mask.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 ee1e750c82..b115d92be3 100644 --- a/scene/resources/bounds.cpp +++ b/scene/resources/bounds.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 3378101836..dfe2fe40c6 100644 --- a/scene/resources/bounds.h +++ b/scene/resources/bounds.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 858e19c9a6..e9e01ed98a 100644 --- a/scene/resources/box_shape.cpp +++ b/scene/resources/box_shape.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 8884c9b75a..5ef16b4766 100644 --- a/scene/resources/box_shape.h +++ b/scene/resources/box_shape.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 f5bf0ad78e..8da1da9541 100644 --- a/scene/resources/canvas.cpp +++ b/scene/resources/canvas.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 b9977e11b9..dfdea82ca5 100644 --- a/scene/resources/canvas.h +++ b/scene/resources/canvas.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 f0eb8232e5..101970bad8 100644 --- a/scene/resources/capsule_shape.cpp +++ b/scene/resources/capsule_shape.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 9b43823c83..f89d07c5f1 100644 --- a/scene/resources/capsule_shape.h +++ b/scene/resources/capsule_shape.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 3caf12feb8..95ab34abb2 100644 --- a/scene/resources/capsule_shape_2d.cpp +++ b/scene/resources/capsule_shape_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,11 +27,31 @@ /* 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" #include "servers/visual_server.h" +Vector<Vector2> CapsuleShape2D::_get_points() const { + + Vector<Vector2> points; + for (int i = 0; i < 24; i++) { + Vector2 ofs = Vector2(0, (i > 6 && i <= 18) ? -get_height() * 0.5 : get_height() * 0.5); + + points.push_back(Vector2(Math::sin(i * Math_PI * 2 / 24.0), Math::cos(i * Math_PI * 2 / 24.0)) * get_radius() + ofs); + if (i == 6 || i == 18) + points.push_back(Vector2(Math::sin(i * Math_PI * 2 / 24.0), Math::cos(i * Math_PI * 2 / 24.0)) * get_radius() - ofs); + } + + return points; +} + +bool CapsuleShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + + return Geometry::is_point_in_polygon(p_point, _get_points()); +} + void CapsuleShape2D::_update_shape() { Physics2DServer::get_singleton()->shape_set_data(get_rid(), Vector2(radius, height)); @@ -62,15 +82,7 @@ real_t CapsuleShape2D::get_height() const { void CapsuleShape2D::draw(const RID &p_to_rid, const Color &p_color) { - Vector<Vector2> points; - for (int i = 0; i < 24; i++) { - Vector2 ofs = Vector2(0, (i > 6 && i <= 18) ? -get_height() * 0.5 : get_height() * 0.5); - - points.push_back(Vector2(Math::sin(i * Math_PI * 2 / 24.0), Math::cos(i * Math_PI * 2 / 24.0)) * get_radius() + ofs); - if (i == 6 || i == 18) - points.push_back(Vector2(Math::sin(i * Math_PI * 2 / 24.0), Math::cos(i * Math_PI * 2 / 24.0)) * get_radius() - ofs); - } - + Vector<Vector2> points = _get_points(); Vector<Color> col; col.push_back(p_color); VisualServer::get_singleton()->canvas_item_add_polygon(p_to_rid, points, col); diff --git a/scene/resources/capsule_shape_2d.h b/scene/resources/capsule_shape_2d.h index 610f9a4cf9..04d663c010 100644 --- a/scene/resources/capsule_shape_2d.h +++ b/scene/resources/capsule_shape_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 @@ -39,11 +40,14 @@ class CapsuleShape2D : public Shape2D { real_t radius; void _update_shape(); + Vector<Vector2> _get_points() const; protected: static void _bind_methods(); public: + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; + void set_height(real_t p_height); real_t get_height() const; diff --git a/scene/resources/circle_shape_2d.cpp b/scene/resources/circle_shape_2d.cpp index bff3ed4d67..1c7bb76b26 100644 --- a/scene/resources/circle_shape_2d.cpp +++ b/scene/resources/circle_shape_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,10 +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 "circle_shape_2d.h" #include "servers/physics_2d_server.h" #include "servers/visual_server.h" + +bool CircleShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + + return p_point.length() < get_radius() + p_tolerance; +} + void CircleShape2D::_update_shape() { Physics2DServer::get_singleton()->shape_set_data(get_rid(), radius); diff --git a/scene/resources/circle_shape_2d.h b/scene/resources/circle_shape_2d.h index 3ba5ec949c..06bb433170 100644 --- a/scene/resources/circle_shape_2d.h +++ b/scene/resources/circle_shape_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 @@ -42,6 +43,8 @@ protected: static void _bind_methods(); public: + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; + void set_radius(real_t p_radius); real_t get_radius() const; diff --git a/scene/resources/color_ramp.cpp b/scene/resources/color_ramp.cpp index 9f6150ae63..b2f586d02d 100644 --- a/scene/resources/color_ramp.cpp +++ b/scene/resources/color_ramp.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 316c188d59..c042a0d3d0 100644 --- a/scene/resources/color_ramp.h +++ b/scene/resources/color_ramp.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 1c48f0c30b..935f041837 100644 --- a/scene/resources/concave_polygon_shape.cpp +++ b/scene/resources/concave_polygon_shape.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 82e052fee7..2cc9095abf 100644 --- a/scene/resources/concave_polygon_shape.h +++ b/scene/resources/concave_polygon_shape.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 e90046fd28..d4343680ef 100644 --- a/scene/resources/concave_polygon_shape_2d.cpp +++ b/scene/resources/concave_polygon_shape_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,11 +27,29 @@ /* 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" #include "servers/visual_server.h" +bool ConcavePolygonShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + + PoolVector<Vector2> s = get_segments(); + int len = s.size(); + if (len == 0 || (len % 2) == 1) + return false; + + PoolVector<Vector2>::Read r = s.read(); + for (int i = 0; i < len; i += 2) { + Vector2 closest = Geometry::get_closest_point_to_segment_2d(p_point, &r[i]); + if (p_point.distance_to(closest) < p_tolerance) + return true; + } + + return false; +} + void ConcavePolygonShape2D::set_segments(const PoolVector<Vector2> &p_segments) { Physics2DServer::get_singleton()->shape_set_data(get_rid(), p_segments); diff --git a/scene/resources/concave_polygon_shape_2d.h b/scene/resources/concave_polygon_shape_2d.h index e766e88a4b..e8fa7369ac 100644 --- a/scene/resources/concave_polygon_shape_2d.h +++ b/scene/resources/concave_polygon_shape_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 @@ -39,6 +40,8 @@ protected: static void _bind_methods(); public: + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; + void set_segments(const PoolVector<Vector2> &p_segments); PoolVector<Vector2> get_segments() const; diff --git a/scene/resources/convex_polygon_shape.cpp b/scene/resources/convex_polygon_shape.cpp index 31c4ea55e9..a2e0996996 100644 --- a/scene/resources/convex_polygon_shape.cpp +++ b/scene/resources/convex_polygon_shape.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 e103f2d90c..62567fc031 100644 --- a/scene/resources/convex_polygon_shape.h +++ b/scene/resources/convex_polygon_shape.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 9c25b6b467..d061c4ea1e 100644 --- a/scene/resources/convex_polygon_shape_2d.cpp +++ b/scene/resources/convex_polygon_shape_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,12 +27,18 @@ /* 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" #include "servers/physics_2d_server.h" #include "servers/visual_server.h" +bool ConvexPolygonShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + + return Geometry::is_point_in_polygon(p_point, points); +} + void ConvexPolygonShape2D::_update_shape() { Physics2DServer::get_singleton()->shape_set_data(get_rid(), points); diff --git a/scene/resources/convex_polygon_shape_2d.h b/scene/resources/convex_polygon_shape_2d.h index 3354199e05..69c237a0e6 100644 --- a/scene/resources/convex_polygon_shape_2d.h +++ b/scene/resources/convex_polygon_shape_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 @@ -42,6 +43,8 @@ protected: static void _bind_methods(); public: + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; + void set_point_cloud(const Vector<Vector2> &p_points); void set_points(const Vector<Vector2> &p_points); Vector<Vector2> get_points() const; diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp index 79cc94c911..4ec1e8973d 100644 --- a/scene/resources/curve.cpp +++ b/scene/resources/curve.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 e7d47f4056..492eb05d1e 100644 --- a/scene/resources/curve.h +++ b/scene/resources/curve.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 f4e6c5e247..3ea856541e 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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)); @@ -543,6 +544,11 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("updown", "SpinBox", make_icon(spinbox_updown_png)); + //scroll container + Ref<StyleBoxEmpty> empty; + empty.instance(); + theme->set_stylebox("bg", "ScrollContainer", empty); + // WindowDialog theme->set_stylebox("panel", "WindowDialog", sb_expand(make_stylebox(popup_window_png, 10, 26, 10, 8), 8, 24, 8, 6)); @@ -582,6 +588,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 +824,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 +894,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 +902,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/default_theme/default_theme.h b/scene/resources/default_theme/default_theme.h index f2a4b2616d..8f211af35b 100644 --- a/scene/resources/default_theme/default_theme.h +++ b/scene/resources/default_theme/default_theme.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/scene/resources/dynamic_font.cpp b/scene/resources/dynamic_font.cpp index a40417f24d..05493d5777 100644 --- a/scene/resources/dynamic_font.cpp +++ b/scene/resources/dynamic_font.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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); + 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; - descent = -face->size->metrics.descender >> 6; + 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,12 +225,12 @@ 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; } +float DynamicFontAtSize::font_oversampling = 1.0; + float DynamicFontAtSize::get_height() const { return ascent + descent; @@ -222,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++) { @@ -243,52 +259,53 @@ 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; - break; - } - } else { - ret.x += delta.x >> 6; - } + Size2 ret(0, get_height()); + + if (ch->found) { + ret.x = ch->advance; } + ret.x += _get_kerning_advance(font, p_char, p_next); return ret; } @@ -303,92 +320,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); + 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; - float advance = 0; + ERR_FAIL_COND_V(!ch, 0.0); - if (!c->found) { - - //not found, try in fallbacks - bool used_fallback = false; + float advance = 0.0; - for (int i = 0; i < p_fallbacks.size(); i++) { - - DynamicFontAtSize *fb = const_cast<DynamicFontAtSize *>(p_fallbacks[i].ptr()); - if (!fb->valid) - continue; - - 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, p_modulate, false, RID(), false); - advance = ch->advance; - used_fallback = true; - break; + 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); } - //not found, try 0xFFFD to display 'not found'. - - if (!used_fallback) { - const_cast<DynamicFontAtSize *>(this)->_update_char(0xFFFD); - c = char_map.getptr(0xFFFD); - } + advance = ch->advance; } - 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, p_modulate, false, RID(), false); - advance = c->advance; - //textures[c->texture_idx].texture->draw(p_canvas_item,Vector2()); - } - - 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; - break; - } - } else { - advance += delta.x >> 6; - } - } + advance += _get_kerning_advance(font, p_char, p_next); return advance; } @@ -413,79 +379,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++) { @@ -498,27 +422,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) @@ -530,13 +454,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; } } @@ -545,12 +469,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(); @@ -558,23 +501,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; } } @@ -584,7 +534,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(); @@ -596,23 +546,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; - chr.v_align = ascent - yofs; // + ascent - descent; - chr.advance = advance; - 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 = 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 = 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; + + FT_Stroker stroker; + if (FT_Stroker_New(library, &stroker) != 0) + return ret; + + 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; +} + +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; + } - //print_line("CHAR: "+String::chr(p_char)+" TEX INDEX: "+itos(tex_index)+" RECT: "+chr.rect+" X OFS: "+itos(xofs)+" Y OFS: "+itos(yofs)); + 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] = chr; + 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(); } DynamicFontAtSize::DynamicFontAtSize() { @@ -623,14 +662,17 @@ DynamicFontAtSize::DynamicFontAtSize() { descent = 1; 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(); } ///////////////////////// @@ -638,11 +680,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(); @@ -652,12 +710,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 { @@ -670,6 +726,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(); } @@ -678,6 +735,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; @@ -688,6 +769,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(); } @@ -701,9 +783,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) { @@ -778,13 +873,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; +} - if (!data_at_size.is_valid()) +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 (!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()); @@ -798,6 +904,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(); @@ -882,6 +990,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); @@ -897,6 +1011,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"); @@ -913,15 +1029,71 @@ void DynamicFont::_bind_methods() { BIND_ENUM_CONSTANT(SPACING_SPACE); } -DynamicFont::DynamicFont() { +Mutex *DynamicFont::dynamic_font_mutex = NULL; + +SelfList<DynamicFont>::List DynamicFont::dynamic_fonts; + +DynamicFont::DynamicFont() : + font_list(this) { spacing_top = 0; 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); + if (dynamic_font_mutex) + dynamic_font_mutex->unlock(); } DynamicFont::~DynamicFont() { + + if (dynamic_font_mutex) + dynamic_font_mutex->lock(); + dynamic_fonts.remove(&font_list); + if (dynamic_font_mutex) + dynamic_font_mutex->unlock(); +} + +void DynamicFont::initialize_dynamic_fonts() { + dynamic_font_mutex = Mutex::create(); +} + +void DynamicFont::finish_dynamic_fonts() { + memdelete(dynamic_font_mutex); + dynamic_font_mutex = NULL; +} + +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(); + + if (E->self()->outline_data_at_size.is_valid()) { + E->self()->outline_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 52c3f30590..f460bca2d4 100644 --- a/scene/resources/dynamic_font.h +++ b/scene/resources/dynamic_font.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,12 +27,15 @@ /* 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 #ifdef FREETYPE_ENABLED #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> @@ -47,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; @@ -87,6 +102,8 @@ public: ~DynamicFontData(); }; +VARIANT_ENUM_CAST(DynamicFontData::Hinting); + class DynamicFontAtSize : public Reference { GDCLASS(DynamicFontAtSize, Reference) @@ -97,10 +114,12 @@ class DynamicFontAtSize : public Reference { FT_Face face; /* handle to face object */ FT_StreamRec stream; - int ascent; - int descent; - int linegap; - int rect_margin; + float ascent; + float descent; + float linegap; + float rect_margin; + float oversampling; + float scale_color_font; uint32_t texture_flags; @@ -121,6 +140,7 @@ class DynamicFontAtSize : public Reference { bool found; int texture_idx; Rect2 rect; + Rect2 rect_uv; float v_align; float h_align; float advance; @@ -129,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); @@ -145,8 +179,9 @@ class DynamicFontAtSize : public Reference { static HashMap<String, Vector<uint8_t> > _fontdata; Error _load(); -protected: public: + static float font_oversampling; + float get_height() const; float get_ascent() const; @@ -154,9 +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); + void update_oversampling(); DynamicFontAtSize(); ~DynamicFontAtSize(); @@ -179,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(); @@ -206,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); @@ -230,7 +278,18 @@ 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; + + static Mutex *dynamic_font_mutex; + static SelfList<DynamicFont>::List dynamic_fonts; + + static void initialize_dynamic_fonts(); + static void finish_dynamic_fonts(); + static void update_oversampling(); DynamicFont(); ~DynamicFont(); diff --git a/scene/resources/dynamic_font_stb.cpp b/scene/resources/dynamic_font_stb.cpp index 4aa47cb664..29f1106d16 100644 --- a/scene/resources/dynamic_font_stb.cpp +++ b/scene/resources/dynamic_font_stb.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 24d764e716..feae29c0c2 100644 --- a/scene/resources/dynamic_font_stb.h +++ b/scene/resources/dynamic_font_stb.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 fe59450f2e..3fab4d3cfc 100644 --- a/scene/resources/environment.cpp +++ b/scene/resources/environment.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 418949a6ad..27fd57aa09 100644 --- a/scene/resources/environment.h +++ b/scene/resources/environment.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 2b44ea4554..0bae9d9b2d 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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); } @@ -257,8 +268,8 @@ Error BitmapFont::create_from_fnt(const String &p_file) { if (keys.has("file")) { - String file = keys["file"]; - file = p_file.get_base_dir() + "/" + file; + String base_dir = p_file.get_base_dir(); + String file = base_dir.plus_file(keys["file"]); Ref<Texture> tex = ResourceLoader::load(file); if (tex.is_null()) { ERR_PRINT("Can't load font texture!"); @@ -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 3bb7aeac85..4e295b6035 100644 --- a/scene/resources/font.h +++ b/scene/resources/font.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 326320c60f..5e7569586a 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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"; @@ -563,7 +552,7 @@ void SpatialMaterial::_update_shader() { //handle animation code += "\tint particle_total_frames = particles_anim_h_frames * particles_anim_v_frames;\n"; - code += "\tint particle_frame = int(INSTANCE_CUSTOM.y * float(particle_total_frames));\n"; + code += "\tint particle_frame = int(INSTANCE_CUSTOM.z * float(particle_total_frames));\n"; code += "\tif (particles_anim_loop) particle_frame=clamp(particle_frame,0,particle_total_frames-1); else particle_frame=abs(particle_frame)%particle_total_frames;\n"; code += "\tUV /= vec2(float(particles_anim_h_frames),float(particles_anim_v_frames));\n"; code += "\tUV += vec2(float(particle_frame % particles_anim_h_frames) / float(particles_anim_h_frames),float(particle_frame / particles_anim_h_frames) / float(particles_anim_v_frames));\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) ) * SIDE;\n"; + 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 d5c3ef83e2..ce733bfb8d 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 bb33962be6..b0620d3363 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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" @@ -221,7 +222,6 @@ Ref<Mesh> Mesh::create_outline(float p_margin) const { continue; Array a = surface_get_arrays(i); - int vcount = 0; if (i == 0) { arrays = a; @@ -229,6 +229,7 @@ Ref<Mesh> Mesh::create_outline(float p_margin) const { index_accum += v.size(); } else { + int vcount = 0; for (int j = 0; j < arrays.size(); j++) { if (arrays[j].get_type() == Variant::NIL || a[j].get_type() == Variant::NIL) { @@ -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 { @@ -1111,13 +1106,14 @@ Error ArrayMesh::lightmap_unwrap(const Transform &p_base_transform, float p_texe for (int j = 0; j < vc; j++) { Vector3 v = p_base_transform.xform(r[j]); + Vector3 n = p_base_transform.basis.xform(rn[j]).normalized(); vertices[(j + vertex_ofs) * 3 + 0] = v.x; vertices[(j + vertex_ofs) * 3 + 1] = v.y; vertices[(j + vertex_ofs) * 3 + 2] = v.z; - normals[(j + vertex_ofs) * 3 + 0] = rn[j].x; - normals[(j + vertex_ofs) * 3 + 1] = rn[j].y; - normals[(j + vertex_ofs) * 3 + 2] = rn[j].z; + normals[(j + vertex_ofs) * 3 + 0] = n.x; + normals[(j + vertex_ofs) * 3 + 1] = n.y; + normals[(j + vertex_ofs) * 3 + 2] = n.z; uv_index[j + vertex_ofs] = Pair<int, int>(i, j); } @@ -1198,8 +1194,6 @@ Error ArrayMesh::lightmap_unwrap(const Transform &p_base_transform, float p_texe for (int j = 0; j < 3; j++) { - int vertex_idx = gen_vertices[gen_indices[i + j]]; - SurfaceTool::Vertex v = surfaces[surface].vertices[uv_index[gen_vertices[gen_indices[i + j]]].second]; if (surfaces[surface].format & ARRAY_FORMAT_COLOR) { @@ -1278,12 +1272,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 ea38ebf2ff..e8b7ecaf9a 100644 --- a/scene/resources/mesh.h +++ b/scene/resources/mesh.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 116ddc4ac6..8639b325c3 100644 --- a/scene/resources/mesh_data_tool.cpp +++ b/scene/resources/mesh_data_tool.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 119b49161b..f614b80c3b 100644 --- a/scene/resources/mesh_data_tool.h +++ b/scene/resources/mesh_data_tool.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 833a4c3d22..e1d3540fd1 100644 --- a/scene/resources/mesh_library.cpp +++ b/scene/resources/mesh_library.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 c5d23ce50f..69719960e2 100644 --- a/scene/resources/mesh_library.h +++ b/scene/resources/mesh_library.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 ee6efa6e85..4d0a14e3aa 100644 --- a/scene/resources/multimesh.cpp +++ b/scene/resources/multimesh.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 0a5310f641..0875d6d06d 100644 --- a/scene/resources/multimesh.h +++ b/scene/resources/multimesh.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 3a5cb7da2e..846f6e356e 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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); } @@ -270,6 +271,8 @@ Node *SceneState::instance(GenEditState p_edit_state) const { if (i > 0) { if (parent) { parent->_add_child_nocheck(node, snames[n.name]); + if (n.index >= 0 && n.index < parent->get_child_count() - 1) + parent->move_child(node, n.index); } else { //it may be possible that an instanced scene has changed //and the node has nowhere to go anymore @@ -386,6 +389,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map nd.name = _nm_get_string(p_node->get_name(), name_map); nd.instance = -1; //not instanced by default + nd.index = p_node->get_index(); // if this node is part of an instanced scene or sub-instanced scene // we need to get the corresponding instance states. @@ -1114,7 +1118,10 @@ void SceneState::set_bundled_scene(const Dictionary &p_dictionary) { nd.parent = r[idx++]; nd.owner = r[idx++]; nd.type = r[idx++]; - nd.name = r[idx++]; + 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 invalid, stored as 1 nd.instance = r[idx++]; nd.properties.resize(r[idx++]); for (int j = 0; j < nd.properties.size(); j++) { @@ -1206,7 +1213,11 @@ Dictionary SceneState::get_bundled_scene() const { rnodes.push_back(nd.parent); rnodes.push_back(nd.owner); rnodes.push_back(nd.type); - rnodes.push_back(nd.name); + uint32_t name_index = nd.name; + 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); rnodes.push_back(nd.instance); rnodes.push_back(nd.properties.size()); for (int j = 0; j < nd.properties.size(); j++) { @@ -1284,6 +1295,11 @@ StringName SceneState::get_node_name(int p_idx) const { return names[nodes[p_idx].name]; } +int SceneState::get_node_index(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx, nodes.size(), -1); + return nodes[p_idx].index; +} + bool SceneState::is_node_instance_placeholder(int p_idx) const { ERR_FAIL_INDEX_V(p_idx, nodes.size(), false); @@ -1524,7 +1540,7 @@ int SceneState::add_node_path(const NodePath &p_path) { node_paths.push_back(p_path); return (node_paths.size() - 1) | FLAG_ID_IS_PATH; } -int SceneState::add_node(int p_parent, int p_owner, int p_type, int p_name, int p_instance) { +int SceneState::add_node(int p_parent, int p_owner, int p_type, int p_name, int p_instance, int p_index) { NodeData nd; nd.parent = p_parent; @@ -1532,6 +1548,7 @@ int SceneState::add_node(int p_parent, int p_owner, int p_type, int p_name, int nd.type = p_type; nd.name = p_name; nd.instance = p_instance; + nd.index = p_index; nodes.push_back(nd); @@ -1605,6 +1622,7 @@ void SceneState::_bind_methods() { ClassDB::bind_method(D_METHOD("get_node_instance_placeholder", "idx"), &SceneState::get_node_instance_placeholder); ClassDB::bind_method(D_METHOD("get_node_instance", "idx"), &SceneState::get_node_instance); ClassDB::bind_method(D_METHOD("get_node_groups", "idx"), &SceneState::_get_node_groups); + ClassDB::bind_method(D_METHOD("get_node_index", "idx"), &SceneState::get_node_index); ClassDB::bind_method(D_METHOD("get_node_property_count", "idx"), &SceneState::get_node_property_count); ClassDB::bind_method(D_METHOD("get_node_property_name", "idx", "prop_idx"), &SceneState::get_node_property_name); ClassDB::bind_method(D_METHOD("get_node_property_value", "idx", "prop_idx"), &SceneState::get_node_property_value); diff --git a/scene/resources/packed_scene.h b/scene/resources/packed_scene.h index 20bfb19b1f..278986eb62 100644 --- a/scene/resources/packed_scene.h +++ b/scene/resources/packed_scene.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 @@ -48,6 +49,8 @@ class SceneState : public Reference { enum { NO_PARENT_SAVED = 0x7FFFFFFF, + NAME_INDEX_BITS = 18, + NAME_MASK = (1 << NAME_INDEX_BITS) - 1, }; struct NodeData { @@ -57,6 +60,7 @@ class SceneState : public Reference { int type; int name; int instance; + int index; struct Property { @@ -151,6 +155,7 @@ public: String get_node_instance_placeholder(int p_idx) const; bool is_node_instance_placeholder(int p_idx) const; Vector<StringName> get_node_groups(int p_idx) const; + int get_node_index(int p_idx) const; int get_node_property_count(int p_idx) const; StringName get_node_property_name(int p_idx, int p_prop) const; @@ -174,7 +179,7 @@ public: int find_name(const StringName &p_name) const; int add_value(const Variant &p_value); int add_node_path(const NodePath &p_path); - int add_node(int p_parent, int p_owner, int p_type, int p_name, int p_instance); + int add_node(int p_parent, int p_owner, int p_type, int p_name, int p_instance, int p_index); void add_node_property(int p_node, int p_name, int p_value); void add_node_group(int p_node, int p_group); void set_base_scene(int p_idx); diff --git a/scene/resources/plane_shape.cpp b/scene/resources/plane_shape.cpp index 2eb2ceff24..4d38ebe090 100644 --- a/scene/resources/plane_shape.cpp +++ b/scene/resources/plane_shape.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 1384b7521b..c24c9474fb 100644 --- a/scene/resources/plane_shape.h +++ b/scene/resources/plane_shape.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 053369e8d8..6fea2e1a8e 100644 --- a/scene/resources/polygon_path_finder.cpp +++ b/scene/resources/polygon_path_finder.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 9b760caa2e..19761c274c 100644 --- a/scene/resources/polygon_path_finder.h +++ b/scene/resources/polygon_path_finder.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 2e8f9cbb33..5b600623b9 100644 --- a/scene/resources/primitive_meshes.cpp +++ b/scene/resources/primitive_meshes.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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"); } @@ -618,7 +676,7 @@ void CubeMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_subdivide_depth", "divisions"), &CubeMesh::set_subdivide_depth); ClassDB::bind_method(D_METHOD("get_subdivide_depth"), &CubeMesh::get_subdivide_depth); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "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"); @@ -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 b38c247827..a91aa09ffe 100644 --- a/scene/resources/primitive_meshes.h +++ b/scene/resources/primitive_meshes.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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 b03a81b6bd..a9dec3e87c 100644 --- a/scene/resources/ray_shape.cpp +++ b/scene/resources/ray_shape.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 06b68adbae..f11b2e7c3c 100644 --- a/scene/resources/ray_shape.h +++ b/scene/resources/ray_shape.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 202024aa96..aeb22f6d0a 100644 --- a/scene/resources/rectangle_shape_2d.cpp +++ b/scene/resources/rectangle_shape_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 81d9cb1f4a..2d66d328fd 100644 --- a/scene/resources/rectangle_shape_2d.h +++ b/scene/resources/rectangle_shape_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 487975dd4e..9493f8fbe6 100644 --- a/scene/resources/room.cpp +++ b/scene/resources/room.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 aadee858c2..359d918665 100644 --- a/scene/resources/room.h +++ b/scene/resources/room.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 @@ -36,7 +37,7 @@ @author Juan Linietsky <reduzio@gmail.com> */ -//left for reference but will be removed when portals are reimplemented using Area +// FIXME: left for reference but will be removed when portals are reimplemented using Area #if 0 class RoomBounds : public Resource { diff --git a/scene/resources/scene_format_text.cpp b/scene/resources/scene_format_text.cpp index aebbb5b562..597866eb74 100644 --- a/scene/resources/scene_format_text.cpp +++ b/scene/resources/scene_format_text.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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" @@ -198,6 +199,7 @@ Ref<PackedScene> ResourceInteractiveLoaderText::_parse_node_tag(VariantParser::R int type = -1; int name = -1; int instance = -1; + int index = -1; //int base_scene=-1; if (next_tag.fields.has("name")) { @@ -249,7 +251,11 @@ Ref<PackedScene> ResourceInteractiveLoaderText::_parse_node_tag(VariantParser::R owner = 0; //if no owner, owner is root } - int node_id = packed_scene->get_state()->add_node(parent, owner, type, name, instance); + if (next_tag.fields.has("index")) { + index = next_tag.fields["index"]; + } + + int node_id = packed_scene->get_state()->add_node(parent, owner, type, name, instance, index); if (next_tag.fields.has("groups")) { @@ -1306,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; @@ -1405,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; @@ -1437,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; } @@ -1498,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 @@ -1609,6 +1623,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r StringName type = state->get_node_type(i); StringName name = state->get_node_name(i); + int index = state->get_node_index(i); NodePath path = state->get_node_path(i, true); NodePath owner = state->get_node_owner_path(i); Ref<PackedScene> instance = state->get_node_instance(i); @@ -1626,6 +1641,9 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r if (owner != NodePath() && owner != NodePath(".")) { header += " owner=\"" + String(owner.simplified()) + "\""; } + if (index >= 0) { + header += " index=\"" + itos(index) + "\""; + } if (groups.size()) { String sgroups = " groups=[\n"; @@ -1654,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++) { @@ -1664,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++) { @@ -1690,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 5d3c2004c1..c28ded3d77 100644 --- a/scene/resources/scene_format_text.h +++ b/scene/resources/scene_format_text.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 e8ef448e23..58027c127d 100644 --- a/scene/resources/segment_shape_2d.cpp +++ b/scene/resources/segment_shape_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,11 +27,19 @@ /* 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" #include "servers/visual_server.h" +bool SegmentShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + + Vector2 l[2] = { a, b }; + Vector2 closest = Geometry::get_closest_point_to_segment_2d(p_point, l); + return p_point.distance_to(closest) < p_tolerance; +} + void SegmentShape2D::_update_shape() { Rect2 r; @@ -98,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(); } @@ -132,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) { @@ -145,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 eb9c228c83..700982ac0a 100644 --- a/scene/resources/segment_shape_2d.h +++ b/scene/resources/segment_shape_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 @@ -44,6 +45,8 @@ protected: static void _bind_methods(); public: + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; + void set_a(const Vector2 &p_a); void set_b(const Vector2 &p_b); @@ -60,6 +63,7 @@ class RayShape2D : public Shape2D { GDCLASS(RayShape2D, Shape2D); real_t length; + bool slips_on_slope; void _update_shape(); @@ -69,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 66df7dfda8..36740a307b 100644 --- a/scene/resources/shader.cpp +++ b/scene/resources/shader.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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" @@ -151,3 +152,80 @@ Shader::~Shader() { VisualServer::get_singleton()->free(shader); } +//////////// + +RES ResourceFormatLoaderShader::load(const String &p_path, const String &p_original_path, Error *r_error) { + + if (r_error) + *r_error = ERR_FILE_CANT_OPEN; + + Ref<Shader> shader; + shader.instance(); + + Vector<uint8_t> buffer = FileAccess::get_file_as_array(p_path); + + String str; + str.parse_utf8((const char *)buffer.ptr(), buffer.size()); + + shader->set_code(str); + + if (r_error) + *r_error = OK; + + return shader; +} + +void ResourceFormatLoaderShader::get_recognized_extensions(List<String> *p_extensions) const { + + p_extensions->push_back("shader"); +} + +bool ResourceFormatLoaderShader::handles_type(const String &p_type) const { + + return (p_type == "Shader"); +} + +String ResourceFormatLoaderShader::get_resource_type(const String &p_path) const { + + String el = p_path.get_extension().to_lower(); + if (el == "shader") + return "Shader"; + return ""; +} + +Error ResourceFormatSaverShader::save(const String &p_path, const RES &p_resource, uint32_t p_flags) { + + Ref<Shader> shader = p_resource; + ERR_FAIL_COND_V(shader.is_null(), ERR_INVALID_PARAMETER); + + String source = shader->get_code(); + + Error err; + FileAccess *file = FileAccess::open(p_path, FileAccess::WRITE, &err); + + if (err) { + + ERR_FAIL_COND_V(err, err); + } + + file->store_string(source); + if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) { + memdelete(file); + return ERR_CANT_CREATE; + } + file->close(); + memdelete(file); + + return OK; +} + +void ResourceFormatSaverShader::get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const { + + if (Object::cast_to<Shader>(*p_resource)) { + p_extensions->push_back("shader"); + } +} +bool ResourceFormatSaverShader::recognize(const RES &p_resource) const { + + return Object::cast_to<Shader>(*p_resource) != NULL; +} diff --git a/scene/resources/shader.h b/scene/resources/shader.h index 5cc70629c7..248a6f0125 100644 --- a/scene/resources/shader.h +++ b/scene/resources/shader.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,10 +27,12 @@ /* 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 #include "io/resource_loader.h" +#include "io/resource_saver.h" #include "resource.h" #include "scene/resources/texture.h" @@ -38,7 +40,6 @@ class Shader : public Resource { GDCLASS(Shader, Resource); OBJ_SAVE_TYPE(Shader); - RES_BASE_EXTENSION("shd"); public: enum Mode { @@ -95,4 +96,19 @@ public: VARIANT_ENUM_CAST(Shader::Mode); +class ResourceFormatLoaderShader : 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; +}; + +class ResourceFormatSaverShader : public ResourceFormatSaver { +public: + virtual Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0); + virtual void get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const; + virtual bool recognize(const RES &p_resource) const; +}; + #endif // SHADER_H diff --git a/scene/resources/shader_graph.cpp b/scene/resources/shader_graph.cpp index 3813854922..070cc84863 100644 --- a/scene/resources/shader_graph.cpp +++ b/scene/resources/shader_graph.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 5d9dd7054f..e3a68f8572 100644 --- a/scene/resources/shader_graph.h +++ b/scene/resources/shader_graph.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 0046ff58a8..418d8ce819 100644 --- a/scene/resources/shape.cpp +++ b/scene/resources/shape.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 bc75e723e9..ad87a69679 100644 --- a/scene/resources/shape.h +++ b/scene/resources/shape.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 4423c78bbe..0ca518e5bb 100644 --- a/scene/resources/shape_2d.cpp +++ b/scene/resources/shape_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 fffc7011f1..7eb0406bd8 100644 --- a/scene/resources/shape_2d.h +++ b/scene/resources/shape_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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,6 +45,8 @@ protected: Shape2D(const RID &p_rid); public: + 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 512ff3bc56..1a81eea6e5 100644 --- a/scene/resources/shape_line_2d.cpp +++ b/scene/resources/shape_line_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,9 +27,25 @@ /* 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" + +bool LineShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + + Vector2 point = get_d() * get_normal(); + Vector2 l[2][2] = { { point - get_normal().tangent() * 100, point + get_normal().tangent() * 100 }, { point, point + get_normal() * 30 } }; + + for (int i = 0; i < 2; i++) { + Vector2 closest = Geometry::get_closest_point_to_segment_2d(p_point, l[i]); + if (p_point.distance_to(closest) < p_tolerance) + return true; + } + + return false; +} + void LineShape2D::_update_shape() { Array arr; diff --git a/scene/resources/shape_line_2d.h b/scene/resources/shape_line_2d.h index 68298b63be..7d31941558 100644 --- a/scene/resources/shape_line_2d.h +++ b/scene/resources/shape_line_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 @@ -44,6 +45,8 @@ protected: static void _bind_methods(); public: + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; + void set_normal(const Vector2 &p_normal); void set_d(real_t p_d); diff --git a/scene/resources/sky_box.cpp b/scene/resources/sky_box.cpp index a2c3f1f111..f2d5cb3516 100644 --- a/scene/resources/sky_box.cpp +++ b/scene/resources/sky_box.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 8934463b6f..e561653a9e 100644 --- a/scene/resources/sky_box.h +++ b/scene/resources/sky_box.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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 4177364de7..062f4099db 100644 --- a/scene/resources/space_2d.cpp +++ b/scene/resources/space_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 8ba392f85d..148285ac6d 100644 --- a/scene/resources/space_2d.h +++ b/scene/resources/space_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 042988dcd3..d8ca1cf3f1 100644 --- a/scene/resources/sphere_shape.cpp +++ b/scene/resources/sphere_shape.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 43d7956246..5dd7daabc5 100644 --- a/scene/resources/sphere_shape.h +++ b/scene/resources/sphere_shape.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 f4a9abc1ea..ebad00b068 100644 --- a/scene/resources/style_box.cpp +++ b/scene/resources/style_box.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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,15 +129,24 @@ 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; } void StyleBoxTexture::set_margin_size(Margin p_margin, float p_size) { + ERR_FAIL_INDEX(p_margin, 4); + margin[p_margin] = p_size; emit_changed(); + static const char *margin_prop[4] = { + "content_margin_left", + "content_margin_top", + "content_margin_right", + "content_margin_bottom", + }; + _change_notify(margin_prop[p_margin]); } float StyleBoxTexture::get_margin_size(Margin p_margin) const { @@ -181,7 +195,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 +663,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 +925,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 4627c900ff..c1d84fe19f 100644 --- a/scene/resources/style_box.h +++ b/scene/resources/style_box.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 d8600e041d..5a42873d79 100644 --- a/scene/resources/surface_tool.cpp +++ b/scene/resources/surface_tool.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 b180ffe260..459d399380 100644 --- a/scene/resources/surface_tool.h +++ b/scene/resources/surface_tool.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 987d6c5f6a..54f5aea160 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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,14 @@ 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_GROUP("Flags", ""); + 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"); + ADD_GROUP("", ""); + + 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 +125,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 +143,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 +161,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) { @@ -227,12 +222,15 @@ Image::Format ImageTexture::get_format() const { return format; } -void ImageTexture::load(const String &p_path) { +Error ImageTexture::load(const String &p_path) { Ref<Image> img; img.instance(); - img->load(p_path); - create_from_image(img); + Error err = img->load(p_path); + if (err == OK) { + create_from_image(img); + } + return err; } void ImageTexture::set_data(const Ref<Image> &p_image) { @@ -362,6 +360,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); @@ -707,6 +708,8 @@ Ref<Image> StreamTexture::get_data() const { } void StreamTexture::set_flags(uint32_t p_flags) { + flags = p_flags; + VS::get_singleton()->texture_set_flags(texture, flags); } void StreamTexture::reload_from_file() { @@ -1130,7 +1133,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 { @@ -1281,8 +1284,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") { @@ -1307,8 +1308,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") { @@ -1328,7 +1327,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")); @@ -1350,6 +1348,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 76c0195ef9..d81fd3b19b 100644 --- a/scene/resources/texture.h +++ b/scene/resources/texture.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 @@ -123,7 +124,7 @@ public: void set_flags(uint32_t p_flags); uint32_t get_flags() const; Image::Format get_format() const; - void load(const String &p_path); + Error load(const String &p_path); void set_data(const Ref<Image> &p_image); Ref<Image> get_data() const; diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp index e2e29b037b..f903669fc7 100644 --- a/scene/resources/theme.cpp +++ b/scene/resources/theme.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 5b375d5585..c23f237c75 100644 --- a/scene/resources/theme.h +++ b/scene/resources/theme.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 bd6b917d4e..58057cda0c 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 (minimal),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)); @@ -893,8 +913,8 @@ void TileSet::clear() { void TileSet::_bind_methods() { ClassDB::bind_method(D_METHOD("create_tile", "id"), &TileSet::create_tile); - ClassDB::bind_method(D_METHOD("autotile_set_bitmask_mode", "mode"), &TileSet::autotile_set_bitmask_mode); - ClassDB::bind_method(D_METHOD("autotile_get_bitmask_mode"), &TileSet::autotile_get_bitmask_mode); + ClassDB::bind_method(D_METHOD("autotile_set_bitmask_mode", "id", "mode"), &TileSet::autotile_set_bitmask_mode); + ClassDB::bind_method(D_METHOD("autotile_get_bitmask_mode", "id"), &TileSet::autotile_get_bitmask_mode); ClassDB::bind_method(D_METHOD("tile_set_name", "id", "name"), &TileSet::tile_set_name); ClassDB::bind_method(D_METHOD("tile_get_name", "id"), &TileSet::tile_get_name); ClassDB::bind_method(D_METHOD("tile_set_texture", "id", "texture"), &TileSet::tile_set_texture); @@ -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); @@ -936,6 +960,7 @@ void TileSet::_bind_methods() { BIND_VMETHOD(MethodInfo(Variant::VECTOR2, "_forward_subtile_selection", PropertyInfo(Variant::INT, "autotile_id"), PropertyInfo(Variant::INT, "bitmask"), PropertyInfo(Variant::OBJECT, "tilemap", PROPERTY_HINT_NONE, "TileMap"), PropertyInfo(Variant::VECTOR2, "tile_location"))); BIND_ENUM_CONSTANT(BITMASK_2X2); + BIND_ENUM_CONSTANT(BITMASK_3X3_MINIMAL); BIND_ENUM_CONSTANT(BITMASK_3X3); BIND_ENUM_CONSTANT(BIND_TOPLEFT); @@ -946,6 +971,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 deac583f62..ec635ee5cc 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 @@ -55,6 +56,7 @@ public: enum BitmaskMode { BITMASK_2X2, + BITMASK_3X3_MINIMAL, BITMASK_3X3 }; @@ -70,6 +72,12 @@ public: BIND_BOTTOMRIGHT = 256 }; + enum TileMode { + SINGLE_TILE, + AUTO_TILE, + ANIMATED_TILE + }; + struct AutotileData { BitmaskMode bitmask_mode; int spacing; @@ -83,6 +91,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 +112,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 +156,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 +223,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 +244,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 2298e2ca9f..3cd8cbae77 100644 --- a/scene/resources/video_stream.cpp +++ b/scene/resources/video_stream.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 fbe52909e7..0d25d9d687 100644 --- a/scene/resources/video_stream.h +++ b/scene/resources/video_stream.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 c63ba24cbd..82183d24e7 100644 --- a/scene/resources/world.cpp +++ b/scene/resources/world.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 e0f1de1fd0..54bdf25784 100644 --- a/scene/resources/world.h +++ b/scene/resources/world.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 a644793bd8..bed6ffd1bd 100644 --- a/scene/resources/world_2d.cpp +++ b/scene/resources/world_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 63d32473e0..59f34e32f2 100644 --- a/scene/resources/world_2d.h +++ b/scene/resources/world_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 52b8e35d5e..2dc32b893d 100644 --- a/scene/scene_string_names.cpp +++ b/scene/scene_string_names.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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 3b110e7a62..2e6da26d68 100644 --- a/scene/scene_string_names.h +++ b/scene/scene_string_names.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +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; |