diff options
Diffstat (limited to 'scene')
103 files changed, 2458 insertions, 820 deletions
diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp index 25ad6bd5c9..5bf70e12b7 100644 --- a/scene/2d/animated_sprite.cpp +++ b/scene/2d/animated_sprite.cpp @@ -646,6 +646,7 @@ void AnimatedSprite::_reset_timeout() { void AnimatedSprite::set_animation(const StringName &p_animation) { ERR_EXPLAIN(vformat("There is no animation with name '%s'.", p_animation)); + ERR_FAIL_COND(frames == NULL); ERR_FAIL_COND(frames->get_animation_names().find(p_animation) == -1); if (animation == p_animation) diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp index 11846654c5..aa14491577 100644 --- a/scene/2d/camera_2d.cpp +++ b/scene/2d/camera_2d.cpp @@ -44,15 +44,16 @@ void Camera2D::_update_scroll() { return; } + if (!viewport) + return; + if (current) { ERR_FAIL_COND(custom_viewport && !ObjectDB::get_instance(custom_viewport_id)); Transform2D xform = get_camera_transform(); - if (viewport) { - viewport->set_canvas_transform(xform); - } + viewport->set_canvas_transform(xform); Size2 screen_size = viewport->get_visible_rect().size; Point2 screen_offset = (anchor_mode == ANCHOR_MODE_DRAG_CENTER ? (screen_size * 0.5) : Point2()); diff --git a/scene/2d/canvas_modulate.cpp b/scene/2d/canvas_modulate.cpp index bd7bb97b03..009d664462 100644 --- a/scene/2d/canvas_modulate.cpp +++ b/scene/2d/canvas_modulate.cpp @@ -70,7 +70,7 @@ void CanvasModulate::_bind_methods() { void CanvasModulate::set_color(const Color &p_color) { color = p_color; - if (is_inside_tree()) { + if (is_visible_in_tree()) { VS::get_singleton()->canvas_set_modulate(get_canvas(), color); } } diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp index 721b52edaa..2b1009a2d1 100644 --- a/scene/2d/cpu_particles_2d.cpp +++ b/scene/2d/cpu_particles_2d.cpp @@ -545,6 +545,8 @@ void CPUParticles2D::_particles_process(float p_delta) { velocity_xform[2] = Vector2(); } + float system_phase = time / lifetime; + for (int i = 0; i < pcount; i++) { Particle &p = parray[i]; @@ -552,21 +554,26 @@ void CPUParticles2D::_particles_process(float p_delta) { if (!emitting && !p.active) continue; - float restart_time = (float(i) / float(pcount)) * lifetime; float local_delta = p_delta; + // The phase is a ratio between 0 (birth) and 1 (end of life) for each particle. + // While we use time in tests later on, for randomness we use the phase as done in the + // original shader code, and we later multiply by lifetime to get the time. + float restart_phase = float(i) / float(pcount); + if (randomness_ratio > 0.0) { uint32_t seed = cycle; - if (restart_time >= time) { + if (restart_phase >= system_phase) { seed -= uint32_t(1); } seed *= uint32_t(pcount); seed += uint32_t(i); float random = float(idhash(seed) % uint32_t(65536)) / 65536.0; - restart_time += randomness_ratio * random * 1.0 / float(pcount); + restart_phase += randomness_ratio * random * 1.0 / float(pcount); } - restart_time *= (1.0 - explosiveness_ratio); + restart_phase *= (1.0 - explosiveness_ratio); + float restart_time = restart_phase * lifetime; bool restart = false; if (time > prev_time) { @@ -748,7 +755,7 @@ void CPUParticles2D::_particles_process(float p_delta) { force += diff.length() > 0.0 ? diff.normalized() * (parameters[PARAM_RADIAL_ACCEL] + tex_radial_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_RADIAL_ACCEL]) : Vector2(); //apply tangential acceleration; Vector2 yx = Vector2(diff.y, diff.x); - force += yx.length() > 0.0 ? (yx * Vector2(-1.0, 1.0)) * ((parameters[PARAM_TANGENTIAL_ACCEL] + tex_tangential_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_TANGENTIAL_ACCEL])) : Vector2(); + force += yx.length() > 0.0 ? (yx * Vector2(-1.0, 1.0)).normalized() * ((parameters[PARAM_TANGENTIAL_ACCEL] + tex_tangential_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_TANGENTIAL_ACCEL])) : Vector2(); //apply attractor forces p.velocity += force * local_delta; //orbit velocity @@ -978,7 +985,7 @@ void CPUParticles2D::_notification(int p_what) { if (p_what == NOTIFICATION_DRAW) { if (!redraw) - return; // dont add to render list + return; // don't add to render list RID texrid; if (texture.is_valid()) { @@ -1174,15 +1181,16 @@ void CPUParticles2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting"); 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_EXP_RANGE, "0.01,600.0,0.01,or_greater"), "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_EXP_RANGE, "0.00,600.0,0.01"), "set_pre_process_time", "get_pre_process_time"); + 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,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"); ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_fps", PROPERTY_HINT_RANGE, "0,1000,1"), "set_fixed_fps", "get_fixed_fps"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fract_delta"), "set_fractional_delta", "get_fractional_delta"); ADD_GROUP("Drawing", ""); + // No visibility_rect property contrarily to Particles2D, it's updated automatically. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "local_coords"), "set_use_local_coordinates", "get_use_local_coordinates"); ADD_PROPERTY(PropertyInfo(Variant::INT, "draw_order", PROPERTY_HINT_ENUM, "Index,Lifetime"), "set_draw_order", "get_draw_order"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture"); diff --git a/scene/2d/cpu_particles_2d.h b/scene/2d/cpu_particles_2d.h index 23d2586331..81343a4604 100644 --- a/scene/2d/cpu_particles_2d.h +++ b/scene/2d/cpu_particles_2d.h @@ -116,7 +116,7 @@ private: const Particle *particles; bool operator()(int p_a, int p_b) const { - return particles[p_a].time < particles[p_b].time; + return particles[p_a].time > particles[p_b].time; } }; diff --git a/scene/2d/line_2d.cpp b/scene/2d/line_2d.cpp index ba06b3ebff..5ba184b324 100644 --- a/scene/2d/line_2d.cpp +++ b/scene/2d/line_2d.cpp @@ -105,6 +105,7 @@ void Line2D::set_point_position(int i, Vector2 pos) { } Vector2 Line2D::get_point_position(int i) const { + ERR_FAIL_INDEX_V(i, _points.size(), Vector2()); return _points.get(i); } diff --git a/scene/2d/mesh_instance_2d.cpp b/scene/2d/mesh_instance_2d.cpp index b382ca7b33..bcd4bca940 100644 --- a/scene/2d/mesh_instance_2d.cpp +++ b/scene/2d/mesh_instance_2d.cpp @@ -50,6 +50,8 @@ void MeshInstance2D::_bind_methods() { 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_SIGNAL(MethodInfo("texture_changed")); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "normal_map", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_normal_map", "get_normal_map"); diff --git a/scene/2d/multimesh_instance_2d.cpp b/scene/2d/multimesh_instance_2d.cpp new file mode 100644 index 0000000000..ca75302163 --- /dev/null +++ b/scene/2d/multimesh_instance_2d.cpp @@ -0,0 +1,111 @@ +/*************************************************************************/ +/* multimesh_instance_2d.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "multimesh_instance_2d.h" + +void MultiMeshInstance2D::_notification(int p_what) { + + if (p_what == NOTIFICATION_DRAW) { + if (multimesh.is_valid()) { + draw_multimesh(multimesh, texture, normal_map); + } + } +} + +void MultiMeshInstance2D::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_multimesh", "multimesh"), &MultiMeshInstance2D::set_multimesh); + ClassDB::bind_method(D_METHOD("get_multimesh"), &MultiMeshInstance2D::get_multimesh); + + ClassDB::bind_method(D_METHOD("set_texture", "texture"), &MultiMeshInstance2D::set_texture); + ClassDB::bind_method(D_METHOD("get_texture"), &MultiMeshInstance2D::get_texture); + + ClassDB::bind_method(D_METHOD("set_normal_map", "normal_map"), &MultiMeshInstance2D::set_normal_map); + ClassDB::bind_method(D_METHOD("get_normal_map"), &MultiMeshInstance2D::get_normal_map); + + ADD_SIGNAL(MethodInfo("texture_changed")); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multimesh", PROPERTY_HINT_RESOURCE_TYPE, "MultiMesh"), "set_multimesh", "get_multimesh"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "normal_map", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_normal_map", "get_normal_map"); +} + +void MultiMeshInstance2D::set_multimesh(const Ref<MultiMesh> &p_multimesh) { + + multimesh = p_multimesh; + update(); +} + +Ref<MultiMesh> MultiMeshInstance2D::get_multimesh() const { + + return multimesh; +} + +void MultiMeshInstance2D::set_texture(const Ref<Texture> &p_texture) { + + if (p_texture == texture) + return; + texture = p_texture; + update(); + emit_signal("texture_changed"); + _change_notify("texture"); +} + +Ref<Texture> MultiMeshInstance2D::get_texture() const { + + return texture; +} + +void MultiMeshInstance2D::set_normal_map(const Ref<Texture> &p_texture) { + + normal_map = p_texture; + update(); +} + +Ref<Texture> MultiMeshInstance2D::get_normal_map() const { + + return normal_map; +} + +Rect2 MultiMeshInstance2D::_edit_get_rect() const { + + if (multimesh.is_valid()) { + AABB aabb = multimesh->get_aabb(); + return Rect2(aabb.position.x, aabb.position.y, aabb.size.x, aabb.size.y); + } + + return Node2D::_edit_get_rect(); +} + +MultiMeshInstance2D::MultiMeshInstance2D() { +} + +MultiMeshInstance2D::~MultiMeshInstance2D() { +} diff --git a/scene/2d/multimesh_instance_2d.h b/scene/2d/multimesh_instance_2d.h new file mode 100644 index 0000000000..3795497183 --- /dev/null +++ b/scene/2d/multimesh_instance_2d.h @@ -0,0 +1,65 @@ +/*************************************************************************/ +/* multimesh_instance_2d.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef MULTIMESH_INSTANCE_2D_H +#define MULTIMESH_INSTANCE_2D_H + +#include "scene/2d/node_2d.h" +#include "scene/resources/multimesh.h" + +class MultiMeshInstance2D : public Node2D { + GDCLASS(MultiMeshInstance2D, Node2D); + + Ref<MultiMesh> multimesh; + + Ref<Texture> texture; + Ref<Texture> normal_map; + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + void set_multimesh(const Ref<MultiMesh> &p_multimesh); + Ref<MultiMesh> get_multimesh() 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; + + MultiMeshInstance2D(); + ~MultiMeshInstance2D(); +}; + +#endif // MULTIMESH_INSTANCE_2D_H diff --git a/scene/2d/navigation_polygon.cpp b/scene/2d/navigation_polygon.cpp index 0f6af358bd..e389d5f98f 100644 --- a/scene/2d/navigation_polygon.cpp +++ b/scene/2d/navigation_polygon.cpp @@ -312,7 +312,7 @@ 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 | PROPERTY_USAGE_INTERNAL), "set_vertices", "get_vertices"); + ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_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"); } diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp index 4097006b33..e062067248 100644 --- a/scene/2d/path_2d.cpp +++ b/scene/2d/path_2d.cpp @@ -264,7 +264,7 @@ void PathFollow2D::_validate_property(PropertyInfo &property) const { if (path && path->get_curve().is_valid()) max = path->get_curve()->get_baked_length(); - property.hint_string = "0," + rtos(max) + ",0.01,or_greater"; + property.hint_string = "0," + rtos(max) + ",0.01,or_lesser"; } } @@ -306,8 +306,8 @@ void PathFollow2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_lookahead", "lookahead"), &PathFollow2D::set_lookahead); ClassDB::bind_method(D_METHOD("get_lookahead"), &PathFollow2D::get_lookahead); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "offset", PROPERTY_HINT_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,or_greater", PROPERTY_USAGE_EDITOR), "set_unit_offset", "get_unit_offset"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "offset", PROPERTY_HINT_RANGE, "0,10000,0.01,or_lesser"), "set_offset", "get_offset"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "unit_offset", PROPERTY_HINT_RANGE, "0,1,0.0001,or_lesser", 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"); @@ -319,8 +319,24 @@ void PathFollow2D::_bind_methods() { void PathFollow2D::set_offset(float p_offset) { offset = p_offset; - if (path) + if (path) { + if (path->get_curve().is_valid() && path->get_curve()->get_baked_length()) { + float path_length = path->get_curve()->get_baked_length(); + + if (loop) { + while (offset > path_length) + offset -= path_length; + + while (offset < 0) + offset += path_length; + + } else { + offset = CLAMP(offset, 0, path_length); + } + } + _update_transform(); + } _change_notify("offset"); _change_notify("unit_offset"); } diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index 578c9aa5f9..2bd9e5e2a2 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -203,8 +203,8 @@ void StaticBody2D::set_friction(real_t p_friction) { return; } - ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physics material instead.") - WARN_DEPRECATED + ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physics material instead."); + WARN_DEPRECATED; ERR_FAIL_COND(p_friction < 0 || p_friction > 1); @@ -217,8 +217,8 @@ void StaticBody2D::set_friction(real_t p_friction) { real_t StaticBody2D::get_friction() const { - ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physics material instead.") - WARN_DEPRECATED + ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physics material instead."); + WARN_DEPRECATED; if (physics_material_override.is_null()) { return 1; @@ -233,8 +233,8 @@ void StaticBody2D::set_bounce(real_t p_bounce) { return; } - ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physics material instead.") - WARN_DEPRECATED + ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physics material instead."); + WARN_DEPRECATED; ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1); @@ -247,8 +247,8 @@ void StaticBody2D::set_bounce(real_t p_bounce) { real_t StaticBody2D::get_bounce() const { - ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physics material instead.") - WARN_DEPRECATED + ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physics material instead."); + WARN_DEPRECATED; if (physics_material_override.is_null()) { return 0; @@ -630,8 +630,8 @@ void RigidBody2D::set_friction(real_t p_friction) { return; } - ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physics material instead.") - WARN_DEPRECATED + ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physics material instead."); + WARN_DEPRECATED; ERR_FAIL_COND(p_friction < 0 || p_friction > 1); @@ -643,8 +643,8 @@ void RigidBody2D::set_friction(real_t p_friction) { } real_t RigidBody2D::get_friction() const { - ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physics material instead.") - WARN_DEPRECATED + ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physics material instead."); + WARN_DEPRECATED; if (physics_material_override.is_null()) { return 1; @@ -659,8 +659,8 @@ void RigidBody2D::set_bounce(real_t p_bounce) { return; } - ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physics material instead.") - WARN_DEPRECATED + ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physics material instead."); + WARN_DEPRECATED; ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1); @@ -672,8 +672,8 @@ void RigidBody2D::set_bounce(real_t p_bounce) { } real_t RigidBody2D::get_bounce() const { - ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physics material instead.") - WARN_DEPRECATED + ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physics material instead."); + WARN_DEPRECATED; if (physics_material_override.is_null()) { return 0; diff --git a/scene/2d/position_2d.cpp b/scene/2d/position_2d.cpp index bed6f8a816..8bccf117fd 100644 --- a/scene/2d/position_2d.cpp +++ b/scene/2d/position_2d.cpp @@ -35,13 +35,15 @@ void Position2D::_draw_cross() { - draw_line(Point2(-10, 0), Point2(+10, 0), Color(1, 0.5, 0.5)); - draw_line(Point2(0, -10), Point2(0, +10), Color(0.5, 1, 0.5)); + float extents = get_gizmo_extents(); + draw_line(Point2(-extents, 0), Point2(+extents, 0), Color(1, 0.5, 0.5)); + draw_line(Point2(0, -extents), Point2(0, +extents), Color(0.5, 1, 0.5)); } Rect2 Position2D::_edit_get_rect() const { - return Rect2(Point2(-10, -10), Size2(20, 20)); + float extents = get_gizmo_extents(); + return Rect2(Point2(-extents, -extents), Size2(extents * 2, extents * 2)); } bool Position2D::_edit_use_rect() const { @@ -66,5 +68,31 @@ void Position2D::_notification(int p_what) { } } +void Position2D::set_gizmo_extents(float p_extents) { + if (p_extents == DEFAULT_GIZMO_EXTENTS) { + set_meta("_gizmo_extents_", Variant()); + } else { + set_meta("_gizmo_extents_", p_extents); + } + + update(); +} + +float Position2D::get_gizmo_extents() const { + if (has_meta("_gizmo_extents_")) { + return get_meta("_gizmo_extents_"); + } else { + return DEFAULT_GIZMO_EXTENTS; + } +} + +void Position2D::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_set_gizmo_extents", "extents"), &Position2D::set_gizmo_extents); + ClassDB::bind_method(D_METHOD("_get_gizmo_extents"), &Position2D::get_gizmo_extents); + + ADD_PROPERTY(PropertyInfo(Variant::REAL, "gizmo_extents", PROPERTY_HINT_RANGE, "0,1000,0.1,or_greater", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_gizmo_extents", "_get_gizmo_extents"); +} + Position2D::Position2D() { } diff --git a/scene/2d/position_2d.h b/scene/2d/position_2d.h index c95315fea3..dc9cc2df15 100644 --- a/scene/2d/position_2d.h +++ b/scene/2d/position_2d.h @@ -37,14 +37,21 @@ class Position2D : public Node2D { GDCLASS(Position2D, Node2D) + const float DEFAULT_GIZMO_EXTENTS = 10.0; + void _draw_cross(); protected: void _notification(int p_what); + static void _bind_methods(); public: virtual Rect2 _edit_get_rect() const; virtual bool _edit_use_rect() const; + + void set_gizmo_extents(float p_extents); + float get_gizmo_extents() const; + Position2D(); }; diff --git a/scene/2d/skeleton_2d.h b/scene/2d/skeleton_2d.h index cf9877e6f8..d24c0a1561 100644 --- a/scene/2d/skeleton_2d.h +++ b/scene/2d/skeleton_2d.h @@ -39,6 +39,9 @@ class Bone2D : public Node2D { GDCLASS(Bone2D, Node2D) friend class Skeleton2D; +#ifdef TOOLS_ENABLED + friend class AnimatedValuesBackup; +#endif Bone2D *parent_bone; Skeleton2D *skeleton; @@ -71,6 +74,9 @@ class Skeleton2D : public Node2D { GDCLASS(Skeleton2D, Node2D); friend class Bone2D; +#ifdef TOOLS_ENABLED + friend class AnimatedValuesBackup; +#endif struct Bone { bool operator<(const Bone &p_bone) const { diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index b321bcf3ce..b3b6f3175d 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -202,47 +202,27 @@ void TileMap::_fix_cell_transform(Transform2D &xform, const Cell &p_cell, const Size2 s = p_sc; Vector2 offset = p_offset; - if (tile_origin == TILE_ORIGIN_BOTTOM_LEFT) - offset.y += cell_size.y; - else if (tile_origin == TILE_ORIGIN_CENTER) { - offset += cell_size / 2; - } - - if (s.y > s.x) { - if ((p_cell.flip_h && (p_cell.flip_v || p_cell.transpose)) || (p_cell.flip_v && !p_cell.transpose)) - offset.y += s.y - s.x; - } else if (s.y < s.x) { - if ((p_cell.flip_v && (p_cell.flip_h || p_cell.transpose)) || (p_cell.flip_h && !p_cell.transpose)) - offset.x += s.x - s.y; - } - if (p_cell.transpose) { SWAP(xform.elements[0].x, xform.elements[0].y); SWAP(xform.elements[1].x, xform.elements[1].y); SWAP(offset.x, offset.y); SWAP(s.x, s.y); } + if (p_cell.flip_h) { xform.elements[0].x = -xform.elements[0].x; xform.elements[1].x = -xform.elements[1].x; - if (tile_origin == TILE_ORIGIN_TOP_LEFT || tile_origin == TILE_ORIGIN_BOTTOM_LEFT) - offset.x = s.x - offset.x; - else if (tile_origin == TILE_ORIGIN_CENTER) - offset.x = s.x - offset.x / 2; + offset.x = s.x - offset.x; } + if (p_cell.flip_v) { xform.elements[0].y = -xform.elements[0].y; xform.elements[1].y = -xform.elements[1].y; - if (tile_origin == TILE_ORIGIN_TOP_LEFT) - offset.y = s.y - offset.y; - else if (tile_origin == TILE_ORIGIN_BOTTOM_LEFT) { - offset.y += s.y; - } else if (tile_origin == TILE_ORIGIN_CENTER) { - offset.y += s.y; - } + offset.y = s.y - offset.y; } - xform.elements[2].x += offset.x; - xform.elements[2].y += offset.y; + /* For a future CheckBox to Center Texture: + offset += cell_size / 2 - s / 2; */ + xform.elements[2] += offset; } void TileMap::update_dirty_quadrants() { @@ -390,64 +370,26 @@ void TileMap::update_dirty_quadrants() { rect.size.x += fp_adjust; rect.size.y += fp_adjust; - if (rect.size.y > rect.size.x) { - if ((c.flip_h && (c.flip_v || c.transpose)) || (c.flip_v && !c.transpose)) - tile_ofs.y += rect.size.y - rect.size.x; - } else if (rect.size.y < rect.size.x) { - if ((c.flip_v && (c.flip_h || c.transpose)) || (c.flip_h && !c.transpose)) - tile_ofs.x += rect.size.x - rect.size.y; - } - - /* rect.size.x+=fp_adjust; - rect.size.y+=fp_adjust;*/ - - if (c.transpose) + if (c.transpose) { SWAP(tile_ofs.x, tile_ofs.y); + /* For a future CheckBox to Center Texture: + rect.position.x += cell_size.x / 2 - rect.size.y / 2; + rect.position.y += cell_size.y / 2 - rect.size.x / 2; + } else { + rect.position += cell_size / 2 - rect.size / 2; */ + } if (c.flip_h) { rect.size.x = -rect.size.x; tile_ofs.x = -tile_ofs.x; } + if (c.flip_v) { rect.size.y = -rect.size.y; tile_ofs.y = -tile_ofs.y; } - Vector2 center_ofs; - - if (tile_origin == TILE_ORIGIN_TOP_LEFT) { - rect.position += tile_ofs; - - } else if (tile_origin == TILE_ORIGIN_BOTTOM_LEFT) { - - rect.position += tile_ofs; - - if (c.transpose) { - if (c.flip_h) - rect.position.x -= cell_size.x; - else - rect.position.x += cell_size.x; - } else { - if (c.flip_v) - rect.position.y -= cell_size.y; - else - rect.position.y += cell_size.y; - } - - } else if (tile_origin == TILE_ORIGIN_CENTER) { - - rect.position += tile_ofs; - - if (c.flip_h) - rect.position.x -= cell_size.x / 2; - else - rect.position.x += cell_size.x / 2; - - if (c.flip_v) - rect.position.y -= cell_size.y / 2; - else - rect.position.y += cell_size.y / 2; - } + rect.position += tile_ofs; Ref<Texture> normal_map = tile_set->tile_get_normal_map(c.id); Color modulate = tile_set->tile_get_modulate(c.id); @@ -471,7 +413,7 @@ void TileMap::update_dirty_quadrants() { Vector2 shape_ofs = shapes[j].shape_transform.get_origin(); - _fix_cell_transform(xform, c, shape_ofs + center_ofs, s); + _fix_cell_transform(xform, c, shape_ofs, s); xform *= shapes[j].shape_transform.untranslated(); @@ -491,7 +433,7 @@ void TileMap::update_dirty_quadrants() { shape_idx++; #ifdef DEBUG_ENABLED } else { - print_error("The TileSet asigned to the TileMap " + get_name() + " has an invalid convex shape."); + print_error("The TileSet assigned to the TileMap " + get_name() + " has an invalid convex shape."); #endif } } @@ -523,7 +465,7 @@ void TileMap::update_dirty_quadrants() { if (navpoly.is_valid()) { Transform2D xform; xform.set_origin(offset.floor() + q.pos); - _fix_cell_transform(xform, c, npoly_ofs + center_ofs, s); + _fix_cell_transform(xform, c, npoly_ofs, s); int pid = navigation->navpoly_add(navpoly, nav_rel * xform); @@ -573,7 +515,7 @@ void TileMap::update_dirty_quadrants() { } Transform2D navxform; navxform.set_origin(offset.floor()); - _fix_cell_transform(navxform, c, npoly_ofs + center_ofs, s); + _fix_cell_transform(navxform, c, npoly_ofs, s); vs->canvas_item_set_transform(debug_navigation_item, navxform); vs->canvas_item_add_triangle_array(debug_navigation_item, indices, vertices, colors); @@ -593,7 +535,7 @@ void TileMap::update_dirty_quadrants() { Vector2 occluder_ofs = tile_set->tile_get_occluder_offset(c.id); Transform2D xform; xform.set_origin(offset.floor() + q.pos); - _fix_cell_transform(xform, c, occluder_ofs + center_ofs, s); + _fix_cell_transform(xform, c, occluder_ofs, s); RID orid = VS::get_singleton()->canvas_light_occluder_create(); VS::get_singleton()->canvas_light_occluder_set_transform(orid, get_global_transform() * xform); @@ -927,8 +869,17 @@ void TileMap::update_cell_bitmask(int p_x, int p_y) { _make_quadrant_dirty(Q); } else if (tile_set->tile_get_tile_mode(id) == TileSet::SINGLE_TILE) { + E->get().autotile_coord_x = 0; E->get().autotile_coord_y = 0; + } else if (tile_set->tile_get_tile_mode(id) == TileSet::ATLAS_TILE) { + + if (tile_set->autotile_get_bitmask(id, Vector2(p_x, p_y)) == TileSet::BIND_CENTER) { + Vector2 coord = tile_set->atlastile_get_subtile_by_priority(id, this, Vector2(p_x, p_y)); + + E->get().autotile_coord_x = (int)coord.x; + E->get().autotile_coord_y = (int)coord.y; + } } } } diff --git a/scene/3d/arvr_nodes.cpp b/scene/3d/arvr_nodes.cpp index 52fa96ee4a..95eec41fb2 100644 --- a/scene/3d/arvr_nodes.cpp +++ b/scene/3d/arvr_nodes.cpp @@ -127,7 +127,7 @@ Point2 ARVRCamera::unproject_position(const Vector3 &p_pos) const { return res; }; -Vector3 ARVRCamera::project_position(const Point2 &p_point) const { +Vector3 ARVRCamera::project_position(const Point2 &p_point, float p_z_depth) const { // get our ARVRServer ARVRServer *arvr_server = ARVRServer::get_singleton(); ERR_FAIL_NULL_V(arvr_server, Vector3()); @@ -135,7 +135,7 @@ Vector3 ARVRCamera::project_position(const Point2 &p_point) const { Ref<ARVRInterface> arvr_interface = arvr_server->get_primary_interface(); if (arvr_interface.is_null()) { // we might be in the editor or have VR turned off, just call superclass - return Camera::project_position(p_point); + return Camera::project_position(p_point, p_z_depth); } if (!is_inside_tree()) { @@ -155,7 +155,7 @@ Vector3 ARVRCamera::project_position(const Point2 &p_point) const { point.y = (1.0 - (p_point.y / viewport_size.y)) * 2.0 - 1.0; point *= vp_size; - Vector3 p(point.x, point.y, -get_znear()); + Vector3 p(point.x, point.y, -p_z_depth); return get_camera_transform().xform(p); }; @@ -390,7 +390,7 @@ String ARVRController::get_configuration_warning() const { }; ARVRController::ARVRController() { - controller_id = 0; + controller_id = 1; is_active = true; button_states = 0; }; @@ -530,7 +530,7 @@ Ref<Mesh> ARVRAnchor::get_mesh() const { } ARVRAnchor::ARVRAnchor() { - anchor_id = 0; + anchor_id = 1; is_active = true; }; diff --git a/scene/3d/arvr_nodes.h b/scene/3d/arvr_nodes.h index 0833e18d48..8e735f7110 100644 --- a/scene/3d/arvr_nodes.h +++ b/scene/3d/arvr_nodes.h @@ -55,7 +55,7 @@ public: virtual Vector3 project_local_ray_normal(const Point2 &p_pos) const; virtual Point2 unproject_position(const Vector3 &p_pos) const; - virtual Vector3 project_position(const Point2 &p_point) const; + virtual Vector3 project_position(const Point2 &p_point, float p_z_depth = 0) const; virtual Vector<Plane> get_frustum() const; ARVRCamera(); diff --git a/scene/3d/camera.cpp b/scene/3d/camera.cpp index 54d7681a3a..a00f2173c0 100644 --- a/scene/3d/camera.cpp +++ b/scene/3d/camera.cpp @@ -106,9 +106,15 @@ void Camera::_notification(int p_what) { case NOTIFICATION_ENTER_WORLD: { - bool first_camera = get_viewport()->_camera_add(this); - if (!get_tree()->is_node_being_edited(this) && (current || first_camera)) - make_current(); + // Needs to track the Viewport because it's needed on NOTIFICATION_EXIT_WORLD + // and Spatial will handle it first, including clearing its reference to the Viewport, + // therefore making it impossible to subclasses to access it + viewport = get_viewport(); + ERR_FAIL_COND(!viewport); + + bool first_camera = viewport->_camera_add(this); + if (current || first_camera) + viewport->_camera_set(this); } break; case NOTIFICATION_TRANSFORM_CHANGED: { @@ -130,17 +136,20 @@ void Camera::_notification(int p_what) { } } - get_viewport()->_camera_remove(this); + if (viewport) { + viewport->_camera_remove(this); + viewport = NULL; + } } break; case NOTIFICATION_BECAME_CURRENT: { - if (get_world().is_valid()) { - get_world()->_register_camera(this); + if (viewport) { + viewport->find_world()->_register_camera(this); } } break; case NOTIFICATION_LOST_CURRENT: { - if (get_world().is_valid()) { - get_world()->_remove_camera(this); + if (viewport) { + viewport->find_world()->_remove_camera(this); } } break; } @@ -255,8 +264,6 @@ bool Camera::is_current() const { return get_viewport()->get_camera() == this; } else return current; - - return false; } bool Camera::_can_gizmo_scale() const { @@ -391,13 +398,17 @@ Point2 Camera::unproject_position(const Vector3 &p_pos) const { return res; } -Vector3 Camera::project_position(const Point2 &p_point) const { +Vector3 Camera::project_position(const Point2 &p_point, float p_z_depth) const { if (!is_inside_tree()) { ERR_EXPLAIN("Camera is not inside scene."); ERR_FAIL_COND_V(!is_inside_tree(), Vector3()); } + if (p_z_depth == 0) { + return get_global_transform().origin; + } + Size2 viewport_size = get_viewport()->get_visible_rect().size; CameraMatrix cm; @@ -415,7 +426,7 @@ Vector3 Camera::project_position(const Point2 &p_point) const { point.y = (1.0 - (p_point.y / viewport_size.y)) * 2.0 - 1.0; point *= vp_size; - Vector3 p(point.x, point.y, -near); + Vector3 p(point.x, point.y, -p_z_depth); return get_camera_transform().xform(p); } @@ -490,7 +501,7 @@ void Camera::_bind_methods() { ClassDB::bind_method(D_METHOD("project_ray_origin", "screen_point"), &Camera::project_ray_origin); ClassDB::bind_method(D_METHOD("unproject_position", "world_point"), &Camera::unproject_position); ClassDB::bind_method(D_METHOD("is_position_behind", "world_point"), &Camera::is_position_behind); - ClassDB::bind_method(D_METHOD("project_position", "screen_point"), &Camera::project_position); + ClassDB::bind_method(D_METHOD("project_position", "screen_point", "z_depth"), &Camera::project_position, DEFVAL(0)); ClassDB::bind_method(D_METHOD("set_perspective", "fov", "z_near", "z_far"), &Camera::set_perspective); ClassDB::bind_method(D_METHOD("set_orthogonal", "size", "z_near", "z_far"), &Camera::set_orthogonal); ClassDB::bind_method(D_METHOD("set_frustum", "size", "offset", "z_near", "z_far"), &Camera::set_frustum); @@ -524,6 +535,7 @@ void Camera::_bind_methods() { ClassDB::bind_method(D_METHOD("set_doppler_tracking", "mode"), &Camera::set_doppler_tracking); ClassDB::bind_method(D_METHOD("get_doppler_tracking"), &Camera::get_doppler_tracking); ClassDB::bind_method(D_METHOD("get_frustum"), &Camera::get_frustum); + ClassDB::bind_method(D_METHOD("get_camera_rid"), &Camera::get_camera); ClassDB::bind_method(D_METHOD("set_cull_mask_bit", "layer", "enable"), &Camera::set_cull_mask_bit); ClassDB::bind_method(D_METHOD("get_cull_mask_bit", "layer"), &Camera::get_cull_mask_bit); @@ -689,6 +701,7 @@ Camera::Camera() { near = 0; far = 0; current = false; + viewport = NULL; force_change = false; mode = PROJECTION_PERSPECTIVE; set_perspective(70.0, 0.05, 100.0); @@ -722,8 +735,9 @@ void ClippedCamera::set_process_mode(ProcessMode p_mode) { if (process_mode == p_mode) { return; } - set_process_internal(p_mode == CLIP_PROCESS_IDLE); - set_physics_process_internal(p_mode == CLIP_PROCESS_PHYSICS); + process_mode = p_mode; + set_process_internal(process_mode == CLIP_PROCESS_IDLE); + set_physics_process_internal(process_mode == CLIP_PROCESS_PHYSICS); } ClippedCamera::ProcessMode ClippedCamera::get_process_mode() const { return process_mode; @@ -786,7 +800,7 @@ void ClippedCamera::_notification(int p_what) { float csafe, cunsafe; if (dspace->cast_motion(pyramid_shape, xf, cam_pos - ray_from, margin, csafe, cunsafe, exclude, collision_mask, clip_to_bodies, clip_to_areas)) { - clip_offset = cam_pos.distance_to(ray_from + (cam_pos - ray_from).normalized() * csafe); + clip_offset = cam_pos.distance_to(ray_from + (cam_pos - ray_from) * csafe); } _update_camera(); diff --git a/scene/3d/camera.h b/scene/3d/camera.h index fe8cb84f0d..1cd729199d 100644 --- a/scene/3d/camera.h +++ b/scene/3d/camera.h @@ -64,6 +64,7 @@ public: private: bool force_change; bool current; + Viewport *viewport; Projection mode; @@ -143,7 +144,7 @@ public: virtual Vector3 project_local_ray_normal(const Point2 &p_pos) const; virtual Point2 unproject_position(const Vector3 &p_pos) const; bool is_position_behind(const Vector3 &p_pos) const; - virtual Vector3 project_position(const Point2 &p_point) const; + virtual Vector3 project_position(const Point2 &p_point, float p_z_depth = 0) const; Vector<Vector3> get_near_plane_points() const; diff --git a/scene/3d/collision_shape.cpp b/scene/3d/collision_shape.cpp index f32b1398fd..219ea56681 100644 --- a/scene/3d/collision_shape.cpp +++ b/scene/3d/collision_shape.cpp @@ -65,7 +65,6 @@ 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; @@ -228,6 +227,11 @@ void CollisionShape::_update_debug_shape() { } void CollisionShape::_shape_changed() { + // If this is a heightfield shape our center may have changed + if (parent) { + _update_in_shape_owner(true); + } + if (is_inside_tree() && get_tree()->is_debugging_collisions_hint() && !debug_shape_dirty) { debug_shape_dirty = true; call_deferred("_update_debug_shape"); diff --git a/scene/3d/cpu_particles.cpp b/scene/3d/cpu_particles.cpp index d4e242dcb7..a81071492b 100644 --- a/scene/3d/cpu_particles.cpp +++ b/scene/3d/cpu_particles.cpp @@ -212,7 +212,7 @@ String CPUParticles::get_configuration_warning() const { get_param_curve(PARAM_ANIM_SPEED).is_valid() || get_param_curve(PARAM_ANIM_OFFSET).is_valid())) { if (warnings != String()) warnings += "\n"; - warnings += "- " + TTR("CPUParticles animation requires the usage of a SpatialMaterial with \"Billboard Particles\" enabled."); + warnings += "- " + TTR("CPUParticles animation requires the usage of a SpatialMaterial whose Billboard Mode is set to \"Particle Billboard\"."); } return warnings; @@ -515,6 +515,8 @@ void CPUParticles::_particles_process(float p_delta) { velocity_xform = emission_xform.basis; } + float system_phase = time / lifetime; + for (int i = 0; i < pcount; i++) { Particle &p = parray[i]; @@ -522,21 +524,26 @@ void CPUParticles::_particles_process(float p_delta) { if (!emitting && !p.active) continue; - float restart_time = (float(i) / float(pcount)) * lifetime; float local_delta = p_delta; + // The phase is a ratio between 0 (birth) and 1 (end of life) for each particle. + // While we use time in tests later on, for randomness we use the phase as done in the + // original shader code, and we later multiply by lifetime to get the time. + float restart_phase = float(i) / float(pcount); + if (randomness_ratio > 0.0) { uint32_t seed = cycle; - if (restart_time >= time) { + if (restart_phase >= system_phase) { seed -= uint32_t(1); } seed *= uint32_t(pcount); seed += uint32_t(i); float random = float(idhash(seed) % uint32_t(65536)) / 65536.0; - restart_time += randomness_ratio * random * 1.0 / float(pcount); + restart_phase += randomness_ratio * random * 1.0 / float(pcount); } - restart_time *= (1.0 - explosiveness_ratio); + restart_phase *= (1.0 - explosiveness_ratio); + float restart_time = restart_phase * lifetime; bool restart = false; if (time > prev_time) { @@ -754,8 +761,9 @@ void CPUParticles::_particles_process(float p_delta) { //apply tangential acceleration; if (flags[FLAG_DISABLE_Z]) { - Vector3 yx = Vector3(diff.y, 0, diff.x); - force += yx.length() > 0.0 ? (yx * Vector3(-1.0, 0, 1.0)) * ((parameters[PARAM_TANGENTIAL_ACCEL] + tex_tangential_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_TANGENTIAL_ACCEL])) : Vector3(); + Vector2 yx = Vector2(diff.y, diff.x); + Vector2 yx2 = (yx * Vector2(-1.0, 1.0)).normalized(); + force += yx.length() > 0.0 ? Vector3(yx2.x, yx2.y, 0.0) * ((parameters[PARAM_TANGENTIAL_ACCEL] + tex_tangential_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_TANGENTIAL_ACCEL])) : Vector3(); } else { Vector3 crossDiff = diff.normalized().cross(gravity.normalized()); diff --git a/scene/3d/navigation_mesh.cpp b/scene/3d/navigation_mesh.cpp index 93731c4023..f82543b789 100644 --- a/scene/3d/navigation_mesh.cpp +++ b/scene/3d/navigation_mesh.cpp @@ -73,6 +73,41 @@ int NavigationMesh::get_sample_partition_type() const { return static_cast<int>(partition_type); } +void NavigationMesh::set_parsed_geometry_type(int p_value) { + ERR_FAIL_COND(p_value >= PARSED_GEOMETRY_MAX); + parsed_geometry_type = static_cast<ParsedGeometryType>(p_value); + _change_notify(); +} + +int NavigationMesh::get_parsed_geometry_type() const { + return parsed_geometry_type; +} + +void NavigationMesh::set_collision_mask(uint32_t p_mask) { + + collision_mask = p_mask; +} + +uint32_t NavigationMesh::get_collision_mask() const { + + return collision_mask; +} + +void NavigationMesh::set_collision_mask_bit(int p_bit, bool p_value) { + + uint32_t mask = get_collision_mask(); + if (p_value) + mask |= 1 << p_bit; + else + mask &= ~(1 << p_bit); + set_collision_mask(mask); +} + +bool NavigationMesh::get_collision_mask_bit(int p_bit) const { + + return get_collision_mask() & (1 << p_bit); +} + void NavigationMesh::set_cell_size(float p_value) { cell_size = p_value; } @@ -204,6 +239,7 @@ bool NavigationMesh::get_filter_walkable_low_height_spans() const { void NavigationMesh::set_vertices(const PoolVector<Vector3> &p_vertices) { vertices = p_vertices; + _change_notify(); } PoolVector<Vector3> NavigationMesh::get_vertices() const { @@ -217,6 +253,7 @@ void NavigationMesh::_set_polygons(const Array &p_array) { for (int i = 0; i < p_array.size(); i++) { polygons.write[i].indices = p_array[i]; } + _change_notify(); } Array NavigationMesh::_get_polygons() const { @@ -235,6 +272,7 @@ void NavigationMesh::add_polygon(const Vector<int> &p_polygon) { Polygon polygon; polygon.indices = p_polygon; polygons.push_back(polygon); + _change_notify(); } int NavigationMesh::get_polygon_count() const { @@ -340,6 +378,15 @@ void NavigationMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_sample_partition_type", "sample_partition_type"), &NavigationMesh::set_sample_partition_type); ClassDB::bind_method(D_METHOD("get_sample_partition_type"), &NavigationMesh::get_sample_partition_type); + ClassDB::bind_method(D_METHOD("set_parsed_geometry_type", "geometry_type"), &NavigationMesh::set_parsed_geometry_type); + ClassDB::bind_method(D_METHOD("get_parsed_geometry_type"), &NavigationMesh::get_parsed_geometry_type); + + ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &NavigationMesh::set_collision_mask); + ClassDB::bind_method(D_METHOD("get_collision_mask"), &NavigationMesh::get_collision_mask); + + ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &NavigationMesh::set_collision_mask_bit); + ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &NavigationMesh::get_collision_mask_bit); + ClassDB::bind_method(D_METHOD("set_cell_size", "cell_size"), &NavigationMesh::set_cell_size); ClassDB::bind_method(D_METHOD("get_cell_size"), &NavigationMesh::get_cell_size); @@ -405,30 +452,45 @@ void NavigationMesh::_bind_methods() { BIND_CONSTANT(SAMPLE_PARTITION_MONOTONE); BIND_CONSTANT(SAMPLE_PARTITION_LAYERS); + BIND_CONSTANT(PARSED_GEOMETRY_MESH_INSTANCES); + BIND_CONSTANT(PARSED_GEOMETRY_STATIC_COLLIDERS); + BIND_CONSTANT(PARSED_GEOMETRY_BOTH); + 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"); - - ADD_PROPERTY(PropertyInfo(Variant::REAL, "cell/size", PROPERTY_HINT_RANGE, "0.1,1.0,0.01"), "set_cell_size", "get_cell_size"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "cell/height", PROPERTY_HINT_RANGE, "0.1,1.0,0.01"), "set_cell_height", "get_cell_height"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "agent/height", PROPERTY_HINT_RANGE, "0.1,5.0,0.01"), "set_agent_height", "get_agent_height"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "agent/radius", PROPERTY_HINT_RANGE, "0.1,5.0,0.01"), "set_agent_radius", "get_agent_radius"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "agent/max_climb", PROPERTY_HINT_RANGE, "0.1,5.0,0.01"), "set_agent_max_climb", "get_agent_max_climb"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "geometry/parsed_geometry_type", PROPERTY_HINT_ENUM, "Mesh Instances,Static Colliders,Both"), "set_parsed_geometry_type", "get_parsed_geometry_type"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "geometry/collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask"); + + ADD_PROPERTY(PropertyInfo(Variant::REAL, "cell/size", PROPERTY_HINT_RANGE, "0.1,1.0,0.01,or_greater"), "set_cell_size", "get_cell_size"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "cell/height", PROPERTY_HINT_RANGE, "0.1,1.0,0.01,or_greater"), "set_cell_height", "get_cell_height"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "agent/height", PROPERTY_HINT_RANGE, "0.1,5.0,0.01,or_greater"), "set_agent_height", "get_agent_height"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "agent/radius", PROPERTY_HINT_RANGE, "0.1,5.0,0.01,or_greater"), "set_agent_radius", "get_agent_radius"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "agent/max_climb", PROPERTY_HINT_RANGE, "0.1,5.0,0.01,or_greater"), "set_agent_max_climb", "get_agent_max_climb"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "agent/max_slope", PROPERTY_HINT_RANGE, "0.0,90.0,0.1"), "set_agent_max_slope", "get_agent_max_slope"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "region/min_size", PROPERTY_HINT_RANGE, "0.0,150.0,0.01"), "set_region_min_size", "get_region_min_size"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "region/merge_size", PROPERTY_HINT_RANGE, "0.0,150.0,0.01"), "set_region_merge_size", "get_region_merge_size"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "edge/max_length", PROPERTY_HINT_RANGE, "0.0,50.0,0.01"), "set_edge_max_length", "get_edge_max_length"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "edge/max_error", PROPERTY_HINT_RANGE, "0.1,3.0,0.01"), "set_edge_max_error", "get_edge_max_error"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "polygon/verts_per_poly", PROPERTY_HINT_RANGE, "3.0,12.0,1.0"), "set_verts_per_poly", "get_verts_per_poly"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "detail/sample_distance", PROPERTY_HINT_RANGE, "0.0,16.0,0.01"), "set_detail_sample_distance", "get_detail_sample_distance"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "detail/sample_max_error", PROPERTY_HINT_RANGE, "0.0,16.0,0.01"), "set_detail_sample_max_error", "get_detail_sample_max_error"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "region/min_size", PROPERTY_HINT_RANGE, "0.0,150.0,0.01,or_greater"), "set_region_min_size", "get_region_min_size"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "region/merge_size", PROPERTY_HINT_RANGE, "0.0,150.0,0.01,or_greater"), "set_region_merge_size", "get_region_merge_size"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "edge/max_length", PROPERTY_HINT_RANGE, "0.0,50.0,0.01,or_greater"), "set_edge_max_length", "get_edge_max_length"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "edge/max_error", PROPERTY_HINT_RANGE, "0.1,3.0,0.01,or_greater"), "set_edge_max_error", "get_edge_max_error"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "polygon/verts_per_poly", PROPERTY_HINT_RANGE, "3.0,12.0,1.0,or_greater"), "set_verts_per_poly", "get_verts_per_poly"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "detail/sample_distance", PROPERTY_HINT_RANGE, "0.0,16.0,0.01,or_greater"), "set_detail_sample_distance", "get_detail_sample_distance"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "detail/sample_max_error", PROPERTY_HINT_RANGE, "0.0,16.0,0.01,or_greater"), "set_detail_sample_max_error", "get_detail_sample_max_error"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter/low_hanging_obstacles"), "set_filter_low_hanging_obstacles", "get_filter_low_hanging_obstacles"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter/ledge_spans"), "set_filter_ledge_spans", "get_filter_ledge_spans"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter/filter_walkable_low_height_spans"), "set_filter_walkable_low_height_spans", "get_filter_walkable_low_height_spans"); } +void NavigationMesh::_validate_property(PropertyInfo &property) const { + if (property.name == "geometry/collision_mask") { + if (parsed_geometry_type == PARSED_GEOMETRY_MESH_INSTANCES) { + property.usage = 0; + return; + } + } +} + NavigationMesh::NavigationMesh() { cell_size = 0.3f; cell_height = 0.2f; @@ -445,7 +507,8 @@ NavigationMesh::NavigationMesh() { detail_sample_max_error = 1.0f; partition_type = SAMPLE_PARTITION_WATERSHED; - + parsed_geometry_type = PARSED_GEOMETRY_MESH_INSTANCES; + collision_mask = 0xFFFFFFFF; filter_low_hanging_obstacles = false; filter_ledge_spans = false; filter_walkable_low_height_spans = false; @@ -566,8 +629,17 @@ void NavigationMeshInstance::set_navigation_mesh(const Ref<NavigationMesh> &p_na navigation->navmesh_remove(nav_id); nav_id = -1; } + + if (navmesh.is_valid()) { + navmesh->remove_change_receptor(this); + } + navmesh = p_navmesh; + if (navmesh.is_valid()) { + navmesh->add_change_receptor(this); + } + if (navigation && navmesh.is_valid() && enabled) { nav_id = navigation->navmesh_add(navmesh, get_relative_transform(navigation), this); } @@ -617,6 +689,11 @@ void NavigationMeshInstance::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled"); } +void NavigationMeshInstance::_changed_callback(Object *p_changed, const char *p_prop) { + update_gizmo(); + update_configuration_warning(); +} + NavigationMeshInstance::NavigationMeshInstance() { debug_view = NULL; @@ -625,3 +702,8 @@ NavigationMeshInstance::NavigationMeshInstance() { enabled = true; set_notify_transform(true); } + +NavigationMeshInstance::~NavigationMeshInstance() { + if (navmesh.is_valid()) + navmesh->remove_change_receptor(this); +} diff --git a/scene/3d/navigation_mesh.h b/scene/3d/navigation_mesh.h index 74531e2423..5fbf3998ff 100644 --- a/scene/3d/navigation_mesh.h +++ b/scene/3d/navigation_mesh.h @@ -57,6 +57,7 @@ class NavigationMesh : public Resource { protected: static void _bind_methods(); + virtual void _validate_property(PropertyInfo &property) const; void _set_polygons(const Array &p_array); Array _get_polygons() const; @@ -69,6 +70,13 @@ public: SAMPLE_PARTITION_MAX }; + enum ParsedGeometryType { + PARSED_GEOMETRY_MESH_INSTANCES = 0, + PARSED_GEOMETRY_STATIC_COLLIDERS, + PARSED_GEOMETRY_BOTH, + PARSED_GEOMETRY_MAX + }; + protected: float cell_size; float cell_height; @@ -85,6 +93,8 @@ protected: float detail_sample_max_error; SamplePartitionType partition_type; + ParsedGeometryType parsed_geometry_type; + uint32_t collision_mask; bool filter_low_hanging_obstacles; bool filter_ledge_spans; @@ -95,6 +105,15 @@ public: void set_sample_partition_type(int p_value); int get_sample_partition_type() const; + void set_parsed_geometry_type(int p_value); + int get_parsed_geometry_type() const; + + void set_collision_mask(uint32_t p_mask); + uint32_t get_collision_mask() const; + + void set_collision_mask_bit(int p_bit, bool p_value); + bool get_collision_mask_bit(int p_bit) const; + void set_cell_size(float p_value); float get_cell_size() const; @@ -174,6 +193,7 @@ class NavigationMeshInstance : public Spatial { protected: void _notification(int p_what); static void _bind_methods(); + void _changed_callback(Object *p_changed, const char *p_prop); public: void set_enabled(bool p_enabled); @@ -185,6 +205,7 @@ public: String get_configuration_warning() const; NavigationMeshInstance(); + ~NavigationMeshInstance(); }; #endif // NAVIGATION_MESH_H diff --git a/scene/3d/particles.cpp b/scene/3d/particles.cpp index 57ab01f7be..156560f802 100644 --- a/scene/3d/particles.cpp +++ b/scene/3d/particles.cpp @@ -268,7 +268,7 @@ String Particles::get_configuration_warning() const { process->get_param_texture(ParticlesMaterial::PARAM_ANIM_SPEED).is_valid() || process->get_param_texture(ParticlesMaterial::PARAM_ANIM_OFFSET).is_valid())) { if (warnings != String()) warnings += "\n"; - warnings += "- " + TTR("Particles animation requires the usage of a SpatialMaterial with \"Billboard Particles\" enabled."); + warnings += "- " + TTR("Particles animation requires the usage of a SpatialMaterial whose Billboard Mode is set to \"Particle Billboard\"."); } } diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp index 57af951110..3624e04434 100644 --- a/scene/3d/physics_body.cpp +++ b/scene/3d/physics_body.cpp @@ -192,8 +192,8 @@ void StaticBody::set_friction(real_t p_friction) { return; } - ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physics material instead.") - WARN_DEPRECATED + ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physics material instead."); + WARN_DEPRECATED; ERR_FAIL_COND(p_friction < 0 || p_friction > 1); @@ -206,8 +206,8 @@ void StaticBody::set_friction(real_t p_friction) { real_t StaticBody::get_friction() const { - ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physics material instead.") - WARN_DEPRECATED + ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physics material instead."); + WARN_DEPRECATED; if (physics_material_override.is_null()) { return 1; @@ -222,8 +222,8 @@ void StaticBody::set_bounce(real_t p_bounce) { return; } - ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physics material instead.") - WARN_DEPRECATED + ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physics material instead."); + WARN_DEPRECATED; ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1); @@ -236,8 +236,8 @@ void StaticBody::set_bounce(real_t p_bounce) { real_t StaticBody::get_bounce() const { - ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physics material instead.") - WARN_DEPRECATED + ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physics material instead."); + WARN_DEPRECATED; if (physics_material_override.is_null()) { return 0; @@ -636,8 +636,8 @@ void RigidBody::set_friction(real_t p_friction) { return; } - ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physics material instead.") - WARN_DEPRECATED + ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physics material instead."); + WARN_DEPRECATED; ERR_FAIL_COND(p_friction < 0 || p_friction > 1); @@ -649,8 +649,8 @@ void RigidBody::set_friction(real_t p_friction) { } real_t RigidBody::get_friction() const { - ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physics material instead.") - WARN_DEPRECATED + ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physics material instead."); + WARN_DEPRECATED; if (physics_material_override.is_null()) { return 1; @@ -665,8 +665,8 @@ void RigidBody::set_bounce(real_t p_bounce) { return; } - ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physics material instead.") - WARN_DEPRECATED + ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physics material instead."); + WARN_DEPRECATED; ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1); @@ -677,8 +677,8 @@ void RigidBody::set_bounce(real_t p_bounce) { physics_material_override->set_bounce(p_bounce); } real_t RigidBody::get_bounce() const { - ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physics material instead.") - WARN_DEPRECATED + ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physics material instead."); + WARN_DEPRECATED; if (physics_material_override.is_null()) { return 0; } diff --git a/scene/3d/proximity_group.cpp b/scene/3d/proximity_group.cpp index 12eab2e4e8..96dc3304f2 100644 --- a/scene/3d/proximity_group.cpp +++ b/scene/3d/proximity_group.cpp @@ -204,6 +204,7 @@ ProximityGroup::ProximityGroup() { group_version = 0; dispatch_mode = MODE_PROXY; + cell_size = 1.0; grid_radius = Vector3(1, 1, 1); set_notify_transform(true); }; diff --git a/scene/3d/skeleton.cpp b/scene/3d/skeleton.cpp index 15c089ec10..e192e040f2 100644 --- a/scene/3d/skeleton.cpp +++ b/scene/3d/skeleton.cpp @@ -162,7 +162,7 @@ void Skeleton::_update_process_order() { //now check process order int pass_count = 0; while (pass_count < len * len) { - //using bubblesort because of simplicity, it wont run every frame though. + //using bubblesort because of simplicity, it won't run every frame though. //bublesort worst case is O(n^2), and this may be an infinite loop if cyclic bool swapped = false; for (int i = 0; i < len; i++) { diff --git a/scene/3d/spatial.cpp b/scene/3d/spatial.cpp index 05fd984f93..efd418e3c7 100644 --- a/scene/3d/spatial.cpp +++ b/scene/3d/spatial.cpp @@ -676,28 +676,29 @@ void Spatial::set_identity() { void Spatial::look_at(const Vector3 &p_target, const Vector3 &p_up) { - Transform lookat(get_global_transform()); - if (lookat.origin == p_target) { + Vector3 origin(get_global_transform().origin); + look_at_from_position(origin, p_target, p_up); +} + +void Spatial::look_at_from_position(const Vector3 &p_pos, const Vector3 &p_target, const Vector3 &p_up) { + + if (p_pos == p_target) { ERR_EXPLAIN("Node origin and target are in the same position, look_at() failed"); ERR_FAIL(); } - if (p_up.cross(p_target - lookat.origin) == Vector3()) { + if (p_up.cross(p_target - p_pos) == Vector3()) { ERR_EXPLAIN("Up vector and direction between node origin and target are aligned, look_at() failed"); ERR_FAIL(); } - Vector3 original_scale(lookat.basis.get_scale()); - lookat = lookat.looking_at(p_target, p_up); - // as basis was normalized, we just need to apply original scale back - lookat.basis.scale(original_scale); - set_global_transform(lookat); -} - -void Spatial::look_at_from_position(const Vector3 &p_pos, const Vector3 &p_target, const Vector3 &p_up) { Transform lookat; lookat.origin = p_pos; + + Vector3 original_scale(get_global_transform().basis.get_scale()); lookat = lookat.looking_at(p_target, p_up); + // as basis was normalized, we just need to apply original scale back + lookat.basis.scale(original_scale); set_global_transform(lookat); } diff --git a/scene/3d/vehicle_body.cpp b/scene/3d/vehicle_body.cpp index fde135c972..32b8219ee0 100644 --- a/scene/3d/vehicle_body.cpp +++ b/scene/3d/vehicle_body.cpp @@ -270,6 +270,8 @@ void VehicleWheel::_bind_methods() { ClassDB::bind_method(D_METHOD("get_skidinfo"), &VehicleWheel::get_skidinfo); + ClassDB::bind_method(D_METHOD("get_rpm"), &VehicleWheel::get_rpm); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_as_traction"), "set_use_as_traction", "is_used_as_traction"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_as_steering"), "set_use_as_steering", "is_used_as_steering"); ADD_GROUP("Wheel", "wheel_"); @@ -311,6 +313,11 @@ float VehicleWheel::get_skidinfo() const { return m_skidInfo; } +float VehicleWheel::get_rpm() const { + + return m_rpm; +} + VehicleWheel::VehicleWheel() { steers = false; @@ -865,12 +872,11 @@ void VehicleBody::_direct_state_changed(Object *p_state) { real_t proj2 = fwd.dot(vel); wheel.m_deltaRotation = (proj2 * step) / (wheel.m_wheelRadius); - wheel.m_rotation += wheel.m_deltaRotation; - - } else { - wheel.m_rotation += wheel.m_deltaRotation; } + wheel.m_rotation += wheel.m_deltaRotation; + wheel.m_rpm = ((wheel.m_deltaRotation / step) * 60) / Math_TAU; + wheel.m_deltaRotation *= real_t(0.99); //damping of rotation when not in contact } diff --git a/scene/3d/vehicle_body.h b/scene/3d/vehicle_body.h index 7e7571df4d..9e3fe72282 100644 --- a/scene/3d/vehicle_body.h +++ b/scene/3d/vehicle_body.h @@ -68,6 +68,7 @@ class VehicleWheel : public Spatial { real_t m_steering; real_t m_rotation; real_t m_deltaRotation; + real_t m_rpm; real_t m_rollInfluence; //real_t m_engineForce; real_t m_brake; @@ -134,6 +135,8 @@ public: float get_skidinfo() const; + float get_rpm() const; + String get_configuration_warning() const; VehicleWheel(); diff --git a/scene/3d/visual_instance.cpp b/scene/3d/visual_instance.cpp index 1aded826c0..99c86f0406 100644 --- a/scene/3d/visual_instance.cpp +++ b/scene/3d/visual_instance.cpp @@ -271,6 +271,11 @@ float GeometryInstance::get_extra_cull_margin() const { return extra_cull_margin; } +void GeometryInstance::set_custom_aabb(AABB aabb) { + + VS::get_singleton()->instance_set_custom_aabb(get_instance(), aabb); +} + void GeometryInstance::_bind_methods() { ClassDB::bind_method(D_METHOD("set_material_override", "material"), &GeometryInstance::set_material_override); @@ -297,6 +302,8 @@ void GeometryInstance::_bind_methods() { ClassDB::bind_method(D_METHOD("set_extra_cull_margin", "margin"), &GeometryInstance::set_extra_cull_margin); ClassDB::bind_method(D_METHOD("get_extra_cull_margin"), &GeometryInstance::get_extra_cull_margin); + ClassDB::bind_method(D_METHOD("set_custom_aabb", "aabb"), &GeometryInstance::set_custom_aabb); + ClassDB::bind_method(D_METHOD("get_aabb"), &GeometryInstance::get_aabb); ADD_GROUP("Geometry", ""); diff --git a/scene/3d/visual_instance.h b/scene/3d/visual_instance.h index f5b7479bb1..3b924e0454 100644 --- a/scene/3d/visual_instance.h +++ b/scene/3d/visual_instance.h @@ -89,6 +89,7 @@ class GeometryInstance : public VisualInstance { public: enum Flags { FLAG_USE_BAKED_LIGHT = VS::INSTANCE_FLAG_USE_BAKED_LIGHT, + FLAG_DRAW_NEXT_FRAME_IF_VISIBLE = VS::INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE, FLAG_MAX = VS::INSTANCE_FLAG_MAX, }; @@ -139,6 +140,8 @@ public: void set_extra_cull_margin(float p_margin); float get_extra_cull_margin() const; + void set_custom_aabb(AABB aabb); + GeometryInstance(); }; diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp index e9b38ae990..20a09696e1 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -1049,7 +1049,7 @@ AnimationNodeBlendTree::ConnectionError AnimationNodeBlendTree::can_connect_node return CONNECTION_ERROR_NO_INPUT; } - if (!nodes.has(p_input_node)) { + if (p_input_node == p_output_node) { return CONNECTION_ERROR_SAME_NODE; } diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp index 1e3470cd90..60f8806b25 100644 --- a/scene/animation/animation_node_state_machine.cpp +++ b/scene/animation/animation_node_state_machine.cpp @@ -314,8 +314,9 @@ float AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *sm, if (start_request_travel) { if (!playing) { + String node_name = start_request; start_request = StringName(); - ERR_EXPLAIN("Can't travel to '" + String(start_request) + "' if state machine is not active."); + ERR_EXPLAIN("Can't travel to '" + node_name + "' if state machine is not playing."); ERR_FAIL_V(0); } diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index ead3516116..75088c79fe 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -37,12 +37,20 @@ #ifdef TOOLS_ENABLED #include "editor/editor_settings.h" +#include "scene/2d/skeleton_2d.h" void AnimatedValuesBackup::update_skeletons() { for (int i = 0; i < entries.size(); i++) { if (entries[i].bone_idx != -1) { + // 3D bone Object::cast_to<Skeleton>(entries[i].object)->notification(Skeleton::NOTIFICATION_UPDATE_SKELETON); + } else { + Bone2D *bone = Object::cast_to<Bone2D>(entries[i].object); + if (bone && bone->skeleton) { + // 2D bone + bone->skeleton->_update_transform(); + } } } } @@ -1200,7 +1208,7 @@ void AnimationPlayer::play(const StringName &p_name, float p_custom_blend, float // Animation reset BUT played backwards, set position to the end c.current.pos = c.current.from->animation->get_length(); } else if (!p_from_end && c.current.pos == c.current.from->animation->get_length()) { - // Animation resumed but already ended, set position to the beggining + // Animation resumed but already ended, set position to the beginning c.current.pos = 0; } } diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index d1d3582c9d..54f0fdc26a 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -316,7 +316,7 @@ String AnimationNode::get_caption() const { void AnimationNode::add_input(const String &p_name) { //root nodes can't add inputs - ERR_FAIL_COND(Object::cast_to<AnimationRootNode>(this) != NULL) + ERR_FAIL_COND(Object::cast_to<AnimationRootNode>(this) != NULL); Input input; ERR_FAIL_COND(p_name.find(".") != -1 || p_name.find("/") != -1); input.name = p_name; @@ -1580,6 +1580,7 @@ AnimationTree::AnimationTree() { active = false; cache_valid = false; setup_pass = 1; + process_pass = 1; started = true; properties_dirty = true; last_animation_player = 0; diff --git a/scene/animation/animation_tree_player.cpp b/scene/animation/animation_tree_player.cpp index 3cc90c2ad6..5c3e123ac3 100644 --- a/scene/animation/animation_tree_player.cpp +++ b/scene/animation/animation_tree_player.cpp @@ -404,7 +404,7 @@ void AnimationTreePlayer::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: { ERR_EXPLAIN("AnimationTreePlayer has been deprecated. Use AnimationTree instead."); - WARN_DEPRECATED + WARN_DEPRECATED; if (!processing) { //make sure that a previous process state was not saved diff --git a/scene/animation/skeleton_ik.cpp b/scene/animation/skeleton_ik.cpp index 4da3e6ee28..06d6806f03 100644 --- a/scene/animation/skeleton_ik.cpp +++ b/scene/animation/skeleton_ik.cpp @@ -37,20 +37,20 @@ #ifndef _3D_DISABLED FabrikInverseKinematic::ChainItem *FabrikInverseKinematic::ChainItem::find_child(const BoneId p_bone_id) { - for (int i = childs.size() - 1; 0 <= i; --i) { - if (p_bone_id == childs[i].bone) { - return &childs.write[i]; + for (int i = children.size() - 1; 0 <= i; --i) { + if (p_bone_id == children[i].bone) { + return &children.write[i]; } } return NULL; } FabrikInverseKinematic::ChainItem *FabrikInverseKinematic::ChainItem::add_child(const BoneId p_bone_id) { - const int infant_child_id = childs.size(); - childs.resize(infant_child_id + 1); - childs.write[infant_child_id].bone = p_bone_id; - childs.write[infant_child_id].parent_item = this; - return &childs.write[infant_child_id]; + const int infant_child_id = children.size(); + children.resize(infant_child_id + 1); + children.write[infant_child_id].bone = p_bone_id; + children.write[infant_child_id].parent_item = this; + return &children.write[infant_child_id]; } /// Build a chain that starts from the root to tip @@ -144,8 +144,8 @@ void FabrikInverseKinematic::update_chain(const Skeleton *p_sk, ChainItem *p_cha p_chain_item->initial_transform = p_sk->get_bone_global_pose(p_chain_item->bone); p_chain_item->current_pos = p_chain_item->initial_transform.origin; - for (int i = p_chain_item->childs.size() - 1; 0 <= i; --i) { - update_chain(p_sk, &p_chain_item->childs.write[i]); + for (int i = p_chain_item->children.size() - 1; 0 <= i; --i) { + update_chain(p_sk, &p_chain_item->children.write[i]); } } @@ -210,9 +210,9 @@ void FabrikInverseKinematic::solve_simple_forwards(Chain &r_chain, bool p_solve_ while (sub_chain_root) { // Reach the tip sub_chain_root->current_pos = origin; - if (!sub_chain_root->childs.empty()) { + if (!sub_chain_root->children.empty()) { - ChainItem &child(sub_chain_root->childs.write[0]); + ChainItem &child(sub_chain_root->children.write[0]); // Is not tip // So calculate next origin location @@ -302,10 +302,10 @@ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool ove Transform new_bone_pose(ci->initial_transform); new_bone_pose.origin = ci->current_pos; - if (!ci->childs.empty()) { + if (!ci->children.empty()) { /// Rotate basis - const Vector3 initial_ori((ci->childs[0].initial_transform.origin - ci->initial_transform.origin).normalized()); + const Vector3 initial_ori((ci->children[0].initial_transform.origin - ci->initial_transform.origin).normalized()); const Vector3 rot_axis(initial_ori.cross(ci->current_ori).normalized()); if (rot_axis[0] != 0 && rot_axis[1] != 0 && rot_axis[2] != 0) { @@ -322,8 +322,8 @@ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool ove p_task->skeleton->set_bone_global_pose(ci->bone, new_bone_pose); - if (!ci->childs.empty()) - ci = &ci->childs.write[0]; + if (!ci->children.empty()) + ci = &ci->children.write[0]; else ci = NULL; } diff --git a/scene/animation/skeleton_ik.h b/scene/animation/skeleton_ik.h index 228184a2df..5d48b7be33 100644 --- a/scene/animation/skeleton_ik.h +++ b/scene/animation/skeleton_ik.h @@ -49,7 +49,7 @@ class FabrikInverseKinematic { struct ChainItem { - Vector<ChainItem> childs; + Vector<ChainItem> children; ChainItem *parent_item; // Bone info diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp index 144e58d8b9..2def9fe8fc 100644 --- a/scene/audio/audio_stream_player.cpp +++ b/scene/audio/audio_stream_player.cpp @@ -32,8 +32,7 @@ #include "core/engine.h" - -void AudioStreamPlayer::_mix_to_bus(const AudioFrame *p_frames,int p_amount) { +void AudioStreamPlayer::_mix_to_bus(const AudioFrame *p_frames, int p_amount) { int bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus); @@ -66,10 +65,8 @@ void AudioStreamPlayer::_mix_to_bus(const AudioFrame *p_frames,int p_amount) { } } - void AudioStreamPlayer::_mix_internal(bool p_fadeout) { - //get data AudioFrame *buffer = mix_buffer.ptrw(); int buffer_size = mix_buffer.size(); @@ -94,15 +91,14 @@ void AudioStreamPlayer::_mix_internal(bool p_fadeout) { //set volume for next mix mix_volume_db = target_volume; - _mix_to_bus(buffer,buffer_size); - + _mix_to_bus(buffer, buffer_size); } void AudioStreamPlayer::_mix_audio() { if (use_fadeout) { - _mix_to_bus(fadeout_buffer.ptr(),fadeout_buffer.size()); - use_fadeout=false; + _mix_to_bus(fadeout_buffer.ptr(), fadeout_buffer.size()); + use_fadeout = false; } if (!stream_playback.is_valid() || !active || @@ -121,7 +117,7 @@ void AudioStreamPlayer::_mix_audio() { if (setstop) { _mix_internal(true); stream_playback->stop(); - setstop=false; + setstop = false; } if (setseek >= 0.0 && !stop_has_priority) { @@ -154,7 +150,7 @@ void AudioStreamPlayer::_notification(int p_what) { if (p_what == NOTIFICATION_INTERNAL_PROCESS) { if (!active || (setseek < 0 && !stream_playback->is_playing())) { - active = false; + active = false; set_process_internal(false); emit_signal("finished"); } @@ -200,7 +196,7 @@ void AudioStreamPlayer::set_stream(Ref<AudioStream> p_stream) { vol += vol_inc; } - use_fadeout=true; + use_fadeout = true; } mix_buffer.resize(AudioServer::get_singleton()->thread_get_mix_buffer_size()); @@ -252,7 +248,7 @@ void AudioStreamPlayer::play(float p_from_pos) { if (stream_playback.is_valid()) { //mix_volume_db = volume_db; do not reset volume ramp here, can cause clicks setseek = p_from_pos; - stop_has_priority=false; + stop_has_priority = false; active = true; set_process_internal(true); } @@ -268,8 +264,8 @@ void AudioStreamPlayer::seek(float p_seconds) { void AudioStreamPlayer::stop() { if (stream_playback.is_valid() && active) { - setstop=true; - stop_has_priority=true; + setstop = true; + stop_has_priority = true; } } @@ -357,7 +353,7 @@ void AudioStreamPlayer::_validate_property(PropertyInfo &property) const { if (property.name == "bus") { String options; - for (int i = 0; i <AudioServer::get_singleton()->get_bus_count(); i++) { + for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) { if (i > 0) options += ","; String name = AudioServer::get_singleton()->get_bus_name(i); @@ -442,8 +438,8 @@ AudioStreamPlayer::AudioStreamPlayer() { stream_paused_fade = false; mix_target = MIX_TARGET_STEREO; fadeout_buffer.resize(512); - setstop=false; - use_fadeout=false; + setstop = false; + use_fadeout = false; AudioServer::get_singleton()->connect("bus_layout_changed", this, "_bus_layout_changed"); } diff --git a/scene/audio/audio_stream_player.h b/scene/audio/audio_stream_player.h index 0b782b67e7..7987bd7e7d 100644 --- a/scene/audio/audio_stream_player.h +++ b/scene/audio/audio_stream_player.h @@ -112,7 +112,6 @@ public: Ref<AudioStreamPlayback> get_stream_playback(); - AudioStreamPlayer(); ~AudioStreamPlayer(); }; diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index e95781c181..5ef2557383 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -56,185 +56,53 @@ void BaseButton::_gui_input(Ref<InputEvent> p_event) { if (status.disabled) // no interaction with disabled button return; - Ref<InputEventMouseButton> b = p_event; + Ref<InputEventMouseButton> mouse_button = p_event; + bool ui_accept = p_event->is_action("ui_accept") && !p_event->is_echo(); - if (b.is_valid()) { - if (((1 << (b->get_button_index() - 1)) & button_mask) == 0) - return; - - if (status.pressing_button) - return; - - if (action_mode == ACTION_MODE_BUTTON_PRESS) { - - if (b->is_pressed()) { - - emit_signal("button_down"); - - if (!toggle_mode) { //mouse press attempt - - status.press_attempt = true; - status.pressing_inside = true; + bool button_masked = mouse_button.is_valid() && ((1 << (mouse_button->get_button_index() - 1)) & button_mask) > 0; + if (button_masked || ui_accept) { + if (p_event->is_pressed()) { + status.press_attempt = true; + status.pressing_inside = true; + emit_signal("button_down"); + } - pressed(); - if (get_script_instance()) { - Variant::CallError ce; - get_script_instance()->call(SceneStringNames::get_singleton()->_pressed, NULL, 0, ce); + if (status.press_attempt && status.pressing_inside) { + if (toggle_mode) { + if ((p_event->is_pressed() && action_mode == ACTION_MODE_BUTTON_PRESS) || (!p_event->is_pressed() && action_mode == ACTION_MODE_BUTTON_RELEASE)) { + if (action_mode == ACTION_MODE_BUTTON_PRESS) { + status.press_attempt = false; + status.pressing_inside = false; } - - _unpress_group(); - emit_signal("pressed"); - - } else { - status.pressed = !status.pressed; - pressed(); - _unpress_group(); - emit_signal("pressed"); - - toggled(status.pressed); - if (get_script_instance()) { - get_script_instance()->call(SceneStringNames::get_singleton()->_toggled, status.pressed); - } - emit_signal("toggled", status.pressed); + _toggled(status.pressed); + _pressed(); } - } else { - - emit_signal("button_up"); - - /* this is pointless if (status.press_attempt && status.pressing_inside) { - //released(); - emit_signal("released"); + if (!p_event->is_pressed()) { + _pressed(); } -*/ - status.press_attempt = false; } - update(); - return; } - if (b->is_pressed()) { - - status.press_attempt = true; - status.pressing_inside = true; - emit_signal("button_down"); - - } else { - + if (!p_event->is_pressed()) { // pressed state should be correct with button_up signal emit_signal("button_up"); - - if (status.press_attempt && status.pressing_inside) { - - 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); - } - - _unpress_group(); - emit_signal("pressed"); - - } else { - - status.pressed = !status.pressed; - - pressed(); - _unpress_group(); - emit_signal("pressed"); - - toggled(status.pressed); - if (get_script_instance()) { - get_script_instance()->call(SceneStringNames::get_singleton()->_toggled, status.pressed); - } - emit_signal("toggled", status.pressed); - } - } - status.press_attempt = false; } update(); + return; } - Ref<InputEventMouseMotion> mm = p_event; - - if (mm.is_valid()) { - if (status.press_attempt && status.pressing_button == 0) { + Ref<InputEventMouseMotion> mouse_motion = p_event; + if (mouse_motion.is_valid()) { + if (status.press_attempt) { bool last_press_inside = status.pressing_inside; - status.pressing_inside = has_point(mm->get_position()); - if (last_press_inside != status.pressing_inside) + status.pressing_inside = has_point(mouse_motion->get_position()); + if (last_press_inside != status.pressing_inside) { update(); - } - } - - if (!mm.is_valid() && !b.is_valid()) { - - if (p_event->is_echo()) { - return; - } - - if (status.disabled) { - return; - } - - if (status.press_attempt && status.pressing_button == 0) { - return; - } - - if (p_event->is_action("ui_accept")) { - - if (p_event->is_pressed()) { - - status.pressing_button++; - status.press_attempt = true; - status.pressing_inside = true; - emit_signal("button_down"); - - } else if (status.press_attempt) { - - if (status.pressing_button) - status.pressing_button--; - - if (status.pressing_button) - return; - - status.press_attempt = false; - status.pressing_inside = false; - - emit_signal("button_up"); - - 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); - } - - _unpress_group(); - emit_signal("pressed"); - } else { - - status.pressed = !status.pressed; - - pressed(); - _unpress_group(); - emit_signal("pressed"); - - toggled(status.pressed); - if (get_script_instance()) { - get_script_instance()->call(SceneStringNames::get_singleton()->_toggled, status.pressed); - } - emit_signal("toggled", status.pressed); - } } - - accept_event(); - update(); } } } @@ -255,7 +123,6 @@ void BaseButton::_notification(int p_what) { if (status.press_attempt) { status.press_attempt = false; - status.pressing_button = 0; update(); } } @@ -268,9 +135,8 @@ void BaseButton::_notification(int p_what) { if (p_what == NOTIFICATION_FOCUS_EXIT) { - if (status.pressing_button && status.press_attempt) { + if (status.press_attempt) { status.press_attempt = false; - status.pressing_button = 0; status.hovering = false; update(); } else if (status.hovering) { @@ -290,21 +156,31 @@ void BaseButton::_notification(int p_what) { status.hovering = false; status.press_attempt = false; status.pressing_inside = false; - status.pressing_button = 0; } } -void BaseButton::pressed() { +void BaseButton::_pressed() { - if (get_script_instance()) - get_script_instance()->call("pressed"); + if (get_script_instance()) { + get_script_instance()->call(SceneStringNames::get_singleton()->_pressed); + } + pressed(); + emit_signal("pressed"); } -void BaseButton::toggled(bool p_pressed) { +void BaseButton::_toggled(bool p_pressed) { if (get_script_instance()) { - get_script_instance()->call("toggled", p_pressed); + get_script_instance()->call(SceneStringNames::get_singleton()->_toggled, p_pressed); } + toggled(p_pressed); + emit_signal("toggled", p_pressed); +} + +void BaseButton::pressed() { +} + +void BaseButton::toggled(bool p_pressed) { } void BaseButton::set_disabled(bool p_disabled) { @@ -318,7 +194,6 @@ void BaseButton::set_disabled(bool p_disabled) { } status.press_attempt = false; status.pressing_inside = false; - status.pressing_button = 0; } update(); _change_notify("disabled"); @@ -341,6 +216,10 @@ void BaseButton::set_pressed(bool p_pressed) { if (p_pressed) { _unpress_group(); } + if (toggle_mode) { + _toggled(status.pressed); + } + update(); } @@ -585,7 +464,6 @@ BaseButton::BaseButton() { status.hovering = false; status.pressing_inside = false; status.disabled = false; - status.pressing_button = 0; set_focus_mode(FOCUS_ALL); enabled_focus_mode = FOCUS_ALL; action_mode = ACTION_MODE_BUTTON_RELEASE; diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h index 22a8f6d8fe..abb3f58d6b 100644 --- a/scene/gui/base_button.h +++ b/scene/gui/base_button.h @@ -65,13 +65,14 @@ private: bool pressing_inside; bool disabled; - int pressing_button; } status; Ref<ButtonGroup> button_group; void _unpress_group(); + void _pressed(); + void _toggled(bool p_pressed); protected: virtual void pressed(); diff --git a/scene/gui/container.cpp b/scene/gui/container.cpp index 7f1ca58d58..1f9bfb9936 100644 --- a/scene/gui/container.cpp +++ b/scene/gui/container.cpp @@ -177,7 +177,7 @@ String Container::get_configuration_warning() const { if (warning != String()) { warning += "\n"; } - warning += TTR("Container by itself serves no purpose unless a script configures it's children placement behavior.\nIf you dont't intend to add a script, then please use a plain 'Control' node instead."); + warning += TTR("Container by itself serves no purpose unless a script configures it's children placement behavior.\nIf you don't intend to add a script, then please use a plain 'Control' node instead."); } return warning; } diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index a38f97a90c..76275c2420 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -43,6 +43,7 @@ #include "scene/scene_string_names.h" #ifdef TOOLS_ENABLED #include "editor/editor_settings.h" +#include "editor/plugins/canvas_item_editor_plugin.h" #endif #include <stdio.h> @@ -66,6 +67,7 @@ Dictionary Control::_edit_get_state() const { s["margins"] = margins; return s; } + void Control::_edit_set_state(const Dictionary &p_state) { Dictionary state = p_state; @@ -87,7 +89,12 @@ void Control::_edit_set_state(const Dictionary &p_state) { } void Control::_edit_set_position(const Point2 &p_position) { +#ifdef TOOLS_ENABLED + set_position(p_position, CanvasItemEditor::get_singleton()->is_anchors_mode_enabled()); +#else + // Unlikely to happen. TODO: enclose all _edit_ functions into TOOLS_ENABLED set_position(p_position); +#endif }; Point2 Control::_edit_get_position() const { @@ -103,8 +110,14 @@ Size2 Control::_edit_get_scale() const { } void Control::_edit_set_rect(const Rect2 &p_edit_rect) { +#ifdef TOOLS_ENABLED + set_position((get_position() + get_transform().basis_xform(p_edit_rect.position)).snapped(Vector2(1, 1)), CanvasItemEditor::get_singleton()->is_anchors_mode_enabled()); + set_size(p_edit_rect.size.snapped(Vector2(1, 1)), CanvasItemEditor::get_singleton()->is_anchors_mode_enabled()); +#else + // Unlikely to happen. TODO: enclose all _edit_ functions into TOOLS_ENABLED 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))); +#endif } Rect2 Control::_edit_get_rect() const { @@ -276,7 +289,7 @@ void Control::_update_minimum_size() { Size2 minsize = get_combined_minimum_size(); if (minsize.x > data.size_cache.x || minsize.y > data.size_cache.y) { - _size_changed(); + set_size(data.size_cache); } data.updating_last_minimum_size = false; @@ -1366,7 +1379,7 @@ void Control::set_anchor(Margin p_margin, float p_anchor, bool p_keep_margin, bo 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); + data.anchor[p_margin] = p_anchor; 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])) { @@ -1395,15 +1408,7 @@ void Control::set_anchor(Margin p_margin, float p_anchor, bool p_keep_margin, bo } void Control::_set_anchor(Margin p_margin, float p_anchor) { -#ifdef TOOLS_ENABLED - if (is_inside_tree() && Engine::get_singleton()->is_editor_hint()) { - set_anchor(p_margin, p_anchor, EDITOR_DEF("editors/2d/keep_margins_when_changing_anchors", false)); - } else { - set_anchor(p_margin, p_anchor, false); - } -#else - set_anchor(p_margin, p_anchor, false); -#endif + set_anchor(p_margin, p_anchor); } void Control::set_anchor_and_margin(Margin p_margin, float p_anchor, float p_pos, bool p_push_opposite_anchor) { @@ -1412,7 +1417,7 @@ void Control::set_anchor_and_margin(Margin p_margin, float p_anchor, float p_pos set_margin(p_margin, p_pos); } -void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margin) { +void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margins) { //Left switch (p_preset) { case PRESET_TOP_LEFT: @@ -1423,21 +1428,21 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margin) { case PRESET_LEFT_WIDE: case PRESET_HCENTER_WIDE: case PRESET_WIDE: - set_anchor(MARGIN_LEFT, ANCHOR_BEGIN, p_keep_margin); + set_anchor(MARGIN_LEFT, ANCHOR_BEGIN, p_keep_margins); break; case PRESET_CENTER_TOP: case PRESET_CENTER_BOTTOM: case PRESET_CENTER: case PRESET_VCENTER_WIDE: - set_anchor(MARGIN_LEFT, 0.5, p_keep_margin); + set_anchor(MARGIN_LEFT, 0.5, p_keep_margins); break; case PRESET_TOP_RIGHT: case PRESET_BOTTOM_RIGHT: case PRESET_CENTER_RIGHT: case PRESET_RIGHT_WIDE: - set_anchor(MARGIN_LEFT, ANCHOR_END, p_keep_margin); + set_anchor(MARGIN_LEFT, ANCHOR_END, p_keep_margins); break; } @@ -1451,21 +1456,21 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margin) { case PRESET_TOP_WIDE: case PRESET_VCENTER_WIDE: case PRESET_WIDE: - set_anchor(MARGIN_TOP, ANCHOR_BEGIN, p_keep_margin); + set_anchor(MARGIN_TOP, ANCHOR_BEGIN, p_keep_margins); break; case PRESET_CENTER_LEFT: case PRESET_CENTER_RIGHT: case PRESET_CENTER: case PRESET_HCENTER_WIDE: - set_anchor(MARGIN_TOP, 0.5, p_keep_margin); + set_anchor(MARGIN_TOP, 0.5, p_keep_margins); break; case PRESET_BOTTOM_LEFT: case PRESET_BOTTOM_RIGHT: case PRESET_CENTER_BOTTOM: case PRESET_BOTTOM_WIDE: - set_anchor(MARGIN_TOP, ANCHOR_END, p_keep_margin); + set_anchor(MARGIN_TOP, ANCHOR_END, p_keep_margins); break; } @@ -1475,14 +1480,14 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margin) { case PRESET_BOTTOM_LEFT: case PRESET_CENTER_LEFT: case PRESET_LEFT_WIDE: - set_anchor(MARGIN_RIGHT, ANCHOR_BEGIN, p_keep_margin); + set_anchor(MARGIN_RIGHT, ANCHOR_BEGIN, p_keep_margins); break; case PRESET_CENTER_TOP: case PRESET_CENTER_BOTTOM: case PRESET_CENTER: case PRESET_VCENTER_WIDE: - set_anchor(MARGIN_RIGHT, 0.5, p_keep_margin); + set_anchor(MARGIN_RIGHT, 0.5, p_keep_margins); break; case PRESET_TOP_RIGHT: @@ -1493,7 +1498,7 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margin) { case PRESET_BOTTOM_WIDE: case PRESET_HCENTER_WIDE: case PRESET_WIDE: - set_anchor(MARGIN_RIGHT, ANCHOR_END, p_keep_margin); + set_anchor(MARGIN_RIGHT, ANCHOR_END, p_keep_margins); break; } @@ -1503,14 +1508,14 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margin) { case PRESET_TOP_RIGHT: case PRESET_CENTER_TOP: case PRESET_TOP_WIDE: - set_anchor(MARGIN_BOTTOM, ANCHOR_BEGIN, p_keep_margin); + set_anchor(MARGIN_BOTTOM, ANCHOR_BEGIN, p_keep_margins); break; case PRESET_CENTER_LEFT: case PRESET_CENTER_RIGHT: case PRESET_CENTER: case PRESET_HCENTER_WIDE: - set_anchor(MARGIN_BOTTOM, 0.5, p_keep_margin); + set_anchor(MARGIN_BOTTOM, 0.5, p_keep_margins); break; case PRESET_BOTTOM_LEFT: @@ -1521,7 +1526,7 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margin) { case PRESET_BOTTOM_WIDE: case PRESET_VCENTER_WIDE: case PRESET_WIDE: - set_anchor(MARGIN_BOTTOM, ANCHOR_END, p_keep_margin); + set_anchor(MARGIN_BOTTOM, ANCHOR_END, p_keep_margins); break; } } @@ -1714,7 +1719,11 @@ Point2 Control::get_global_position() const { return get_global_transform().get_origin(); } -void Control::set_global_position(const Point2 &p_point) { +void Control::_set_global_position(const Point2 &p_point) { + set_global_position(p_point); +} + +void Control::set_global_position(const Point2 &p_point, bool p_keep_margins) { Transform2D inv; @@ -1723,7 +1732,7 @@ void Control::set_global_position(const Point2 &p_point) { inv = data.parent_canvas_item->get_global_transform().affine_inverse(); } - set_position(inv.xform(p_point)); + set_position(inv.xform(p_point), p_keep_margins); } Rect2 Control::_compute_child_rect(const float p_anchors[4], const float p_margins[4]) const { @@ -1737,6 +1746,15 @@ Rect2 Control::_compute_child_rect(const float p_anchors[4], const float p_margi return result; } +void Control::_compute_anchors(Rect2 p_rect, const float p_margins[4], float (&r_anchors)[4]) { + + Size2 parent_rect_size = get_parent_anchorable_rect().size; + r_anchors[0] = (p_rect.position.x - p_margins[0]) / parent_rect_size.x; + r_anchors[1] = (p_rect.position.y - p_margins[1]) / parent_rect_size.y; + r_anchors[2] = (p_rect.position.x + p_rect.size.x - p_margins[2]) / parent_rect_size.x; + r_anchors[3] = (p_rect.position.y + p_rect.size.y - p_margins[3]) / parent_rect_size.y; +} + void Control::_compute_margins(Rect2 p_rect, const float p_anchors[4], float (&r_margins)[4]) { Size2 parent_rect_size = get_parent_anchorable_rect().size; @@ -1746,13 +1764,28 @@ void Control::_compute_margins(Rect2 p_rect, const float p_anchors[4], float (&r r_margins[3] = p_rect.position.y + p_rect.size.y - (p_anchors[3] * parent_rect_size.y); } -void Control::set_position(const Size2 &p_point) { +void Control::_set_position(const Size2 &p_point) { + set_position(p_point); +} - _compute_margins(Rect2(p_point, data.size_cache), data.anchor, data.margin); +void Control::set_position(const Size2 &p_point, bool p_keep_margins) { + if (p_keep_margins) { + _compute_anchors(Rect2(p_point, data.size_cache), data.margin, data.anchor); + _change_notify("anchor_left"); + _change_notify("anchor_right"); + _change_notify("anchor_top"); + _change_notify("anchor_bottom"); + } else { + _compute_margins(Rect2(p_point, data.size_cache), data.anchor, data.margin); + } _size_changed(); } -void Control::set_size(const Size2 &p_size) { +void Control::_set_size(const Size2 &p_size) { + set_size(p_size); +} + +void Control::set_size(const Size2 &p_size, bool p_keep_margins) { Size2 new_size = p_size; Size2 min = get_combined_minimum_size(); @@ -1761,7 +1794,15 @@ void Control::set_size(const Size2 &p_size) { if (new_size.y < min.y) new_size.y = min.y; - _compute_margins(Rect2(data.pos_cache, new_size), data.anchor, data.margin); + if (p_keep_margins) { + _compute_anchors(Rect2(data.pos_cache, new_size), data.margin, data.anchor); + _change_notify("anchor_left"); + _change_notify("anchor_right"); + _change_notify("anchor_top"); + _change_notify("anchor_bottom"); + } else { + _compute_margins(Rect2(data.pos_cache, new_size), data.anchor, data.margin); + } _size_changed(); } @@ -1799,53 +1840,72 @@ Rect2 Control::get_anchorable_rect() const { void Control::add_icon_override(const StringName &p_name, const Ref<Texture> &p_icon) { - ERR_FAIL_COND(p_icon.is_null()); if (data.icon_override.has(p_name)) { data.icon_override[p_name]->disconnect("changed", this, "_override_changed"); } - data.icon_override[p_name] = p_icon; - if (data.icon_override[p_name].is_valid()) { - data.icon_override[p_name]->connect("changed", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED); + + // clear if "null" is passed instead of a icon + if (p_icon.is_null()) { + data.icon_override.erase(p_name); + } else { + data.icon_override[p_name] = p_icon; + if (data.icon_override[p_name].is_valid()) { + data.icon_override[p_name]->connect("changed", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED); + } } notification(NOTIFICATION_THEME_CHANGED); } void Control::add_shader_override(const StringName &p_name, const Ref<Shader> &p_shader) { - ERR_FAIL_COND(p_shader.is_null()); + if (data.shader_override.has(p_name)) { data.shader_override[p_name]->disconnect("changed", this, "_override_changed"); } - data.shader_override[p_name] = p_shader; - if (data.shader_override[p_name].is_valid()) { - data.shader_override[p_name]->connect("changed", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED); + + // clear if "null" is passed instead of a shader + if (p_shader.is_null()) { + data.shader_override.erase(p_name); + } else { + data.shader_override[p_name] = p_shader; + if (data.shader_override[p_name].is_valid()) { + data.shader_override[p_name]->connect("changed", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED); + } } notification(NOTIFICATION_THEME_CHANGED); } void Control::add_style_override(const StringName &p_name, const Ref<StyleBox> &p_style) { - ERR_FAIL_COND(p_style.is_null()); if (data.style_override.has(p_name)) { data.style_override[p_name]->disconnect("changed", this, "_override_changed"); } - data.style_override[p_name] = p_style; - if (data.style_override[p_name].is_valid()) { - data.style_override[p_name]->connect("changed", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED); - } + // clear if "null" is passed instead of a style + if (p_style.is_null()) { + data.style_override.erase(p_name); + } else { + data.style_override[p_name] = p_style; + if (data.style_override[p_name].is_valid()) { + data.style_override[p_name]->connect("changed", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED); + } + } notification(NOTIFICATION_THEME_CHANGED); } void Control::add_font_override(const StringName &p_name, const Ref<Font> &p_font) { - ERR_FAIL_COND(p_font.is_null()); if (data.font_override.has(p_name)) { data.font_override[p_name]->disconnect("changed", this, "_override_changed"); } - data.font_override[p_name] = p_font; - if (data.font_override[p_name].is_valid()) { - data.font_override[p_name]->connect("changed", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED); - } + // clear if "null" is passed instead of a font + if (p_font.is_null()) { + data.font_override.erase(p_name); + } else { + data.font_override[p_name] = p_font; + if (data.font_override[p_name].is_valid()) { + data.font_override[p_name]->connect("changed", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED); + } + } notification(NOTIFICATION_THEME_CHANGED); } void Control::add_color_override(const StringName &p_name, const Color &p_color) { @@ -2702,20 +2762,23 @@ void Control::_bind_methods() { ClassDB::bind_method(D_METHOD("accept_event"), &Control::accept_event); ClassDB::bind_method(D_METHOD("get_minimum_size"), &Control::get_minimum_size); ClassDB::bind_method(D_METHOD("get_combined_minimum_size"), &Control::get_combined_minimum_size); - ClassDB::bind_method(D_METHOD("set_anchors_preset", "preset", "keep_margin"), &Control::set_anchors_preset, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("set_anchors_preset", "preset", "keep_margins"), &Control::set_anchors_preset, DEFVAL(false)); ClassDB::bind_method(D_METHOD("set_margins_preset", "preset", "resize_mode", "margin"), &Control::set_margins_preset, DEFVAL(PRESET_MODE_MINSIZE), DEFVAL(0)); ClassDB::bind_method(D_METHOD("set_anchors_and_margins_preset", "preset", "resize_mode", "margin"), &Control::set_anchors_and_margins_preset, DEFVAL(PRESET_MODE_MINSIZE), DEFVAL(0)); - ClassDB::bind_method(D_METHOD("set_anchor", "margin", "anchor", "keep_margin", "push_opposite_anchor"), &Control::set_anchor, DEFVAL(false), DEFVAL(true)); ClassDB::bind_method(D_METHOD("_set_anchor", "margin", "anchor"), &Control::_set_anchor); + ClassDB::bind_method(D_METHOD("set_anchor", "margin", "anchor", "keep_margin", "push_opposite_anchor"), &Control::set_anchor, DEFVAL(false), DEFVAL(true)); ClassDB::bind_method(D_METHOD("get_anchor", "margin"), &Control::get_anchor); ClassDB::bind_method(D_METHOD("set_margin", "margin", "offset"), &Control::set_margin); ClassDB::bind_method(D_METHOD("set_anchor_and_margin", "margin", "anchor", "offset", "push_opposite_anchor"), &Control::set_anchor_and_margin, DEFVAL(false)); ClassDB::bind_method(D_METHOD("set_begin", "position"), &Control::set_begin); ClassDB::bind_method(D_METHOD("set_end", "position"), &Control::set_end); - ClassDB::bind_method(D_METHOD("set_position", "position"), &Control::set_position); - ClassDB::bind_method(D_METHOD("set_size", "size"), &Control::set_size); + ClassDB::bind_method(D_METHOD("set_position", "position", "keep_margins"), &Control::set_position, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("_set_position", "margin"), &Control::_set_position); + ClassDB::bind_method(D_METHOD("set_size", "size", "keep_margins"), &Control::set_size, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("_set_size", "size"), &Control::_set_size); ClassDB::bind_method(D_METHOD("set_custom_minimum_size", "size"), &Control::set_custom_minimum_size); - ClassDB::bind_method(D_METHOD("set_global_position", "position"), &Control::set_global_position); + ClassDB::bind_method(D_METHOD("set_global_position", "position", "keep_margins"), &Control::set_global_position, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("_set_global_position", "position"), &Control::_set_global_position); ClassDB::bind_method(D_METHOD("set_rotation", "radians"), &Control::set_rotation); ClassDB::bind_method(D_METHOD("set_rotation_degrees", "degrees"), &Control::set_rotation_degrees); ClassDB::bind_method(D_METHOD("set_scale", "scale"), &Control::set_scale); @@ -2835,10 +2898,10 @@ void Control::_bind_methods() { BIND_VMETHOD(MethodInfo(Variant::BOOL, "_clips_input")); ADD_GROUP("Anchor", "anchor_"); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_left", PROPERTY_HINT_RANGE, "0,1,0.01"), "_set_anchor", "get_anchor", MARGIN_LEFT); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_top", PROPERTY_HINT_RANGE, "0,1,0.01"), "_set_anchor", "get_anchor", MARGIN_TOP); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_right", PROPERTY_HINT_RANGE, "0,1,0.01"), "_set_anchor", "get_anchor", MARGIN_RIGHT); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_bottom", PROPERTY_HINT_RANGE, "0,1,0.01"), "_set_anchor", "get_anchor", MARGIN_BOTTOM); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_left", PROPERTY_HINT_RANGE, "0,1,0.01,or_lesser,or_greater"), "_set_anchor", "get_anchor", MARGIN_LEFT); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_top", PROPERTY_HINT_RANGE, "0,1,0.01,or_lesser,or_greater"), "_set_anchor", "get_anchor", MARGIN_TOP); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_right", PROPERTY_HINT_RANGE, "0,1,0.01,or_lesser,or_greater"), "_set_anchor", "get_anchor", MARGIN_RIGHT); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_bottom", PROPERTY_HINT_RANGE, "0,1,0.01,or_lesser,or_greater"), "_set_anchor", "get_anchor", MARGIN_BOTTOM); ADD_GROUP("Margin", "margin_"); ADD_PROPERTYI(PropertyInfo(Variant::INT, "margin_left", PROPERTY_HINT_RANGE, "-4096,4096"), "set_margin", "get_margin", MARGIN_LEFT); @@ -2851,9 +2914,9 @@ void Control::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "grow_vertical", PROPERTY_HINT_ENUM, "Begin,End,Both"), "set_v_grow_direction", "get_v_grow_direction"); ADD_GROUP("Rect", "rect_"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_position", "get_position"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_global_position", PROPERTY_HINT_NONE, "", 0), "set_global_position", "get_global_position"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_size", "get_size"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_position", "get_position"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_global_position", PROPERTY_HINT_NONE, "", 0), "_set_global_position", "get_global_position"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_size", "get_size"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_min_size"), "set_custom_minimum_size", "get_custom_minimum_size"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "rect_rotation", PROPERTY_HINT_RANGE, "-1080,1080,0.01"), "set_rotation_degrees", "get_rotation_degrees"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_scale"), "set_scale", "get_scale"); diff --git a/scene/gui/control.h b/scene/gui/control.h index 5e33f6ba43..a6f9a442ae 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -217,6 +217,9 @@ private: Control *_get_focus_neighbour(Margin p_margin, int p_count = 0); void _set_anchor(Margin p_margin, float p_anchor); + void _set_position(const Point2 &p_point); + void _set_global_position(const Point2 &p_point); + void _set_size(const Size2 &p_size); void _propagate_theme_changed(CanvasItem *p_at, Control *p_owner, bool p_assign = true); void _theme_changed(); @@ -229,6 +232,7 @@ private: Rect2 _compute_child_rect(const float p_anchors[4], const float p_margins[4]) const; void _compute_margins(Rect2 p_rect, const float p_anchors[4], float (&r_margins)[4]); + void _compute_anchors(Rect2 p_rect, const float p_margins[4], float (&r_anchors)[4]); void _size_changed(); String _get_tooltip() const; @@ -325,7 +329,7 @@ public: /* POSITIONING */ - void set_anchors_preset(LayoutPreset p_preset, bool p_keep_margin = true); + void set_anchors_preset(LayoutPreset p_preset, bool p_keep_margins = 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); @@ -343,12 +347,12 @@ public: Point2 get_begin() const; Point2 get_end() const; - void set_position(const Point2 &p_point); - void set_global_position(const Point2 &p_point); + void set_position(const Point2 &p_point, bool p_keep_margins = false); + void set_global_position(const Point2 &p_point, bool p_keep_margins = false); Point2 get_position() const; Point2 get_global_position() const; - void set_size(const Size2 &p_size); + void set_size(const Size2 &p_size, bool p_keep_margins = false); Size2 get_size() const; Rect2 get_rect() const; diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index 5671b41de8..bdb1342019 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -431,8 +431,6 @@ void FileDialog::update_file_list() { dirs.pop_front(); } - dirs.clear(); - List<String> patterns; // build filter if (filter->get_selected() == filter->get_item_count() - 1) { @@ -507,8 +505,6 @@ void FileDialog::update_file_list() { if (tree->get_root() && tree->get_root()->get_children() && tree->get_selected() == NULL) tree->get_root()->get_children()->select(0); - - files.clear(); } void FileDialog::_filter_selected(int) { diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp index e8692d56d2..6463ee5ad5 100644 --- a/scene/gui/graph_node.cpp +++ b/scene/gui/graph_node.cpp @@ -159,9 +159,7 @@ void GraphNode::_resort() { fit_child_in_rect(c, r); cache_y.push_back(vofs + size.y * 0.5); - if (vofs > 0) - vofs += sep; - vofs += size.y; + vofs += size.y + sep; } update(); diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index f5e979e9e6..91b76839d7 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -369,7 +369,6 @@ void ItemList::clear() { update(); shape_changed = true; defer_select_single = -1; - scroll_bar->set_value(0); } void ItemList::set_fixed_column_width(int p_size) { @@ -471,6 +470,8 @@ Size2 ItemList::Item::get_icon_size() const { void ItemList::_gui_input(const Ref<InputEvent> &p_event) { + double prev_scroll = scroll_bar->get_value(); + Ref<InputEventMouseMotion> mm = p_event; if (defer_select_single >= 0 && mm.is_valid()) { defer_select_single = -1; @@ -748,9 +749,21 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) { search_string = ""; } - search_string += String::chr(k->get_unicode()); - for (int i = 0; i < items.size(); i++) { - if (items[i].text.begins_with(search_string)) { + if (String::chr(k->get_unicode()) != search_string) + search_string += String::chr(k->get_unicode()); + + for (int i = current + 1; i <= items.size(); i++) { + if (i == items.size()) { + if (current == 0) + break; + else + i = 0; + } + + if (i == current) + break; + + if (items[i].text.findn(search_string) == 0) { set_current(i); ensure_current_is_visible(); if (select_mode == SELECT_SINGLE) { @@ -768,6 +781,9 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) { scroll_bar->set_value(scroll_bar->get_value() + scroll_bar->get_page() * pan_gesture->get_delta().y / 8); } + + if (scroll_bar->get_value() != prev_scroll) + accept_event(); //accept event if scroll changed } void ItemList::ensure_current_is_visible() { @@ -1244,7 +1260,7 @@ int ItemList::get_item_at_position(const Point2 &p_pos, bool p_exact) const { Rect2 rc = items[i].rect_cache; if (i % current_columns == current_columns - 1) { - rc.size.width = get_size().width; //not right but works + rc.size.width = get_size().width - rc.position.x; //make sure you can still select the last item when clicking past the column } if (rc.has_point(pos)) { diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index d889c8d8b8..da6bff8ab8 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -44,7 +44,7 @@ static bool _is_text_char(CharType c) { - return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'; + return !is_symbol(c); } void LineEdit::_gui_input(Ref<InputEvent> p_event) { @@ -68,6 +68,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { _reset_caret_blink_timer(); if (b->is_pressed()) { + accept_event(); //don't pass event further when clicked on text field if (!text.empty() && is_editable() && _is_over_clear_button(b->get_position())) { clear_button_status.press_attempt = true; clear_button_status.pressing_inside = true; @@ -159,6 +160,38 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { if (!k->is_pressed()) return; + +#ifdef APPLE_STYLE_KEYS + if (k->get_control() && !k->get_shift() && !k->get_alt() && !k->get_command()) { + uint32_t remap_key = KEY_UNKNOWN; + switch (k->get_scancode()) { + case KEY_F: { + remap_key = KEY_RIGHT; + } break; + case KEY_B: { + remap_key = KEY_LEFT; + } break; + case KEY_P: { + remap_key = KEY_UP; + } break; + case KEY_N: { + remap_key = KEY_DOWN; + } break; + case KEY_D: { + remap_key = KEY_DELETE; + } break; + case KEY_H: { + remap_key = KEY_BACKSPACE; + } break; + } + + if (remap_key != KEY_UNKNOWN) { + k->set_scancode(remap_key); + k->set_control(false); + } + } +#endif + unsigned int code = k->get_scancode(); if (k->get_command()) { @@ -506,7 +539,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { if (handled) { accept_event(); - } else if (!k->get_alt() && !k->get_command()) { + } else if (!k->get_command()) { if (k->get_unicode() >= 32 && k->get_scancode() != KEY_DELETE) { if (editable) { @@ -613,9 +646,7 @@ void LineEdit::_notification(int p_what) { #endif case NOTIFICATION_RESIZED: { - if (expand_to_text_length) { - window_pos = 0; //force scroll back since it's expanding to text length - } + window_pos = 0; set_cursor_position(get_cursor_position()); } break; @@ -892,7 +923,8 @@ void LineEdit::cut_text() { void LineEdit::paste_text() { - String paste_buffer = OS::get_singleton()->get_clipboard(); + // Strip escape characters like \n and \t as they can't be displayed on LineEdit. + String paste_buffer = OS::get_singleton()->get_clipboard().strip_escapes(); if (paste_buffer != "") { @@ -1658,6 +1690,7 @@ LineEdit::LineEdit() { context_menu_enabled = true; menu = memnew(PopupMenu); add_child(menu); + editable = false; // initialise to opposite first, so we get past the early-out in set_editable set_editable(true); menu->connect("id_pressed", this, "menu_option"); expand_to_text_length = false; diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index 7238543a14..7027fceb84 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -115,15 +115,15 @@ void OptionButton::pressed() { popup->popup(); } -void OptionButton::add_icon_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID) { +void OptionButton::add_icon_item(const Ref<Texture> &p_icon, const String &p_label, int p_id) { - popup->add_icon_radio_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) { +void OptionButton::add_item(const String &p_label, int p_id) { - popup->add_radio_check_item(p_label, p_ID); + popup->add_radio_check_item(p_label, p_id); if (popup->get_item_count() == 1) select(0); } @@ -136,9 +136,9 @@ void OptionButton::set_item_icon(int p_idx, const Ref<Texture> &p_icon) { popup->set_item_icon(p_idx, p_icon); } -void OptionButton::set_item_id(int p_idx, int p_ID) { +void OptionButton::set_item_id(int p_idx, int p_id) { - popup->set_item_id(p_idx, p_ID); + popup->set_item_id(p_idx, p_id); } void OptionButton::set_item_metadata(int p_idx, const Variant &p_metadata) { @@ -338,8 +338,8 @@ void OptionButton::_bind_methods() { 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_SIGNAL(MethodInfo("item_selected", PropertyInfo(Variant::INT, "ID"))); - ADD_SIGNAL(MethodInfo("item_focused", PropertyInfo(Variant::INT, "ID"))); + ADD_SIGNAL(MethodInfo("item_selected", PropertyInfo(Variant::INT, "id"))); + ADD_SIGNAL(MethodInfo("item_focused", PropertyInfo(Variant::INT, "id"))); } OptionButton::OptionButton() { @@ -354,6 +354,7 @@ OptionButton::OptionButton() { add_child(popup); popup->set_pass_on_modal_close_click(false); popup->set_notify_transform(true); + popup->set_allow_search(true); popup->connect("id_pressed", this, "_selected"); popup->connect("id_focused", this, "_focused"); popup->connect("popup_hide", this, "set_pressed", varray(false)); diff --git a/scene/gui/option_button.h b/scene/gui/option_button.h index 63b451377a..51d5fd6947 100644 --- a/scene/gui/option_button.h +++ b/scene/gui/option_button.h @@ -59,12 +59,12 @@ protected: static void _bind_methods(); public: - void add_icon_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID = -1); - void add_item(const String &p_label, int p_ID = -1); + void add_icon_item(const Ref<Texture> &p_icon, const String &p_label, int p_id = -1); + void add_item(const String &p_label, int p_id = -1); void set_item_text(int p_idx, const String &p_text); void set_item_icon(int p_idx, const Ref<Texture> &p_icon); - void set_item_id(int p_idx, int p_ID); + void set_item_id(int p_idx, int p_id); void set_item_metadata(int p_idx, const Variant &p_metadata); void set_item_disabled(int p_idx, bool p_disabled); diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp index b7601bdd3e..fdb1b65f77 100644 --- a/scene/gui/popup.cpp +++ b/scene/gui/popup.cpp @@ -115,6 +115,18 @@ void Popup::set_as_minsize() { set_size(total_minsize); } +void Popup::popup_centered_clamped(const Size2 &p_size, float p_fallback_ratio) { + + Size2 popup_size = p_size; + Size2 window_size = get_viewport_rect().size; + + // clamp popup size in each dimension if window size is too small (using fallback ratio) + popup_size.x = MIN(window_size.x * p_fallback_ratio, popup_size.x); + popup_size.y = MIN(window_size.y * p_fallback_ratio, popup_size.y); + + popup_centered(popup_size); +} + void Popup::popup_centered_minsize(const Size2 &p_minsize) { set_custom_minimum_size(p_minsize); @@ -179,6 +191,7 @@ void Popup::_bind_methods() { ClassDB::bind_method(D_METHOD("popup_centered", "size"), &Popup::popup_centered, DEFVAL(Size2())); ClassDB::bind_method(D_METHOD("popup_centered_ratio", "ratio"), &Popup::popup_centered_ratio, DEFVAL(0.75)); ClassDB::bind_method(D_METHOD("popup_centered_minsize", "minsize"), &Popup::popup_centered_minsize, DEFVAL(Size2())); + ClassDB::bind_method(D_METHOD("popup_centered_clamped", "size", "fallback_ratio"), &Popup::popup_centered_clamped, DEFVAL(Size2()), DEFVAL(0.75)); ClassDB::bind_method(D_METHOD("popup", "bounds"), &Popup::popup, DEFVAL(Rect2())); ClassDB::bind_method(D_METHOD("set_exclusive", "enable"), &Popup::set_exclusive); ClassDB::bind_method(D_METHOD("is_exclusive"), &Popup::is_exclusive); diff --git a/scene/gui/popup.h b/scene/gui/popup.h index 7ccefe1d75..6615c51ec5 100644 --- a/scene/gui/popup.h +++ b/scene/gui/popup.h @@ -64,6 +64,7 @@ public: void popup_centered(const Size2 &p_size = Size2()); void popup_centered_minsize(const Size2 &p_minsize = Size2()); void set_as_minsize(); + void popup_centered_clamped(const Size2 &p_size = Size2(), float p_fallback_ratio = 0.75); virtual void popup(const Rect2 &p_bounds = Rect2()); virtual String get_configuration_warning() const; diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index 5df18cece6..2cac345dae 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -31,6 +31,7 @@ #include "popup_menu.h" #include "core/os/input.h" #include "core/os/keyboard.h" +#include "core/os/os.h" #include "core/print_string.h" #include "core/translation.h" @@ -355,7 +356,7 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) { } int over = _get_mouse_over(m->get_position()); - int id = (over < 0 || items[over].separator || items[over].disabled) ? -1 : (items[over].ID >= 0 ? items[over].ID : over); + int id = (over < 0 || items[over].separator || items[over].disabled) ? -1 : (items[over].id >= 0 ? items[over].id : over); if (id < 0) { mouse_over = -1; @@ -380,6 +381,43 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) { _scroll(-pan_gesture->get_delta().y, pan_gesture->get_position()); } } + + Ref<InputEventKey> k = p_event; + + if (allow_search && k.is_valid() && k->get_unicode()) { + + uint64_t now = OS::get_singleton()->get_ticks_msec(); + uint64_t diff = now - search_time_msec; + uint64_t max_interval = uint64_t(GLOBAL_DEF("gui/timers/incremental_search_max_interval_msec", 2000)); + search_time_msec = now; + + if (diff > max_interval) { + search_string = ""; + } + + if (String::chr(k->get_unicode()) != search_string) + search_string += String::chr(k->get_unicode()); + + for (int i = mouse_over + 1; i <= items.size(); i++) { + if (i == items.size()) { + if (mouse_over <= 0) + break; + else + i = 0; + } + + if (i == mouse_over) + break; + + if (items[i].text.findn(search_string) == 0) { + mouse_over = i; + emit_signal("id_focused", i); + update(); + accept_event(); + break; + } + } + } } bool PopupMenu::has_point(const Point2 &p_point) const { @@ -591,93 +629,93 @@ void PopupMenu::_notification(int p_what) { } } -void PopupMenu::add_icon_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID, uint32_t p_accel) { +void PopupMenu::add_icon_item(const Ref<Texture> &p_icon, const String &p_label, int p_id, uint32_t p_accel) { Item item; item.icon = p_icon; item.text = p_label; item.xl_text = tr(p_label); item.accel = p_accel; - item.ID = p_ID; + 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) { +void PopupMenu::add_item(const String &p_label, int p_id, uint32_t p_accel) { Item item; item.text = p_label; item.xl_text = tr(p_label); item.accel = p_accel; - item.ID = p_ID == -1 ? items.size() : p_ID; + item.id = p_id == -1 ? items.size() : 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) { +void PopupMenu::add_submenu_item(const String &p_label, const String &p_submenu, int p_id) { Item item; item.text = p_label; item.xl_text = tr(p_label); - item.ID = p_ID; + item.id = p_id; 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) { +void PopupMenu::add_icon_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_id, uint32_t p_accel) { Item item; item.icon = p_icon; item.text = p_label; item.xl_text = tr(p_label); item.accel = p_accel; - item.ID = p_ID; + item.id = p_id; 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) { +void PopupMenu::add_check_item(const String &p_label, int p_id, uint32_t p_accel) { Item item; item.text = p_label; item.xl_text = tr(p_label); item.accel = p_accel; - item.ID = p_ID == -1 ? items.size() : p_ID; + item.id = p_id == -1 ? items.size() : p_id; 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) { +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); + add_check_item(p_label, p_id, p_accel); items.write[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) { +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); + add_icon_check_item(p_icon, p_label, p_id, p_accel); items.write[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) { +void PopupMenu::add_icon_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) { ERR_FAIL_COND(p_shortcut.is_null()); _ref_shortcut(p_shortcut); Item item; - item.ID = p_ID; + item.id = p_id; item.icon = p_icon; item.shortcut = p_shortcut; item.shortcut_is_global = p_global; @@ -686,14 +724,14 @@ void PopupMenu::add_icon_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut minimum_size_changed(); } -void PopupMenu::add_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) { +void PopupMenu::add_shortcut(const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) { ERR_FAIL_COND(p_shortcut.is_null()); _ref_shortcut(p_shortcut); Item item; - item.ID = p_ID; + item.id = p_id; item.shortcut = p_shortcut; item.shortcut_is_global = p_global; items.push_back(item); @@ -701,14 +739,14 @@ void PopupMenu::add_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bool p_g 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) { +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()); _ref_shortcut(p_shortcut); Item item; - item.ID = p_ID; + item.id = p_id; item.shortcut = p_shortcut; item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; item.icon = p_icon; @@ -718,14 +756,14 @@ void PopupMenu::add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<Sh minimum_size_changed(); } -void PopupMenu::add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) { +void PopupMenu::add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) { ERR_FAIL_COND(p_shortcut.is_null()); _ref_shortcut(p_shortcut); Item item; - item.ID = p_ID; + item.id = p_id; item.shortcut = p_shortcut; item.shortcut_is_global = p_global; item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; @@ -734,21 +772,21 @@ void PopupMenu::add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bo minimum_size_changed(); } -void PopupMenu::add_radio_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) { +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); + add_check_shortcut(p_shortcut, p_id, p_global); items.write[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) { +void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int p_default_state, int p_id, uint32_t p_accel) { Item item; item.text = p_label; item.xl_text = tr(p_label); item.accel = p_accel; - item.ID = p_ID; + item.id = p_id; item.max_states = p_max_states; item.state = p_default_state; items.push_back(item); @@ -782,10 +820,10 @@ void PopupMenu::set_item_checked(int p_idx, bool p_checked) { update(); minimum_size_changed(); } -void PopupMenu::set_item_id(int p_idx, int p_ID) { +void PopupMenu::set_item_id(int p_idx, int p_id) { ERR_FAIL_INDEX(p_idx, items.size()); - items.write[p_idx].ID = p_ID; + items.write[p_idx].id = p_id; update(); minimum_size_changed(); @@ -881,14 +919,14 @@ bool PopupMenu::is_item_checked(int p_idx) const { int PopupMenu::get_item_id(int p_idx) const { ERR_FAIL_INDEX_V(p_idx, items.size(), 0); - return items[p_idx].ID; + return items[p_idx].id; } -int PopupMenu::get_item_index(int p_ID) const { +int PopupMenu::get_item_index(int p_id) const { for (int i = 0; i < items.size(); i++) { - if (items[i].ID == p_ID) + if (items[i].id == p_id) return i; } @@ -1077,7 +1115,7 @@ void PopupMenu::activate_item(int p_item) { ERR_FAIL_INDEX(p_item, items.size()); ERR_FAIL_COND(items[p_item].separator); - int id = items[p_item].ID >= 0 ? items[p_item].ID : p_item; + int id = items[p_item].id >= 0 ? items[p_item].id : p_item; //hide all parent PopupMenus Node *next = get_parent(); @@ -1139,7 +1177,7 @@ void PopupMenu::add_separator(const String &p_text) { Item sep; sep.separator = true; - sep.ID = -1; + sep.id = -1; if (p_text != String()) { sep.text = p_text; sep.xl_text = tr(p_text); @@ -1289,6 +1327,16 @@ float PopupMenu::get_submenu_popup_delay() const { return submenu_timer->get_wait_time(); } +void PopupMenu::set_allow_search(bool p_allow) { + + allow_search = p_allow; +} + +bool PopupMenu::get_allow_search() const { + + return allow_search; +} + void PopupMenu::set_hide_on_window_lose_focus(bool p_enabled) { hide_on_window_lose_focus = p_enabled; @@ -1407,6 +1455,9 @@ void PopupMenu::_bind_methods() { ClassDB::bind_method(D_METHOD("set_hide_on_window_lose_focus", "enable"), &PopupMenu::set_hide_on_window_lose_focus); ClassDB::bind_method(D_METHOD("is_hide_on_window_lose_focus"), &PopupMenu::is_hide_on_window_lose_focus); + ClassDB::bind_method(D_METHOD("set_allow_search", "allow"), &PopupMenu::set_allow_search); + ClassDB::bind_method(D_METHOD("get_allow_search"), &PopupMenu::get_allow_search); + ClassDB::bind_method(D_METHOD("_submenu_timeout"), &PopupMenu::_submenu_timeout); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items"); @@ -1414,9 +1465,10 @@ void PopupMenu::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_on_checkable_item_selection"), "set_hide_on_checkable_item_selection", "is_hide_on_checkable_item_selection"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_on_state_item_selection"), "set_hide_on_state_item_selection", "is_hide_on_state_item_selection"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "submenu_popup_delay"), "set_submenu_popup_delay", "get_submenu_popup_delay"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_search"), "set_allow_search", "get_allow_search"); - ADD_SIGNAL(MethodInfo("id_pressed", PropertyInfo(Variant::INT, "ID"))); - ADD_SIGNAL(MethodInfo("id_focused", PropertyInfo(Variant::INT, "ID"))); + 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"))); } @@ -1435,6 +1487,10 @@ PopupMenu::PopupMenu() { initial_button_mask = 0; during_grabbed_click = false; + allow_search = false; + search_time_msec = 0; + search_string = ""; + set_focus_mode(FOCUS_ALL); set_as_toplevel(true); set_hide_on_item_selection(true); diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h index 767ff568fe..babdd21281 100644 --- a/scene/gui/popup_menu.h +++ b/scene/gui/popup_menu.h @@ -55,7 +55,7 @@ class PopupMenu : public Popup { int state; bool separator; bool disabled; - int ID; + int id; Variant metadata; String submenu; String tooltip; @@ -112,6 +112,10 @@ class PopupMenu : public Popup { void _ref_shortcut(Ref<ShortCut> p_sc); void _unref_shortcut(Ref<ShortCut> p_sc); + bool allow_search; + uint64_t search_time_msec; + String search_string; + protected: virtual bool has_point(const Point2 &p_point) const; @@ -120,26 +124,26 @@ protected: static void _bind_methods(); public: - void add_icon_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID = -1, uint32_t p_accel = 0); - 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); + void add_icon_item(const Ref<Texture> &p_icon, const String &p_label, int p_id = -1, uint32_t p_accel = 0); + 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); void set_item_text(int p_idx, const String &p_text); void set_item_icon(int p_idx, const Ref<Texture> &p_icon); void set_item_checked(int p_idx, bool p_checked); - void set_item_id(int p_idx, int p_ID); + void set_item_id(int p_idx, int p_id); void set_item_accelerator(int p_idx, uint32_t p_accel); void set_item_metadata(int p_idx, const Variant &p_meta); void set_item_disabled(int p_idx, bool p_disabled); @@ -161,7 +165,7 @@ public: Ref<Texture> get_item_icon(int p_idx) const; bool is_item_checked(int p_idx) const; int get_item_id(int p_idx) const; - int get_item_index(int p_ID) const; + int get_item_index(int p_id) const; uint32_t get_item_accelerator(int p_idx) const; Variant get_item_metadata(int p_idx) const; bool is_item_disabled(int p_idx) const; @@ -206,6 +210,9 @@ public: void set_submenu_popup_delay(float p_time); float get_submenu_popup_delay() const; + void set_allow_search(bool p_allow); + bool get_allow_search() const; + virtual void popup(const Rect2 &p_bounds = Rect2()); void set_hide_on_window_lose_focus(bool p_enabled); diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 101eb2ac88..c6b6d29384 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -592,7 +592,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & //assign actual widths for (int i = 0; i < table->columns.size(); i++) { table->columns.write[i].width = table->columns[i].min_width; - if (table->columns[i].expand) + if (table->columns[i].expand && total_ratio > 0) table->columns.write[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio; table->total_width += table->columns[i].width + hseparation; } @@ -2336,6 +2336,7 @@ RichTextLabel::RichTextLabel() { tab_size = 4; default_align = ALIGN_LEFT; underline_meta = true; + meta_hovering = NULL; override_selected_font_color = false; scroll_visible = false; diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp index 2938654ed9..686d1c96cc 100644 --- a/scene/gui/scroll_bar.cpp +++ b/scene/gui/scroll_bar.cpp @@ -53,29 +53,19 @@ void ScrollBar::_gui_input(Ref<InputEvent> p_event) { if (b.is_valid()) { accept_event(); - if (b->get_button_index() == 5 && b->is_pressed()) { + if (b->get_button_index() == BUTTON_WHEEL_DOWN && b->is_pressed()) { - /* - if (orientation==VERTICAL) - set_val( get_val() + get_page() / 4.0 ); - else - */ set_value(get_value() + get_page() / 4.0); accept_event(); } - if (b->get_button_index() == 4 && b->is_pressed()) { + if (b->get_button_index() == BUTTON_WHEEL_UP && b->is_pressed()) { - /* - if (orientation==HORIZONTAL) - set_val( get_val() - get_page() / 4.0 ); - else - */ set_value(get_value() - get_page() / 4.0); accept_event(); } - if (b->get_button_index() != 1) + if (b->get_button_index() != BUTTON_LEFT) return; if (b->is_pressed()) { diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp index e50a71b0ff..a1034937b5 100644 --- a/scene/gui/scroll_container.cpp +++ b/scene/gui/scroll_container.cpp @@ -88,13 +88,16 @@ void ScrollContainer::_cancel_drag() { void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) { + double prev_v_scroll = v_scroll->get_value(); + double prev_h_scroll = h_scroll->get_value(); + Ref<InputEventMouseButton> mb = p_gui_input; if (mb.is_valid()) { if (mb->get_button_index() == BUTTON_WHEEL_UP && mb->is_pressed()) { // only horizontal is enabled, scroll horizontally - if (h_scroll->is_visible() && !v_scroll->is_visible()) { + if (h_scroll->is_visible() && (!v_scroll->is_visible() || mb->get_shift())) { h_scroll->set_value(h_scroll->get_value() - h_scroll->get_page() / 8 * mb->get_factor()); } else if (v_scroll->is_visible_in_tree()) { v_scroll->set_value(v_scroll->get_value() - v_scroll->get_page() / 8 * mb->get_factor()); @@ -103,7 +106,7 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) { if (mb->get_button_index() == BUTTON_WHEEL_DOWN && mb->is_pressed()) { // only horizontal is enabled, scroll horizontally - if (h_scroll->is_visible() && !v_scroll->is_visible()) { + if (h_scroll->is_visible() && (!v_scroll->is_visible() || mb->get_shift())) { h_scroll->set_value(h_scroll->get_value() + h_scroll->get_page() / 8 * mb->get_factor()); } else if (v_scroll->is_visible()) { v_scroll->set_value(v_scroll->get_value() + v_scroll->get_page() / 8 * mb->get_factor()); @@ -122,6 +125,9 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) { } } + if (v_scroll->get_value() != prev_v_scroll || h_scroll->get_value() != prev_h_scroll) + accept_event(); //accept event if scroll changed + if (!OS::get_singleton()->has_touchscreen_ui_hint()) return; @@ -204,6 +210,9 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) { v_scroll->set_value(v_scroll->get_value() + v_scroll->get_page() * pan_gesture->get_delta().y / 8); } } + + if (v_scroll->get_value() != prev_v_scroll || h_scroll->get_value() != prev_h_scroll) + accept_event(); //accept event if scroll changed } void ScrollContainer::_update_scrollbar_position() { diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp index 028ca41cbf..b777e77bc3 100644 --- a/scene/gui/slider.cpp +++ b/scene/gui/slider.cpp @@ -295,6 +295,7 @@ Slider::Slider(Orientation p_orientation) { mouse_inside = false; grab.active = false; ticks = 0; + ticks_on_borders = false; custom_step = -1; editable = true; scrollable = true; diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp index d21143739c..e778af3ceb 100644 --- a/scene/gui/spin_box.cpp +++ b/scene/gui/spin_box.cpp @@ -277,6 +277,7 @@ SpinBox::SpinBox() { add_child(line_edit); line_edit->set_anchors_and_margins_preset(Control::PRESET_WIDE); + line_edit->set_mouse_filter(MOUSE_FILTER_PASS); //connect("value_changed",this,"_value_changed"); line_edit->connect("text_entered", this, "_text_entered", Vector<Variant>(), CONNECT_DEFERRED); line_edit->connect("focus_exited", this, "_line_edit_focus_exit", Vector<Variant>(), CONNECT_DEFERRED); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 10bd9e2a8b..6b40ecfc6b 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -50,7 +50,7 @@ inline bool _is_symbol(CharType c) { static bool _is_text_char(CharType c) { - return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'; + return !is_symbol(c); } static bool _is_whitespace(CharType c) { @@ -104,6 +104,13 @@ static CharType _get_right_pair_symbol(CharType c) { return 0; } +static int _find_first_non_whitespace_column_of_line(const String &line) { + int left = 0; + while (left < line.length() && _is_whitespace(line[left])) + left++; + return left; +} + void TextEdit::Text::set_font(const Ref<Font> &p_font) { font = p_font; @@ -292,6 +299,7 @@ void TextEdit::Text::insert(int p_at, const String &p_text) { line.marked = false; line.safe = false; line.breakpoint = false; + line.bookmark = false; line.hidden = false; line.width_cache = -1; line.wrap_amount_cache = -1; @@ -346,7 +354,7 @@ void TextEdit::_update_scrollbars() { if (line_numbers) total_width += cache.line_number_w; - if (draw_breakpoint_gutter) { + if (draw_breakpoint_gutter || draw_bookmark_gutter) { total_width += cache.breakpoint_gutter_width; } @@ -398,6 +406,7 @@ void TextEdit::_update_scrollbars() { cursor.line_ofs = 0; cursor.wrap_ofs = 0; v_scroll->set_value(0); + v_scroll->set_max(0); v_scroll->hide(); } @@ -416,6 +425,7 @@ void TextEdit::_update_scrollbars() { cursor.x_ofs = 0; h_scroll->set_value(0); + h_scroll->set_max(0); h_scroll->hide(); } @@ -605,7 +615,7 @@ void TextEdit::_notification(int p_what) { draw_caret = false; } - if (draw_breakpoint_gutter) { + if (draw_breakpoint_gutter || draw_bookmark_gutter) { breakpoint_gutter_width = (get_row_height() * 55) / 100; cache.breakpoint_gutter_width = breakpoint_gutter_width; } else { @@ -954,6 +964,16 @@ void TextEdit::_notification(int p_what) { #endif } + // draw bookmark marker + if (text.is_bookmark(line)) { + if (draw_bookmark_gutter) { + int vertical_gap = (get_row_height() * 40) / 100; + int horizontal_gap = (cache.breakpoint_gutter_width * 30) / 100; + int marker_radius = get_row_height() - (vertical_gap * 2); + VisualServer::get_singleton()->canvas_item_add_circle(ci, Point2(cache.style_normal->get_margin(MARGIN_LEFT) + horizontal_gap - 2 + marker_radius / 2, ofs_y + vertical_gap + marker_radius / 2), marker_radius, Color(cache.bookmark_color.r, cache.bookmark_color.g, cache.bookmark_color.b)); + } + } + // draw breakpoint marker if (text.is_breakpoint(line)) { if (draw_breakpoint_gutter) { @@ -1072,10 +1092,7 @@ void TextEdit::_notification(int p_what) { } if ((char_ofs + char_margin + char_w) >= xmargin_end) { - if (syntax_coloring) - continue; - else - break; + break; } bool in_search_result = false; @@ -1098,7 +1115,7 @@ void TextEdit::_notification(int p_what) { 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); + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(char_ofs + char_margin - indent_px, ofs_y, indent_px, get_row_height()), cache.current_line_color); } // if its the last char draw to end of the line if (j == str.length() - 1) { @@ -1255,6 +1272,11 @@ void TextEdit::_notification(int p_what) { 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 (draw_spaces && str[j] == ' ') { + int yofs = (get_row_height() - cache.space_icon->get_height()) / 2; + cache.space_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 (line_wrap_index == line_wrap_amount && j == str.length() - 1 && is_folded(line)) { @@ -1632,31 +1654,25 @@ void TextEdit::backspace_at_cursor() { _consume_backspace_for_pair_symbol(prev_line, prev_column); } else { // handle space indentation - if (cursor.column - indent_size >= 0 && indent_using_spaces) { - - // if there is enough spaces to count as a tab + if (cursor.column != 0 && indent_using_spaces) { + // check if there are no other chars before cursor, just indentation bool unindent = true; - for (int i = 1; i <= indent_size; i++) { - if (text[cursor.line][cursor.column - i] != ' ') { - unindent = false; - break; - } - } - - // and it is before the first character int i = 0; while (i < cursor.column && i < text[cursor.line].length()) { - if (text[cursor.line][i] != ' ' && text[cursor.line][i] != '\t') { + if (!_is_whitespace(text[cursor.line][i])) { unindent = false; break; } i++; } - // then we can remove it as a single character. + // then we can remove all spaces as a single character. if (unindent) { - _remove_text(cursor.line, cursor.column - indent_size, cursor.line, cursor.column); - prev_column = cursor.column - indent_size; + // we want to remove spaces up to closest indent + // or whole indent if cursor is pointing at it + int spaces_to_delete = _calculate_spaces_till_next_left_indent(cursor.column); + prev_column = cursor.column - spaces_to_delete; + _remove_text(cursor.line, prev_column, cursor.line, cursor.column); } else { _remove_text(prev_line, prev_column, cursor.line, cursor.column); } @@ -1673,6 +1689,10 @@ void TextEdit::indent_right() { int start_line; int end_line; + + // this value informs us by how much we changed selection position by indenting right + // default is 1 for tab indentation + int selection_offset = 1; begin_complex_operation(); if (is_selection_active()) { @@ -1691,18 +1711,24 @@ void TextEdit::indent_right() { for (int i = start_line; i <= end_line; i++) { String line_text = get_line(i); if (indent_using_spaces) { - line_text = space_indent + line_text; + // we don't really care where selection is - we just need to know indentation level at the beginning of the line + int left = _find_first_non_whitespace_column_of_line(line_text); + int spaces_to_add = _calculate_spaces_till_next_right_indent(left); + // since we will add this much spaces we want move whole selection and cursor by this much + selection_offset = spaces_to_add; + for (int j = 0; j < spaces_to_add; j++) + line_text = ' ' + line_text; } else { line_text = '\t' + line_text; } set_line(i, line_text); } - // fix selection and cursor being off by one on the last line + // fix selection and cursor being off after shifting selection right if (is_selection_active()) { - select(selection.from_line, selection.from_column + 1, selection.to_line, selection.to_column + 1); + select(selection.from_line, selection.from_column + selection_offset, selection.to_line, selection.to_column + selection_offset); } - cursor_set_column(cursor.column + 1, false); + cursor_set_column(cursor.column + selection_offset, false); end_complex_operation(); update(); } @@ -1711,6 +1737,15 @@ void TextEdit::indent_left() { int start_line; int end_line; + + // moving cursor and selection after unindenting can get tricky + // because changing content of line can move cursor and selection on it's own (if new line ends before previous position of either) + // therefore we just remember initial values + // and at the end of the operation offset them by number of removed characters + int removed_characters = 0; + int initial_selection_end_column = selection.to_column; + int initial_cursor_column = cursor.column; + begin_complex_operation(); if (is_selection_active()) { @@ -1733,21 +1768,43 @@ void TextEdit::indent_left() { if (line_text.begins_with("\t")) { line_text = line_text.substr(1, line_text.length()); set_line(i, line_text); - } else if (line_text.begins_with(space_indent)) { - line_text = line_text.substr(indent_size, line_text.length()); + removed_characters = 1; + } else if (line_text.begins_with(" ")) { + // when unindenting we aim to remove spaces before line that has selection no matter what is selected + // so we start of by finding first non whitespace character of line + int left = _find_first_non_whitespace_column_of_line(line_text); + + // here we remove only enough spaces to align text to nearest full multiple of indentation_size + // in case where selection begins at the start of indentation_size multiple we remove whole indentation level + int spaces_to_remove = _calculate_spaces_till_next_left_indent(left); + + line_text = line_text.substr(spaces_to_remove, line_text.length()); set_line(i, line_text); + removed_characters = spaces_to_remove; } } // 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); + select(selection.from_line, selection.from_column - removed_characters, + selection.to_line, initial_selection_end_column - removed_characters); } - cursor_set_column(cursor.column - 1, false); + cursor_set_column(initial_cursor_column - removed_characters, false); end_complex_operation(); update(); } +int TextEdit::_calculate_spaces_till_next_left_indent(int column) { + int spaces_till_indent = column % indent_size; + if (spaces_till_indent == 0) + spaces_till_indent = indent_size; + return spaces_till_indent; +} + +int TextEdit::_calculate_spaces_till_next_right_indent(int column) { + return indent_size - column % indent_size; +} + void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) const { float rows = p_mouse.y; @@ -1799,6 +1856,9 @@ void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) co void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { + double prev_v_scroll = v_scroll->get_value(); + double prev_h_scroll = h_scroll->get_value(); + Ref<InputEventMouseButton> mb = p_gui_input; if (mb.is_valid()) { @@ -2053,6 +2113,9 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { _scroll_down(delta); } h_scroll->set_value(h_scroll->get_value() + pan_gesture->get_delta().x * 100); + if (v_scroll->get_value() != prev_v_scroll || h_scroll->get_value() != prev_h_scroll) + accept_event(); //accept event if scroll changed + return; } @@ -2096,6 +2159,9 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { } } + if (v_scroll->get_value() != prev_v_scroll || h_scroll->get_value() != prev_h_scroll) + accept_event(); //accept event if scroll changed + Ref<InputEventKey> k = p_gui_input; if (k.is_valid()) { @@ -2504,15 +2570,11 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { } else { if (k->get_shift()) { - //simple unindent + // 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++; + int left = _find_first_non_whitespace_column_of_line(line); cc = MIN(cc, left); while (cc < indent_size && cc < left && line[cc] == ' ') @@ -2520,24 +2582,18 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { if (cc > 0 && cc <= text[cursor.line].length()) { if (text[cursor.line][cc - 1] == '\t') { + // tabs unindentation _remove_text(cursor.line, cc - 1, cursor.line, cc); if (cursor.column >= left) cursor_set_column(MAX(0, cursor.column - 1)); update(); } else { - int n = 0; - - for (int i = 1; i <= MIN(cc, indent_size); i++) { - if (line[cc - i] != ' ') { - break; - } - n++; - } - - 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)); + // spaces unindentation + int spaces_to_remove = _calculate_spaces_till_next_left_indent(cc); + if (spaces_to_remove > 0) { + _remove_text(cursor.line, cc - spaces_to_remove, cursor.line, cc); + if (cursor.column > left - spaces_to_remove) // inside text? + cursor_set_column(MAX(0, cursor.column - spaces_to_remove)); update(); } } @@ -2546,9 +2602,14 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { update(); } } else { - //simple indent + // simple indent if (indent_using_spaces) { - _insert_text_at_cursor(space_indent); + // insert only as much spaces as needed till next indentation level + int spaces_to_add = _calculate_spaces_till_next_right_indent(cursor.column); + String indent_to_insert = String(); + for (int i = 0; i < spaces_to_add; i++) + indent_to_insert = ' ' + indent_to_insert; + _insert_text_at_cursor(indent_to_insert); } else { _insert_text_at_cursor("\t"); } @@ -3601,7 +3662,7 @@ void TextEdit::_insert_text(int p_line, int p_char, const String &p_text, int *r op.chain_forward = false; op.chain_backward = false; - //see if it shold just be set as current op + //see if it should just be set as current op if (current_op.type != op.type) { op.prev_version = get_version(); _push_current_op(); @@ -3652,7 +3713,7 @@ void TextEdit::_remove_text(int p_from_line, int p_from_column, int p_to_line, i op.chain_forward = false; op.chain_backward = false; - //see if it shold just be set as current op + //see if it should just be set as current op if (current_op.type != op.type) { op.prev_version = get_version(); _push_current_op(); @@ -4108,7 +4169,7 @@ void TextEdit::_scroll_moved(double p_to_val) { int v_scroll_i = floor(get_v_scroll()); int sc = 0; int n_line; - for (n_line = 0; n_line < text.size(); n_line++) { + for (n_line = 0; n_line < text.size() - 1; n_line++) { if (!is_line_hidden(n_line)) { sc++; sc += times_line_wraps(n_line); @@ -4301,17 +4362,22 @@ Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const { void TextEdit::set_text(String p_text) { setting_text = true; - _clear(); - _insert_text_at_cursor(p_text); - clear_undo_history(); - cursor.column = 0; - cursor.line = 0; - cursor.x_ofs = 0; - cursor.line_ofs = 0; - cursor.wrap_ofs = 0; - cursor.last_fit_x = 0; - cursor_set_line(0); - cursor_set_column(0); + if (!undo_enabled) { + _clear(); + _insert_text_at_cursor(p_text); + } + + if (undo_enabled) { + cursor_set_line(0); + cursor_set_column(0); + + begin_complex_operation(); + _remove_text(0, 0, MAX(0, get_line_count() - 1), MAX(get_line(MAX(get_line_count() - 1, 0)).size() - 1, 0)); + _insert_text_at_cursor(p_text); + end_complex_operation(); + selection.active = false; + } + update(); setting_text = false; @@ -4497,6 +4563,7 @@ void TextEdit::_update_caches() { cache.mark_color = get_color("mark_color"); cache.current_line_color = get_color("current_line_color"); cache.line_length_guideline_color = get_color("line_length_guideline_color"); + cache.bookmark_color = get_color("bookmark_color"); cache.breakpoint_color = get_color("breakpoint_color"); cache.executing_line_color = get_color("executing_line_color"); cache.code_folding_color = get_color("code_folding_color"); @@ -4513,6 +4580,7 @@ void TextEdit::_update_caches() { #endif cache.row_height = cache.font->get_height() + cache.line_spacing; cache.tab_icon = get_icon("tab"); + cache.space_icon = get_icon("space"); cache.folded_icon = get_icon("folded"); cache.can_fold_icon = get_icon("fold"); cache.folded_eol_icon = get_icon("GuiEllipsis", "EditorIcons"); @@ -4614,6 +4682,8 @@ bool TextEdit::has_keyword_color(String p_keyword) const { } Color TextEdit::get_keyword_color(String p_keyword) const { + + ERR_FAIL_COND_V(!keywords.has(p_keyword), Color()); return keywords[p_keyword]; } @@ -5093,6 +5163,37 @@ void TextEdit::clear_executing_line() { update(); } +bool TextEdit::is_line_set_as_bookmark(int p_line) const { + + ERR_FAIL_INDEX_V(p_line, text.size(), false); + return text.is_bookmark(p_line); +} + +void TextEdit::set_line_as_bookmark(int p_line, bool p_bookmark) { + + ERR_FAIL_INDEX(p_line, text.size()); + text.set_bookmark(p_line, p_bookmark); + update(); +} + +void TextEdit::get_bookmarks(List<int> *p_bookmarks) const { + + for (int i = 0; i < text.size(); i++) { + if (text.is_bookmark(i)) + p_bookmarks->push_back(i); + } +} + +Array TextEdit::get_bookmarks_array() const { + + Array arr; + for (int i = 0; i < text.size(); i++) { + if (text.is_bookmark(i)) + arr.append(i); + } + return arr; +} + bool TextEdit::is_line_set_as_breakpoint(int p_line) const { ERR_FAIL_INDEX_V(p_line, text.size(), false); @@ -5649,6 +5750,16 @@ bool TextEdit::is_drawing_tabs() const { return draw_tabs; } +void TextEdit::set_draw_spaces(bool p_draw) { + + draw_spaces = p_draw; +} + +bool TextEdit::is_drawing_spaces() const { + + return draw_spaces; +} + void TextEdit::set_override_selected_font_color(bool p_override_selected_font_color) { override_selected_font_color = p_override_selected_font_color; } @@ -6170,6 +6281,15 @@ void TextEdit::set_line_length_guideline_column(int p_column) { update(); } +void TextEdit::set_bookmark_gutter_enabled(bool p_draw) { + draw_bookmark_gutter = p_draw; + update(); +} + +bool TextEdit::is_bookmark_gutter_enabled() const { + return draw_bookmark_gutter; +} + void TextEdit::set_breakpoint_gutter_enabled(bool p_draw) { draw_breakpoint_gutter = p_draw; update(); @@ -6224,14 +6344,14 @@ int TextEdit::get_info_gutter_width() const { return info_gutter_width; } -void TextEdit::set_hiding_enabled(int p_enabled) { +void TextEdit::set_hiding_enabled(bool p_enabled) { if (!p_enabled) unhide_all_lines(); hiding_enabled = p_enabled; update(); } -int TextEdit::is_hiding_enabled() const { +bool TextEdit::is_hiding_enabled() const { return hiding_enabled; } @@ -6382,6 +6502,8 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("is_show_line_numbers_enabled"), &TextEdit::is_show_line_numbers_enabled); ClassDB::bind_method(D_METHOD("set_draw_tabs"), &TextEdit::set_draw_tabs); ClassDB::bind_method(D_METHOD("is_drawing_tabs"), &TextEdit::is_drawing_tabs); + ClassDB::bind_method(D_METHOD("set_draw_spaces"), &TextEdit::set_draw_spaces); + ClassDB::bind_method(D_METHOD("is_drawing_spaces"), &TextEdit::is_drawing_spaces); ClassDB::bind_method(D_METHOD("set_breakpoint_gutter_enabled", "enable"), &TextEdit::set_breakpoint_gutter_enabled); ClassDB::bind_method(D_METHOD("is_breakpoint_gutter_enabled"), &TextEdit::is_breakpoint_gutter_enabled); ClassDB::bind_method(D_METHOD("set_draw_fold_gutter"), &TextEdit::set_draw_fold_gutter); @@ -6433,6 +6555,7 @@ void TextEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "syntax_highlighting"), "set_syntax_coloring", "is_syntax_coloring_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_line_numbers"), "set_show_line_numbers", "is_show_line_numbers_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_tabs"), "set_draw_tabs", "is_drawing_tabs"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_spaces"), "set_draw_spaces", "is_drawing_spaces"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "breakpoint_gutter"), "set_breakpoint_gutter_enabled", "is_breakpoint_gutter_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fold_gutter"), "set_draw_fold_gutter", "is_drawing_fold_gutter"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_all_occurrences"), "set_highlight_all_occurrences", "is_highlight_all_occurrences_enabled"); @@ -6474,6 +6597,7 @@ TextEdit::TextEdit() { setting_row = false; draw_tabs = false; + draw_spaces = false; override_selected_font_color = false; draw_caret = true; max_chars = 0; @@ -6562,6 +6686,7 @@ TextEdit::TextEdit() { line_numbers_zero_padded = false; line_length_guideline = false; line_length_guideline_col = 80; + draw_bookmark_gutter = false; draw_breakpoint_gutter = false; draw_fold_gutter = false; draw_info_gutter = false; @@ -6586,6 +6711,7 @@ TextEdit::TextEdit() { context_menu_enabled = true; menu = memnew(PopupMenu); add_child(menu); + readonly = true; // initialise to opposite first, so we get past the early-out in set_readonly set_readonly(false); menu->connect("id_pressed", this, "menu_option"); first_draw = true; diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 4badd85e07..0c26602d2b 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -75,6 +75,7 @@ public: int width_cache : 24; bool marked : 1; bool breakpoint : 1; + bool bookmark : 1; bool hidden : 1; bool safe : 1; int wrap_amount_cache : 24; @@ -105,6 +106,8 @@ public: void set(int p_line, const String &p_text); void set_marked(int p_line, bool p_marked) { text.write[p_line].marked = p_marked; } bool is_marked(int p_line) const { return text[p_line].marked; } + void set_bookmark(int p_line, bool p_bookmark) { text.write[p_line].bookmark = p_bookmark; } + bool is_bookmark(int p_line) const { return text[p_line].bookmark; } void set_breakpoint(int p_line, bool p_breakpoint) { text.write[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.write[p_line].hidden = p_hidden; } @@ -163,6 +166,7 @@ private: struct Cache { Ref<Texture> tab_icon; + Ref<Texture> space_icon; Ref<Texture> can_fold_icon; Ref<Texture> folded_icon; Ref<Texture> folded_eol_icon; @@ -187,6 +191,7 @@ private: Color member_variable_color; Color selection_color; Color mark_color; + Color bookmark_color; Color breakpoint_color; Color executing_line_color; Color code_folding_color; @@ -290,6 +295,7 @@ private: bool first_draw; bool setting_row; bool draw_tabs; + bool draw_spaces; bool override_selected_font_color; bool cursor_changed_dirty; bool text_changed_dirty; @@ -298,6 +304,7 @@ private: bool line_numbers_zero_padded; bool line_length_guideline; int line_length_guideline_col; + bool draw_bookmark_gutter; bool draw_breakpoint_gutter; int breakpoint_gutter_width; bool draw_fold_gutter; @@ -430,6 +437,9 @@ private: void _confirm_completion(); void _update_completion_candidates(); + int _calculate_spaces_till_next_left_indent(int column); + int _calculate_spaces_till_next_right_indent(int column); + protected: virtual String get_tooltip(const Point2 &p_pos) const; @@ -488,6 +498,10 @@ public: void insert_at(const String &p_text, int at); int get_line_count() const; void set_line_as_marked(int p_line, bool p_marked); + void set_line_as_bookmark(int p_line, bool p_bookmark); + bool is_line_set_as_bookmark(int p_line) const; + void get_bookmarks(List<int> *p_bookmarks) const; + Array get_bookmarks_array() const; void set_line_as_breakpoint(int p_line, bool p_breakpoint); bool is_line_set_as_breakpoint(int p_line) const; void set_executing_line(int p_line); @@ -613,6 +627,8 @@ public: int get_indent_size(); void set_draw_tabs(bool p_draw); bool is_drawing_tabs() const; + void set_draw_spaces(bool p_draw); + bool is_drawing_spaces() const; void set_override_selected_font_color(bool p_override_selected_font_color); bool is_overriding_selected_font_color() const; @@ -660,6 +676,9 @@ public: void set_show_line_length_guideline(bool p_show); void set_line_length_guideline_column(int p_column); + void set_bookmark_gutter_enabled(bool p_draw); + bool is_bookmark_gutter_enabled() const; + void set_breakpoint_gutter_enabled(bool p_draw); bool is_breakpoint_gutter_enabled() const; @@ -678,8 +697,8 @@ public: void set_info_gutter_width(int p_gutter_width); int get_info_gutter_width() const; - void set_hiding_enabled(int p_enabled); - int is_hiding_enabled() const; + void set_hiding_enabled(bool p_enabled); + bool is_hiding_enabled() const; void set_tooltip_request_func(Object *p_obj, const StringName &p_function, const Variant &p_udata); @@ -716,7 +735,7 @@ 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 String get_name() const = 0; virtual List<String> get_supported_languages() = 0; void set_text_editor(TextEdit *p_text_editor); diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index f22fe5b6a5..451241af40 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -318,7 +318,7 @@ void TreeItem::set_custom_draw(int p_column, Object *p_object, const StringName void TreeItem::set_collapsed(bool p_collapsed) { - if (collapsed == p_collapsed) + if (collapsed == p_collapsed || !tree) return; collapsed = p_collapsed; TreeItem *ci = tree->selected_item; @@ -344,8 +344,7 @@ void TreeItem::set_collapsed(bool p_collapsed) { } _changed_notify(); - if (tree) - tree->emit_signal("item_collapsed", this); + tree->emit_signal("item_collapsed", this); } bool TreeItem::is_collapsed() { @@ -389,7 +388,7 @@ TreeItem *TreeItem::get_children() { return children; } -TreeItem *TreeItem::get_prev_visible() { +TreeItem *TreeItem::get_prev_visible(bool p_wrap) { TreeItem *current = this; @@ -398,8 +397,20 @@ TreeItem *TreeItem::get_prev_visible() { if (!prev) { current = current->parent; - if (!current || (current == tree->root && tree->hide_root)) + if (current == tree->root && tree->hide_root) { return NULL; + } else if (!current) { + if (p_wrap) { + current = this; + TreeItem *temp = this->get_next_visible(); + while (temp) { + current = temp; + temp = temp->get_next_visible(); + } + } else { + return NULL; + } + } } else { current = prev; @@ -415,7 +426,7 @@ TreeItem *TreeItem::get_prev_visible() { return current; } -TreeItem *TreeItem::get_next_visible() { +TreeItem *TreeItem::get_next_visible(bool p_wrap) { TreeItem *current = this; @@ -433,10 +444,14 @@ TreeItem *TreeItem::get_next_visible() { current = current->parent; } - if (current == NULL) - return NULL; - else + if (!current) { + if (p_wrap) + return tree->root; + else + return NULL; + } else { current = current->next; + } } return current; @@ -740,8 +755,8 @@ void TreeItem::_bind_methods() { ClassDB::bind_method(D_METHOD("get_parent"), &TreeItem::get_parent); ClassDB::bind_method(D_METHOD("get_children"), &TreeItem::get_children); - ClassDB::bind_method(D_METHOD("get_next_visible"), &TreeItem::get_next_visible); - ClassDB::bind_method(D_METHOD("get_prev_visible"), &TreeItem::get_prev_visible); + ClassDB::bind_method(D_METHOD("get_next_visible", "wrap"), &TreeItem::get_next_visible, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_prev_visible", "wrap"), &TreeItem::get_prev_visible, DEFVAL(false)); ClassDB::bind_method(D_METHOD("remove_child", "child"), &TreeItem::_remove_child); @@ -1970,6 +1985,9 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool item_h += child_h; } } + if (p_item == root && p_button == BUTTON_RIGHT) { + emit_signal("empty_rmb", get_local_mouse_position()); + } } return item_h; // nothing found @@ -2266,7 +2284,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { next = _n; } else { - return; + break; } } if (next == selected_item) @@ -2304,7 +2322,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { prev = _n; } else { - return; + break; } } if (prev == selected_item) @@ -3488,6 +3506,7 @@ void Tree::scroll_to_item(TreeItem *p_item) { TreeItem *Tree::_search_item_text(TreeItem *p_at, const String &p_find, int *r_col, bool p_selectable, bool p_backwards) { + TreeItem *from = p_at; while (p_at) { for (int i = 0; i < columns.size(); i++) { @@ -3499,9 +3518,12 @@ TreeItem *Tree::_search_item_text(TreeItem *p_at, const String &p_find, int *r_c } if (p_backwards) - p_at = p_at->get_prev_visible(); + p_at = p_at->get_prev_visible(true); else - p_at = p_at->get_next_visible(); + p_at = p_at->get_next_visible(true); + + if ((p_at) == from) + break; } return NULL; @@ -3509,10 +3531,14 @@ TreeItem *Tree::_search_item_text(TreeItem *p_at, const String &p_find, int *r_c TreeItem *Tree::search_item_text(const String &p_find, int *r_col, bool p_selectable) { - if (!root) + TreeItem *from = get_selected(); + + if (!from) + from = root; + if (!from) return NULL; - return _search_item_text(root, p_find, r_col, p_selectable); + return _search_item_text(from->get_next_visible(true), p_find, r_col, p_selectable); } void Tree::_do_incr_search(const String &p_add) { @@ -3521,7 +3547,7 @@ void Tree::_do_incr_search(const String &p_add) { uint64_t diff = time - last_keypress; if (diff > uint64_t(GLOBAL_DEF("gui/timers/incremental_search_max_interval_msec", 2000))) incr_search = p_add; - else + else if (incr_search != p_add) incr_search += p_add; last_keypress = time; @@ -3694,6 +3720,10 @@ String Tree::get_tooltip(const Point2 &p_pos) const { const TreeItem::Cell &c = it->cells[col]; int col_width = get_column_width(col); + + for (int i = 0; i < col; i++) + pos.x -= get_column_width(i); + for (int j = c.buttons.size() - 1; j >= 0; j--) { Ref<Texture> b = c.buttons[j].texture; Size2 size = b->get_size() + cache.button_pressed->get_minimum_size(); @@ -3853,6 +3883,7 @@ void Tree::_bind_methods() { ADD_SIGNAL(MethodInfo("cell_selected")); ADD_SIGNAL(MethodInfo("multi_selected", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "TreeItem"), PropertyInfo(Variant::INT, "column"), PropertyInfo(Variant::BOOL, "selected"))); ADD_SIGNAL(MethodInfo("item_rmb_selected", PropertyInfo(Variant::VECTOR2, "position"))); + ADD_SIGNAL(MethodInfo("empty_rmb", PropertyInfo(Variant::VECTOR2, "position"))); ADD_SIGNAL(MethodInfo("empty_tree_rmb_selected", PropertyInfo(Variant::VECTOR2, "position"))); ADD_SIGNAL(MethodInfo("item_edited")); ADD_SIGNAL(MethodInfo("item_rmb_edited")); diff --git a/scene/gui/tree.h b/scene/gui/tree.h index 26d64baafb..45d451eb72 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -238,8 +238,8 @@ public: TreeItem *get_parent(); TreeItem *get_children(); - TreeItem *get_prev_visible(); - TreeItem *get_next_visible(); + TreeItem *get_prev_visible(bool p_wrap = false); + TreeItem *get_next_visible(bool p_wrap = false); void remove_child(TreeItem *p_item); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index e80de68e3b..ee23b24b3c 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -949,6 +949,7 @@ void Node::set_name(const String &p_name) { if (is_inside_tree()) { emit_signal("renamed"); + get_tree()->node_renamed(this); get_tree()->tree_changed(); } } @@ -1164,7 +1165,7 @@ void Node::add_child(Node *p_child, bool p_legible_unique_name) { ERR_FAIL_NULL(p_child); if (p_child == this) { - ERR_EXPLAIN("Can't add child '" + p_child->get_name() + "' to itself.") + ERR_EXPLAIN("Can't add child '" + p_child->get_name() + "' to itself."); ERR_FAIL_COND(p_child == this); // adding to itself! } @@ -1198,7 +1199,7 @@ void Node::add_child_below_node(Node *p_node, Node *p_child, bool p_legible_uniq if (is_a_parent_of(p_node)) { move_child(p_child, p_node->get_position_in_parent() + 1); } else { - WARN_PRINTS("Cannot move under node " + p_node->get_name() + " as " + p_child->get_name() + " does not share a parent.") + WARN_PRINTS("Cannot move under node " + p_node->get_name() + " as " + p_child->get_name() + " does not share a parent."); } } @@ -2076,7 +2077,9 @@ Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const } } - node->set_name(get_name()); + if (get_name() != String()) { + node->set_name(get_name()); + } #ifdef TOOLS_ENABLED if ((p_flags & DUPLICATE_FROM_EDITOR) && r_duplimap) diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index b81364e2f0..7f0cebd492 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -105,6 +105,11 @@ void SceneTree::node_removed(Node *p_node) { call_skip.insert(p_node); } +void SceneTree::node_renamed(Node *p_node) { + + emit_signal(node_renamed_name, p_node); +} + SceneTree::Group *SceneTree::add_to_group(const StringName &p_group, Node *p_node) { Map<StringName, Group>::Element *E = group_map.find(p_group); @@ -1239,7 +1244,7 @@ void SceneTree::_update_root_rect() { root->update_canvas_items(); //force them to update just in case if (use_font_oversampling) { - WARN_PRINT("Font oversampling does not work in 'Viewport' stretch mode, only '2D'.") + WARN_PRINT("Font oversampling does not work in 'Viewport' stretch mode, only '2D'."); } } break; @@ -1895,6 +1900,7 @@ void SceneTree::_bind_methods() { ADD_SIGNAL(MethodInfo("tree_changed")); ADD_SIGNAL(MethodInfo("node_added", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); ADD_SIGNAL(MethodInfo("node_removed", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); + ADD_SIGNAL(MethodInfo("node_renamed", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); ADD_SIGNAL(MethodInfo("screen_resized")); ADD_SIGNAL(MethodInfo("node_configuration_warning_changed", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); @@ -1983,6 +1989,7 @@ SceneTree::SceneTree() { tree_changed_name = "tree_changed"; node_added_name = "node_added"; node_removed_name = "node_removed"; + node_renamed_name = "node_renamed"; ugc_locked = false; call_lock = 0; root_lock = 0; diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h index e098b3d53c..0bcb724929 100644 --- a/scene/main/scene_tree.h +++ b/scene/main/scene_tree.h @@ -126,6 +126,7 @@ private: StringName tree_changed_name; StringName node_added_name; StringName node_removed_name; + StringName node_renamed_name; bool use_font_oversampling; int64_t current_frame; @@ -201,6 +202,7 @@ private: void tree_changed(); void node_added(Node *p_node); void node_removed(Node *p_node); + void node_renamed(Node *p_node); Group *add_to_group(const StringName &p_group, Node *p_node); void remove_from_group(const StringName &p_group, Node *p_node); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index ae2c571201..c02873cdeb 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -886,7 +886,7 @@ void Viewport::_camera_set(Camera *p_camera) { if (camera == p_camera) return; - if (camera && find_world().is_valid()) { + if (camera) { camera->notification(Camera::NOTIFICATION_LOST_CURRENT); } camera = p_camera; @@ -895,7 +895,7 @@ void Viewport::_camera_set(Camera *p_camera) { else VisualServer::get_singleton()->viewport_attach_camera(viewport, RID()); - if (camera && find_world().is_valid()) { + if (camera) { camera->notification(Camera::NOTIFICATION_BECAME_CURRENT); } @@ -914,9 +914,7 @@ void Viewport::_camera_remove(Camera *p_camera) { cameras.erase(p_camera); if (camera == p_camera) { - if (camera && find_world().is_valid()) { - camera->notification(Camera::NOTIFICATION_LOST_CURRENT); - } + camera->notification(Camera::NOTIFICATION_LOST_CURRENT); camera = NULL; } } @@ -1013,7 +1011,7 @@ void Viewport::_propagate_enter_world(Node *p_node) { Viewport *v = Object::cast_to<Viewport>(p_node); if (v) { - if (v->world.is_valid()) + if (v->world.is_valid() || v->own_world.is_valid()) return; } } @@ -1050,7 +1048,7 @@ void Viewport::_propagate_exit_world(Node *p_node) { Viewport *v = Object::cast_to<Viewport>(p_node); if (v) { - if (v->world.is_valid()) + if (v->world.is_valid() || v->own_world.is_valid()) return; } } @@ -1070,23 +1068,11 @@ void Viewport::set_world(const Ref<World> &p_world) { if (is_inside_tree()) _propagate_exit_world(this); -#ifndef _3D_DISABLED - if (find_world().is_valid() && camera) - camera->notification(Camera::NOTIFICATION_LOST_CURRENT); -#endif - world = p_world; if (is_inside_tree()) _propagate_enter_world(this); -#ifndef _3D_DISABLED - if (find_world().is_valid() && camera) - camera->notification(Camera::NOTIFICATION_BECAME_CURRENT); -#endif - - //propagate exit - if (is_inside_tree()) { VisualServer::get_singleton()->viewport_set_scenario(viewport, find_world()->get_scenario()); } @@ -2722,11 +2708,6 @@ void Viewport::set_use_own_world(bool p_world) { if (is_inside_tree()) _propagate_exit_world(this); -#ifndef _3D_DISABLED - if (find_world().is_valid() && camera) - camera->notification(Camera::NOTIFICATION_LOST_CURRENT); -#endif - if (!p_world) own_world = Ref<World>(); else @@ -2735,13 +2716,6 @@ void Viewport::set_use_own_world(bool p_world) { if (is_inside_tree()) _propagate_enter_world(this); -#ifndef _3D_DISABLED - if (find_world().is_valid() && camera) - camera->notification(Camera::NOTIFICATION_BECAME_CURRENT); -#endif - - //propagate exit - if (is_inside_tree()) { VisualServer::get_singleton()->viewport_set_scenario(viewport, find_world()->get_scenario()); } @@ -2765,6 +2739,19 @@ Rect2 Viewport::get_attach_to_screen_rect() const { return to_screen_rect; } +void Viewport::set_use_render_direct_to_screen(bool p_render_direct_to_screen) { + + if (p_render_direct_to_screen == render_direct_to_screen) + return; + + render_direct_to_screen = p_render_direct_to_screen; + VS::get_singleton()->viewport_set_render_direct_to_screen(viewport, p_render_direct_to_screen); +} + +bool Viewport::is_using_render_direct_to_screen() const { + return render_direct_to_screen; +} + void Viewport::set_physics_object_picking(bool p_enable) { physics_object_picking = p_enable; @@ -3030,6 +3017,8 @@ void Viewport::_bind_methods() { ClassDB::bind_method(D_METHOD("set_as_audio_listener_2d", "enable"), &Viewport::set_as_audio_listener_2d); ClassDB::bind_method(D_METHOD("is_audio_listener_2d"), &Viewport::is_audio_listener_2d); ClassDB::bind_method(D_METHOD("set_attach_to_screen_rect", "rect"), &Viewport::set_attach_to_screen_rect); + ClassDB::bind_method(D_METHOD("set_use_render_direct_to_screen", "enable"), &Viewport::set_use_render_direct_to_screen); + ClassDB::bind_method(D_METHOD("is_using_render_direct_to_screen"), &Viewport::is_using_render_direct_to_screen); ClassDB::bind_method(D_METHOD("get_mouse_position"), &Viewport::get_mouse_position); ClassDB::bind_method(D_METHOD("warp_mouse", "to_position"), &Viewport::warp_mouse); @@ -3084,6 +3073,7 @@ void Viewport::_bind_methods() { 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::BOOL, "render_direct_to_screen"), "set_use_render_direct_to_screen", "is_using_render_direct_to_screen"); 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_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "render_target_v_flip"), "set_vflip", "get_vflip"); @@ -3166,6 +3156,8 @@ Viewport::Viewport() { texture_rid = VisualServer::get_singleton()->viewport_get_texture(viewport); texture_flags = 0; + render_direct_to_screen = false; + default_texture.instance(); default_texture->vp = const_cast<Viewport *>(this); viewport_textures.insert(default_texture.ptr()); diff --git a/scene/main/viewport.h b/scene/main/viewport.h index d67b4ac348..b7160d5139 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -182,6 +182,7 @@ private: Size2 size; Rect2 to_screen_rect; + bool render_direct_to_screen; RID contact_2d_debug; RID contact_3d_debug_multimesh; @@ -481,6 +482,9 @@ public: void set_attach_to_screen_rect(const Rect2 &p_rect); Rect2 get_attach_to_screen_rect() const; + void set_use_render_direct_to_screen(bool p_render_direct_to_screen); + bool is_using_render_direct_to_screen() const; + Vector2 get_mouse_position() const; void warp_mouse(const Vector2 &p_pos); diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 47f5b152f0..5e4cf9c2ee 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -48,6 +48,7 @@ #include "scene/2d/light_occluder_2d.h" #include "scene/2d/line_2d.h" #include "scene/2d/mesh_instance_2d.h" +#include "scene/2d/multimesh_instance_2d.h" #include "scene/2d/navigation_2d.h" #include "scene/2d/parallax_background.h" #include "scene/2d/parallax_layer.h" @@ -144,7 +145,6 @@ #include "scene/resources/material.h" #include "scene/resources/mesh.h" #include "scene/resources/mesh_data_tool.h" -#include "scene/resources/mesh_library.h" #include "scene/resources/packed_scene.h" #include "scene/resources/particles_material.h" #include "scene/resources/physics_material.h" @@ -209,6 +209,7 @@ #include "scene/3d/visibility_notifier.h" #include "scene/animation/skeleton_ik.h" #include "scene/resources/environment.h" +#include "scene/resources/mesh_library.h" #endif static Ref<ResourceFormatSaverText> resource_saver_text; @@ -468,7 +469,7 @@ void register_scene_types() { OS::get_singleton()->yield(); //may take time to init #endif - ClassDB::register_class<MeshLibrary>(); + AcceptDialog::set_swap_ok_cancel(GLOBAL_DEF("gui/common/swap_ok_cancel", bool(OS::get_singleton()->get_swap_ok_cancel()))); ClassDB::register_class<Shader>(); @@ -476,6 +477,7 @@ void register_scene_types() { ClassDB::register_virtual_class<VisualShaderNode>(); ClassDB::register_class<VisualShaderNodeInput>(); ClassDB::register_virtual_class<VisualShaderNodeOutput>(); + ClassDB::register_class<VisualShaderNodeGroupBase>(); ClassDB::register_class<VisualShaderNodeScalarConstant>(); ClassDB::register_class<VisualShaderNodeBooleanConstant>(); ClassDB::register_class<VisualShaderNodeColorConstant>(); @@ -523,6 +525,8 @@ void register_scene_types() { ClassDB::register_class<VisualShaderNodeCubeMapUniform>(); ClassDB::register_class<VisualShaderNodeIf>(); ClassDB::register_class<VisualShaderNodeSwitch>(); + ClassDB::register_class<VisualShaderNodeFresnel>(); + ClassDB::register_class<VisualShaderNodeExpression>(); ClassDB::register_class<ShaderMaterial>(); ClassDB::register_virtual_class<CanvasItem>(); @@ -540,6 +544,7 @@ void register_scene_types() { ClassDB::register_class<Position2D>(); ClassDB::register_class<Line2D>(); ClassDB::register_class<MeshInstance2D>(); + ClassDB::register_class<MultiMeshInstance2D>(); ClassDB::register_virtual_class<CollisionObject2D>(); ClassDB::register_virtual_class<PhysicsBody2D>(); ClassDB::register_class<StaticBody2D>(); @@ -584,9 +589,13 @@ void register_scene_types() { SceneTree::add_idle_callback(ParticlesMaterial::flush_changes); ParticlesMaterial::init_shaders(); -#ifndef _3D_DISABLED ClassDB::register_virtual_class<Mesh>(); ClassDB::register_class<ArrayMesh>(); + ClassDB::register_class<MultiMesh>(); + ClassDB::register_class<SurfaceTool>(); + ClassDB::register_class<MeshDataTool>(); + +#ifndef _3D_DISABLED ClassDB::register_virtual_class<PrimitiveMesh>(); ClassDB::register_class<CapsuleMesh>(); ClassDB::register_class<CubeMesh>(); @@ -600,7 +609,6 @@ void register_scene_types() { SceneTree::add_idle_callback(SpatialMaterial::flush_changes); SpatialMaterial::init_shaders(); - ClassDB::register_class<MultiMesh>(); ClassDB::register_class<MeshLibrary>(); OS::get_singleton()->yield(); //may take time to init @@ -616,9 +624,6 @@ void register_scene_types() { ClassDB::register_class<ConvexPolygonShape>(); ClassDB::register_class<ConcavePolygonShape>(); - ClassDB::register_class<SurfaceTool>(); - ClassDB::register_class<MeshDataTool>(); - OS::get_singleton()->yield(); //may take time to init ClassDB::register_class<SpatialVelocityTracker>(); diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index 9c79b2ba3b..e4da659b0d 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -93,7 +93,7 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) { TransformTrack *tt = static_cast<TransformTrack *>(tracks[track]); PoolVector<float> values = p_value; int vcount = values.size(); - ERR_FAIL_COND_V(vcount % 12, false); // shuld be multiple of 11 + ERR_FAIL_COND_V(vcount % 12, false); // should be multiple of 11 PoolVector<float>::Read r = values.read(); @@ -819,15 +819,17 @@ int Animation::_insert(float p_time, T &p_keys, const V &p_value) { while (true) { - if (idx == 0 || p_keys[idx - 1].time < p_time) { - //condition for insertion. - p_keys.insert(idx, p_value); - return idx; - } else if (p_keys[idx - 1].time == p_time) { + // Condition for replacement. + if (idx > 0 && Math::is_equal_approx(p_keys[idx - 1].time, p_time)) { - // condition for replacing. p_keys.write[idx - 1] = p_value; return idx - 1; + + // Condition for insert. + } else if (idx == 0 || p_keys[idx - 1].time < p_time) { + + p_keys.insert(idx, p_value); + return idx; } idx--; @@ -1296,6 +1298,78 @@ float Animation::track_get_key_time(int p_track, int p_key_idx) const { ERR_FAIL_V(-1); } +void Animation::track_set_key_time(int p_track, int p_key_idx, float p_time) { + + ERR_FAIL_INDEX(p_track, tracks.size()); + Track *t = tracks[p_track]; + + switch (t->type) { + + case TYPE_TRANSFORM: { + + TransformTrack *tt = static_cast<TransformTrack *>(t); + ERR_FAIL_INDEX(p_key_idx, tt->transforms.size()); + TKey<TransformKey> key = tt->transforms[p_key_idx]; + key.time = p_time; + tt->transforms.remove(p_key_idx); + _insert(p_time, tt->transforms, key); + return; + } + case TYPE_VALUE: { + + ValueTrack *vt = static_cast<ValueTrack *>(t); + ERR_FAIL_INDEX(p_key_idx, vt->values.size()); + TKey<Variant> key = vt->values[p_key_idx]; + key.time = p_time; + vt->values.remove(p_key_idx); + _insert(p_time, vt->values, key); + return; + } + case TYPE_METHOD: { + + MethodTrack *mt = static_cast<MethodTrack *>(t); + ERR_FAIL_INDEX(p_key_idx, mt->methods.size()); + MethodKey key = mt->methods[p_key_idx]; + key.time = p_time; + mt->methods.remove(p_key_idx); + _insert(p_time, mt->methods, key); + return; + } + case TYPE_BEZIER: { + + BezierTrack *bt = static_cast<BezierTrack *>(t); + ERR_FAIL_INDEX(p_key_idx, bt->values.size()); + TKey<BezierKey> key = bt->values[p_key_idx]; + key.time = p_time; + bt->values.remove(p_key_idx); + _insert(p_time, bt->values, key); + return; + } + case TYPE_AUDIO: { + + AudioTrack *at = static_cast<AudioTrack *>(t); + ERR_FAIL_INDEX(p_key_idx, at->values.size()); + TKey<AudioKey> key = at->values[p_key_idx]; + key.time = p_time; + at->values.remove(p_key_idx); + _insert(p_time, at->values, key); + return; + } + case TYPE_ANIMATION: { + + AnimationTrack *at = static_cast<AnimationTrack *>(t); + ERR_FAIL_INDEX(p_key_idx, at->values.size()); + TKey<StringName> key = at->values[p_key_idx]; + key.time = p_time; + at->values.remove(p_key_idx); + _insert(p_time, at->values, key); + return; + } + } + + ERR_FAIL(); +} + float Animation::track_get_key_transition(int p_track, int p_key_idx) const { ERR_FAIL_INDEX_V(p_track, tracks.size(), -1); @@ -2559,17 +2633,6 @@ bool Animation::has_loop() const { return loop; } -void Animation::track_move_up(int p_track) { - - if (p_track >= 0 && p_track < (tracks.size() - 1)) { - - SWAP(tracks.write[p_track], tracks.write[p_track + 1]); - } - - emit_changed(); - emit_signal(SceneStringNames::get_singleton()->tracks_changed); -} - void Animation::track_set_imported(int p_track, bool p_imported) { ERR_FAIL_INDEX(p_track, tracks.size()); @@ -2595,12 +2658,40 @@ bool Animation::track_is_enabled(int p_track) const { return tracks[p_track]->enabled; } +void Animation::track_move_up(int p_track) { + + if (p_track >= 0 && p_track < (tracks.size() - 1)) { + + SWAP(tracks.write[p_track], tracks.write[p_track + 1]); + } + + emit_changed(); + emit_signal(SceneStringNames::get_singleton()->tracks_changed); +} + void Animation::track_move_down(int p_track) { if (p_track > 0 && p_track < tracks.size()) { SWAP(tracks.write[p_track], tracks.write[p_track - 1]); } + + emit_changed(); + emit_signal(SceneStringNames::get_singleton()->tracks_changed); +} + +void Animation::track_move_to(int p_track, int p_to_index) { + + ERR_FAIL_INDEX(p_track, tracks.size()); + ERR_FAIL_INDEX(p_to_index, tracks.size() + 1); + if (p_track == p_to_index || p_track == p_to_index - 1) + return; + + Track *track = tracks.get(p_track); + tracks.remove(p_track); + // Take into account that the position of the tracks that come after the one removed will change. + tracks.insert(p_to_index > p_track ? p_to_index - 1 : p_to_index, track); + emit_changed(); emit_signal(SceneStringNames::get_singleton()->tracks_changed); } @@ -2612,6 +2703,7 @@ void Animation::track_swap(int p_track, int p_with_track) { if (p_track == p_with_track) return; SWAP(tracks.write[p_track], tracks.write[p_with_track]); + emit_changed(); emit_signal(SceneStringNames::get_singleton()->tracks_changed); } @@ -2656,6 +2748,7 @@ void Animation::_bind_methods() { ClassDB::bind_method(D_METHOD("track_move_up", "idx"), &Animation::track_move_up); ClassDB::bind_method(D_METHOD("track_move_down", "idx"), &Animation::track_move_down); + ClassDB::bind_method(D_METHOD("track_move_to", "idx", "to_idx"), &Animation::track_move_to); ClassDB::bind_method(D_METHOD("track_swap", "idx", "with_idx"), &Animation::track_swap); ClassDB::bind_method(D_METHOD("track_set_imported", "idx", "imported"), &Animation::track_set_imported); @@ -2670,6 +2763,7 @@ void Animation::_bind_methods() { ClassDB::bind_method(D_METHOD("track_remove_key_at_position", "idx", "position"), &Animation::track_remove_key_at_position); ClassDB::bind_method(D_METHOD("track_set_key_value", "idx", "key", "value"), &Animation::track_set_key_value); ClassDB::bind_method(D_METHOD("track_set_key_transition", "idx", "key_idx", "transition"), &Animation::track_set_key_transition); + ClassDB::bind_method(D_METHOD("track_set_key_time", "idx", "key_idx", "time"), &Animation::track_set_key_time); ClassDB::bind_method(D_METHOD("track_get_key_transition", "idx", "key_idx"), &Animation::track_get_key_transition); ClassDB::bind_method(D_METHOD("track_get_key_count", "idx"), &Animation::track_get_key_count); diff --git a/scene/resources/animation.h b/scene/resources/animation.h index 3d38a8902f..59f2ae24c7 100644 --- a/scene/resources/animation.h +++ b/scene/resources/animation.h @@ -293,6 +293,7 @@ public: void track_move_up(int p_track); void track_move_down(int p_track); + void track_move_to(int p_track, int p_to_index); void track_swap(int p_track, int p_with_track); void track_set_imported(int p_track, bool p_imported); @@ -304,6 +305,7 @@ public: void track_insert_key(int p_track, float p_time, const Variant &p_key, float p_transition = 1); void track_set_key_transition(int p_track, int p_key_idx, float p_transition); void track_set_key_value(int p_track, int p_key_idx, const Variant &p_value); + void track_set_key_time(int p_track, int p_key_idx, float p_time); int track_find_key(int p_track, float p_time, bool p_exact = false) const; void track_remove_key(int p_track, int p_idx); void track_remove_key_at_position(int p_track, float p_pos); diff --git a/scene/resources/bit_map.cpp b/scene/resources/bit_map.cpp index a9d85be0dc..e4a64a1de1 100644 --- a/scene/resources/bit_map.cpp +++ b/scene/resources/bit_map.cpp @@ -595,16 +595,16 @@ Array BitMap::_opaque_to_polygons_bind(const Rect2 &p_rect, float p_epsilon) con return result_array; } -void BitMap::resize(const Size2& p_new_size) { +void BitMap::resize(const Size2 &p_new_size) { Ref<BitMap> new_bitmap; new_bitmap.instance(); new_bitmap->create(p_new_size); - int lw = MIN(width,p_new_size.width); - int lh = MIN(height,p_new_size.height); - for(int x=0;x<lw;x++) { - for(int y=0;y<lh;y++) { - new_bitmap->set_bit(Vector2(x,y),get_bit(Vector2(x,y))); + int lw = MIN(width, p_new_size.width); + int lh = MIN(height, p_new_size.height); + for (int x = 0; x < lw; x++) { + for (int y = 0; y < lh; y++) { + new_bitmap->set_bit(Vector2(x, y), get_bit(Vector2(x, y))); } } @@ -617,11 +617,11 @@ Ref<Image> BitMap::convert_to_image() const { Ref<Image> image; image.instance(); - image->create(width,height,false,Image::FORMAT_L8); + image->create(width, height, false, Image::FORMAT_L8); image->lock(); - for(int i=0;i<width;i++) { - for(int j=0;j<height;j++) { - image->set_pixel( i,j,get_bit(Point2(i,j)) ? Color(1,1,1) : Color(0,0,0)); + for (int i = 0; i < width; i++) { + for (int j = 0; j < height; j++) { + image->set_pixel(i, j, get_bit(Point2(i, j)) ? Color(1, 1, 1) : Color(0, 0, 0)); } } @@ -629,30 +629,28 @@ Ref<Image> BitMap::convert_to_image() const { return image; } -void BitMap::blit(const Vector2& p_pos,const Ref<BitMap>& p_bitmap) { +void BitMap::blit(const Vector2 &p_pos, const Ref<BitMap> &p_bitmap) { int x = p_pos.x; int y = p_pos.y; int w = p_bitmap->get_size().width; int h = p_bitmap->get_size().height; - for(int i=0;i<w;i++) { - for (int j=0;j<h;j++) { - int px = x+i; - int py = y+j; - if (px<0 || px>=width) + for (int i = 0; i < w; i++) { + for (int j = 0; j < h; j++) { + int px = x + i; + int py = y + j; + if (px < 0 || px >= width) continue; - if (py<0 || py>=height) + if (py < 0 || py >= height) continue; - if (p_bitmap->get_bit(Vector2(i,j))) { - set_bit(Vector2(x,y),true); + if (p_bitmap->get_bit(Vector2(i, j))) { + set_bit(Vector2(x, y), true); } } } - } - void BitMap::_bind_methods() { ClassDB::bind_method(D_METHOD("create", "size"), &BitMap::create); diff --git a/scene/resources/bit_map.h b/scene/resources/bit_map.h index 6e1171b8a9..daf24affb1 100644 --- a/scene/resources/bit_map.h +++ b/scene/resources/bit_map.h @@ -64,11 +64,11 @@ public: int get_true_bit_count() const; Size2 get_size() const; - void resize(const Size2& p_new_size); + void resize(const Size2 &p_new_size); void grow_mask(int p_pixels, const Rect2 &p_rect); - void blit(const Vector2& p_pos,const Ref<BitMap>& p_bitmap); + void blit(const Vector2 &p_pos, const Ref<BitMap> &p_bitmap); Ref<Image> convert_to_image() const; Vector<Vector<Vector2> > clip_opaque_to_polygons(const Rect2 &p_rect, float p_epsilon = 2.0) const; diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp index ece8ad4bb0..950518aa6e 100644 --- a/scene/resources/curve.cpp +++ b/scene/resources/curve.cpp @@ -782,7 +782,8 @@ Vector2 Curve2D::interpolate_baked(float p_offset, bool p_cubic) const { if (idx >= bpc - 1) { return r[bpc - 1]; } else if (idx == bpc - 2) { - frac /= Math::fmod(baked_max_ofs, bake_interval); + if (frac > 0) + frac /= Math::fmod(baked_max_ofs, bake_interval); } else { frac /= bake_interval; } @@ -1352,7 +1353,8 @@ Vector3 Curve3D::interpolate_baked(float p_offset, bool p_cubic) const { if (idx >= bpc - 1) { return r[bpc - 1]; } else if (idx == bpc - 2) { - frac /= Math::fmod(baked_max_ofs, bake_interval); + if (frac > 0) + frac /= Math::fmod(baked_max_ofs, bake_interval); } else { frac /= bake_interval; } @@ -1396,7 +1398,8 @@ float Curve3D::interpolate_baked_tilt(float p_offset) const { if (idx >= bpc - 1) { return r[bpc - 1]; } else if (idx == bpc - 2) { - frac /= Math::fmod(baked_max_ofs, bake_interval); + if (frac > 0) + frac /= Math::fmod(baked_max_ofs, bake_interval); } else { frac /= bake_interval; } diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index b3bd3f47e6..e8359e3dfe 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -425,6 +425,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_stylebox("completion", "TextEdit", make_stylebox(tree_bg_png, 3, 3, 3, 3, 0, 0, 0, 0)); theme->set_icon("tab", "TextEdit", make_icon(tab_png)); + theme->set_icon("space", "TextEdit", make_icon(space_png)); theme->set_icon("folded", "TextEdit", make_icon(arrow_right_png)); theme->set_icon("fold", "TextEdit", make_icon(arrow_down_png)); @@ -440,6 +441,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("font_color_selected", "TextEdit", Color(0, 0, 0)); theme->set_color("selection_color", "TextEdit", font_color_selection); theme->set_color("mark_color", "TextEdit", Color(1.0, 0.4, 0.4, 0.4)); + theme->set_color("bookmark_color", "TextEdit", Color(0.08, 0.49, 0.98)); theme->set_color("breakpoint_color", "TextEdit", Color(0.8, 0.8, 0.4, 0.2)); theme->set_color("executing_line_color", "TextEdit", Color(0.2, 0.8, 0.2, 0.4)); theme->set_color("code_folding_color", "TextEdit", Color(0.8, 0.8, 0.8, 0.8)); @@ -580,14 +582,14 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const // GraphNode - Ref<StyleBoxTexture> graphsb = make_stylebox(graph_node_png, 6, 24, 6, 5, 16, 24, 16, 5); - Ref<StyleBoxTexture> graphsbcomment = make_stylebox(graph_node_comment_png, 6, 24, 6, 5, 16, 24, 16, 5); - Ref<StyleBoxTexture> graphsbcommentselected = make_stylebox(graph_node_comment_focus_png, 6, 24, 6, 5, 16, 24, 16, 5); - Ref<StyleBoxTexture> graphsbselected = make_stylebox(graph_node_selected_png, 6, 24, 6, 5, 16, 24, 16, 5); + Ref<StyleBoxTexture> graphsb = make_stylebox(graph_node_png, 6, 24, 6, 5, 16, 24, 16, 6); + Ref<StyleBoxTexture> graphsbcomment = make_stylebox(graph_node_comment_png, 6, 24, 6, 5, 16, 24, 16, 6); + Ref<StyleBoxTexture> graphsbcommentselected = make_stylebox(graph_node_comment_focus_png, 6, 24, 6, 5, 16, 24, 16, 6); + Ref<StyleBoxTexture> graphsbselected = make_stylebox(graph_node_selected_png, 6, 24, 6, 5, 16, 24, 16, 6); Ref<StyleBoxTexture> graphsbdefault = make_stylebox(graph_node_default_png, 4, 4, 4, 4, 6, 4, 4, 4); Ref<StyleBoxTexture> graphsbdeffocus = make_stylebox(graph_node_default_focus_png, 4, 4, 4, 4, 6, 4, 4, 4); - Ref<StyleBoxTexture> graph_bpoint = make_stylebox(graph_node_breakpoint_png, 6, 24, 6, 5, 16, 24, 16, 5); - Ref<StyleBoxTexture> graph_position = make_stylebox(graph_node_position_png, 6, 24, 6, 5, 16, 24, 16, 5); + Ref<StyleBoxTexture> graph_bpoint = make_stylebox(graph_node_breakpoint_png, 6, 24, 6, 5, 16, 24, 16, 6); + Ref<StyleBoxTexture> graph_position = make_stylebox(graph_node_position_png, 6, 24, 6, 5, 16, 24, 16, 6); //graphsb->set_expand_margin_size(MARGIN_LEFT,10); //graphsb->set_expand_margin_size(MARGIN_RIGHT,10); diff --git a/scene/resources/default_theme/space.png b/scene/resources/default_theme/space.png Binary files differnew file mode 100644 index 0000000000..7e458a6c45 --- /dev/null +++ b/scene/resources/default_theme/space.png diff --git a/scene/resources/default_theme/theme_data.h b/scene/resources/default_theme/theme_data.h index a09b811b37..87b8afd6a3 100644 --- a/scene/resources/default_theme/theme_data.h +++ b/scene/resources/default_theme/theme_data.h @@ -358,6 +358,10 @@ static const unsigned char selection_oof_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x2d, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2c, 0x2, 0xfd, 0xfb, 0xff, 0xfd, 0xfb, 0xff, 0xfd, 0xfb, 0xff, 0xfd, 0xfb, 0xff, 0xaf, 0xdf, 0x90, 0xa5, 0x0, 0x0, 0x0, 0xf, 0x74, 0x52, 0x4e, 0x53, 0xa, 0x1a, 0x26, 0x29, 0x2a, 0x48, 0x65, 0x6d, 0x6e, 0x66, 0x3, 0x20, 0x25, 0x16, 0xc, 0x1f, 0x74, 0xbf, 0x74, 0x0, 0x0, 0x0, 0x37, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x64, 0x54, 0x52, 0x64, 0x60, 0x60, 0x78, 0x77, 0x8f, 0x51, 0x34, 0x8, 0xcc, 0xb8, 0xcd, 0xa8, 0xd9, 0x4, 0x66, 0xdc, 0x60, 0x74, 0x2f, 0x33, 0x4, 0x32, 0xde, 0xce, 0x64, 0xf4, 0x68, 0x53, 0x0, 0x32, 0xfe, 0xcd, 0xa0, 0x90, 0x1, 0x37, 0x10, 0x6e, 0x5, 0xdc, 0x52, 0xb8, 0x33, 0x0, 0xcc, 0x7, 0x26, 0xff, 0x1f, 0x38, 0x23, 0x97, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; +static const unsigned char space_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x6, 0x0, 0x0, 0x0, 0xc4, 0xf, 0xbe, 0x8b, 0x0, 0x0, 0x0, 0x6, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xa0, 0xbd, 0xa7, 0x93, 0x0, 0x0, 0x0, 0x2f, 0x49, 0x44, 0x41, 0x54, 0x18, 0x95, 0x63, 0x60, 0xa0, 0x2a, 0xf8, 0xff, 0xff, 0xbf, 0xe0, 0xff, 0xff, 0xff, 0x5, 0x91, 0xc5, 0x58, 0x90, 0x24, 0x85, 0x18, 0x18, 0x18, 0x14, 0xa0, 0x6c, 0x6, 0x46, 0x46, 0xc6, 0xf7, 0xc, 0xc, 0xc, 0xc, 0x4c, 0x14, 0x5b, 0x41, 0x39, 0x0, 0x0, 0x71, 0x1a, 0x13, 0x5d, 0x87, 0xfe, 0x9e, 0x7d, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; + static const unsigned char spinbox_updown_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x59, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x11, 0xdc, 0x4f, 0x7f, 0x98, 0x86, 0x47, 0xfa, 0x81, 0xe5, 0x83, 0x1f, 0xf, 0x7e, 0x3d, 0xb2, 0xc5, 0xa5, 0x5b, 0xe2, 0xc1, 0x93, 0x7, 0xff, 0x81, 0xf0, 0xf9, 0x63, 0x69, 0x2c, 0xd2, 0x67, 0x58, 0xef, 0x1f, 0x2, 0x4a, 0x42, 0xe0, 0xf1, 0xdb, 0xec, 0x98, 0xfa, 0x67, 0x2, 0x25, 0xe0, 0xf0, 0xe1, 0x2, 0x86, 0x41, 0x7, 0x30, 0x1d, 0x39, 0x3, 0xbf, 0x37, 0x8f, 0xdd, 0x66, 0x27, 0x29, 0xa0, 0x10, 0x4a, 0x2c, 0xa0, 0x41, 0x8d, 0x1b, 0x3c, 0x4c, 0x3, 0x46, 0x16, 0x69, 0x0, 0x0, 0x87, 0x2a, 0x58, 0xb5, 0x18, 0xe9, 0x80, 0x2c, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp index 17609ed505..52dfffda5b 100644 --- a/scene/resources/environment.cpp +++ b/scene/resources/environment.cpp @@ -1294,8 +1294,8 @@ void Environment::_bind_methods() { Environment::Environment() : bg_mode(BG_CLEAR_COLOR), tone_mapper(TONE_MAPPER_LINEAR), - ssao_blur(SSAO_BLUR_DISABLED), - ssao_quality(SSAO_QUALITY_LOW), + ssao_blur(SSAO_BLUR_3x3), + ssao_quality(SSAO_QUALITY_MEDIUM), glow_blend_mode(GLOW_BLEND_MODE_ADDITIVE), dof_blur_far_quality(DOF_BLUR_QUALITY_LOW), dof_blur_near_quality(DOF_BLUR_QUALITY_LOW) { @@ -1346,7 +1346,7 @@ Environment::Environment() : ssao_ao_channel_affect = 0.0; ssao_blur = SSAO_BLUR_3x3; set_ssao_edge_sharpness(4); - set_ssao_quality(SSAO_QUALITY_LOW); + set_ssao_quality(SSAO_QUALITY_MEDIUM); glow_enabled = false; glow_levels = (1 << 2) | (1 << 4); diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index 128db3f109..627397f0ab 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -530,7 +530,13 @@ Size2 Font::get_wordwrap_string_size(const String &p_string, float p_width) cons void BitmapFont::set_fallback(const Ref<BitmapFont> &p_fallback) { - ERR_FAIL_COND(p_fallback == this); + for (Ref<BitmapFont> fallback_child = p_fallback; fallback_child != NULL; fallback_child = fallback_child->get_fallback()) { + if (fallback_child == this) { + ERR_EXPLAIN("Can't set as fallback one of its parents to prevent crashes due to recursive loop."); + ERR_FAIL_COND(fallback_child == this); + } + } + fallback = p_fallback; } diff --git a/scene/resources/height_map_shape.cpp b/scene/resources/height_map_shape.cpp index 32e9c527ef..f763700d52 100644 --- a/scene/resources/height_map_shape.cpp +++ b/scene/resources/height_map_shape.cpp @@ -34,35 +34,43 @@ Vector<Vector3> HeightMapShape::_gen_debug_mesh_lines() { Vector<Vector3> points; - // This will be slow for large maps... - // also we'll have to figure out how well bullet centers this shape... + if ((map_width != 0) && (map_depth != 0)) { - Vector2 size(map_width - 1, map_depth - 1); - Vector2 start = size * -0.5; - int offset = 0; + // This will be slow for large maps... + // also we'll have to figure out how well bullet centers this shape... - PoolRealArray::Read r = map_data.read(); + Vector2 size(map_width - 1, map_depth - 1); + Vector2 start = size * -0.5; - for (int d = 0; d < map_depth; d++) { - Vector3 height(start.x, 0.0, start.y); + PoolRealArray::Read r = map_data.read(); - for (int w = 0; w < map_width; w++) { - height.y = r[offset++]; + // reserve some memory for our points.. + points.resize(((map_width - 1) * map_depth * 2) + (map_width * (map_depth - 1) * 2)); - if (w != map_width - 1) { - points.push_back(height); - points.push_back(Vector3(height.x + 1.0, r[offset], height.z)); - } + // now set our points + int r_offset = 0; + int w_offset = 0; + for (int d = 0; d < map_depth; d++) { + Vector3 height(start.x, 0.0, start.y); + + for (int w = 0; w < map_width; w++) { + height.y = r[r_offset++]; + + if (w != map_width - 1) { + points.write[w_offset++] = height; + points.write[w_offset++] = Vector3(height.x + 1.0, r[r_offset], height.z); + } - if (d != map_depth - 1) { - points.push_back(height); - points.push_back(Vector3(height.x, r[offset + map_width - 1], height.z + 1.0)); + if (d != map_depth - 1) { + points.write[w_offset++] = height; + points.write[w_offset++] = Vector3(height.x, r[r_offset + map_width - 1], height.z + 1.0); + } + + height.x += 1.0; } - height.x += 1.0; + start.y += 1.0; } - - start.y += 1.0; } return points; @@ -77,6 +85,7 @@ void HeightMapShape::_update_shape() { d["min_height"] = min_height; d["max_height"] = max_height; PhysicsServer::get_singleton()->shape_set_data(get_shape(), d); + Shape::_update_shape(); } void HeightMapShape::set_map_width(int p_new) { diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 766c7bbd9f..ada0ac07a3 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -38,7 +38,12 @@ void Material::set_next_pass(const Ref<Material> &p_pass) { - ERR_FAIL_COND(p_pass == this); + for (Ref<Material> pass_child = p_pass; pass_child != NULL; pass_child = pass_child->get_next_pass()) { + if (pass_child == this) { + ERR_EXPLAIN("Can't set as next_pass one of its parents to prevent crashes due to recursive loop."); + ERR_FAIL_COND(pass_child == this); + } + } if (next_pass == p_pass) return; @@ -468,6 +473,9 @@ void SpatialMaterial::_update_shader() { if (flags[FLAG_ENSURE_CORRECT_NORMALS]) { code += ",ensure_correct_normals"; } + if (flags[FLAG_USE_SHADOW_TO_OPACITY]) { + code += ",shadow_to_opacity"; + } code += ";\n"; code += "uniform vec4 albedo : hint_color;\n"; @@ -849,7 +857,7 @@ void SpatialMaterial::_update_shader() { code += "\tALBEDO *= 1.0 - ref_amount;\n"; code += "\tALPHA = 1.0;\n"; - } else if (features[FEATURE_TRANSPARENT] || flags[FLAG_USE_ALPHA_SCISSOR] || (distance_fade == DISTANCE_FADE_PIXEL_ALPHA) || proximity_fade_enabled) { + } else if (features[FEATURE_TRANSPARENT] || flags[FLAG_USE_ALPHA_SCISSOR] || flags[FLAG_USE_SHADOW_TO_OPACITY] || (distance_fade == DISTANCE_FADE_PIXEL_ALPHA) || proximity_fade_enabled) { code += "\tALPHA = albedo.a * albedo_tex.a;\n"; } @@ -1349,7 +1357,7 @@ void SpatialMaterial::set_flag(Flags p_flag, bool p_enabled) { return; flags[p_flag] = p_enabled; - if (p_flag == FLAG_USE_ALPHA_SCISSOR || p_flag == FLAG_UNSHADED) { + if ((p_flag == FLAG_USE_ALPHA_SCISSOR) || (p_flag == FLAG_UNSHADED) || (p_flag == FLAG_USE_SHADOW_TO_OPACITY)) { _change_notify(); } _queue_shader_change(); @@ -2060,6 +2068,7 @@ void SpatialMaterial::_bind_methods() { ADD_GROUP("Flags", "flags_"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_transparent"), "set_feature", "get_feature", FEATURE_TRANSPARENT); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_use_shadow_to_opacity"), "set_flag", "get_flag", FLAG_USE_SHADOW_TO_OPACITY); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_unshaded"), "set_flag", "get_flag", FLAG_UNSHADED); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_vertex_lighting"), "set_flag", "get_flag", FLAG_USE_VERTEX_LIGHTING); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_no_depth_test"), "set_flag", "get_flag", FLAG_DISABLE_DEPTH_TEST); @@ -2266,6 +2275,7 @@ void SpatialMaterial::_bind_methods() { BIND_ENUM_CONSTANT(FLAG_DONT_RECEIVE_SHADOWS); BIND_ENUM_CONSTANT(FLAG_DISABLE_AMBIENT_LIGHT); BIND_ENUM_CONSTANT(FLAG_ENSURE_CORRECT_NORMALS); + BIND_ENUM_CONSTANT(FLAG_USE_SHADOW_TO_OPACITY); BIND_ENUM_CONSTANT(FLAG_MAX); BIND_ENUM_CONSTANT(DIFFUSE_BURLEY); diff --git a/scene/resources/material.h b/scene/resources/material.h index 1f7fc267af..29b45f769b 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -196,6 +196,7 @@ public: FLAG_DONT_RECEIVE_SHADOWS, FLAG_ENSURE_CORRECT_NORMALS, FLAG_DISABLE_AMBIENT_LIGHT, + FLAG_USE_SHADOW_TO_OPACITY, FLAG_MAX }; diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index 67639858ce..6443b44bb6 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -489,6 +489,7 @@ void Mesh::_bind_methods() { ClassDB::bind_method(D_METHOD("get_surface_count"), &Mesh::get_surface_count); ClassDB::bind_method(D_METHOD("surface_get_arrays", "surf_idx"), &Mesh::surface_get_arrays); ClassDB::bind_method(D_METHOD("surface_get_blend_shape_arrays", "surf_idx"), &Mesh::surface_get_blend_shape_arrays); + ClassDB::bind_method(D_METHOD("surface_set_material", "surf_idx", "material"), &Mesh::surface_set_material); ClassDB::bind_method(D_METHOD("surface_get_material", "surf_idx"), &Mesh::surface_get_material); BIND_ENUM_CONSTANT(PRIMITIVE_POINTS); @@ -1305,7 +1306,6 @@ void ArrayMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("surface_get_array_index_len", "surf_idx"), &ArrayMesh::surface_get_array_index_len); ClassDB::bind_method(D_METHOD("surface_get_format", "surf_idx"), &ArrayMesh::surface_get_format); ClassDB::bind_method(D_METHOD("surface_get_primitive_type", "surf_idx"), &ArrayMesh::surface_get_primitive_type); - ClassDB::bind_method(D_METHOD("surface_set_material", "surf_idx", "material"), &ArrayMesh::surface_set_material); ClassDB::bind_method(D_METHOD("surface_find_by_name", "name"), &ArrayMesh::surface_find_by_name); ClassDB::bind_method(D_METHOD("surface_set_name", "surf_idx", "name"), &ArrayMesh::surface_set_name); ClassDB::bind_method(D_METHOD("surface_get_name", "surf_idx"), &ArrayMesh::surface_get_name); diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h index 1457d283bd..b38791b9a6 100644 --- a/scene/resources/mesh.h +++ b/scene/resources/mesh.h @@ -128,6 +128,7 @@ public: virtual Array surface_get_blend_shape_arrays(int p_surface) const = 0; virtual uint32_t surface_get_format(int p_idx) const = 0; virtual PrimitiveType surface_get_primitive_type(int p_idx) const = 0; + virtual void surface_set_material(int p_idx, const Ref<Material> &p_material) = 0; virtual Ref<Material> surface_get_material(int p_idx) const = 0; virtual int get_blend_shape_count() const = 0; virtual StringName get_blend_shape_name(int p_index) const = 0; @@ -215,8 +216,8 @@ public: PrimitiveType surface_get_primitive_type(int p_idx) const; bool surface_is_alpha_sorting_enabled(int p_idx) const; - void surface_set_material(int p_idx, const Ref<Material> &p_material); - Ref<Material> surface_get_material(int p_idx) const; + virtual void surface_set_material(int p_idx, const Ref<Material> &p_material); + virtual Ref<Material> surface_get_material(int p_idx) const; int surface_find_by_name(const String &p_name) const; void surface_set_name(int p_idx, const String &p_name); diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index 2c6f30f429..99286668ce 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -92,8 +92,8 @@ Node *SceneState::instance(GenEditState p_edit_state) const { if (i > 0) { - ERR_EXPLAIN(vformat("Invalid scene: node %s does not specify its parent node.", snames[n.name])) - ERR_FAIL_COND_V(n.parent == -1, NULL) + ERR_EXPLAIN(vformat("Invalid scene: node %s does not specify its parent node.", snames[n.name])); + ERR_FAIL_COND_V(n.parent == -1, NULL); NODE_FROM_ID(nparent, n.parent); #ifdef DEBUG_ENABLED if (!nparent && (n.parent & FLAG_ID_IS_PATH)) { diff --git a/scene/resources/particles_material.cpp b/scene/resources/particles_material.cpp index ef67e6ea80..758475b75e 100644 --- a/scene/resources/particles_material.cpp +++ b/scene/resources/particles_material.cpp @@ -1186,6 +1186,7 @@ void ParticlesMaterial::_bind_methods() { BIND_ENUM_CONSTANT(FLAG_ALIGN_Y_TO_VELOCITY); BIND_ENUM_CONSTANT(FLAG_ROTATE_Y); + BIND_ENUM_CONSTANT(FLAG_DISABLE_Z); BIND_ENUM_CONSTANT(FLAG_MAX); BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINT); diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp index db58fe7823..04d13c8869 100644 --- a/scene/resources/primitive_meshes.cpp +++ b/scene/resources/primitive_meshes.cpp @@ -157,6 +157,12 @@ Mesh::PrimitiveType PrimitiveMesh::surface_get_primitive_type(int p_idx) const { return primitive_type; } +void PrimitiveMesh::surface_set_material(int p_idx, const Ref<Material> &p_material) { + ERR_FAIL_INDEX(p_idx, 1); + + set_material(p_material); +} + Ref<Material> PrimitiveMesh::surface_get_material(int p_idx) const { ERR_FAIL_INDEX_V(p_idx, 1, NULL); diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h index 88a26801b7..0045a48736 100644 --- a/scene/resources/primitive_meshes.h +++ b/scene/resources/primitive_meshes.h @@ -72,6 +72,7 @@ public: virtual Array surface_get_blend_shape_arrays(int p_surface) const; virtual uint32_t surface_get_format(int p_idx) const; virtual Mesh::PrimitiveType surface_get_primitive_type(int p_idx) const; + virtual void surface_set_material(int p_idx, const Ref<Material> &p_material); virtual Ref<Material> surface_get_material(int p_idx) const; virtual int get_blend_shape_count() const; virtual StringName get_blend_shape_name(int p_index) const; diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index e9f90fc85f..0921c0dae9 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -447,9 +447,8 @@ Error ResourceInteractiveLoaderText::poll() { resource_cache.push_back(res); #ifdef TOOLS_ENABLED //remember ID for saving - res->set_id_for_path(local_path,index); + res->set_id_for_path(local_path, index); #endif - } ExtResource er; @@ -1545,9 +1544,6 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r } { - - - } #ifdef TOOLS_ENABLED @@ -1569,7 +1565,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r } int attempt = 1; //start from one, more readable format - while(cached_ids_found.has(attempt)) { + while (cached_ids_found.has(attempt)) { attempt++; } @@ -1577,7 +1573,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r E->get() = attempt; //update also in resource Ref<Resource> res = E->key(); - res->set_id_for_path(local_path,attempt); + res->set_id_for_path(local_path, attempt); } #else //make sure to start from one, as it makes format more readable @@ -1598,7 +1594,6 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r sorted_er.sort(); - for (int i = 0; i < sorted_er.size(); i++) { String p = sorted_er[i].resource->get_path(); @@ -1717,15 +1712,15 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r Vector<StringName> groups = state->get_node_groups(i); String header = "[node"; - header += " name=\"" + String(name) + "\""; + header += " name=\"" + String(name).c_escape() + "\""; if (type != StringName()) { header += " type=\"" + String(type) + "\""; } if (path != NodePath()) { - header += " parent=\"" + String(path.simplified()) + "\""; + header += " parent=\"" + String(path.simplified()).c_escape() + "\""; } if (owner != NodePath() && owner != NodePath(".")) { - header += " owner=\"" + String(owner.simplified()) + "\""; + header += " owner=\"" + String(owner.simplified()).c_escape() + "\""; } if (index >= 0) { header += " index=\"" + itos(index) + "\""; diff --git a/scene/resources/resource_format_text.h b/scene/resources/resource_format_text.h index ab6f94986c..06c841229b 100644 --- a/scene/resources/resource_format_text.h +++ b/scene/resources/resource_format_text.h @@ -172,10 +172,9 @@ class ResourceFormatSaverTextInstance { struct ResourceSort { RES resource; int index; - bool operator<(const ResourceSort& p_right) const { + bool operator<(const ResourceSort &p_right) const { return index < p_right.index; } - }; void _find_resources(const Variant &p_variant, bool p_main = false); diff --git a/scene/resources/sky.cpp b/scene/resources/sky.cpp index 48945d4e63..292fdcdbd2 100644 --- a/scene/resources/sky.cpp +++ b/scene/resources/sky.cpp @@ -62,7 +62,7 @@ void Sky::_bind_methods() { } Sky::Sky() { - radiance_size = RADIANCE_SIZE_512; + radiance_size = RADIANCE_SIZE_128; } ///////////////////////////////////////// diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp index 3ba43006a3..496b1b2bdc 100644 --- a/scene/resources/surface_tool.cpp +++ b/scene/resources/surface_tool.cpp @@ -115,7 +115,7 @@ void SurfaceTool::add_vertex(const Vector3 &p_vertex) { //ensure vertices are the expected amount ERR_FAIL_COND(vtx.weights.size() != vtx.bones.size()); if (vtx.weights.size() < expected_vertices) { - //less than requred, fill + //less than required, fill for (int i = vtx.weights.size(); i < expected_vertices; i++) { vtx.weights.push_back(0); vtx.bones.push_back(0); @@ -769,6 +769,26 @@ void SurfaceTool::create_from(const Ref<Mesh> &p_existing, int p_surface) { material = p_existing->surface_get_material(p_surface); } +void SurfaceTool::create_from_blend_shape(const Ref<Mesh> &p_existing, int p_surface, const String p_blend_shape_name) { + clear(); + primitive = p_existing->surface_get_primitive_type(p_surface); + Array arr = p_existing->surface_get_blend_shape_arrays(p_surface); + Array blend_shape_names; + int32_t shape_idx = -1; + for (int32_t i = 0; i < p_existing->get_blend_shape_count(); i++) { + String name = p_existing->get_blend_shape_name(i); + if (name == p_blend_shape_name) { + shape_idx = i; + break; + } + } + ERR_FAIL_COND(shape_idx == -1); + ERR_FAIL_COND(shape_idx >= arr.size()); + Array mesh = arr[shape_idx]; + ERR_FAIL_COND(mesh.size() != VS::ARRAY_MAX); + _create_list_from_arrays(arr[shape_idx], &vertex_array, &index_array, format); +} + void SurfaceTool::append_from(const Ref<Mesh> &p_existing, int p_surface, const Transform &p_xform) { if (vertex_array.size() == 0) { @@ -1071,8 +1091,10 @@ void SurfaceTool::_bind_methods() { ClassDB::bind_method(D_METHOD("clear"), &SurfaceTool::clear); ClassDB::bind_method(D_METHOD("create_from", "existing", "surface"), &SurfaceTool::create_from); + ClassDB::bind_method(D_METHOD("create_from_blend_shape", "existing", "surface", "blend_shape"), &SurfaceTool::create_from_blend_shape); ClassDB::bind_method(D_METHOD("append_from", "existing", "surface", "transform"), &SurfaceTool::append_from); ClassDB::bind_method(D_METHOD("commit", "existing", "flags"), &SurfaceTool::commit, DEFVAL(Variant()), DEFVAL(Mesh::ARRAY_COMPRESS_DEFAULT)); + ClassDB::bind_method(D_METHOD("commit_to_arrays"), &SurfaceTool::commit_to_arrays); } SurfaceTool::SurfaceTool() { diff --git a/scene/resources/surface_tool.h b/scene/resources/surface_tool.h index c55cade813..c4c71dca13 100644 --- a/scene/resources/surface_tool.h +++ b/scene/resources/surface_tool.h @@ -136,6 +136,7 @@ public: static Vector<Vertex> create_vertex_array_from_triangle_arrays(const Array &p_arrays); Array commit_to_arrays(); void create_from(const Ref<Mesh> &p_existing, int p_surface); + void create_from_blend_shape(const Ref<Mesh> &p_existing, int p_surface, const String p_blend_shape_name); void append_from(const Ref<Mesh> &p_existing, int p_surface, const Transform &p_xform); Ref<ArrayMesh> commit(const Ref<ArrayMesh> &p_existing = Ref<ArrayMesh>(), uint32_t p_flags = Mesh::ARRAY_COMPRESS_DEFAULT); diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index a5eb950c36..503949fd60 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -111,10 +111,12 @@ void ImageTexture::reload_from_file() { Ref<Image> img; img.instance(); - Error err = ImageLoader::load_image(path, img); - ERR_FAIL_COND(err != OK); - - create_from_image(img, flags); + if (ImageLoader::load_image(path, img) == OK) { + create_from_image(img, flags); + } else { + Resource::reload_from_file(); + _change_notify(); + } } bool ImageTexture::_set(const StringName &p_name, const Variant &p_value) { @@ -230,7 +232,7 @@ Image::Format ImageTexture::get_format() const { #ifndef DISABLE_DEPRECATED Error ImageTexture::load(const String &p_path) { - WARN_DEPRECATED + WARN_DEPRECATED; Ref<Image> img; img.instance(); Error err = img->load(p_path); @@ -638,7 +640,7 @@ Error StreamTexture::_load_data(const String &p_path, int &tw, int &th, int &tw_ bool mipmaps = df & FORMAT_BIT_HAS_MIPMAPS; if (!mipmaps) { - int size = Image::get_image_data_size(tw, th, format, 0); + int size = Image::get_image_data_size(tw, th, format, false); PoolVector<uint8_t> img_data; img_data.resize(size); @@ -660,7 +662,6 @@ Error StreamTexture::_load_data(const String &p_path, int &tw, int &th, int &tw_ int mipmaps2 = Image::get_image_required_mipmaps(tw, th, format); int total_size = Image::get_image_data_size(tw, th, format, true); int idx = 0; - int ofs = 0; while (mipmaps2 > 1 && p_size_limit > 0 && (sw > p_size_limit || sh > p_size_limit)) { @@ -670,9 +671,7 @@ Error StreamTexture::_load_data(const String &p_path, int &tw, int &th, int &tw_ idx++; } - if (idx > 0) { - ofs = Image::get_image_data_size(tw, th, format, idx - 1); - } + int ofs = Image::get_image_mipmap_offset(tw, th, format, idx); if (total_size - ofs <= 0) { memdelete(f); diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index 5b5968c10f..d09fac47f0 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -319,6 +319,7 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const { 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::ARRAY, pre + "autotile/z_index_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); } p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "occluder_offset")); @@ -646,6 +647,36 @@ Vector2 TileSet::autotile_get_subtile_for_bitmask(int p_id, uint16_t p_bitmask, } } +Vector2 TileSet::atlastile_get_subtile_by_priority(int p_id, const Node *p_tilemap_node, const Vector2 &p_tile_location) { + + ERR_FAIL_COND_V(!tile_map.has(p_id), Vector2()); + //First try to forward selection to script + if (get_script_instance() != NULL) { + if (get_script_instance()->has_method("_forward_atlas_subtile_selection")) { + Variant ret = get_script_instance()->call("_forward_atlas_subtile_selection", p_id, p_tilemap_node, p_tile_location); + if (ret.get_type() == Variant::VECTOR2) { + return ret; + } + } + } + + Vector2 coord = tile_get_region(p_id).size / autotile_get_size(p_id); + + List<Vector2> coords; + for (int x = 0; x < coord.x; x++) { + for (int y = 0; y < coord.y; y++) { + for (int i = 0; i < autotile_get_subtile_priority(p_id, Vector2(x, y)); i++) { + coords.push_back(Vector2(x, y)); + } + } + } + if (coords.size() == 0) { + return autotile_get_icon_coordinate(p_id); + } else { + return coords[Math::random(0, (int)coords.size())]; + } +} + void TileSet::tile_set_name(int p_id, const String &p_name) { ERR_FAIL_COND(!tile_map.has(p_id)); @@ -1141,6 +1172,7 @@ void TileSet::_bind_methods() { BIND_VMETHOD(MethodInfo(Variant::BOOL, "_is_tile_bound", PropertyInfo(Variant::INT, "drawn_id"), PropertyInfo(Variant::INT, "neighbor_id"))); 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_VMETHOD(MethodInfo(Variant::VECTOR2, "_forward_atlas_subtile_selection", PropertyInfo(Variant::INT, "atlastile_id"), PropertyInfo(Variant::OBJECT, "tilemap", PROPERTY_HINT_NONE, "TileMap"), PropertyInfo(Variant::VECTOR2, "tile_location"))); BIND_ENUM_CONSTANT(BITMASK_2X2); BIND_ENUM_CONSTANT(BITMASK_3X3_MINIMAL); diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index fb84cee218..5fc22b9fc6 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -195,6 +195,7 @@ public: uint32_t autotile_get_bitmask(int p_id, Vector2 p_coord); const Map<Vector2, uint32_t> &autotile_get_bitmask_map(int p_id); Vector2 autotile_get_subtile_for_bitmask(int p_id, uint16_t p_bitmask, const Node *p_tilemap_node = NULL, const Vector2 &p_tile_location = Vector2()); + Vector2 atlastile_get_subtile_by_priority(int p_id, const Node *p_tilemap_node = NULL, const Vector2 &p_tile_location = Vector2()); void tile_set_shape(int p_id, int p_shape_id, const Ref<Shape2D> &p_shape); Ref<Shape2D> tile_get_shape(int p_id, int p_shape_id) const; diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index b8f21948c3..dd595d9ff8 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "visual_shader.h" + #include "core/vmap.h" #include "servers/visual/shader_types.h" @@ -159,6 +160,7 @@ Vector2 VisualShader::get_node_position(Type p_type, int p_id) const { ERR_FAIL_COND_V(!g->nodes.has(p_id), Vector2()); return g->nodes[p_id].position; } + Ref<VisualShaderNode> VisualShader::get_node(Type p_type, int p_id) const { ERR_FAIL_INDEX_V(p_type, TYPE_MAX, Ref<VisualShaderNode>()); const Graph *g = &graph[p_type]; @@ -269,6 +271,18 @@ bool VisualShader::can_connect_nodes(Type p_type, int p_from_node, int p_from_po return true; } +void VisualShader::connect_nodes_forced(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) { + ERR_FAIL_INDEX(p_type, TYPE_MAX); + Graph *g = &graph[p_type]; + Connection c; + c.from_node = p_from_node; + c.from_port = p_from_port; + c.to_node = p_to_node; + c.to_port = p_to_port; + g->connections.push_back(c); + _queue_update(); +} + Error VisualShader::connect_nodes(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) { ERR_FAIL_INDEX_V(p_type, TYPE_MAX, ERR_CANT_CONNECT); Graph *g = &graph[p_type]; @@ -283,7 +297,7 @@ Error VisualShader::connect_nodes(Type p_type, int p_from_node, int p_from_port, if (MAX(0, from_port_type - 2) != (MAX(0, to_port_type - 2))) { ERR_EXPLAIN("Incompatible port types (scalar/vec/bool with transform"); - ERR_FAIL_V(ERR_INVALID_PARAMETER) + ERR_FAIL_V(ERR_INVALID_PARAMETER); return ERR_INVALID_PARAMETER; } @@ -304,6 +318,7 @@ Error VisualShader::connect_nodes(Type p_type, int p_from_node, int p_from_port, _queue_update(); return OK; } + void VisualShader::disconnect_nodes(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) { ERR_FAIL_INDEX(p_type, TYPE_MAX); Graph *g = &graph[p_type]; @@ -334,6 +349,7 @@ Array VisualShader::_get_node_connections(Type p_type) const { return ret; } + void VisualShader::get_node_connections(Type p_type, List<Connection> *r_connections) const { ERR_FAIL_INDEX(p_type, TYPE_MAX); const Graph *g = &graph[p_type]; @@ -477,6 +493,54 @@ String VisualShader::generate_preview_shader(Type p_type, int p_node, int p_port #define IS_SYMBOL_CHAR(m_d) (((m_d) >= 'a' && (m_d) <= 'z') || ((m_d) >= 'A' && (m_d) <= 'Z') || ((m_d) >= '0' && (m_d) <= '9') || (m_d) == '_') +String VisualShader::validate_port_name(const String &p_name, const List<String> &p_input_ports, const List<String> &p_output_ports) const { + String name = p_name; + + while (name.length() && !IS_INITIAL_CHAR(name[0])) { + name = name.substr(1, name.length() - 1); + } + + if (name != String()) { + + String valid_name; + + for (int i = 0; i < name.length(); i++) { + if (IS_SYMBOL_CHAR(name[i])) { + valid_name += String::chr(name[i]); + } else if (name[i] == ' ') { + valid_name += "_"; + } + } + + name = valid_name; + } + + String valid_name = name; + bool is_equal = false; + + for (int i = 0; i < p_input_ports.size(); i++) { + if (name == p_input_ports[i]) { + is_equal = true; + break; + } + } + + if (!is_equal) { + for (int i = 0; i < p_output_ports.size(); i++) { + if (name == p_output_ports[i]) { + is_equal = true; + break; + } + } + } + + if (is_equal) { + name = ""; + } + + return name; +} + String VisualShader::validate_uniform_name(const String &p_name, const Ref<VisualShaderNodeUniform> &p_uniform) const { String name = p_name; //validate name first @@ -596,7 +660,7 @@ bool VisualShader::_set(const StringName &p_name, const Variant &p_value) { Vector<int> conns = p_value; if (conns.size() % 4 == 0) { for (int i = 0; i < conns.size(); i += 4) { - connect_nodes(type, conns[i + 0], conns[i + 1], conns[i + 2], conns[i + 3]); + connect_nodes_forced(type, conns[i + 0], conns[i + 1], conns[i + 2], conns[i + 3]); } } return true; @@ -611,6 +675,18 @@ bool VisualShader::_set(const StringName &p_name, const Variant &p_value) { } else if (what == "position") { set_node_position(type, id, p_value); return true; + } else if (what == "size") { + ((VisualShaderNodeGroupBase *)get_node(type, id).ptr())->set_size(p_value); + return true; + } else if (what == "input_ports") { + ((VisualShaderNodeGroupBase *)get_node(type, id).ptr())->set_inputs(p_value); + return true; + } else if (what == "output_ports") { + ((VisualShaderNodeGroupBase *)get_node(type, id).ptr())->set_outputs(p_value); + return true; + } else if (what == "expression") { + ((VisualShaderNodeExpression *)get_node(type, id).ptr())->set_expression(p_value); + return true; } } return false; @@ -668,6 +744,18 @@ bool VisualShader::_get(const StringName &p_name, Variant &r_ret) const { } else if (what == "position") { r_ret = get_node_position(type, id); return true; + } else if (what == "size") { + r_ret = ((VisualShaderNodeGroupBase *)get_node(type, id).ptr())->get_size(); + return true; + } else if (what == "input_ports") { + r_ret = ((VisualShaderNodeGroupBase *)get_node(type, id).ptr())->get_inputs(); + return true; + } else if (what == "output_ports") { + r_ret = ((VisualShaderNodeGroupBase *)get_node(type, id).ptr())->get_outputs(); + return true; + } else if (what == "expression") { + r_ret = ((VisualShaderNodeExpression *)get_node(type, id).ptr())->get_expression(); + return true; } } return false; @@ -727,6 +815,15 @@ void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::OBJECT, prop_name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "VisualShaderNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); } p_list->push_back(PropertyInfo(Variant::VECTOR2, prop_name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + + if (Object::cast_to<VisualShaderNodeGroupBase>(E->get().node.ptr()) != NULL) { + p_list->push_back(PropertyInfo(Variant::VECTOR2, prop_name + "/size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/input_ports", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/output_ports", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + } + if (Object::cast_to<VisualShaderNodeExpression>(E->get().node.ptr()) != NULL) { + p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/expression", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + } } p_list->push_back(PropertyInfo(Variant::POOL_INT_ARRAY, "nodes/" + String(type_string[i]) + "/connections", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); } @@ -993,26 +1090,33 @@ void VisualShader::_input_type_changed(Type p_type, int p_id) { } } +void VisualShader::rebuild() { + dirty = true; + _update_shader(); +} + void VisualShader::_bind_methods() { ClassDB::bind_method(D_METHOD("set_mode", "mode"), &VisualShader::set_mode); ClassDB::bind_method(D_METHOD("add_node", "type", "node", "position", "id"), &VisualShader::add_node); - ClassDB::bind_method(D_METHOD("set_node_position", "type", "id", "position"), &VisualShader::set_node_position); - ClassDB::bind_method(D_METHOD("get_node", "type", "id"), &VisualShader::get_node); + + ClassDB::bind_method(D_METHOD("set_node_position", "type", "id", "position"), &VisualShader::set_node_position); ClassDB::bind_method(D_METHOD("get_node_position", "type", "id"), &VisualShader::get_node_position); ClassDB::bind_method(D_METHOD("get_node_list", "type"), &VisualShader::get_node_list); ClassDB::bind_method(D_METHOD("get_valid_node_id", "type"), &VisualShader::get_valid_node_id); ClassDB::bind_method(D_METHOD("remove_node", "type", "id"), &VisualShader::remove_node); + ClassDB::bind_method(D_METHOD("rebuild"), &VisualShader::rebuild); ClassDB::bind_method(D_METHOD("is_node_connection", "type", "from_node", "from_port", "to_node", "to_port"), &VisualShader::is_node_connection); ClassDB::bind_method(D_METHOD("can_connect_nodes", "type", "from_node", "from_port", "to_node", "to_port"), &VisualShader::is_node_connection); ClassDB::bind_method(D_METHOD("connect_nodes", "type", "from_node", "from_port", "to_node", "to_port"), &VisualShader::connect_nodes); ClassDB::bind_method(D_METHOD("disconnect_nodes", "type", "from_node", "from_port", "to_node", "to_port"), &VisualShader::disconnect_nodes); + ClassDB::bind_method(D_METHOD("connect_nodes_forced", "type", "from_node", "from_port", "to_node", "to_port"), &VisualShader::connect_nodes_forced); ClassDB::bind_method(D_METHOD("get_node_connections", "type"), &VisualShader::_get_node_connections); @@ -1627,3 +1731,555 @@ void VisualShaderNodeUniform::_bind_methods() { VisualShaderNodeUniform::VisualShaderNodeUniform() { } + +////////////// GroupBase + +String VisualShaderNodeGroupBase::get_caption() const { + return "Group"; +} + +void VisualShaderNodeGroupBase::set_size(const Vector2 &p_size) { + size = p_size; +} + +Vector2 VisualShaderNodeGroupBase::get_size() const { + return size; +} + +void VisualShaderNodeGroupBase::set_inputs(const String &p_inputs) { + + if (inputs == p_inputs) + return; + + clear_input_ports(); + + inputs = p_inputs; + + Vector<String> input_strings = inputs.split(";", false); + + int input_port_count = input_strings.size(); + + for (int i = 0; i < input_port_count; i++) { + + Vector<String> arr = input_strings[i].split(","); + + int port_idx = arr[0].to_int(); + int port_type = arr[1].to_int(); + String port_name = arr[2]; + + Port port; + port.type = (PortType)port_type; + port.name = port_name; + input_ports[port_idx] = port; + } +} + +String VisualShaderNodeGroupBase::get_inputs() const { + return inputs; +} + +void VisualShaderNodeGroupBase::set_outputs(const String &p_outputs) { + + if (outputs == p_outputs) + return; + + clear_output_ports(); + + outputs = p_outputs; + + Vector<String> output_strings = outputs.split(";", false); + + int output_port_count = output_strings.size(); + + for (int i = 0; i < output_port_count; i++) { + + Vector<String> arr = output_strings[i].split(","); + + int port_idx = arr[0].to_int(); + int port_type = arr[1].to_int(); + String port_name = arr[2]; + + Port port; + port.type = (PortType)port_type; + port.name = port_name; + output_ports[port_idx] = port; + } +} + +String VisualShaderNodeGroupBase::get_outputs() const { + return outputs; +} + +void VisualShaderNodeGroupBase::add_input_port(int p_id, int p_type, const String &p_name) { + + String str = itos(p_id) + "," + itos(p_type) + "," + p_name + ";"; + Vector<String> inputs_strings = inputs.split(";", false); + int index = 0; + if (p_id < inputs_strings.size()) { + for (int i = 0; i < inputs_strings.size(); i++) { + if (i == p_id) { + inputs = inputs.insert(index, str); + break; + } + index += inputs_strings[i].size(); + } + } else { + inputs += str; + } + + inputs_strings = inputs.split(";", false); + index = 0; + + for (int i = 0; i < inputs_strings.size(); i++) { + int count = 0; + for (int j = 0; j < inputs_strings[i].size(); j++) { + if (inputs_strings[i][j] == ',') { + break; + } + count++; + } + + inputs.erase(index, count); + inputs = inputs.insert(index, itos(i)); + index += inputs_strings[i].size(); + } + + _apply_port_changes(); +} + +void VisualShaderNodeGroupBase::remove_input_port(int p_id) { + + Vector<String> inputs_strings = inputs.split(";", false); + int count = 0; + int index = 0; + for (int i = 0; i < inputs_strings.size(); i++) { + Vector<String> arr = inputs_strings[i].split(","); + if (arr[0].to_int() == p_id) { + count = inputs_strings[i].size(); + break; + } + index += inputs_strings[i].size(); + } + inputs.erase(index, count); + + inputs_strings = inputs.split(";", false); + for (int i = p_id; i < inputs_strings.size(); i++) { + inputs = inputs.replace_first(inputs_strings[i].split(",")[0], itos(i)); + } + + _apply_port_changes(); +} + +int VisualShaderNodeGroupBase::get_input_port_count() const { + return input_ports.size(); +} + +bool VisualShaderNodeGroupBase::has_input_port(int p_id) const { + return input_ports.has(p_id); +} + +void VisualShaderNodeGroupBase::add_output_port(int p_id, int p_type, const String &p_name) { + + String str = itos(p_id) + "," + itos(p_type) + "," + p_name + ";"; + Vector<String> outputs_strings = outputs.split(";", false); + int index = 0; + if (p_id < outputs_strings.size()) { + for (int i = 0; i < outputs_strings.size(); i++) { + if (i == p_id) { + outputs = outputs.insert(index, str); + break; + } + index += outputs_strings[i].size(); + } + } else { + outputs += str; + } + + outputs_strings = outputs.split(";", false); + index = 0; + + for (int i = 0; i < outputs_strings.size(); i++) { + int count = 0; + for (int j = 0; j < outputs_strings[i].size(); j++) { + if (outputs_strings[i][j] == ',') { + break; + } + count++; + } + + outputs.erase(index, count); + outputs = outputs.insert(index, itos(i)); + index += outputs_strings[i].size(); + } + + _apply_port_changes(); +} + +void VisualShaderNodeGroupBase::remove_output_port(int p_id) { + + Vector<String> outputs_strings = outputs.split(";", false); + int count = 0; + int index = 0; + for (int i = 0; i < outputs_strings.size(); i++) { + Vector<String> arr = outputs_strings[i].split(","); + if (arr[0].to_int() == p_id) { + count = outputs_strings[i].size(); + break; + } + index += outputs_strings[i].size(); + } + outputs.erase(index, count); + + outputs_strings = outputs.split(";", false); + for (int i = p_id; i < outputs_strings.size(); i++) { + outputs = outputs.replace_first(outputs_strings[i].split(",")[0], itos(i)); + } + + _apply_port_changes(); +} + +int VisualShaderNodeGroupBase::get_output_port_count() const { + return output_ports.size(); +} + +bool VisualShaderNodeGroupBase::has_output_port(int p_id) const { + return output_ports.has(p_id); +} + +void VisualShaderNodeGroupBase::clear_input_ports() { + input_ports.clear(); +} + +void VisualShaderNodeGroupBase::clear_output_ports() { + output_ports.clear(); +} + +void VisualShaderNodeGroupBase::set_input_port_type(int p_id, int p_type) { + + if (input_ports[p_id].type == p_type) + return; + + Vector<String> inputs_strings = inputs.split(";", false); + int count = 0; + int index = 0; + for (int i = 0; i < inputs_strings.size(); i++) { + Vector<String> arr = inputs_strings[i].split(","); + if (arr[0].to_int() == p_id) { + index += arr[0].size(); + count = arr[1].size() - 1; + break; + } + index += inputs_strings[i].size(); + } + + inputs.erase(index, count); + + inputs = inputs.insert(index, itos(p_type)); + + _apply_port_changes(); +} + +VisualShaderNodeGroupBase::PortType VisualShaderNodeGroupBase::get_input_port_type(int p_id) const { + ERR_FAIL_COND_V(!input_ports.has(p_id), (PortType)0); + return input_ports[p_id].type; +} + +void VisualShaderNodeGroupBase::set_input_port_name(int p_id, const String &p_name) { + + if (input_ports[p_id].name == p_name) + return; + + Vector<String> inputs_strings = inputs.split(";", false); + int count = 0; + int index = 0; + for (int i = 0; i < inputs_strings.size(); i++) { + Vector<String> arr = inputs_strings[i].split(","); + if (arr[0].to_int() == p_id) { + index += arr[0].size() + arr[1].size(); + count = arr[2].size() - 1; + break; + } + index += inputs_strings[i].size(); + } + + inputs.erase(index, count); + + inputs = inputs.insert(index, p_name); + + _apply_port_changes(); +} + +String VisualShaderNodeGroupBase::get_input_port_name(int p_id) const { + ERR_FAIL_COND_V(!input_ports.has(p_id), ""); + return input_ports[p_id].name; +} + +void VisualShaderNodeGroupBase::set_output_port_type(int p_id, int p_type) { + + if (output_ports[p_id].type == p_type) + return; + + Vector<String> output_strings = outputs.split(";", false); + int count = 0; + int index = 0; + for (int i = 0; i < output_strings.size(); i++) { + Vector<String> arr = output_strings[i].split(","); + if (arr[0].to_int() == p_id) { + index += arr[0].size(); + count = arr[1].size() - 1; + break; + } + index += output_strings[i].size(); + } + + outputs.erase(index, count); + + outputs = outputs.insert(index, itos(p_type)); + + _apply_port_changes(); +} + +VisualShaderNodeGroupBase::PortType VisualShaderNodeGroupBase::get_output_port_type(int p_id) const { + ERR_FAIL_COND_V(!output_ports.has(p_id), (PortType)0); + return output_ports[p_id].type; +} + +void VisualShaderNodeGroupBase::set_output_port_name(int p_id, const String &p_name) { + if (output_ports[p_id].name == p_name) + return; + + Vector<String> output_strings = outputs.split(";", false); + int count = 0; + int index = 0; + for (int i = 0; i < output_strings.size(); i++) { + Vector<String> arr = output_strings[i].split(","); + if (arr[0].to_int() == p_id) { + index += arr[0].size() + arr[1].size(); + count = arr[2].size() - 1; + break; + } + index += output_strings[i].size(); + } + + outputs.erase(index, count); + + outputs = outputs.insert(index, p_name); + + _apply_port_changes(); +} + +String VisualShaderNodeGroupBase::get_output_port_name(int p_id) const { + ERR_FAIL_COND_V(!output_ports.has(p_id), ""); + return output_ports[p_id].name; +} + +int VisualShaderNodeGroupBase::get_free_input_port_id() const { + return input_ports.size(); +} + +int VisualShaderNodeGroupBase::get_free_output_port_id() const { + return output_ports.size(); +} + +void VisualShaderNodeGroupBase::set_control(Control *p_control, int p_index) { + controls[p_index] = p_control; +} + +Control *VisualShaderNodeGroupBase::get_control(int p_index) { + ERR_FAIL_COND_V(!controls.has(p_index), NULL); + return controls[p_index]; +} + +void VisualShaderNodeGroupBase::_apply_port_changes() { + + Vector<String> inputs_strings = inputs.split(";", false); + Vector<String> outputs_strings = outputs.split(";", false); + + clear_input_ports(); + clear_output_ports(); + + for (int i = 0; i < inputs_strings.size(); i++) { + Vector<String> arr = inputs_strings[i].split(","); + Port port; + port.type = (PortType)arr[1].to_int(); + port.name = arr[2]; + input_ports[i] = port; + } + for (int i = 0; i < outputs_strings.size(); i++) { + Vector<String> arr = outputs_strings[i].split(","); + Port port; + port.type = (PortType)arr[1].to_int(); + port.name = arr[2]; + output_ports[i] = port; + } +} + +void VisualShaderNodeGroupBase::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_size", "size"), &VisualShaderNodeGroupBase::set_size); + ClassDB::bind_method(D_METHOD("get_size"), &VisualShaderNodeGroupBase::get_size); + + ClassDB::bind_method(D_METHOD("set_inputs", "inputs"), &VisualShaderNodeGroupBase::set_inputs); + ClassDB::bind_method(D_METHOD("get_inputs"), &VisualShaderNodeGroupBase::get_inputs); + + ClassDB::bind_method(D_METHOD("set_outputs", "outputs"), &VisualShaderNodeGroupBase::set_outputs); + ClassDB::bind_method(D_METHOD("get_outputs"), &VisualShaderNodeGroupBase::get_outputs); + + ClassDB::bind_method(D_METHOD("add_input_port", "id", "type", "name"), &VisualShaderNodeGroupBase::add_input_port); + ClassDB::bind_method(D_METHOD("remove_input_port", "id"), &VisualShaderNodeGroupBase::remove_input_port); + ClassDB::bind_method(D_METHOD("get_input_port_count"), &VisualShaderNodeGroupBase::get_input_port_count); + ClassDB::bind_method(D_METHOD("has_input_port", "id"), &VisualShaderNodeGroupBase::has_input_port); + ClassDB::bind_method(D_METHOD("clear_input_ports"), &VisualShaderNodeGroupBase::clear_input_ports); + + ClassDB::bind_method(D_METHOD("add_output_port", "id", "type", "name"), &VisualShaderNodeGroupBase::add_output_port); + ClassDB::bind_method(D_METHOD("remove_output_port", "id"), &VisualShaderNodeGroupBase::remove_output_port); + ClassDB::bind_method(D_METHOD("get_output_port_count"), &VisualShaderNodeGroupBase::get_output_port_count); + ClassDB::bind_method(D_METHOD("has_output_port", "id"), &VisualShaderNodeGroupBase::has_output_port); + ClassDB::bind_method(D_METHOD("clear_output_ports"), &VisualShaderNodeGroupBase::clear_output_ports); + + ClassDB::bind_method(D_METHOD("set_input_port_name"), &VisualShaderNodeGroupBase::set_input_port_name); + ClassDB::bind_method(D_METHOD("set_input_port_type"), &VisualShaderNodeGroupBase::set_input_port_type); + ClassDB::bind_method(D_METHOD("set_output_port_name"), &VisualShaderNodeGroupBase::set_output_port_name); + ClassDB::bind_method(D_METHOD("set_output_port_type"), &VisualShaderNodeGroupBase::set_output_port_type); + + ClassDB::bind_method(D_METHOD("get_free_input_port_id"), &VisualShaderNodeGroupBase::get_free_input_port_id); + ClassDB::bind_method(D_METHOD("get_free_output_port_id"), &VisualShaderNodeGroupBase::get_free_output_port_id); + + ClassDB::bind_method(D_METHOD("set_control", "control", "index"), &VisualShaderNodeGroupBase::set_control); + ClassDB::bind_method(D_METHOD("get_control", "index"), &VisualShaderNodeGroupBase::get_control); +} + +String VisualShaderNodeGroupBase::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { + return ""; +} + +VisualShaderNodeGroupBase::VisualShaderNodeGroupBase() { + size = Size2(0, 0); + inputs = ""; + outputs = ""; +} + +////////////// Expression + +String VisualShaderNodeExpression::get_caption() const { + return "Expression"; +} + +void VisualShaderNodeExpression::set_expression(const String &p_expression) { + expression = p_expression; +} + +void VisualShaderNodeExpression::build() { + emit_changed(); +} + +String VisualShaderNodeExpression::get_expression() const { + return expression; +} + +String VisualShaderNodeExpression::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { + + String _expression = expression; + + _expression = _expression.insert(0, "\n"); + _expression = _expression.replace("\n", "\n\t\t"); + + static Vector<String> pre_symbols; + if (pre_symbols.empty()) { + pre_symbols.push_back("\t"); + pre_symbols.push_back("{"); + pre_symbols.push_back("["); + pre_symbols.push_back("("); + pre_symbols.push_back(" "); + pre_symbols.push_back("-"); + pre_symbols.push_back("*"); + pre_symbols.push_back("/"); + pre_symbols.push_back("+"); + pre_symbols.push_back("="); + pre_symbols.push_back("&"); + pre_symbols.push_back("|"); + pre_symbols.push_back("!"); + } + + static Vector<String> post_symbols; + if (post_symbols.empty()) { + post_symbols.push_back("\0"); + post_symbols.push_back("\t"); + post_symbols.push_back("\n"); + post_symbols.push_back(";"); + post_symbols.push_back("}"); + post_symbols.push_back("]"); + post_symbols.push_back(")"); + post_symbols.push_back(" "); + post_symbols.push_back("."); + post_symbols.push_back("-"); + post_symbols.push_back("*"); + post_symbols.push_back("/"); + post_symbols.push_back("+"); + post_symbols.push_back("="); + post_symbols.push_back("&"); + post_symbols.push_back("|"); + post_symbols.push_back("!"); + } + + for (int i = 0; i < get_input_port_count(); i++) { + for (int j = 0; j < pre_symbols.size(); j++) { + for (int k = 0; k < post_symbols.size(); k++) { + _expression = _expression.replace(pre_symbols[j] + get_input_port_name(i) + post_symbols[k], pre_symbols[j] + p_input_vars[i] + post_symbols[k]); + } + } + } + for (int i = 0; i < get_output_port_count(); i++) { + for (int j = 0; j < pre_symbols.size(); j++) { + for (int k = 0; k < post_symbols.size(); k++) { + _expression = _expression.replace(pre_symbols[j] + get_output_port_name(i) + post_symbols[k], pre_symbols[j] + p_output_vars[i] + post_symbols[k]); + } + } + } + + String output_initializer; + + for (int i = 0; i < get_output_port_count(); i++) { + int port_type = get_output_port_type(i); + String tk = ""; + switch (port_type) { + case PORT_TYPE_SCALAR: + tk = "0.0"; + break; + case PORT_TYPE_VECTOR: + tk = "vec3(0.0, 0.0, 0.0)"; + break; + case PORT_TYPE_BOOLEAN: + tk = "false"; + break; + case PORT_TYPE_TRANSFORM: + tk = "mat4(1.0)"; + break; + default: + continue; + } + output_initializer += "\t" + p_output_vars[i] + "=" + tk + ";\n"; + } + + String code; + code += output_initializer; + code += "\t{"; + code += _expression; + code += "\n\t}"; + + return code; +} + +void VisualShaderNodeExpression::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_expression", "expression"), &VisualShaderNodeExpression::set_expression); + ClassDB::bind_method(D_METHOD("get_expression"), &VisualShaderNodeExpression::get_expression); + + ClassDB::bind_method(D_METHOD("build"), &VisualShaderNodeExpression::build); + + ADD_PROPERTY(PropertyInfo(Variant::STRING, "expression"), "set_expression", "get_expression"); +} + +VisualShaderNodeExpression::VisualShaderNodeExpression() { + expression = ""; +} diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h index cf10de9bf5..838c2c618d 100644 --- a/scene/resources/visual_shader.h +++ b/scene/resources/visual_shader.h @@ -32,6 +32,7 @@ #define VISUAL_SHADER_H #include "core/string_builder.h" +#include "scene/gui/control.h" #include "scene/resources/shader.h" class VisualShaderNodeUniform; @@ -135,7 +136,9 @@ public: bool can_connect_nodes(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) const; Error connect_nodes(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port); void disconnect_nodes(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port); + void connect_nodes_forced(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port); + void rebuild(); void get_node_connections(Type p_type, List<Connection> *r_connections) const; void set_mode(Mode p_mode); @@ -148,6 +151,7 @@ public: String generate_preview_shader(Type p_type, int p_node, int p_port, Vector<DefaultTextureParam> &r_default_tex_params) const; + String validate_port_name(const String &p_name, const List<String> &p_input_ports, const List<String> &p_output_ports) const; String validate_uniform_name(const String &p_name, const Ref<VisualShaderNodeUniform> &p_uniform) const; VisualShader(); @@ -314,4 +318,93 @@ public: VisualShaderNodeUniform(); }; +class VisualShaderNodeGroupBase : public VisualShaderNode { + GDCLASS(VisualShaderNodeGroupBase, VisualShaderNode) +private: + void _apply_port_changes(); + +protected: + Vector2 size; + String inputs; + String outputs; + + struct Port { + PortType type; + String name; + }; + + Map<int, Port> input_ports; + Map<int, Port> output_ports; + Map<int, Control *> controls; + +protected: + static void _bind_methods(); + +public: + virtual String get_caption() const; + + void set_size(const Vector2 &p_size); + Vector2 get_size() const; + + void set_inputs(const String &p_inputs); + String get_inputs() const; + + void set_outputs(const String &p_outputs); + String get_outputs() const; + + void add_input_port(int p_id, int p_type, const String &p_name); + void remove_input_port(int p_id); + virtual int get_input_port_count() const; + bool has_input_port(int p_id) const; + void clear_input_ports(); + + void add_output_port(int p_id, int p_type, const String &p_name); + void remove_output_port(int p_id); + virtual int get_output_port_count() const; + bool has_output_port(int p_id) const; + void clear_output_ports(); + + void set_input_port_type(int p_id, int p_type); + virtual PortType get_input_port_type(int p_id) const; + void set_input_port_name(int p_id, const String &p_name); + virtual String get_input_port_name(int p_id) const; + + void set_output_port_type(int p_id, int p_type); + virtual PortType get_output_port_type(int p_id) const; + void set_output_port_name(int p_id, const String &p_name); + virtual String get_output_port_name(int p_id) const; + + int get_free_input_port_id() const; + int get_free_output_port_id() const; + + void set_control(Control *p_control, int p_index); + Control *get_control(int p_index); + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; + + VisualShaderNodeGroupBase(); +}; + +class VisualShaderNodeExpression : public VisualShaderNodeGroupBase { + GDCLASS(VisualShaderNodeExpression, VisualShaderNodeGroupBase) + +private: + String expression; + +protected: + static void _bind_methods(); + +public: + virtual String get_caption() const; + + void set_expression(const String &p_expression); + String get_expression() const; + + void build(); + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; + + VisualShaderNodeExpression(); +}; + #endif // VISUAL_SHADER_H diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp index 9b8037965b..a44471a365 100644 --- a/scene/resources/visual_shader_nodes.cpp +++ b/scene/resources/visual_shader_nodes.cpp @@ -1192,7 +1192,7 @@ String VisualShaderNodeScalarFunc::get_output_port_name(int p_port) const { String VisualShaderNodeScalarFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { - static const char *scalar_func_id[FUNC_TRUNC + 1] = { + static const char *scalar_func_id[FUNC_ONEMINUS + 1] = { "sin($)", "cos($)", "tan($)", @@ -1223,7 +1223,8 @@ String VisualShaderNodeScalarFunc::generate_code(Shader::Mode p_mode, VisualShad "radians($)", "1.0/($)", "roundEven($)", - "trunc($)" + "trunc($)", + "1.0-$" }; return "\t" + p_output_vars[0] + " = " + String(scalar_func_id[func]).replace("$", p_input_vars[0]) + ";\n"; @@ -1251,7 +1252,7 @@ void VisualShaderNodeScalarFunc::_bind_methods() { ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeScalarFunc::set_function); ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeScalarFunc::get_function); - ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Sin,Cos,Tan,ASin,ACos,ATan,SinH,CosH,TanH,Log,Exp,Sqrt,Abs,Sign,Floor,Round,Ceil,Frac,Saturate,Negate,ACosH,ASinH,ATanH,Degrees,Exp2,InverseSqrt,Log2,Radians,Reciprocal,RoundEven,Trunc"), "set_function", "get_function"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Sin,Cos,Tan,ASin,ACos,ATan,SinH,CosH,TanH,Log,Exp,Sqrt,Abs,Sign,Floor,Round,Ceil,Frac,Saturate,Negate,ACosH,ASinH,ATanH,Degrees,Exp2,InverseSqrt,Log2,Radians,Reciprocal,RoundEven,Trunc,OneMinus"), "set_function", "get_function"); BIND_ENUM_CONSTANT(FUNC_SIN); BIND_ENUM_CONSTANT(FUNC_COS); @@ -1284,6 +1285,7 @@ void VisualShaderNodeScalarFunc::_bind_methods() { BIND_ENUM_CONSTANT(FUNC_RECIPROCAL); BIND_ENUM_CONSTANT(FUNC_ROUNDEVEN); BIND_ENUM_CONSTANT(FUNC_TRUNC); + BIND_ENUM_CONSTANT(FUNC_ONEMINUS); } VisualShaderNodeScalarFunc::VisualShaderNodeScalarFunc() { @@ -1319,7 +1321,7 @@ String VisualShaderNodeVectorFunc::get_output_port_name(int p_port) const { String VisualShaderNodeVectorFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { - static const char *vec_func_id[FUNC_TRUNC + 1] = { + static const char *vec_func_id[FUNC_ONEMINUS + 1] = { "normalize($)", "max(min($,vec3(1.0)),vec3(0.0))", "-($)", @@ -1353,7 +1355,8 @@ String VisualShaderNodeVectorFunc::generate_code(Shader::Mode p_mode, VisualShad "sqrt($)", "tan($)", "tanh($)", - "trunc($)" + "trunc($)", + "vec3(1.0, 1.0, 1.0)-$" }; String code; @@ -1405,7 +1408,7 @@ void VisualShaderNodeVectorFunc::_bind_methods() { ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeVectorFunc::set_function); ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeVectorFunc::get_function); - ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Normalize,Saturate,Negate,Reciprocal,RGB2HSV,HSV2RGB,Abs,ACos,ACosH,ASin,ASinH,ATan,ATanH,Ceil,Cos,CosH,Degrees,Exp,Exp2,Floor,Frac,InverseSqrt,Log,Log2,Radians,Round,RoundEven,Sign,Sin,SinH,Sqrt,Tan,TanH,Trunc"), "set_function", "get_function"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Normalize,Saturate,Negate,Reciprocal,RGB2HSV,HSV2RGB,Abs,ACos,ACosH,ASin,ASinH,ATan,ATanH,Ceil,Cos,CosH,Degrees,Exp,Exp2,Floor,Frac,InverseSqrt,Log,Log2,Radians,Round,RoundEven,Sign,Sin,SinH,Sqrt,Tan,TanH,Trunc,OneMinus"), "set_function", "get_function"); BIND_ENUM_CONSTANT(FUNC_NORMALIZE); BIND_ENUM_CONSTANT(FUNC_SATURATE); @@ -1441,6 +1444,7 @@ void VisualShaderNodeVectorFunc::_bind_methods() { BIND_ENUM_CONSTANT(FUNC_TAN); BIND_ENUM_CONSTANT(FUNC_TANH); BIND_ENUM_CONSTANT(FUNC_TRUNC); + BIND_ENUM_CONSTANT(FUNC_ONEMINUS); } VisualShaderNodeVectorFunc::VisualShaderNodeVectorFunc() { @@ -3085,3 +3089,66 @@ VisualShaderNodeSwitch::VisualShaderNodeSwitch() { set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0)); set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0)); } + +////////////// Fresnel + +String VisualShaderNodeFresnel::get_caption() const { + return "Fresnel"; +} + +int VisualShaderNodeFresnel::get_input_port_count() const { + return 4; +} + +VisualShaderNodeFresnel::PortType VisualShaderNodeFresnel::get_input_port_type(int p_port) const { + switch (p_port) { + case 0: + return PORT_TYPE_VECTOR; + case 1: + return PORT_TYPE_VECTOR; + case 2: + return PORT_TYPE_BOOLEAN; + case 3: + return PORT_TYPE_SCALAR; + default: + return PORT_TYPE_VECTOR; + } +} + +String VisualShaderNodeFresnel::get_input_port_name(int p_port) const { + switch (p_port) { + case 0: + return "normal"; + case 1: + return "view"; + case 2: + return "invert"; + case 3: + return "power"; + default: + return ""; + } +} + +int VisualShaderNodeFresnel::get_output_port_count() const { + return 1; +} + +VisualShaderNodeFresnel::PortType VisualShaderNodeFresnel::get_output_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeFresnel::get_output_port_name(int p_port) const { + return "result"; +} + +String VisualShaderNodeFresnel::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { + return "\t" + p_output_vars[0] + " = " + p_input_vars[2] + " ? (pow(clamp(dot(" + p_input_vars[0] + ", " + p_input_vars[1] + "), 0.0, 1.0), " + p_input_vars[3] + ")) : (pow(1.0 - clamp(dot(" + p_input_vars[0] + ", " + p_input_vars[1] + "), 0.0, 1.0), " + p_input_vars[3] + "));"; +} + +VisualShaderNodeFresnel::VisualShaderNodeFresnel() { + set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0)); + set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0)); + set_input_port_default_value(2, false); + set_input_port_default_value(3, 1.0); +} diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h index 90c479bd48..852248b9b4 100644 --- a/scene/resources/visual_shader_nodes.h +++ b/scene/resources/visual_shader_nodes.h @@ -562,7 +562,8 @@ public: FUNC_RADIANS, FUNC_RECIPROCAL, FUNC_ROUNDEVEN, - FUNC_TRUNC + FUNC_TRUNC, + FUNC_ONEMINUS }; protected: @@ -635,7 +636,8 @@ public: FUNC_SQRT, FUNC_TAN, FUNC_TANH, - FUNC_TRUNC + FUNC_TRUNC, + FUNC_ONEMINUS }; protected: @@ -1484,4 +1486,26 @@ public: VisualShaderNodeSwitch(); }; +/////////////////////////////////////// +/// FRESNEL +/////////////////////////////////////// + +class VisualShaderNodeFresnel : public VisualShaderNode { + GDCLASS(VisualShaderNodeFresnel, VisualShaderNode) +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; + + VisualShaderNodeFresnel(); +}; + #endif // VISUAL_SHADER_NODES_H |