diff options
Diffstat (limited to 'scene')
249 files changed, 13309 insertions, 46106 deletions
diff --git a/scene/2d/animated_sprite_2d.cpp b/scene/2d/animated_sprite_2d.cpp index f39850441b..9ee37670d1 100644 --- a/scene/2d/animated_sprite_2d.cpp +++ b/scene/2d/animated_sprite_2d.cpp @@ -105,214 +105,6 @@ Rect2 AnimatedSprite2D::_get_rect() const { return Rect2(ofs, s); } -void SpriteFrames::add_frame(const StringName &p_anim, const Ref<Texture2D> &p_frame, int p_at_pos) { - Map<StringName, Anim>::Element *E = animations.find(p_anim); - ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist."); - - if (p_at_pos >= 0 && p_at_pos < E->get().frames.size()) { - E->get().frames.insert(p_at_pos, p_frame); - } else { - E->get().frames.push_back(p_frame); - } - - emit_changed(); -} - -int SpriteFrames::get_frame_count(const StringName &p_anim) const { - const Map<StringName, Anim>::Element *E = animations.find(p_anim); - ERR_FAIL_COND_V_MSG(!E, 0, "Animation '" + String(p_anim) + "' doesn't exist."); - - return E->get().frames.size(); -} - -void SpriteFrames::remove_frame(const StringName &p_anim, int p_idx) { - Map<StringName, Anim>::Element *E = animations.find(p_anim); - ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist."); - - E->get().frames.remove(p_idx); - emit_changed(); -} - -void SpriteFrames::clear(const StringName &p_anim) { - Map<StringName, Anim>::Element *E = animations.find(p_anim); - ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist."); - - E->get().frames.clear(); - emit_changed(); -} - -void SpriteFrames::clear_all() { - animations.clear(); - add_animation("default"); -} - -void SpriteFrames::add_animation(const StringName &p_anim) { - ERR_FAIL_COND_MSG(animations.has(p_anim), "SpriteFrames already has animation '" + p_anim + "'."); - - animations[p_anim] = Anim(); -} - -bool SpriteFrames::has_animation(const StringName &p_anim) const { - return animations.has(p_anim); -} - -void SpriteFrames::remove_animation(const StringName &p_anim) { - animations.erase(p_anim); -} - -void SpriteFrames::rename_animation(const StringName &p_prev, const StringName &p_next) { - ERR_FAIL_COND_MSG(!animations.has(p_prev), "SpriteFrames doesn't have animation '" + String(p_prev) + "'."); - ERR_FAIL_COND_MSG(animations.has(p_next), "Animation '" + String(p_next) + "' already exists."); - - Anim anim = animations[p_prev]; - animations.erase(p_prev); - animations[p_next] = anim; -} - -Vector<String> SpriteFrames::_get_animation_list() const { - Vector<String> ret; - List<StringName> al; - get_animation_list(&al); - for (List<StringName>::Element *E = al.front(); E; E = E->next()) { - ret.push_back(E->get()); - } - - return ret; -} - -void SpriteFrames::get_animation_list(List<StringName> *r_animations) const { - for (const Map<StringName, Anim>::Element *E = animations.front(); E; E = E->next()) { - r_animations->push_back(E->key()); - } -} - -Vector<String> SpriteFrames::get_animation_names() const { - Vector<String> names; - for (const Map<StringName, Anim>::Element *E = animations.front(); E; E = E->next()) { - names.push_back(E->key()); - } - names.sort(); - return names; -} - -void SpriteFrames::set_animation_speed(const StringName &p_anim, float p_fps) { - ERR_FAIL_COND_MSG(p_fps < 0, "Animation speed cannot be negative (" + itos(p_fps) + ")."); - Map<StringName, Anim>::Element *E = animations.find(p_anim); - ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist."); - E->get().speed = p_fps; -} - -float SpriteFrames::get_animation_speed(const StringName &p_anim) const { - const Map<StringName, Anim>::Element *E = animations.find(p_anim); - ERR_FAIL_COND_V_MSG(!E, 0, "Animation '" + String(p_anim) + "' doesn't exist."); - return E->get().speed; -} - -void SpriteFrames::set_animation_loop(const StringName &p_anim, bool p_loop) { - Map<StringName, Anim>::Element *E = animations.find(p_anim); - ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist."); - E->get().loop = p_loop; -} - -bool SpriteFrames::get_animation_loop(const StringName &p_anim) const { - const Map<StringName, Anim>::Element *E = animations.find(p_anim); - ERR_FAIL_COND_V_MSG(!E, false, "Animation '" + String(p_anim) + "' doesn't exist."); - return E->get().loop; -} - -void SpriteFrames::_set_frames(const Array &p_frames) { - clear_all(); - Map<StringName, Anim>::Element *E = animations.find(SceneStringNames::get_singleton()->_default); - ERR_FAIL_COND(!E); - - E->get().frames.resize(p_frames.size()); - for (int i = 0; i < E->get().frames.size(); i++) { - E->get().frames.write[i] = p_frames[i]; - } -} - -Array SpriteFrames::_get_frames() const { - return Array(); -} - -Array SpriteFrames::_get_animations() const { - Array anims; - for (Map<StringName, Anim>::Element *E = animations.front(); E; E = E->next()) { - Dictionary d; - d["name"] = E->key(); - d["speed"] = E->get().speed; - d["loop"] = E->get().loop; - Array frames; - for (int i = 0; i < E->get().frames.size(); i++) { - frames.push_back(E->get().frames[i]); - } - d["frames"] = frames; - anims.push_back(d); - } - - return anims; -} - -void SpriteFrames::_set_animations(const Array &p_animations) { - animations.clear(); - for (int i = 0; i < p_animations.size(); i++) { - Dictionary d = p_animations[i]; - - ERR_CONTINUE(!d.has("name")); - ERR_CONTINUE(!d.has("speed")); - ERR_CONTINUE(!d.has("loop")); - ERR_CONTINUE(!d.has("frames")); - - Anim anim; - anim.speed = d["speed"]; - anim.loop = d["loop"]; - Array frames = d["frames"]; - for (int j = 0; j < frames.size(); j++) { - RES res = frames[j]; - anim.frames.push_back(res); - } - - animations[d["name"]] = anim; - } -} - -void SpriteFrames::_bind_methods() { - ClassDB::bind_method(D_METHOD("add_animation", "anim"), &SpriteFrames::add_animation); - ClassDB::bind_method(D_METHOD("has_animation", "anim"), &SpriteFrames::has_animation); - ClassDB::bind_method(D_METHOD("remove_animation", "anim"), &SpriteFrames::remove_animation); - ClassDB::bind_method(D_METHOD("rename_animation", "anim", "newname"), &SpriteFrames::rename_animation); - - ClassDB::bind_method(D_METHOD("get_animation_names"), &SpriteFrames::get_animation_names); - - ClassDB::bind_method(D_METHOD("set_animation_speed", "anim", "speed"), &SpriteFrames::set_animation_speed); - ClassDB::bind_method(D_METHOD("get_animation_speed", "anim"), &SpriteFrames::get_animation_speed); - - ClassDB::bind_method(D_METHOD("set_animation_loop", "anim", "loop"), &SpriteFrames::set_animation_loop); - ClassDB::bind_method(D_METHOD("get_animation_loop", "anim"), &SpriteFrames::get_animation_loop); - - ClassDB::bind_method(D_METHOD("add_frame", "anim", "frame", "at_position"), &SpriteFrames::add_frame, DEFVAL(-1)); - ClassDB::bind_method(D_METHOD("get_frame_count", "anim"), &SpriteFrames::get_frame_count); - ClassDB::bind_method(D_METHOD("get_frame", "anim", "idx"), &SpriteFrames::get_frame); - ClassDB::bind_method(D_METHOD("set_frame", "anim", "idx", "txt"), &SpriteFrames::set_frame); - ClassDB::bind_method(D_METHOD("remove_frame", "anim", "idx"), &SpriteFrames::remove_frame); - ClassDB::bind_method(D_METHOD("clear", "anim"), &SpriteFrames::clear); - ClassDB::bind_method(D_METHOD("clear_all"), &SpriteFrames::clear_all); - - ClassDB::bind_method(D_METHOD("_set_frames"), &SpriteFrames::_set_frames); - ClassDB::bind_method(D_METHOD("_get_frames"), &SpriteFrames::_get_frames); - - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "frames", PROPERTY_HINT_NONE, "", 0), "_set_frames", "_get_frames"); //compatibility - - ClassDB::bind_method(D_METHOD("_set_animations"), &SpriteFrames::_set_animations); - ClassDB::bind_method(D_METHOD("_get_animations"), &SpriteFrames::_get_animations); - - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "animations", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_animations", "_get_animations"); //compatibility -} - -SpriteFrames::SpriteFrames() { - add_animation(SceneStringNames::get_singleton()->_default); -} - void AnimatedSprite2D::_validate_property(PropertyInfo &property) const { if (!frames.is_valid()) { return; @@ -480,7 +272,7 @@ void AnimatedSprite2D::set_sprite_frames(const Ref<SpriteFrames> &p_frames) { notify_property_list_changed(); _reset_timeout(); update(); - update_configuration_warning(); + update_configuration_warnings(); } Ref<SpriteFrames> AnimatedSprite2D::get_sprite_frames() const { @@ -594,7 +386,7 @@ void AnimatedSprite2D::play(const StringName &p_animation, const bool p_backward if (p_animation) { set_animation(p_animation); - if (backwards && get_frame() == 0) { + if (frames.is_valid() && backwards && get_frame() == 0) { set_frame(frames->get_frame_count(p_animation) - 1); } } @@ -648,17 +440,14 @@ StringName AnimatedSprite2D::get_animation() const { return animation; } -String AnimatedSprite2D::get_configuration_warning() const { - String warning = Node2D::get_configuration_warning(); +TypedArray<String> AnimatedSprite2D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); if (frames.is_null()) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("A SpriteFrames resource must be created or set in the \"Frames\" property in order for AnimatedSprite to display frames."); + warnings.push_back(TTR("A SpriteFrames resource must be created or set in the \"Frames\" property in order for AnimatedSprite to display frames.")); } - return warning; + return warnings; } void AnimatedSprite2D::_bind_methods() { diff --git a/scene/2d/animated_sprite_2d.h b/scene/2d/animated_sprite_2d.h index 5e53a401e2..ef0027edf1 100644 --- a/scene/2d/animated_sprite_2d.h +++ b/scene/2d/animated_sprite_2d.h @@ -32,74 +32,9 @@ #define ANIMATED_SPRITE_2D_H #include "scene/2d/node_2d.h" +#include "scene/resources/sprite_frames.h" #include "scene/resources/texture.h" -class SpriteFrames : public Resource { - GDCLASS(SpriteFrames, Resource); - - struct Anim { - float speed = 5.0; - bool loop = true; - Vector<Ref<Texture2D>> frames; - }; - - Map<StringName, Anim> animations; - - Array _get_frames() const; - void _set_frames(const Array &p_frames); - - Array _get_animations() const; - void _set_animations(const Array &p_animations); - - Vector<String> _get_animation_list() const; - -protected: - static void _bind_methods(); - -public: - void add_animation(const StringName &p_anim); - bool has_animation(const StringName &p_anim) const; - void remove_animation(const StringName &p_anim); - void rename_animation(const StringName &p_prev, const StringName &p_next); - - void get_animation_list(List<StringName> *r_animations) const; - Vector<String> get_animation_names() const; - - void set_animation_speed(const StringName &p_anim, float p_fps); - float get_animation_speed(const StringName &p_anim) const; - - void set_animation_loop(const StringName &p_anim, bool p_loop); - bool get_animation_loop(const StringName &p_anim) const; - - void add_frame(const StringName &p_anim, const Ref<Texture2D> &p_frame, int p_at_pos = -1); - int get_frame_count(const StringName &p_anim) const; - _FORCE_INLINE_ Ref<Texture2D> get_frame(const StringName &p_anim, int p_idx) const { - const Map<StringName, Anim>::Element *E = animations.find(p_anim); - ERR_FAIL_COND_V_MSG(!E, Ref<Texture2D>(), "Animation '" + String(p_anim) + "' doesn't exist."); - ERR_FAIL_COND_V(p_idx < 0, Ref<Texture2D>()); - if (p_idx >= E->get().frames.size()) { - return Ref<Texture2D>(); - } - - return E->get().frames[p_idx]; - } - - void set_frame(const StringName &p_anim, int p_idx, const Ref<Texture2D> &p_frame) { - Map<StringName, Anim>::Element *E = animations.find(p_anim); - ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist."); - ERR_FAIL_COND(p_idx < 0); - if (p_idx >= E->get().frames.size()) { - return; - } - E->get().frames.write[p_idx] = p_frame; - } - void remove_frame(const StringName &p_anim, int p_idx); - void clear(const StringName &p_anim); - void clear_all(); - - SpriteFrames(); -}; - class AnimatedSprite2D : public Node2D { GDCLASS(AnimatedSprite2D, Node2D); @@ -174,7 +109,7 @@ public: void set_flip_v(bool p_flip); bool is_flipped_v() const; - virtual String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; AnimatedSprite2D(); }; diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp index 24243cb462..597693aa6a 100644 --- a/scene/2d/area_2d.cpp +++ b/scene/2d/area_2d.cpp @@ -452,52 +452,6 @@ bool Area2D::overlaps_body(Node *p_body) const { return E->get().in_tree; } -void Area2D::set_collision_mask(uint32_t p_mask) { - collision_mask = p_mask; - PhysicsServer2D::get_singleton()->area_set_collision_mask(get_rid(), p_mask); -} - -uint32_t Area2D::get_collision_mask() const { - return collision_mask; -} - -void Area2D::set_collision_layer(uint32_t p_layer) { - collision_layer = p_layer; - PhysicsServer2D::get_singleton()->area_set_collision_layer(get_rid(), p_layer); -} - -uint32_t Area2D::get_collision_layer() const { - return collision_layer; -} - -void Area2D::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 Area2D::get_collision_mask_bit(int p_bit) const { - return get_collision_mask() & (1 << p_bit); -} - -void Area2D::set_collision_layer_bit(int p_bit, bool p_value) { - uint32_t layer = get_collision_layer(); - if (p_value) { - layer |= 1 << p_bit; - } else { - layer &= ~(1 << p_bit); - } - set_collision_layer(layer); -} - -bool Area2D::get_collision_layer_bit(int p_bit) const { - return get_collision_layer() & (1 << p_bit); -} - void Area2D::set_audio_bus_override(bool p_override) { audio_bus_override = p_override; } @@ -559,18 +513,6 @@ void Area2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_priority", "priority"), &Area2D::set_priority); ClassDB::bind_method(D_METHOD("get_priority"), &Area2D::get_priority); - ClassDB::bind_method(D_METHOD("set_collision_mask", "collision_mask"), &Area2D::set_collision_mask); - ClassDB::bind_method(D_METHOD("get_collision_mask"), &Area2D::get_collision_mask); - - ClassDB::bind_method(D_METHOD("set_collision_layer", "collision_layer"), &Area2D::set_collision_layer); - ClassDB::bind_method(D_METHOD("get_collision_layer"), &Area2D::get_collision_layer); - - ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &Area2D::set_collision_mask_bit); - ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &Area2D::get_collision_mask_bit); - - ClassDB::bind_method(D_METHOD("set_collision_layer_bit", "bit", "value"), &Area2D::set_collision_layer_bit); - ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &Area2D::get_collision_layer_bit); - ClassDB::bind_method(D_METHOD("set_monitoring", "enable"), &Area2D::set_monitoring); ClassDB::bind_method(D_METHOD("is_monitoring"), &Area2D::is_monitoring); @@ -602,19 +544,18 @@ void Area2D::_bind_methods() { ADD_SIGNAL(MethodInfo("area_entered", PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area2D"))); ADD_SIGNAL(MethodInfo("area_exited", PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area2D"))); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "monitoring"), "set_monitoring", "is_monitoring"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "monitorable"), "set_monitorable", "is_monitorable"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,128,1"), "set_priority", "get_priority"); + + ADD_GROUP("Physics Overrides", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "space_override", PROPERTY_HINT_ENUM, "Disabled,Combine,Combine-Replace,Replace,Replace-Combine"), "set_space_override_mode", "get_space_override_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gravity_point"), "set_gravity_is_point", "is_gravity_a_point"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_distance_scale", PROPERTY_HINT_EXP_RANGE, "0,1024,0.001,or_greater"), "set_gravity_distance_scale", "get_gravity_distance_scale"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "gravity_vec"), "set_gravity_vector", "get_gravity_vector"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity", PROPERTY_HINT_RANGE, "-1024,1024,0.001"), "set_gravity", "get_gravity"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity", PROPERTY_HINT_RANGE, "-4096,4096,0.001,or_lesser,or_greater"), "set_gravity", "get_gravity"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "linear_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_linear_damp", "get_linear_damp"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_angular_damp", "get_angular_damp"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,128,1"), "set_priority", "get_priority"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "monitoring"), "set_monitoring", "is_monitoring"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "monitorable"), "set_monitorable", "is_monitorable"); - ADD_GROUP("Collision", "collision_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_layer", "get_collision_layer"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_mask", "get_collision_mask"); ADD_GROUP("Audio Bus", "audio_bus_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "audio_bus_override"), "set_audio_bus_override", "is_overriding_audio_bus"); @@ -629,7 +570,7 @@ void Area2D::_bind_methods() { Area2D::Area2D() : CollisionObject2D(PhysicsServer2D::get_singleton()->area_create(), true) { - set_gravity(98); + set_gravity(980); set_gravity_vector(Vector2(0, 1)); set_monitoring(true); set_monitorable(true); diff --git a/scene/2d/area_2d.h b/scene/2d/area_2d.h index 9033623b22..2c29e4660d 100644 --- a/scene/2d/area_2d.h +++ b/scene/2d/area_2d.h @@ -54,8 +54,6 @@ private: real_t gravity_distance_scale = 0.0; real_t linear_damp = 0.1; real_t angular_damp = 1.0; - uint32_t collision_mask = 1; - uint32_t collision_layer = 1; int priority = 0; bool monitoring = false; bool monitorable = false; @@ -165,18 +163,6 @@ public: void set_monitorable(bool p_enable); bool is_monitorable() const; - void set_collision_mask(uint32_t p_mask); - uint32_t get_collision_mask() const; - - void set_collision_layer(uint32_t p_layer); - uint32_t get_collision_layer() const; - - void set_collision_mask_bit(int p_bit, bool p_value); - bool get_collision_mask_bit(int p_bit) const; - - void set_collision_layer_bit(int p_bit, bool p_value); - bool get_collision_layer_bit(int p_bit) const; - TypedArray<Node2D> get_overlapping_bodies() const; //function for script TypedArray<Area2D> get_overlapping_areas() const; //function for script diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp index c5ac4e1a05..01045502d5 100644 --- a/scene/2d/camera_2d.cpp +++ b/scene/2d/camera_2d.cpp @@ -157,7 +157,7 @@ Transform2D Camera2D::get_camera_transform() { } if (smoothing_enabled && !Engine::get_singleton()->is_editor_hint()) { - float c = smoothing * (process_callback == CAMERA2D_PROCESS_PHYSICS ? get_physics_process_delta_time() : get_process_delta_time()); + real_t c = smoothing * (process_callback == CAMERA2D_PROCESS_PHYSICS ? get_physics_process_delta_time() : get_process_delta_time()); smoothed_camera_pos = ((camera_pos - smoothed_camera_pos) * c) + smoothed_camera_pos; ret_camera_pos = smoothed_camera_pos; //camera_pos=camera_pos*(1.0-smoothing)+new_camera_pos*smoothing; @@ -172,7 +172,7 @@ Transform2D Camera2D::get_camera_transform() { Point2 screen_offset = (anchor_mode == ANCHOR_MODE_DRAG_CENTER ? (screen_size * 0.5 * zoom) : Point2()); - float angle = get_global_transform().get_rotation(); + real_t angle = get_global_transform().get_rotation(); if (rotating) { screen_offset = screen_offset.rotated(angle); } @@ -271,7 +271,7 @@ void Camera2D::_notification(int p_what) { if (screen_drawing_enabled) { Color area_axis_color(0.5, 0.42, 0.87, 0.63); - float area_axis_width = 1; + real_t area_axis_width = 1; if (is_current()) { area_axis_width = 3; area_axis_color.a = 0.83; @@ -296,7 +296,7 @@ void Camera2D::_notification(int p_what) { if (limit_drawing_enabled) { Color limit_drawing_color(1, 1, 0, 0.63); - float limit_drawing_width = 1; + real_t limit_drawing_width = 1; if (is_current()) { limit_drawing_color.a = 0.83; limit_drawing_width = 3; @@ -318,7 +318,7 @@ void Camera2D::_notification(int p_what) { if (margin_drawing_enabled) { Color margin_drawing_color(0, 1, 1, 0.63); - float margin_drawing_width = 1; + real_t margin_drawing_width = 1; if (is_current()) { margin_drawing_width = 3; margin_drawing_color.a = 0.83; @@ -446,13 +446,13 @@ bool Camera2D::is_limit_smoothing_enabled() const { return limit_smoothing_enabled; } -void Camera2D::set_drag_margin(Side p_side, float p_drag_margin) { +void Camera2D::set_drag_margin(Side p_side, real_t p_drag_margin) { ERR_FAIL_INDEX((int)p_side, 4); drag_margin[p_side] = p_drag_margin; update(); } -float Camera2D::get_drag_margin(Side p_side) const { +real_t Camera2D::get_drag_margin(Side p_side) const { ERR_FAIL_INDEX_V((int)p_side, 4, 0); return drag_margin[p_side]; } @@ -494,7 +494,7 @@ void Camera2D::align() { _update_scroll(); } -void Camera2D::set_follow_smoothing(float p_speed) { +void Camera2D::set_follow_smoothing(real_t p_speed) { smoothing = p_speed; if (smoothing > 0 && !(is_inside_tree() && Engine::get_singleton()->is_editor_hint())) { set_process_internal(true); @@ -503,7 +503,7 @@ void Camera2D::set_follow_smoothing(float p_speed) { } } -float Camera2D::get_follow_smoothing() const { +real_t Camera2D::get_follow_smoothing() const { return smoothing; } @@ -535,7 +535,7 @@ bool Camera2D::is_drag_vertical_enabled() const { return drag_vertical_enabled; } -void Camera2D::set_drag_vertical_offset(float p_offset) { +void Camera2D::set_drag_vertical_offset(real_t p_offset) { drag_vertical_offset = p_offset; drag_vertical_offset_changed = true; Point2 old_smoothed_camera_pos = smoothed_camera_pos; @@ -543,11 +543,11 @@ void Camera2D::set_drag_vertical_offset(float p_offset) { smoothed_camera_pos = old_smoothed_camera_pos; } -float Camera2D::get_drag_vertical_offset() const { +real_t Camera2D::get_drag_vertical_offset() const { return drag_vertical_offset; } -void Camera2D::set_drag_horizontal_offset(float p_offset) { +void Camera2D::set_drag_horizontal_offset(real_t p_offset) { drag_horizontal_offset = p_offset; drag_horizontal_offset_changed = true; Point2 old_smoothed_camera_pos = smoothed_camera_pos; @@ -555,11 +555,11 @@ void Camera2D::set_drag_horizontal_offset(float p_offset) { smoothed_camera_pos = old_smoothed_camera_pos; } -float Camera2D::get_drag_horizontal_offset() const { +real_t Camera2D::get_drag_horizontal_offset() const { return drag_horizontal_offset; } -void Camera2D::_set_old_smoothing(float p_enable) { +void Camera2D::_set_old_smoothing(real_t p_enable) { //compatibility if (p_enable > 0) { smoothing_enabled = true; @@ -569,6 +569,7 @@ void Camera2D::_set_old_smoothing(float p_enable) { void Camera2D::set_enable_follow_smoothing(bool p_enabled) { smoothing_enabled = p_enabled; + notify_property_list_changed(); } bool Camera2D::is_follow_smoothing_enabled() const { @@ -642,6 +643,12 @@ bool Camera2D::is_margin_drawing_enabled() const { return margin_drawing_enabled; } +void Camera2D::_validate_property(PropertyInfo &property) const { + if (!smoothing_enabled && property.name == "smoothing_speed") { + property.usage = PROPERTY_USAGE_NOEDITOR; + } +} + void Camera2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_offset", "offset"), &Camera2D::set_offset); ClassDB::bind_method(D_METHOD("get_offset"), &Camera2D::get_offset); diff --git a/scene/2d/camera_2d.h b/scene/2d/camera_2d.h index 252d2686fc..7494e526cc 100644 --- a/scene/2d/camera_2d.h +++ b/scene/2d/camera_2d.h @@ -65,16 +65,16 @@ protected: AnchorMode anchor_mode = ANCHOR_MODE_DRAG_CENTER; bool rotating = false; bool current = false; - float smoothing = 5.0; + real_t smoothing = 5.0; bool smoothing_enabled = false; int limit[4]; bool limit_smoothing_enabled = false; - float drag_margin[4]; + real_t drag_margin[4]; bool drag_horizontal_enabled = false; bool drag_vertical_enabled = false; - float drag_horizontal_offset = 0.0; - float drag_vertical_offset = 0.0; + real_t drag_horizontal_offset = 0.0; + real_t drag_vertical_offset = 0.0; bool drag_horizontal_offset_changed = false; bool drag_vertical_offset_changed = false; @@ -85,7 +85,7 @@ protected: void _make_current(Object *p_which); void _set_current(bool p_current); - void _set_old_smoothing(float p_enable); + void _set_old_smoothing(real_t p_enable); bool screen_drawing_enabled = true; bool limit_drawing_enabled = false; @@ -97,8 +97,10 @@ protected: protected: virtual Transform2D get_camera_transform(); + void _notification(int p_what); static void _bind_methods(); + void _validate_property(PropertyInfo &property) const override; public: void set_offset(const Vector2 &p_offset); @@ -122,20 +124,20 @@ public: void set_drag_vertical_enabled(bool p_enabled); bool is_drag_vertical_enabled() const; - void set_drag_margin(Side p_side, float p_drag_margin); - float get_drag_margin(Side p_side) const; + void set_drag_margin(Side p_side, real_t p_drag_margin); + real_t get_drag_margin(Side p_side) const; - void set_drag_horizontal_offset(float p_offset); - float get_drag_horizontal_offset() const; + void set_drag_horizontal_offset(real_t p_offset); + real_t get_drag_horizontal_offset() const; - void set_drag_vertical_offset(float p_offset); - float get_drag_vertical_offset() const; + void set_drag_vertical_offset(real_t p_offset); + real_t get_drag_vertical_offset() const; void set_enable_follow_smoothing(bool p_enabled); bool is_follow_smoothing_enabled() const; - void set_follow_smoothing(float p_speed); - float get_follow_smoothing() const; + void set_follow_smoothing(real_t p_speed); + real_t get_follow_smoothing() const; void set_process_callback(Camera2DProcessCallback p_mode); Camera2DProcessCallback get_process_callback() const; diff --git a/scene/2d/canvas_group.cpp b/scene/2d/canvas_group.cpp index 0f0e583ea7..ee025b6dfc 100644 --- a/scene/2d/canvas_group.cpp +++ b/scene/2d/canvas_group.cpp @@ -30,7 +30,7 @@ #include "canvas_group.h" -void CanvasGroup::set_fit_margin(float p_fit_margin) { +void CanvasGroup::set_fit_margin(real_t p_fit_margin) { ERR_FAIL_COND(p_fit_margin < 0.0); fit_margin = p_fit_margin; @@ -39,11 +39,11 @@ void CanvasGroup::set_fit_margin(float p_fit_margin) { update(); } -float CanvasGroup::get_fit_margin() const { +real_t CanvasGroup::get_fit_margin() const { return fit_margin; } -void CanvasGroup::set_clear_margin(float p_clear_margin) { +void CanvasGroup::set_clear_margin(real_t p_clear_margin) { ERR_FAIL_COND(p_clear_margin < 0.0); clear_margin = p_clear_margin; @@ -52,7 +52,7 @@ void CanvasGroup::set_clear_margin(float p_clear_margin) { update(); } -float CanvasGroup::get_clear_margin() const { +real_t CanvasGroup::get_clear_margin() const { return clear_margin; } diff --git a/scene/2d/canvas_group.h b/scene/2d/canvas_group.h index cecf7c24f4..b487d7a098 100644 --- a/scene/2d/canvas_group.h +++ b/scene/2d/canvas_group.h @@ -35,19 +35,19 @@ class CanvasGroup : public Node2D { GDCLASS(CanvasGroup, Node2D) - float fit_margin = 10.0; - float clear_margin = 10.0; + real_t fit_margin = 10.0; + real_t clear_margin = 10.0; bool use_mipmaps = false; protected: static void _bind_methods(); public: - void set_fit_margin(float p_fit_margin); - float get_fit_margin() const; + void set_fit_margin(real_t p_fit_margin); + real_t get_fit_margin() const; - void set_clear_margin(float p_clear_margin); - float get_clear_margin() const; + void set_clear_margin(real_t p_clear_margin); + real_t get_clear_margin() const; void set_use_mipmaps(bool p_use_mipmaps); bool is_using_mipmaps() const; diff --git a/scene/2d/canvas_modulate.cpp b/scene/2d/canvas_modulate.cpp index 5d5aaae505..52eabefbcb 100644 --- a/scene/2d/canvas_modulate.cpp +++ b/scene/2d/canvas_modulate.cpp @@ -51,7 +51,7 @@ void CanvasModulate::_notification(int p_what) { remove_from_group("_canvas_modulate_" + itos(get_canvas().get_id())); } - update_configuration_warning(); + update_configuration_warnings(); } } @@ -73,24 +73,19 @@ Color CanvasModulate::get_color() const { return color; } -String CanvasModulate::get_configuration_warning() const { - if (!is_visible_in_tree() || !is_inside_tree()) { - return String(); - } - - String warning = Node2D::get_configuration_warning(); +TypedArray<String> CanvasModulate::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); - List<Node *> nodes; - get_tree()->get_nodes_in_group("_canvas_modulate_" + itos(get_canvas().get_id()), &nodes); + if (is_visible_in_tree() && is_inside_tree()) { + List<Node *> nodes; + get_tree()->get_nodes_in_group("_canvas_modulate_" + itos(get_canvas().get_id()), &nodes); - if (nodes.size() > 1) { - if (!warning.is_empty()) { - warning += "\n\n"; + if (nodes.size() > 1) { + warnings.push_back(TTR("Only one visible CanvasModulate is allowed per scene (or set of instanced scenes). The first created one will work, while the rest will be ignored.")); } - warning += TTR("Only one visible CanvasModulate is allowed per scene (or set of instanced scenes). The first created one will work, while the rest will be ignored."); } - return warning; + return warnings; } CanvasModulate::CanvasModulate() { diff --git a/scene/2d/canvas_modulate.h b/scene/2d/canvas_modulate.h index 4d55a5d9cb..3d85a92a11 100644 --- a/scene/2d/canvas_modulate.h +++ b/scene/2d/canvas_modulate.h @@ -46,7 +46,7 @@ public: void set_color(const Color &p_color); Color get_color() const; - String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; CanvasModulate(); ~CanvasModulate(); diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp index c83ed36917..de648d404c 100644 --- a/scene/2d/collision_object_2d.cpp +++ b/scene/2d/collision_object_2d.cpp @@ -100,6 +100,64 @@ void CollisionObject2D::_notification(int p_what) { } } +void CollisionObject2D::set_collision_layer(uint32_t p_layer) { + collision_layer = p_layer; + if (area) { + PhysicsServer2D::get_singleton()->area_set_collision_layer(get_rid(), p_layer); + } else { + PhysicsServer2D::get_singleton()->body_set_collision_layer(get_rid(), p_layer); + } +} + +uint32_t CollisionObject2D::get_collision_layer() const { + return collision_layer; +} + +void CollisionObject2D::set_collision_mask(uint32_t p_mask) { + collision_mask = p_mask; + if (area) { + PhysicsServer2D::get_singleton()->area_set_collision_mask(get_rid(), p_mask); + } else { + PhysicsServer2D::get_singleton()->body_set_collision_mask(get_rid(), p_mask); + } +} + +uint32_t CollisionObject2D::get_collision_mask() const { + return collision_mask; +} + +void CollisionObject2D::set_collision_layer_bit(int p_bit, bool p_value) { + ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision layer bit must be between 0 and 31 inclusive."); + uint32_t collision_layer = get_collision_layer(); + if (p_value) { + collision_layer |= 1 << p_bit; + } else { + collision_layer &= ~(1 << p_bit); + } + set_collision_layer(collision_layer); +} + +bool CollisionObject2D::get_collision_layer_bit(int p_bit) const { + ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision layer bit must be between 0 and 31 inclusive."); + return get_collision_layer() & (1 << p_bit); +} + +void CollisionObject2D::set_collision_mask_bit(int p_bit, bool p_value) { + ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision mask bit must be between 0 and 31 inclusive."); + uint32_t mask = get_collision_mask(); + if (p_value) { + mask |= 1 << p_bit; + } else { + mask &= ~(1 << p_bit); + } + set_collision_mask(mask); +} + +bool CollisionObject2D::get_collision_mask_bit(int p_bit) const { + ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision mask bit must be between 0 and 31 inclusive."); + return get_collision_mask() & (1 << p_bit); +} + uint32_t CollisionObject2D::create_shape_owner(Object *p_owner) { ShapeData sd; uint32_t id; @@ -363,22 +421,26 @@ void CollisionObject2D::_update_pickable() { } } -String CollisionObject2D::get_configuration_warning() const { - String warning = Node2D::get_configuration_warning(); +TypedArray<String> CollisionObject2D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); if (shapes.is_empty()) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("This node has no shape, so it can't collide or interact with other objects.\nConsider adding a CollisionShape2D or CollisionPolygon2D as a child to define its shape."); + warnings.push_back(TTR("This node has no shape, so it can't collide or interact with other objects.\nConsider adding a CollisionShape2D or CollisionPolygon2D as a child to define its shape.")); } - return warning; + return warnings; } void CollisionObject2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_rid"), &CollisionObject2D::get_rid); - + ClassDB::bind_method(D_METHOD("set_collision_layer", "layer"), &CollisionObject2D::set_collision_layer); + ClassDB::bind_method(D_METHOD("get_collision_layer"), &CollisionObject2D::get_collision_layer); + ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &CollisionObject2D::set_collision_mask); + ClassDB::bind_method(D_METHOD("get_collision_mask"), &CollisionObject2D::get_collision_mask); + ClassDB::bind_method(D_METHOD("set_collision_layer_bit", "bit", "value"), &CollisionObject2D::set_collision_layer_bit); + ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &CollisionObject2D::get_collision_layer_bit); + ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &CollisionObject2D::set_collision_mask_bit); + ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &CollisionObject2D::get_collision_mask_bit); ClassDB::bind_method(D_METHOD("set_pickable", "enabled"), &CollisionObject2D::set_pickable); ClassDB::bind_method(D_METHOD("is_pickable"), &CollisionObject2D::is_pickable); ClassDB::bind_method(D_METHOD("create_shape_owner", "owner"), &CollisionObject2D::create_shape_owner); @@ -407,9 +469,12 @@ void CollisionObject2D::_bind_methods() { ADD_SIGNAL(MethodInfo("mouse_entered")); ADD_SIGNAL(MethodInfo("mouse_exited")); - ADD_GROUP("Pickable", "input_"); + ADD_GROUP("Collision", "collision_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_layer", "get_collision_layer"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_mask", "get_collision_mask"); + + ADD_GROUP("Input", "input_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "input_pickable"), "set_pickable", "is_pickable"); - ADD_GROUP("", ""); } CollisionObject2D::CollisionObject2D(RID p_rid, bool p_area) { diff --git a/scene/2d/collision_object_2d.h b/scene/2d/collision_object_2d.h index e82b61d441..bb1a9dfcf5 100644 --- a/scene/2d/collision_object_2d.h +++ b/scene/2d/collision_object_2d.h @@ -37,6 +37,9 @@ class CollisionObject2D : public Node2D { GDCLASS(CollisionObject2D, Node2D); + uint32_t collision_layer = 1; + uint32_t collision_mask = 1; + bool area = false; RID rid; bool pickable = false; @@ -76,6 +79,18 @@ protected: void set_only_update_transform_changes(bool p_enable); public: + void set_collision_layer(uint32_t p_layer); + uint32_t get_collision_layer() const; + + void set_collision_mask(uint32_t p_mask); + uint32_t get_collision_mask() const; + + void set_collision_layer_bit(int p_bit, bool p_value); + bool get_collision_layer_bit(int p_bit) const; + + void set_collision_mask_bit(int p_bit, bool p_value); + bool get_collision_mask_bit(int p_bit) const; + uint32_t create_shape_owner(Object *p_owner); void remove_shape_owner(uint32_t owner); void get_shape_owners(List<uint32_t> *r_owners); @@ -107,7 +122,7 @@ public: void set_pickable(bool p_enabled); bool is_pickable() const; - String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; _FORCE_INLINE_ RID get_rid() const { return rid; } diff --git a/scene/2d/collision_polygon_2d.cpp b/scene/2d/collision_polygon_2d.cpp index 38198c496e..a69ef73a54 100644 --- a/scene/2d/collision_polygon_2d.cpp +++ b/scene/2d/collision_polygon_2d.cpp @@ -204,7 +204,7 @@ void CollisionPolygon2D::set_polygon(const Vector<Point2> &p_polygon) { _update_in_shape_owner(); } update(); - update_configuration_warning(); + update_configuration_warnings(); } Vector<Point2> CollisionPolygon2D::get_polygon() const { @@ -219,7 +219,7 @@ void CollisionPolygon2D::set_build_mode(BuildMode p_mode) { _update_in_shape_owner(); } update(); - update_configuration_warning(); + update_configuration_warnings(); } CollisionPolygon2D::BuildMode CollisionPolygon2D::get_build_mode() const { @@ -240,40 +240,28 @@ bool CollisionPolygon2D::_edit_is_selected_on_click(const Point2 &p_point, doubl } #endif -String CollisionPolygon2D::get_configuration_warning() const { - String warning = Node2D::get_configuration_warning(); +TypedArray<String> CollisionPolygon2D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); if (!Object::cast_to<CollisionObject2D>(get_parent())) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("CollisionPolygon2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape."); + warnings.push_back(TTR("CollisionPolygon2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape.")); } int polygon_count = polygon.size(); if (polygon_count == 0) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("An empty CollisionPolygon2D has no effect on collision."); + warnings.push_back(TTR("An empty CollisionPolygon2D has no effect on collision.")); } else { bool solids = build_mode == BUILD_SOLIDS; if (solids) { if (polygon_count < 3) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("Invalid polygon. At least 3 points are needed in 'Solids' build mode."); + warnings.push_back(TTR("Invalid polygon. At least 3 points are needed in 'Solids' build mode.")); } } else if (polygon_count < 2) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("Invalid polygon. At least 2 points are needed in 'Segments' build mode."); + warnings.push_back(TTR("Invalid polygon. At least 2 points are needed in 'Segments' build mode.")); } } - return warning; + return warnings; } void CollisionPolygon2D::set_disabled(bool p_disabled) { diff --git a/scene/2d/collision_polygon_2d.h b/scene/2d/collision_polygon_2d.h index 9df9802629..95dd8c9e21 100644 --- a/scene/2d/collision_polygon_2d.h +++ b/scene/2d/collision_polygon_2d.h @@ -78,7 +78,7 @@ public: void set_polygon(const Vector<Point2> &p_polygon); Vector<Point2> get_polygon() const; - virtual String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; void set_disabled(bool p_disabled); bool is_disabled() const; diff --git a/scene/2d/collision_shape_2d.cpp b/scene/2d/collision_shape_2d.cpp index 93949f741b..d9009ef85c 100644 --- a/scene/2d/collision_shape_2d.cpp +++ b/scene/2d/collision_shape_2d.cpp @@ -162,7 +162,7 @@ void CollisionShape2D::set_shape(const Ref<Shape2D> &p_shape) { shape->connect("changed", callable_mp(this, &CollisionShape2D::_shape_changed)); } - update_configuration_warning(); + update_configuration_warnings(); } Ref<Shape2D> CollisionShape2D::get_shape() const { @@ -177,19 +177,23 @@ bool CollisionShape2D::_edit_is_selected_on_click(const Point2 &p_point, double return shape->_edit_is_selected_on_click(p_point, p_tolerance); } -String CollisionShape2D::get_configuration_warning() const { +TypedArray<String> CollisionShape2D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); + if (!Object::cast_to<CollisionObject2D>(get_parent())) { - return TTR("CollisionShape2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape."); + warnings.push_back(TTR("CollisionShape2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape.")); } if (!shape.is_valid()) { - return TTR("A shape must be provided for CollisionShape2D to function. Please create a shape resource for it!"); + warnings.push_back(TTR("A shape must be provided for CollisionShape2D to function. Please create a shape resource for it!")); } + Ref<ConvexPolygonShape2D> convex = shape; Ref<ConcavePolygonShape2D> concave = shape; if (convex.is_valid() || concave.is_valid()) { - return TTR("Polygon-based shapes are not meant be used nor edited directly through the CollisionShape2D node. Please use the CollisionPolygon2D node instead."); + warnings.push_back(TTR("Polygon-based shapes are not meant be used nor edited directly through the CollisionShape2D node. Please use the CollisionPolygon2D node instead.")); } - return String(); + + return warnings; } void CollisionShape2D::set_disabled(bool p_disabled) { diff --git a/scene/2d/collision_shape_2d.h b/scene/2d/collision_shape_2d.h index 695d0c6657..eaf72627c8 100644 --- a/scene/2d/collision_shape_2d.h +++ b/scene/2d/collision_shape_2d.h @@ -72,7 +72,7 @@ public: void set_one_way_collision_margin(real_t p_margin); real_t get_one_way_collision_margin() const; - virtual String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; CollisionShape2D(); }; diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp index 48acee1bc4..1578643d14 100644 --- a/scene/2d/cpu_particles_2d.cpp +++ b/scene/2d/cpu_particles_2d.cpp @@ -78,11 +78,11 @@ void CPUParticles2D::set_pre_process_time(float p_time) { pre_process_time = p_time; } -void CPUParticles2D::set_explosiveness_ratio(float p_ratio) { +void CPUParticles2D::set_explosiveness_ratio(real_t p_ratio) { explosiveness_ratio = p_ratio; } -void CPUParticles2D::set_randomness_ratio(float p_ratio) { +void CPUParticles2D::set_randomness_ratio(real_t p_ratio) { randomness_ratio = p_ratio; } @@ -95,7 +95,7 @@ void CPUParticles2D::set_use_local_coordinates(bool p_enable) { set_notify_transform(!p_enable); } -void CPUParticles2D::set_speed_scale(float p_scale) { +void CPUParticles2D::set_speed_scale(real_t p_scale) { speed_scale = p_scale; } @@ -119,11 +119,11 @@ float CPUParticles2D::get_pre_process_time() const { return pre_process_time; } -float CPUParticles2D::get_explosiveness_ratio() const { +real_t CPUParticles2D::get_explosiveness_ratio() const { return explosiveness_ratio; } -float CPUParticles2D::get_randomness_ratio() const { +real_t CPUParticles2D::get_randomness_ratio() const { return randomness_ratio; } @@ -135,7 +135,7 @@ bool CPUParticles2D::get_use_local_coordinates() const { return local_coords; } -float CPUParticles2D::get_speed_scale() const { +real_t CPUParticles2D::get_speed_scale() const { return speed_scale; } @@ -244,18 +244,15 @@ bool CPUParticles2D::get_fractional_delta() const { return fractional_delta; } -String CPUParticles2D::get_configuration_warning() const { - String warnings = Node2D::get_configuration_warning(); +TypedArray<String> CPUParticles2D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); CanvasItemMaterial *mat = Object::cast_to<CanvasItemMaterial>(get_material().ptr()); if (get_material().is_null() || (mat && !mat->get_particles_animation())) { if (get_param(PARAM_ANIM_SPEED) != 0.0 || get_param(PARAM_ANIM_OFFSET) != 0.0 || get_param_curve(PARAM_ANIM_SPEED).is_valid() || get_param_curve(PARAM_ANIM_OFFSET).is_valid()) { - if (warnings != String()) { - warnings += "\n"; - } - warnings += "- " + TTR("CPUParticles2D animation requires the usage of a CanvasItemMaterial with \"Particles Animation\" enabled."); + warnings.push_back(TTR("CPUParticles2D animation requires the usage of a CanvasItemMaterial with \"Particles Animation\" enabled.")); } } @@ -289,39 +286,39 @@ Vector2 CPUParticles2D::get_direction() const { return direction; } -void CPUParticles2D::set_spread(float p_spread) { +void CPUParticles2D::set_spread(real_t p_spread) { spread = p_spread; } -float CPUParticles2D::get_spread() const { +real_t CPUParticles2D::get_spread() const { return spread; } -void CPUParticles2D::set_param(Parameter p_param, float p_value) { +void CPUParticles2D::set_param(Parameter p_param, real_t p_value) { ERR_FAIL_INDEX(p_param, PARAM_MAX); parameters[p_param] = p_value; } -float CPUParticles2D::get_param(Parameter p_param) const { +real_t CPUParticles2D::get_param(Parameter p_param) const { ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0); return parameters[p_param]; } -void CPUParticles2D::set_param_randomness(Parameter p_param, float p_value) { +void CPUParticles2D::set_param_randomness(Parameter p_param, real_t p_value) { ERR_FAIL_INDEX(p_param, PARAM_MAX); randomness[p_param] = p_value; } -float CPUParticles2D::get_param_randomness(Parameter p_param) const { +real_t CPUParticles2D::get_param_randomness(Parameter p_param) const { ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0); return randomness[p_param]; } -static void _adjust_curve_range(const Ref<Curve> &p_curve, float p_min, float p_max) { +static void _adjust_curve_range(const Ref<Curve> &p_curve, real_t p_min, real_t p_max) { Ref<Curve> curve = p_curve; if (!curve.is_valid()) { return; @@ -413,7 +410,7 @@ void CPUParticles2D::set_emission_shape(EmissionShape p_shape) { notify_property_list_changed(); } -void CPUParticles2D::set_emission_sphere_radius(float p_radius) { +void CPUParticles2D::set_emission_sphere_radius(real_t p_radius) { emission_sphere_radius = p_radius; } @@ -433,7 +430,7 @@ void CPUParticles2D::set_emission_colors(const Vector<Color> &p_colors) { emission_colors = p_colors; } -float CPUParticles2D::get_emission_sphere_radius() const { +real_t CPUParticles2D::get_emission_sphere_radius() const { return emission_sphere_radius; } @@ -502,7 +499,7 @@ static uint32_t idhash(uint32_t x) { return x; } -static float rand_from_seed(uint32_t &seed) { +static real_t rand_from_seed(uint32_t &seed) { int k; int s = int(seed); if (s == 0) { @@ -514,7 +511,7 @@ static float rand_from_seed(uint32_t &seed) { s += 2147483647; } seed = uint32_t(s); - return float(seed % uint32_t(65536)) / 65535.0; + return (seed % uint32_t(65536)) / 65535.0; } void CPUParticles2D::_update_internal() { @@ -625,7 +622,7 @@ void CPUParticles2D::_particles_process(float 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); + real_t restart_phase = real_t(i) / real_t(pcount); if (randomness_ratio > 0.0) { uint32_t seed = cycle; @@ -634,8 +631,8 @@ void CPUParticles2D::_particles_process(float p_delta) { } seed *= uint32_t(pcount); seed += uint32_t(i); - float random = float(idhash(seed) % uint32_t(65536)) / 65536.0; - restart_phase += randomness_ratio * random * 1.0 / float(pcount); + real_t random = (idhash(seed) % uint32_t(65536)) / 65536.0; + restart_phase += randomness_ratio * random * 1.0 / pcount; } restart_phase *= (1.0 - explosiveness_ratio); @@ -680,17 +677,17 @@ void CPUParticles2D::_particles_process(float p_delta) { } p.active = true; - /*float tex_linear_velocity = 0; + /*real_t tex_linear_velocity = 0; if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { tex_linear_velocity = curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY]->interpolate(0); }*/ - float tex_angle = 0.0; + real_t tex_angle = 0.0; if (curve_parameters[PARAM_ANGLE].is_valid()) { tex_angle = curve_parameters[PARAM_ANGLE]->interpolate(tv); } - float tex_anim_offset = 0.0; + real_t tex_anim_offset = 0.0; if (curve_parameters[PARAM_ANGLE].is_valid()) { tex_anim_offset = curve_parameters[PARAM_ANGLE]->interpolate(tv); } @@ -702,16 +699,16 @@ void CPUParticles2D::_particles_process(float p_delta) { p.hue_rot_rand = Math::randf(); p.anim_offset_rand = Math::randf(); - float angle1_rad = Math::atan2(direction.y, direction.x) + Math::deg2rad((Math::randf() * 2.0 - 1.0) * spread); + real_t angle1_rad = Math::atan2(direction.y, direction.x) + Math::deg2rad((Math::randf() * 2.0 - 1.0) * spread); Vector2 rot = Vector2(Math::cos(angle1_rad), Math::sin(angle1_rad)); - p.velocity = rot * parameters[PARAM_INITIAL_LINEAR_VELOCITY] * Math::lerp(1.0f, float(Math::randf()), randomness[PARAM_INITIAL_LINEAR_VELOCITY]); + p.velocity = rot * parameters[PARAM_INITIAL_LINEAR_VELOCITY] * Math::lerp((real_t)1.0, real_t(Math::randf()), randomness[PARAM_INITIAL_LINEAR_VELOCITY]); - float base_angle = (parameters[PARAM_ANGLE] + tex_angle) * Math::lerp(1.0f, p.angle_rand, randomness[PARAM_ANGLE]); + real_t base_angle = (parameters[PARAM_ANGLE] + tex_angle) * Math::lerp((real_t)1.0, p.angle_rand, randomness[PARAM_ANGLE]); p.rotation = Math::deg2rad(base_angle); p.custom[0] = 0.0; // unused p.custom[1] = 0.0; // phase [0..1] - p.custom[2] = (parameters[PARAM_ANIM_OFFSET] + tex_anim_offset) * Math::lerp(1.0f, p.anim_offset_rand, randomness[PARAM_ANIM_OFFSET]); //animation phase [0..1] + p.custom[2] = (parameters[PARAM_ANIM_OFFSET] + tex_anim_offset) * Math::lerp((real_t)1.0, p.anim_offset_rand, randomness[PARAM_ANIM_OFFSET]); //animation phase [0..1] p.custom[3] = 0.0; p.transform = Transform2D(); p.time = 0; @@ -723,8 +720,8 @@ void CPUParticles2D::_particles_process(float p_delta) { //do none } break; case EMISSION_SHAPE_SPHERE: { - float s = Math::randf(), t = Math_TAU * Math::randf(); - float radius = emission_sphere_radius * Math::sqrt(1.0 - s * s); + real_t s = Math::randf(), t = Math_TAU * Math::randf(); + real_t radius = emission_sphere_radius * Math::sqrt(1.0 - s * s); p.transform[2] = Vector2(Math::cos(t), Math::sin(t)) * radius; } break; case EMISSION_SHAPE_RECTANGLE: { @@ -775,51 +772,51 @@ void CPUParticles2D::_particles_process(float p_delta) { p.custom[1] = p.time / lifetime; tv = p.time / p.lifetime; - float tex_linear_velocity = 0.0; + real_t tex_linear_velocity = 0.0; if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { tex_linear_velocity = curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY]->interpolate(tv); } - float tex_orbit_velocity = 0.0; + real_t tex_orbit_velocity = 0.0; if (curve_parameters[PARAM_ORBIT_VELOCITY].is_valid()) { tex_orbit_velocity = curve_parameters[PARAM_ORBIT_VELOCITY]->interpolate(tv); } - float tex_angular_velocity = 0.0; + real_t tex_angular_velocity = 0.0; if (curve_parameters[PARAM_ANGULAR_VELOCITY].is_valid()) { tex_angular_velocity = curve_parameters[PARAM_ANGULAR_VELOCITY]->interpolate(tv); } - float tex_linear_accel = 0.0; + real_t tex_linear_accel = 0.0; if (curve_parameters[PARAM_LINEAR_ACCEL].is_valid()) { tex_linear_accel = curve_parameters[PARAM_LINEAR_ACCEL]->interpolate(tv); } - float tex_tangential_accel = 0.0; + real_t tex_tangential_accel = 0.0; if (curve_parameters[PARAM_TANGENTIAL_ACCEL].is_valid()) { tex_tangential_accel = curve_parameters[PARAM_TANGENTIAL_ACCEL]->interpolate(tv); } - float tex_radial_accel = 0.0; + real_t tex_radial_accel = 0.0; if (curve_parameters[PARAM_RADIAL_ACCEL].is_valid()) { tex_radial_accel = curve_parameters[PARAM_RADIAL_ACCEL]->interpolate(tv); } - float tex_damping = 0.0; + real_t tex_damping = 0.0; if (curve_parameters[PARAM_DAMPING].is_valid()) { tex_damping = curve_parameters[PARAM_DAMPING]->interpolate(tv); } - float tex_angle = 0.0; + real_t tex_angle = 0.0; if (curve_parameters[PARAM_ANGLE].is_valid()) { tex_angle = curve_parameters[PARAM_ANGLE]->interpolate(tv); } - float tex_anim_speed = 0.0; + real_t tex_anim_speed = 0.0; if (curve_parameters[PARAM_ANIM_SPEED].is_valid()) { tex_anim_speed = curve_parameters[PARAM_ANIM_SPEED]->interpolate(tv); } - float tex_anim_offset = 0.0; + real_t tex_anim_offset = 0.0; if (curve_parameters[PARAM_ANIM_OFFSET].is_valid()) { tex_anim_offset = curve_parameters[PARAM_ANIM_OFFSET]->interpolate(tv); } @@ -828,20 +825,20 @@ void CPUParticles2D::_particles_process(float p_delta) { Vector2 pos = p.transform[2]; //apply linear acceleration - force += p.velocity.length() > 0.0 ? p.velocity.normalized() * (parameters[PARAM_LINEAR_ACCEL] + tex_linear_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_LINEAR_ACCEL]) : Vector2(); + force += p.velocity.length() > 0.0 ? p.velocity.normalized() * (parameters[PARAM_LINEAR_ACCEL] + tex_linear_accel) * Math::lerp((real_t)1.0, rand_from_seed(alt_seed), randomness[PARAM_LINEAR_ACCEL]) : Vector2(); //apply radial acceleration Vector2 org = emission_xform[2]; Vector2 diff = pos - org; - 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(); + force += diff.length() > 0.0 ? diff.normalized() * (parameters[PARAM_RADIAL_ACCEL] + tex_radial_accel) * Math::lerp((real_t)1.0, 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)).normalized() * ((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((real_t)1.0, rand_from_seed(alt_seed), randomness[PARAM_TANGENTIAL_ACCEL])) : Vector2(); //apply attractor forces p.velocity += force * local_delta; //orbit velocity - float orbit_amount = (parameters[PARAM_ORBIT_VELOCITY] + tex_orbit_velocity) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_ORBIT_VELOCITY]); + real_t orbit_amount = (parameters[PARAM_ORBIT_VELOCITY] + tex_orbit_velocity) * Math::lerp((real_t)1.0, rand_from_seed(alt_seed), randomness[PARAM_ORBIT_VELOCITY]); if (orbit_amount != 0.0) { - float ang = orbit_amount * local_delta * Math_TAU; + real_t ang = orbit_amount * local_delta * Math_TAU; // Not sure why the ParticlesMaterial code uses a clockwise rotation matrix, // but we use -ang here to reproduce its behavior. Transform2D rot = Transform2D(-ang, Vector2()); @@ -853,8 +850,8 @@ void CPUParticles2D::_particles_process(float p_delta) { } if (parameters[PARAM_DAMPING] + tex_damping > 0.0) { - float v = p.velocity.length(); - float damp = (parameters[PARAM_DAMPING] + tex_damping) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_DAMPING]); + real_t v = p.velocity.length(); + real_t damp = (parameters[PARAM_DAMPING] + tex_damping) * Math::lerp((real_t)1.0, rand_from_seed(alt_seed), randomness[PARAM_DAMPING]); v -= damp * local_delta; if (v < 0.0) { p.velocity = Vector2(); @@ -862,28 +859,28 @@ void CPUParticles2D::_particles_process(float p_delta) { p.velocity = p.velocity.normalized() * v; } } - float base_angle = (parameters[PARAM_ANGLE] + tex_angle) * Math::lerp(1.0f, p.angle_rand, randomness[PARAM_ANGLE]); - base_angle += p.custom[1] * lifetime * (parameters[PARAM_ANGULAR_VELOCITY] + tex_angular_velocity) * Math::lerp(1.0f, rand_from_seed(alt_seed) * 2.0f - 1.0f, randomness[PARAM_ANGULAR_VELOCITY]); + real_t base_angle = (parameters[PARAM_ANGLE] + tex_angle) * Math::lerp((real_t)1.0, p.angle_rand, randomness[PARAM_ANGLE]); + base_angle += p.custom[1] * lifetime * (parameters[PARAM_ANGULAR_VELOCITY] + tex_angular_velocity) * Math::lerp((real_t)1.0, rand_from_seed(alt_seed) * 2.0f - 1.0f, randomness[PARAM_ANGULAR_VELOCITY]); p.rotation = Math::deg2rad(base_angle); //angle - float animation_phase = (parameters[PARAM_ANIM_OFFSET] + tex_anim_offset) * Math::lerp(1.0f, p.anim_offset_rand, randomness[PARAM_ANIM_OFFSET]) + p.custom[1] * (parameters[PARAM_ANIM_SPEED] + tex_anim_speed) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_ANIM_SPEED]); + real_t animation_phase = (parameters[PARAM_ANIM_OFFSET] + tex_anim_offset) * Math::lerp((real_t)1.0, p.anim_offset_rand, randomness[PARAM_ANIM_OFFSET]) + p.custom[1] * (parameters[PARAM_ANIM_SPEED] + tex_anim_speed) * Math::lerp((real_t)1.0, rand_from_seed(alt_seed), randomness[PARAM_ANIM_SPEED]); p.custom[2] = animation_phase; } //apply color //apply hue rotation - float tex_scale = 1.0; + real_t tex_scale = 1.0; if (curve_parameters[PARAM_SCALE].is_valid()) { tex_scale = curve_parameters[PARAM_SCALE]->interpolate(tv); } - float tex_hue_variation = 0.0; + real_t tex_hue_variation = 0.0; if (curve_parameters[PARAM_HUE_VARIATION].is_valid()) { tex_hue_variation = curve_parameters[PARAM_HUE_VARIATION]->interpolate(tv); } - float hue_rot_angle = (parameters[PARAM_HUE_VARIATION] + tex_hue_variation) * Math_TAU * Math::lerp(1.0f, p.hue_rot_rand * 2.0f - 1.0f, randomness[PARAM_HUE_VARIATION]); - float hue_rot_c = Math::cos(hue_rot_angle); - float hue_rot_s = Math::sin(hue_rot_angle); + real_t hue_rot_angle = (parameters[PARAM_HUE_VARIATION] + tex_hue_variation) * Math_TAU * Math::lerp(1.0f, p.hue_rot_rand * 2.0f - 1.0f, randomness[PARAM_HUE_VARIATION]); + real_t hue_rot_c = Math::cos(hue_rot_angle); + real_t hue_rot_s = Math::sin(hue_rot_angle); Basis hue_rot_mat; { @@ -921,7 +918,7 @@ void CPUParticles2D::_particles_process(float p_delta) { } //scale by scale - float base_scale = tex_scale * Math::lerp(parameters[PARAM_SCALE], 1.0f, p.scale_rand * randomness[PARAM_SCALE]); + real_t base_scale = tex_scale * Math::lerp(parameters[PARAM_SCALE], (real_t)1.0, p.scale_rand * randomness[PARAM_SCALE]); if (base_scale < 0.000001) { base_scale = 0.000001; } @@ -979,7 +976,7 @@ void CPUParticles2D::_update_particle_data_buffer() { ptr[7] = t.elements[2][1]; } else { - zeromem(ptr, sizeof(float) * 8); + memset(ptr, 0, sizeof(float) * 8); } Color c = r[idx].color; @@ -1083,7 +1080,7 @@ void CPUParticles2D::_notification(int p_what) { ptr[7] = t.elements[2][1]; } else { - zeromem(ptr, sizeof(float) * 8); + memset(ptr, 0, sizeof(float) * 8); } ptr += 16; diff --git a/scene/2d/cpu_particles_2d.h b/scene/2d/cpu_particles_2d.h index 7ee165b3e1..92b8be77cf 100644 --- a/scene/2d/cpu_particles_2d.h +++ b/scene/2d/cpu_particles_2d.h @@ -84,13 +84,13 @@ private: Transform2D transform; Color color; float custom[4] = {}; - float rotation = 0.0; + real_t rotation = 0.0; Vector2 velocity; bool active = false; - float angle_rand = 0.0; - float scale_rand = 0.0; - float hue_rot_rand = 0.0; - float anim_offset_rand = 0.0; + real_t angle_rand = 0.0; + real_t scale_rand = 0.0; + real_t hue_rot_rand = 0.0; + real_t anim_offset_rand = 0.0; float time = 0.0; float lifetime = 0.0; Color base_color; @@ -133,10 +133,10 @@ private: float lifetime = 1.0; float pre_process_time = 0.0; - float explosiveness_ratio = 0.0; - float randomness_ratio = 0.0; - float lifetime_randomness = 0.0; - float speed_scale = 1.0; + real_t explosiveness_ratio = 0.0; + real_t randomness_ratio = 0.0; + real_t lifetime_randomness = 0.0; + real_t speed_scale = 1.0; bool local_coords; int fixed_fps = 0; bool fractional_delta = true; @@ -150,10 +150,10 @@ private: //////// Vector2 direction = Vector2(1, 0); - float spread = 45.0; + real_t spread = 45.0; - float parameters[PARAM_MAX]; - float randomness[PARAM_MAX]; + real_t parameters[PARAM_MAX]; + real_t randomness[PARAM_MAX]; Ref<Curve> curve_parameters[PARAM_MAX]; Color color; @@ -162,14 +162,14 @@ private: bool particle_flags[PARTICLE_FLAG_MAX]; EmissionShape emission_shape = EMISSION_SHAPE_POINT; - float emission_sphere_radius = 1.0; + real_t emission_sphere_radius = 1.0; Vector2 emission_rect_extents = Vector2(1, 1); Vector<Vector2> emission_points; Vector<Vector2> emission_normals; Vector<Color> emission_colors; int emission_point_count = 0; - Vector2 gravity = Vector2(0, 98); + Vector2 gravity = Vector2(0, 980); void _update_internal(); void _particles_process(float p_delta); @@ -196,24 +196,24 @@ public: void set_lifetime(float p_lifetime); void set_one_shot(bool p_one_shot); void set_pre_process_time(float p_time); - void set_explosiveness_ratio(float p_ratio); - void set_randomness_ratio(float p_ratio); + void set_explosiveness_ratio(real_t p_ratio); + void set_randomness_ratio(real_t p_ratio); void set_lifetime_randomness(float p_random); void set_visibility_aabb(const Rect2 &p_aabb); void set_use_local_coordinates(bool p_enable); - void set_speed_scale(float p_scale); + void set_speed_scale(real_t p_scale); bool is_emitting() const; int get_amount() const; float get_lifetime() const; bool get_one_shot() const; float get_pre_process_time() const; - float get_explosiveness_ratio() const; - float get_randomness_ratio() const; + real_t get_explosiveness_ratio() const; + real_t get_randomness_ratio() const; float get_lifetime_randomness() const; Rect2 get_visibility_aabb() const; bool get_use_local_coordinates() const; - float get_speed_scale() const; + real_t get_speed_scale() const; void set_fixed_fps(int p_count); int get_fixed_fps() const; @@ -235,14 +235,14 @@ public: void set_direction(Vector2 p_direction); Vector2 get_direction() const; - void set_spread(float p_spread); - float get_spread() const; + void set_spread(real_t p_spread); + real_t get_spread() const; - void set_param(Parameter p_param, float p_value); - float get_param(Parameter p_param) const; + void set_param(Parameter p_param, real_t p_value); + real_t get_param(Parameter p_param) const; - void set_param_randomness(Parameter p_param, float p_value); - float get_param_randomness(Parameter p_param) const; + void set_param_randomness(Parameter p_param, real_t p_value); + real_t get_param_randomness(Parameter p_param) const; void set_param_curve(Parameter p_param, const Ref<Curve> &p_curve); Ref<Curve> get_param_curve(Parameter p_param) const; @@ -257,7 +257,7 @@ public: bool get_particle_flag(ParticleFlags p_particle_flag) const; void set_emission_shape(EmissionShape p_shape); - void set_emission_sphere_radius(float p_radius); + void set_emission_sphere_radius(real_t p_radius); void set_emission_rect_extents(Vector2 p_extents); void set_emission_points(const Vector<Vector2> &p_points); void set_emission_normals(const Vector<Vector2> &p_normals); @@ -265,7 +265,7 @@ public: void set_emission_point_count(int p_count); EmissionShape get_emission_shape() const; - float get_emission_sphere_radius() const; + real_t get_emission_sphere_radius() const; Vector2 get_emission_rect_extents() const; Vector<Vector2> get_emission_points() const; Vector<Vector2> get_emission_normals() const; @@ -275,7 +275,7 @@ public: void set_gravity(const Vector2 &p_gravity); Vector2 get_gravity() const; - virtual String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; void restart(); diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp index af70c47f7c..774a194e39 100644 --- a/scene/2d/gpu_particles_2d.cpp +++ b/scene/2d/gpu_particles_2d.cpp @@ -137,7 +137,7 @@ void GPUParticles2D::set_process_material(const Ref<Material> &p_material) { } RS::get_singleton()->particles_set_process_material(particles, material_rid); - update_configuration_warning(); + update_configuration_warnings(); } void GPUParticles2D::set_speed_scale(float p_scale) { @@ -216,18 +216,15 @@ bool GPUParticles2D::get_fractional_delta() const { return fractional_delta; } -String GPUParticles2D::get_configuration_warning() const { +TypedArray<String> GPUParticles2D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); + if (RenderingServer::get_singleton()->is_low_end()) { - return TTR("GPU-based particles are not supported by the GLES2 video driver.\nUse the CPUParticles2D node instead. You can use the \"Convert to CPUParticles2D\" option for this purpose."); + warnings.push_back(TTR("GPU-based particles are not supported by the GLES2 video driver.\nUse the CPUParticles2D node instead. You can use the \"Convert to CPUParticles2D\" option for this purpose.")); } - String warnings = Node2D::get_configuration_warning(); - if (process_material.is_null()) { - if (warnings != String()) { - warnings += "\n"; - } - warnings += "- " + TTR("A material to process the particles is not assigned, so no behavior is imprinted."); + warnings.push_back(TTR("A material to process the particles is not assigned, so no behavior is imprinted.")); } else { CanvasItemMaterial *mat = Object::cast_to<CanvasItemMaterial>(get_material().ptr()); @@ -236,10 +233,7 @@ String GPUParticles2D::get_configuration_warning() const { if (process && (process->get_param(ParticlesMaterial::PARAM_ANIM_SPEED) != 0.0 || process->get_param(ParticlesMaterial::PARAM_ANIM_OFFSET) != 0.0 || process->get_param_texture(ParticlesMaterial::PARAM_ANIM_SPEED).is_valid() || process->get_param_texture(ParticlesMaterial::PARAM_ANIM_OFFSET).is_valid())) { - if (warnings != String()) { - warnings += "\n"; - } - warnings += "- " + TTR("Particles2D animation requires the usage of a CanvasItemMaterial with \"Particles Animation\" enabled."); + warnings.push_back(TTR("Particles2D animation requires the usage of a CanvasItemMaterial with \"Particles Animation\" enabled.")); } } } @@ -375,6 +369,7 @@ void GPUParticles2D::_bind_methods() { GPUParticles2D::GPUParticles2D() { particles = RS::get_singleton()->particles_create(); + RS::get_singleton()->particles_set_mode(particles, RS::PARTICLES_MODE_2D); one_shot = false; // Needed so that set_emitting doesn't access uninitialized values set_emitting(true); diff --git a/scene/2d/gpu_particles_2d.h b/scene/2d/gpu_particles_2d.h index 774cef9cc9..20f9f768ed 100644 --- a/scene/2d/gpu_particles_2d.h +++ b/scene/2d/gpu_particles_2d.h @@ -110,7 +110,7 @@ public: void set_texture(const Ref<Texture2D> &p_texture); Ref<Texture2D> get_texture() const; - virtual String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; void restart(); Rect2 capture_rect() const; diff --git a/scene/2d/joints_2d.cpp b/scene/2d/joints_2d.cpp index 7d9cdd52ac..8a4ccc2f96 100644 --- a/scene/2d/joints_2d.cpp +++ b/scene/2d/joints_2d.cpp @@ -66,6 +66,7 @@ void Joint2D::_update_joint(bool p_only_free) { if (p_only_free || !is_inside_tree()) { PhysicsServer2D::get_singleton()->joint_clear(joint); warning = String(); + update_configuration_warnings(); return; } @@ -76,43 +77,26 @@ void Joint2D::_update_joint(bool p_only_free) { PhysicsBody2D *body_b = Object::cast_to<PhysicsBody2D>(node_b); if (node_a && !body_a && node_b && !body_b) { - PhysicsServer2D::get_singleton()->joint_clear(joint); warning = TTR("Node A and Node B must be PhysicsBody2Ds"); - update_configuration_warning(); - return; - } - - if (node_a && !body_a) { - PhysicsServer2D::get_singleton()->joint_clear(joint); + } else if (node_a && !body_a) { warning = TTR("Node A must be a PhysicsBody2D"); - update_configuration_warning(); - return; - } - - if (node_b && !body_b) { - PhysicsServer2D::get_singleton()->joint_clear(joint); + } else if (node_b && !body_b) { warning = TTR("Node B must be a PhysicsBody2D"); - update_configuration_warning(); - return; - } - - if (!body_a || !body_b) { - PhysicsServer2D::get_singleton()->joint_clear(joint); + } else if (!body_a || !body_b) { warning = TTR("Joint is not connected to two PhysicsBody2Ds"); - update_configuration_warning(); - return; + } else if (body_a == body_b) { + warning = TTR("Node A and Node B must be different PhysicsBody2Ds"); + } else { + warning = String(); } - if (body_a == body_b) { + update_configuration_warnings(); + + if (!warning.is_empty()) { PhysicsServer2D::get_singleton()->joint_clear(joint); - warning = TTR("Node A and Node B must be different PhysicsBody2Ds"); - update_configuration_warning(); return; } - warning = String(); - update_configuration_warning(); - if (body_a) { body_a->force_update_transform(); } @@ -211,17 +195,14 @@ bool Joint2D::get_exclude_nodes_from_collision() const { return exclude_from_collision; } -String Joint2D::get_configuration_warning() const { - String node_warning = Node2D::get_configuration_warning(); +TypedArray<String> Joint2D::get_configuration_warnings() const { + TypedArray<String> warnings = Node2D::get_configuration_warnings(); if (!warning.is_empty()) { - if (!node_warning.is_empty()) { - node_warning += "\n\n"; - } - node_warning += warning; + warnings.push_back(warning); } - return node_warning; + return warnings; } void Joint2D::_bind_methods() { diff --git a/scene/2d/joints_2d.h b/scene/2d/joints_2d.h index 08e02ee29d..dc5a08f815 100644 --- a/scene/2d/joints_2d.h +++ b/scene/2d/joints_2d.h @@ -62,7 +62,7 @@ protected: _FORCE_INLINE_ bool is_configured() const { return configured; } public: - virtual String get_configuration_warning() const override; + virtual TypedArray<String> get_configuration_warnings() const override; void set_node_a(const NodePath &p_node_a); NodePath get_node_a() const; diff --git a/scene/2d/light_2d.cpp b/scene/2d/light_2d.cpp index 15fcb08422..8fb765f16b 100644 --- a/scene/2d/light_2d.cpp +++ b/scene/2d/light_2d.cpp @@ -84,21 +84,21 @@ Color Light2D::get_color() const { return color; } -void Light2D::set_height(float p_height) { +void Light2D::set_height(real_t p_height) { height = p_height; RS::get_singleton()->canvas_light_set_height(canvas_light, height); } -float Light2D::get_height() const { +real_t Light2D::get_height() const { return height; } -void Light2D::set_energy(float p_energy) { +void Light2D::set_energy(real_t p_energy) { energy = p_energy; RS::get_singleton()->canvas_light_set_energy(canvas_light, energy); } -float Light2D::get_energy() const { +real_t Light2D::get_energy() const { return energy; } @@ -159,6 +159,7 @@ int Light2D::get_item_shadow_cull_mask() const { void Light2D::set_shadow_enabled(bool p_enabled) { shadow = p_enabled; RS::get_singleton()->canvas_light_set_shadow_enabled(canvas_light, shadow); + notify_property_list_changed(); } bool Light2D::is_shadow_enabled() const { @@ -212,15 +213,21 @@ void Light2D::_notification(int p_what) { } } -void Light2D::set_shadow_smooth(float p_amount) { +void Light2D::set_shadow_smooth(real_t p_amount) { shadow_smooth = p_amount; RS::get_singleton()->canvas_light_set_shadow_smooth(canvas_light, shadow_smooth); } -float Light2D::get_shadow_smooth() const { +real_t Light2D::get_shadow_smooth() const { return shadow_smooth; } +void Light2D::_validate_property(PropertyInfo &property) const { + if (!shadow && (property.name == "shadow_color" || property.name == "shadow_filter" || property.name == "shadow_filter_smooth" || property.name == "shadow_item_cull_mask")) { + property.usage = PROPERTY_USAGE_NOEDITOR; + } +} + void Light2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &Light2D::set_enabled); ClassDB::bind_method(D_METHOD("is_enabled"), &Light2D::is_enabled); @@ -366,7 +373,7 @@ void PointLight2D::set_texture(const Ref<Texture2D> &p_texture) { RS::get_singleton()->canvas_light_set_texture(_get_light(), RID()); } - update_configuration_warning(); + update_configuration_warnings(); } Ref<Texture2D> PointLight2D::get_texture() const { @@ -383,20 +390,17 @@ Vector2 PointLight2D::get_texture_offset() const { return texture_offset; } -String PointLight2D::get_configuration_warning() const { - String warning = Node2D::get_configuration_warning(); +TypedArray<String> PointLight2D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); if (!texture.is_valid()) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("A texture with the shape of the light must be supplied to the \"Texture\" property."); + warnings.push_back(TTR("A texture with the shape of the light must be supplied to the \"Texture\" property.")); } - return warning; + return warnings; } -void PointLight2D::set_texture_scale(float p_scale) { +void PointLight2D::set_texture_scale(real_t p_scale) { _scale = p_scale; // Avoid having 0 scale values, can lead to errors in physics and rendering. if (_scale == 0) { @@ -406,7 +410,7 @@ void PointLight2D::set_texture_scale(float p_scale) { item_rect_changed(); } -float PointLight2D::get_texture_scale() const { +real_t PointLight2D::get_texture_scale() const { return _scale; } @@ -432,12 +436,12 @@ PointLight2D::PointLight2D() { ////////// -void DirectionalLight2D::set_max_distance(float p_distance) { +void DirectionalLight2D::set_max_distance(real_t p_distance) { max_distance = p_distance; RS::get_singleton()->canvas_light_set_directional_distance(_get_light(), max_distance); } -float DirectionalLight2D::get_max_distance() const { +real_t DirectionalLight2D::get_max_distance() const { return max_distance; } diff --git a/scene/2d/light_2d.h b/scene/2d/light_2d.h index 4279baf15b..d9ecd81f1c 100644 --- a/scene/2d/light_2d.h +++ b/scene/2d/light_2d.h @@ -57,15 +57,15 @@ private: bool shadow = false; Color color = Color(1, 1, 1); Color shadow_color = Color(0, 0, 0, 0); - float height = 0.0; - float energy = 1.0; + real_t height = 0.0; + real_t energy = 1.0; int z_min = -1024; int z_max = 1024; int layer_min = 0; int layer_max = 0; int item_mask = 1; int item_shadow_mask = 1; - float shadow_smooth = 0.0; + real_t shadow_smooth = 0.0; Ref<Texture2D> texture; Vector2 texture_offset; ShadowFilter shadow_filter = SHADOW_FILTER_NONE; @@ -77,6 +77,7 @@ protected: _FORCE_INLINE_ RID _get_light() const { return canvas_light; } void _notification(int p_what); static void _bind_methods(); + void _validate_property(PropertyInfo &property) const override; public: void set_enabled(bool p_enabled); @@ -88,11 +89,11 @@ public: void set_color(const Color &p_color); Color get_color() const; - void set_height(float p_height); - float get_height() const; + void set_height(real_t p_height); + real_t get_height() const; - void set_energy(float p_energy); - float get_energy() const; + void set_energy(real_t p_energy); + real_t get_energy() const; void set_z_range_min(int p_min_z); int get_z_range_min() const; @@ -121,8 +122,8 @@ public: void set_shadow_color(const Color &p_shadow_color); Color get_shadow_color() const; - void set_shadow_smooth(float p_amount); - float get_shadow_smooth() const; + void set_shadow_smooth(real_t p_amount); + real_t get_shadow_smooth() const; void set_blend_mode(BlendMode p_mode); BlendMode get_blend_mode() const; @@ -138,7 +139,7 @@ class PointLight2D : public Light2D { GDCLASS(PointLight2D, Light2D); private: - float _scale = 1.0; + real_t _scale = 1.0; Ref<Texture2D> texture; Vector2 texture_offset; @@ -165,10 +166,10 @@ public: void set_texture_offset(const Vector2 &p_offset); Vector2 get_texture_offset() const; - void set_texture_scale(float p_scale); - float get_texture_scale() const; + void set_texture_scale(real_t p_scale); + real_t get_texture_scale() const; - String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; PointLight2D(); }; @@ -176,14 +177,14 @@ public: class DirectionalLight2D : public Light2D { GDCLASS(DirectionalLight2D, Light2D); - float max_distance = 10000.0; + real_t max_distance = 10000.0; protected: static void _bind_methods(); public: - void set_max_distance(float p_distance); - float get_max_distance() const; + void set_max_distance(real_t p_distance); + real_t get_max_distance() const; DirectionalLight2D(); }; diff --git a/scene/2d/light_occluder_2d.cpp b/scene/2d/light_occluder_2d.cpp index 9589702e2e..fdc28f81c2 100644 --- a/scene/2d/light_occluder_2d.cpp +++ b/scene/2d/light_occluder_2d.cpp @@ -242,24 +242,18 @@ int LightOccluder2D::get_occluder_light_mask() const { return mask; } -String LightOccluder2D::get_configuration_warning() const { - String warning = Node2D::get_configuration_warning(); +TypedArray<String> LightOccluder2D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); if (!occluder_polygon.is_valid()) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("An occluder polygon must be set (or drawn) for this occluder to take effect."); + warnings.push_back(TTR("An occluder polygon must be set (or drawn) for this occluder to take effect.")); } if (occluder_polygon.is_valid() && occluder_polygon->get_polygon().size() == 0) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("The occluder polygon for this occluder is empty. Please draw a polygon."); + warnings.push_back(TTR("The occluder polygon for this occluder is empty. Please draw a polygon.")); } - return warning; + return warnings; } void LightOccluder2D::set_as_sdf_collision(bool p_enable) { diff --git a/scene/2d/light_occluder_2d.h b/scene/2d/light_occluder_2d.h index f567c6d965..b4a48d1062 100644 --- a/scene/2d/light_occluder_2d.h +++ b/scene/2d/light_occluder_2d.h @@ -106,7 +106,7 @@ public: void set_as_sdf_collision(bool p_enable); bool is_set_as_sdf_collision() const; - String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; LightOccluder2D(); ~LightOccluder2D(); diff --git a/scene/2d/line_builder.cpp b/scene/2d/line_builder.cpp index 892ccadfda..c478f03356 100644 --- a/scene/2d/line_builder.cpp +++ b/scene/2d/line_builder.cpp @@ -375,7 +375,7 @@ void LineBuilder::build() { } if (intersection_result != SEGMENT_INTERSECT) { - // In this case the joint is too corrputed to be re-used, + // In this case the joint is too corrupted to be re-used, // start again the strip with fallback points strip_begin(pos_up0, pos_down0, color1, uvx1); } @@ -485,7 +485,7 @@ void LineBuilder::strip_add_tri(Vector2 up, Orientation orientation) { if (texture_mode != Line2D::LINE_TEXTURE_NONE) { // UVs are just one slice of the texture all along - // (otherwise we can't share the bottom vertice) + // (otherwise we can't share the bottom vertex) uvs.push_back(uvs[_last_index[opposite_orientation]]); } @@ -520,7 +520,7 @@ void LineBuilder::strip_add_arc(Vector2 center, float angle_delta, Orientation o strip_add_tri(rpos, orientation); } - // Last arc vertice + // Last arc vertex rpos = center + Vector2(Math::cos(end_angle), Math::sin(end_angle)) * radius; strip_add_tri(rpos, orientation); } @@ -569,7 +569,7 @@ void LineBuilder::new_arc(Vector2 center, Vector2 vbegin, float angle_delta, Col } } - // Last arc vertice + // Last arc vertex Vector2 sc = Vector2(Math::cos(end_angle), Math::sin(end_angle)); rpos = center + sc * radius; vertices.push_back(rpos); diff --git a/scene/2d/navigation_2d.cpp b/scene/2d/navigation_2d.cpp deleted file mode 100644 index bec5ee7984..0000000000 --- a/scene/2d/navigation_2d.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/*************************************************************************/ -/* navigation_2d.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "navigation_2d.h" -#include "servers/navigation_server_2d.h" - -void Navigation2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("get_rid"), &Navigation2D::get_rid); - - ClassDB::bind_method(D_METHOD("get_simple_path", "start", "end", "optimize"), &Navigation2D::get_simple_path, DEFVAL(true)); - ClassDB::bind_method(D_METHOD("get_closest_point", "to_point"), &Navigation2D::get_closest_point); - ClassDB::bind_method(D_METHOD("get_closest_point_owner", "to_point"), &Navigation2D::get_closest_point_owner); - - ClassDB::bind_method(D_METHOD("set_cell_size", "cell_size"), &Navigation2D::set_cell_size); - ClassDB::bind_method(D_METHOD("get_cell_size"), &Navigation2D::get_cell_size); - - ClassDB::bind_method(D_METHOD("set_edge_connection_margin", "margin"), &Navigation2D::set_edge_connection_margin); - ClassDB::bind_method(D_METHOD("get_edge_connection_margin"), &Navigation2D::get_edge_connection_margin); - - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "cell_size"), "set_cell_size", "get_cell_size"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "edge_connection_margin"), "set_edge_connection_margin", "get_edge_connection_margin"); -} - -void Navigation2D::_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_READY: { - NavigationServer2D::get_singleton()->map_set_active(map, true); - } break; - case NOTIFICATION_EXIT_TREE: { - NavigationServer2D::get_singleton()->map_set_active(map, false); - } break; - } -} - -void Navigation2D::set_cell_size(float p_cell_size) { - cell_size = p_cell_size; - NavigationServer2D::get_singleton()->map_set_cell_size(map, cell_size); -} - -void Navigation2D::set_edge_connection_margin(float p_edge_connection_margin) { - edge_connection_margin = p_edge_connection_margin; - NavigationServer2D::get_singleton()->map_set_edge_connection_margin(map, edge_connection_margin); -} - -Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vector2 &p_end, bool p_optimize) const { - return NavigationServer2D::get_singleton()->map_get_path(map, p_start, p_end, p_optimize); -} - -Vector2 Navigation2D::get_closest_point(const Vector2 &p_point) const { - return NavigationServer2D::get_singleton()->map_get_closest_point(map, p_point); -} - -RID Navigation2D::get_closest_point_owner(const Vector2 &p_point) const { - return NavigationServer2D::get_singleton()->map_get_closest_point_owner(map, p_point); -} - -Navigation2D::Navigation2D() { - map = NavigationServer2D::get_singleton()->map_create(); - set_cell_size(10); // Ten pixels - set_edge_connection_margin(100); -} - -Navigation2D::~Navigation2D() { - NavigationServer2D::get_singleton()->free(map); -} diff --git a/scene/2d/navigation_agent_2d.cpp b/scene/2d/navigation_agent_2d.cpp index 534e31b1f2..f9cbdbf377 100644 --- a/scene/2d/navigation_agent_2d.cpp +++ b/scene/2d/navigation_agent_2d.cpp @@ -32,19 +32,17 @@ #include "core/config/engine.h" #include "core/math/geometry_2d.h" -#include "scene/2d/navigation_2d.h" #include "servers/navigation_server_2d.h" void NavigationAgent2D::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_rid"), &NavigationAgent2D::get_rid); + ClassDB::bind_method(D_METHOD("set_target_desired_distance", "desired_distance"), &NavigationAgent2D::set_target_desired_distance); ClassDB::bind_method(D_METHOD("get_target_desired_distance"), &NavigationAgent2D::get_target_desired_distance); ClassDB::bind_method(D_METHOD("set_radius", "radius"), &NavigationAgent2D::set_radius); ClassDB::bind_method(D_METHOD("get_radius"), &NavigationAgent2D::get_radius); - ClassDB::bind_method(D_METHOD("set_navigation", "navigation"), &NavigationAgent2D::set_navigation_node); - ClassDB::bind_method(D_METHOD("get_navigation"), &NavigationAgent2D::get_navigation_node); - ClassDB::bind_method(D_METHOD("set_neighbor_dist", "neighbor_dist"), &NavigationAgent2D::set_neighbor_dist); ClassDB::bind_method(D_METHOD("get_neighbor_dist"), &NavigationAgent2D::get_neighbor_dist); @@ -92,41 +90,21 @@ void NavigationAgent2D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_READY: { agent_parent = Object::cast_to<Node2D>(get_parent()); - - NavigationServer2D::get_singleton()->agent_set_callback(agent, this, "_avoidance_done"); - - // Search the navigation node and set it - { - Navigation2D *nav = nullptr; - Node *p = get_parent(); - while (p != nullptr) { - nav = Object::cast_to<Navigation2D>(p); - if (nav != nullptr) { - p = nullptr; - } else { - p = p->get_parent(); - } - } - - set_navigation(nav); + if (agent_parent != nullptr) { + // place agent on navigation map first or else the RVO agent callback creation fails silently later + NavigationServer2D::get_singleton()->agent_set_map(get_rid(), agent_parent->get_world_2d()->get_navigation_map()); + NavigationServer2D::get_singleton()->agent_set_callback(agent, this, "_avoidance_done"); } - set_physics_process_internal(true); } break; case NOTIFICATION_EXIT_TREE: { agent_parent = nullptr; - set_navigation(nullptr); set_physics_process_internal(false); } break; case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { if (agent_parent) { NavigationServer2D::get_singleton()->agent_set_position(agent, agent_parent->get_global_transform().get_origin()); - if (!target_reached) { - if (distance_to_target() < target_desired_distance) { - emit_signal("target_reached"); - target_reached = true; - } - } + _check_distance_to_target(); } } break; } @@ -146,23 +124,13 @@ NavigationAgent2D::~NavigationAgent2D() { agent = RID(); // Pointless } -void NavigationAgent2D::set_navigation(Navigation2D *p_nav) { - if (navigation == p_nav) { - return; // Pointless - } - - navigation = p_nav; - NavigationServer2D::get_singleton()->agent_set_map(agent, navigation == nullptr ? RID() : navigation->get_rid()); -} - -void NavigationAgent2D::set_navigation_node(Node *p_nav) { - Navigation2D *nav = Object::cast_to<Navigation2D>(p_nav); - ERR_FAIL_COND(nav == nullptr); - set_navigation(nav); +void NavigationAgent2D::set_navigable_layers(uint32_t p_layers) { + navigable_layers = p_layers; + update_navigation(); } -Node *NavigationAgent2D::get_navigation_node() const { - return Object::cast_to<Node>(navigation); +uint32_t NavigationAgent2D::get_navigable_layers() const { + return navigable_layers; } void NavigationAgent2D::set_target_desired_distance(real_t p_dd) { @@ -270,24 +238,21 @@ void NavigationAgent2D::_avoidance_done(Vector3 p_new_velocity) { emit_signal("velocity_computed", velocity); } -String NavigationAgent2D::get_configuration_warning() const { - String warning = Node::get_configuration_warning(); +TypedArray<String> NavigationAgent2D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); if (!Object::cast_to<Node2D>(get_parent())) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("The NavigationAgent2D can be used only under a Node2D node"); + warnings.push_back(TTR("The NavigationAgent2D can be used only under a Node2D node")); } - return warning; + return warnings; } void NavigationAgent2D::update_navigation() { if (agent_parent == nullptr) { return; } - if (navigation == nullptr) { + if (!agent_parent->is_inside_tree()) { return; } if (update_frame_id == Engine::get_singleton()->get_physics_frames()) { @@ -319,7 +284,7 @@ void NavigationAgent2D::update_navigation() { } if (reload_path) { - navigation_path = NavigationServer2D::get_singleton()->map_get_path(navigation->get_rid(), o, target_location, true); + navigation_path = NavigationServer2D::get_singleton()->map_get_path(agent_parent->get_world_2d()->get_navigation_map(), o, target_location, true, navigable_layers); navigation_finished = false; nav_path_index = 0; emit_signal("path_changed"); @@ -335,6 +300,7 @@ void NavigationAgent2D::update_navigation() { while (o.distance_to(navigation_path[nav_path_index]) < target_desired_distance) { nav_path_index += 1; if (nav_path_index == navigation_path.size()) { + _check_distance_to_target(); nav_path_index -= 1; navigation_finished = true; emit_signal("navigation_finished"); @@ -343,3 +309,12 @@ void NavigationAgent2D::update_navigation() { } } } + +void NavigationAgent2D::_check_distance_to_target() { + if (!target_reached) { + if (distance_to_target() < target_desired_distance) { + emit_signal("target_reached"); + target_reached = true; + } + } +} diff --git a/scene/2d/navigation_agent_2d.h b/scene/2d/navigation_agent_2d.h index 6b7da4a5f2..234cad333f 100644 --- a/scene/2d/navigation_agent_2d.h +++ b/scene/2d/navigation_agent_2d.h @@ -35,16 +35,16 @@ #include "scene/main/node.h" class Node2D; -class Navigation2D; class NavigationAgent2D : public Node { GDCLASS(NavigationAgent2D, Node); Node2D *agent_parent = nullptr; - Navigation2D *navigation = nullptr; RID agent; + uint32_t navigable_layers = 1; + real_t target_desired_distance = 1.0; real_t radius; real_t neighbor_dist; @@ -74,18 +74,13 @@ public: NavigationAgent2D(); virtual ~NavigationAgent2D(); - void set_navigation(Navigation2D *p_nav); - const Navigation2D *get_navigation() const { - return navigation; - } - - void set_navigation_node(Node *p_nav); - Node *get_navigation_node() const; - RID get_rid() const { return agent; } + void set_navigable_layers(uint32_t p_layers); + uint32_t get_navigable_layers() const; + void set_target_desired_distance(real_t p_dd); real_t get_target_desired_distance() const { return target_desired_distance; @@ -141,10 +136,11 @@ public: void set_velocity(Vector2 p_velocity); void _avoidance_done(Vector3 p_new_velocity); - virtual String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; private: void update_navigation(); + void _check_distance_to_target(); }; #endif diff --git a/scene/2d/navigation_obstacle_2d.cpp b/scene/2d/navigation_obstacle_2d.cpp index 7e1aefe5e2..a06f7a9fd0 100644 --- a/scene/2d/navigation_obstacle_2d.cpp +++ b/scene/2d/navigation_obstacle_2d.cpp @@ -31,48 +31,31 @@ #include "navigation_obstacle_2d.h" #include "scene/2d/collision_shape_2d.h" -#include "scene/2d/navigation_2d.h" #include "scene/2d/physics_body_2d.h" #include "servers/navigation_server_2d.h" void NavigationObstacle2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_navigation", "navigation"), &NavigationObstacle2D::set_navigation_node); - ClassDB::bind_method(D_METHOD("get_navigation"), &NavigationObstacle2D::get_navigation_node); } void NavigationObstacle2D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_READY: { - update_agent_shape(); - - // Search the navigation node and set it - { - Navigation2D *nav = nullptr; - Node *p = get_parent(); - while (p != nullptr) { - nav = Object::cast_to<Navigation2D>(p); - if (nav != nullptr) { - p = nullptr; - } else { - p = p->get_parent(); - } - } - - set_navigation(nav); - } - set_physics_process_internal(true); } break; case NOTIFICATION_EXIT_TREE: { - set_navigation(nullptr); set_physics_process_internal(false); } break; + case NOTIFICATION_PARENTED: { + parent_node2d = Object::cast_to<Node2D>(get_parent()); + update_agent_shape(); + } break; + case NOTIFICATION_UNPARENTED: { + parent_node2d = nullptr; + } break; case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { - Node2D *node = Object::cast_to<Node2D>(get_parent()); - if (node) { - NavigationServer2D::get_singleton()->agent_set_position(agent, node->get_global_transform().get_origin()); + if (parent_node2d) { + NavigationServer2D::get_singleton()->agent_set_position(agent, parent_node2d->get_global_transform().get_origin()); } - } break; } } @@ -86,73 +69,48 @@ NavigationObstacle2D::~NavigationObstacle2D() { agent = RID(); // Pointless } -void NavigationObstacle2D::set_navigation(Navigation2D *p_nav) { - if (navigation == p_nav) { - return; // Pointless - } - - navigation = p_nav; - NavigationServer2D::get_singleton()->agent_set_map(agent, navigation == nullptr ? RID() : navigation->get_rid()); -} - -void NavigationObstacle2D::set_navigation_node(Node *p_nav) { - Navigation2D *nav = Object::cast_to<Navigation2D>(p_nav); - ERR_FAIL_COND(nav == nullptr); - set_navigation(nav); -} - -Node *NavigationObstacle2D::get_navigation_node() const { - return Object::cast_to<Node>(navigation); -} - -String NavigationObstacle2D::get_configuration_warning() const { - String warning = Node::get_configuration_warning(); +TypedArray<String> NavigationObstacle2D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); if (!Object::cast_to<Node2D>(get_parent())) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("The NavigationObstacle2D only serves to provide collision avoidance to a Node2D object."); + warnings.push_back(TTR("The NavigationObstacle2D only serves to provide collision avoidance to a Node2D object.")); } - return warning; + return warnings; } void NavigationObstacle2D::update_agent_shape() { - Node *node = get_parent(); - - // Estimate the radius of this physics body - real_t radius = 0.0; - for (int i(0); i < node->get_child_count(); i++) { - // For each collision shape - CollisionShape2D *cs = Object::cast_to<CollisionShape2D>(node->get_child(i)); - if (cs) { - // Take the distance between the Body center to the shape center - real_t r = cs->get_transform().get_origin().length(); - if (cs->get_shape().is_valid()) { - // and add the enclosing shape radius - r += cs->get_shape()->get_enclosing_radius(); + if (parent_node2d) { + // Estimate the radius of this physics body + real_t radius = 0.0; + for (int i(0); i < parent_node2d->get_child_count(); i++) { + // For each collision shape + CollisionShape2D *cs = Object::cast_to<CollisionShape2D>(parent_node2d->get_child(i)); + if (cs) { + // Take the distance between the Body center to the shape center + real_t r = cs->get_transform().get_origin().length(); + if (cs->get_shape().is_valid()) { + // and add the enclosing shape radius + r += cs->get_shape()->get_enclosing_radius(); + } + Size2 s = cs->get_global_transform().get_scale(); + r *= MAX(s.x, s.y); + // Takes the biggest radius + radius = MAX(radius, r); } - Size2 s = cs->get_global_transform().get_scale(); - r *= MAX(s.x, s.y); - // Takes the biggest radius - radius = MAX(radius, r); } - } - Node2D *node_2d = Object::cast_to<Node2D>(node); - if (node_2d) { - Vector2 s = node_2d->get_global_transform().get_scale(); + Vector2 s = parent_node2d->get_global_transform().get_scale(); radius *= MAX(s.x, s.y); - } - if (radius == 0.0) { - radius = 1.0; // Never a 0 radius - } + if (radius == 0.0) { + radius = 1.0; // Never a 0 radius + } - // Initialize the Agent as an object - NavigationServer2D::get_singleton()->agent_set_neighbor_dist(agent, 0.0); - NavigationServer2D::get_singleton()->agent_set_max_neighbors(agent, 0); - NavigationServer2D::get_singleton()->agent_set_time_horizon(agent, 0.0); - NavigationServer2D::get_singleton()->agent_set_radius(agent, radius); - NavigationServer2D::get_singleton()->agent_set_max_speed(agent, 0.0); + // Initialize the Agent as an object + NavigationServer2D::get_singleton()->agent_set_neighbor_dist(agent, 0.0); + NavigationServer2D::get_singleton()->agent_set_max_neighbors(agent, 0); + NavigationServer2D::get_singleton()->agent_set_time_horizon(agent, 0.0); + NavigationServer2D::get_singleton()->agent_set_radius(agent, radius); + NavigationServer2D::get_singleton()->agent_set_max_speed(agent, 0.0); + } } diff --git a/scene/2d/navigation_obstacle_2d.h b/scene/2d/navigation_obstacle_2d.h index 421f8ca7cd..9cffc2c0c3 100644 --- a/scene/2d/navigation_obstacle_2d.h +++ b/scene/2d/navigation_obstacle_2d.h @@ -31,15 +31,13 @@ #ifndef NAVIGATION_OBSTACLE_2D_H #define NAVIGATION_OBSTACLE_2D_H +#include "scene/2d/node_2d.h" #include "scene/main/node.h" -class Navigation2D; - class NavigationObstacle2D : public Node { GDCLASS(NavigationObstacle2D, Node); - Navigation2D *navigation = nullptr; - + Node2D *parent_node2d = nullptr; RID agent; protected: @@ -50,19 +48,11 @@ public: NavigationObstacle2D(); virtual ~NavigationObstacle2D(); - void set_navigation(Navigation2D *p_nav); - const Navigation2D *get_navigation() const { - return navigation; - } - - void set_navigation_node(Node *p_nav); - Node *get_navigation_node() const; - RID get_rid() const { return agent; } - virtual String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; private: void update_agent_shape(); diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp index b02cdf12ad..d2caf5bea8 100644 --- a/scene/2d/navigation_region_2d.cpp +++ b/scene/2d/navigation_region_2d.cpp @@ -34,7 +34,6 @@ #include "core/core_string_names.h" #include "core/math/geometry_2d.h" #include "core/os/mutex.h" -#include "navigation_2d.h" #include "servers/navigation_server_2d.h" #include "thirdparty/misc/polypartition.h" @@ -365,10 +364,10 @@ void NavigationRegion2D::set_enabled(bool p_enabled) { if (!enabled) { NavigationServer2D::get_singleton()->region_set_map(region, RID()); + NavigationServer2D::get_singleton()->disconnect("map_changed", callable_mp(this, &NavigationRegion2D::_map_changed)); } else { - if (navigation) { - NavigationServer2D::get_singleton()->region_set_map(region, navigation->get_rid()); - } + NavigationServer2D::get_singleton()->region_set_map(region, get_world_2d()->get_navigation_map()); + NavigationServer2D::get_singleton()->connect("map_changed", callable_mp(this, &NavigationRegion2D::_map_changed)); } if (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint()) { @@ -380,6 +379,14 @@ bool NavigationRegion2D::is_enabled() const { return enabled; } +void NavigationRegion2D::set_layers(uint32_t p_layers) { + NavigationServer2D::get_singleton()->region_set_layers(region, p_layers); +} + +uint32_t NavigationRegion2D::get_layers() const { + return NavigationServer2D::get_singleton()->region_get_layers(region); +} + ///////////////////////////// #ifdef TOOLS_ENABLED Rect2 NavigationRegion2D::_edit_get_rect() const { @@ -394,35 +401,24 @@ bool NavigationRegion2D::_edit_is_selected_on_click(const Point2 &p_point, doubl void NavigationRegion2D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { - Node2D *c = this; - while (c) { - navigation = Object::cast_to<Navigation2D>(c); - if (navigation) { - if (enabled) { - NavigationServer2D::get_singleton()->region_set_map(region, navigation->get_rid()); - } - break; - } - - c = Object::cast_to<Node2D>(c->get_parent()); + if (enabled) { + NavigationServer2D::get_singleton()->region_set_map(region, get_world_2d()->get_navigation_map()); + NavigationServer2D::get_singleton()->connect("map_changed", callable_mp(this, &NavigationRegion2D::_map_changed)); } - } break; case NOTIFICATION_TRANSFORM_CHANGED: { NavigationServer2D::get_singleton()->region_set_transform(region, get_global_transform()); - } break; case NOTIFICATION_EXIT_TREE: { - if (navigation) { - NavigationServer2D::get_singleton()->region_set_map(region, RID()); + NavigationServer2D::get_singleton()->region_set_map(region, RID()); + if (enabled) { + NavigationServer2D::get_singleton()->disconnect("map_changed", callable_mp(this, &NavigationRegion2D::_map_changed)); } - navigation = nullptr; } break; case NOTIFICATION_DRAW: { if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint()) && navpoly.is_valid()) { Vector<Vector2> verts = navpoly->get_vertices(); - int vsize = verts.size(); - if (vsize < 3) { + if (verts.size() < 3) { return; } @@ -432,33 +428,47 @@ void NavigationRegion2D::_notification(int p_what) { } else { color = get_tree()->get_debug_navigation_disabled_color(); } - Vector<Color> colors; - Vector<Vector2> vertices; - vertices.resize(vsize); - colors.resize(vsize); - { - const Vector2 *vr = verts.ptr(); - for (int i = 0; i < vsize; i++) { - vertices.write[i] = vr[i]; - colors.write[i] = color; - } - } + Color doors_color = color.lightened(0.2); - Vector<int> indices; + RandomPCG rand; for (int i = 0; i < navpoly->get_polygon_count(); i++) { + // An array of vertices for this polygon. Vector<int> polygon = navpoly->get_polygon(i); - - for (int j = 2; j < polygon.size(); j++) { - int kofs[3] = { 0, j - 1, j }; - for (int k = 0; k < 3; k++) { - int idx = polygon[kofs[k]]; - ERR_FAIL_INDEX(idx, vsize); - indices.push_back(idx); - } + Vector<Vector2> vertices; + vertices.resize(polygon.size()); + for (int j = 0; j < polygon.size(); j++) { + ERR_FAIL_INDEX(polygon[j], verts.size()); + vertices.write[j] = verts[polygon[j]]; } + + // Generate the polygon color, slightly randomly modified from the settings one. + Color random_variation_color; + random_variation_color.set_hsv(color.get_h() + rand.random(-1.0, 1.0) * 0.05, color.get_s(), color.get_v() + rand.random(-1.0, 1.0) * 0.1); + random_variation_color.a = color.a; + Vector<Color> colors; + colors.push_back(random_variation_color); + + RS::get_singleton()->canvas_item_add_polygon(get_canvas_item(), vertices, colors); + } + + // Draw the region + Transform2D xform = get_global_transform(); + const NavigationServer2D *ns = NavigationServer2D::get_singleton(); + float radius = ns->map_get_edge_connection_margin(get_world_2d()->get_navigation_map()) / 2.0; + for (int i = 0; i < ns->region_get_connections_count(region); i++) { + // Two main points + Vector2 a = ns->region_get_connection_pathway_start(region, i); + a = xform.affine_inverse().xform(a); + Vector2 b = ns->region_get_connection_pathway_end(region, i); + b = xform.affine_inverse().xform(b); + draw_line(a, b, doors_color); + + // Draw a circle to illustrate the margins. + float angle = (b - a).angle(); + draw_arc(a, radius, angle + Math_PI / 2.0, angle - Math_PI / 2.0 + Math_TAU, 10, doors_color); + draw_arc(b, radius, angle - Math_PI / 2.0, angle + Math_PI / 2.0, 10, doors_color); } - RS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), indices, vertices, colors); } } break; } @@ -481,7 +491,7 @@ void NavigationRegion2D::set_navigation_polygon(const Ref<NavigationPolygon> &p_ } _navpoly_changed(); - update_configuration_warning(); + update_configuration_warnings(); } Ref<NavigationPolygon> NavigationRegion2D::get_navigation_polygon() const { @@ -493,32 +503,22 @@ void NavigationRegion2D::_navpoly_changed() { update(); } } - -String NavigationRegion2D::get_configuration_warning() const { - if (!is_visible_in_tree() || !is_inside_tree()) { - return String(); +void NavigationRegion2D::_map_changed(RID p_map) { + if (enabled && get_world_2d()->get_navigation_map() == p_map) { + update(); } +} - String warning = Node2D::get_configuration_warning(); +TypedArray<String> NavigationRegion2D::get_configuration_warnings() const { + TypedArray<String> warnings = Node2D::get_configuration_warnings(); - if (!navpoly.is_valid()) { - if (!warning.is_empty()) { - warning += "\n\n"; + if (is_visible_in_tree() && is_inside_tree()) { + if (!navpoly.is_valid()) { + warnings.push_back(TTR("A NavigationMesh resource must be set or created for this node to work. Please set a property or draw a polygon.")); } - warning += TTR("A NavigationPolygon resource must be set or created for this node to work. Please set a property or draw a polygon."); } - const Node2D *c = this; - while (c) { - if (Object::cast_to<Navigation2D>(c)) { - return warning; - } - c = Object::cast_to<Node2D>(c->get_parent()); - } - if (!warning.is_empty()) { - warning += "\n\n"; - } - return warning + TTR("NavigationRegion2D must be a child or grandchild to a Navigation2D node. It only provides navigation data."); + return warnings; } void NavigationRegion2D::_bind_methods() { @@ -528,10 +528,14 @@ void NavigationRegion2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &NavigationRegion2D::set_enabled); ClassDB::bind_method(D_METHOD("is_enabled"), &NavigationRegion2D::is_enabled); + ClassDB::bind_method(D_METHOD("set_layers", "layers"), &NavigationRegion2D::set_layers); + ClassDB::bind_method(D_METHOD("get_layers"), &NavigationRegion2D::get_layers); + ClassDB::bind_method(D_METHOD("_navpoly_changed"), &NavigationRegion2D::_navpoly_changed); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navpoly", PROPERTY_HINT_RESOURCE_TYPE, "NavigationPolygon"), "set_navigation_polygon", "get_navigation_polygon"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "layers", PROPERTY_HINT_LAYERS_2D_NAVIGATION), "set_layers", "get_layers"); } NavigationRegion2D::NavigationRegion2D() { diff --git a/scene/2d/navigation_region_2d.h b/scene/2d/navigation_region_2d.h index 0b9a258a25..2db8d70791 100644 --- a/scene/2d/navigation_region_2d.h +++ b/scene/2d/navigation_region_2d.h @@ -91,17 +91,15 @@ public: ~NavigationPolygon() {} }; -class Navigation2D; - class NavigationRegion2D : public Node2D { GDCLASS(NavigationRegion2D, Node2D); bool enabled = true; RID region; - Navigation2D *navigation = nullptr; Ref<NavigationPolygon> navpoly; void _navpoly_changed(); + void _map_changed(RID p_RID); protected: void _notification(int p_what); @@ -116,10 +114,13 @@ public: void set_enabled(bool p_enabled); bool is_enabled() const; + void set_layers(uint32_t p_layers); + uint32_t get_layers() const; + void set_navigation_polygon(const Ref<NavigationPolygon> &p_navpoly); Ref<NavigationPolygon> get_navigation_polygon() const; - String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; NavigationRegion2D(); ~NavigationRegion2D(); diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp index bf311632c8..8afc43ddc9 100644 --- a/scene/2d/node_2d.cpp +++ b/scene/2d/node_2d.cpp @@ -71,12 +71,12 @@ Size2 Node2D::_edit_get_scale() const { return _scale; } -void Node2D::_edit_set_rotation(float p_rotation) { +void Node2D::_edit_set_rotation(real_t p_rotation) { angle = p_rotation; _update_transform(); } -float Node2D::_edit_get_rotation() const { +real_t Node2D::_edit_get_rotation() const { return angle; } @@ -148,7 +148,7 @@ void Node2D::set_position(const Point2 &p_pos) { _update_transform(); } -void Node2D::set_rotation(float p_radians) { +void Node2D::set_rotation(real_t p_radians) { if (_xform_dirty) { ((Node2D *)this)->_update_xform_values(); } @@ -156,7 +156,7 @@ void Node2D::set_rotation(float p_radians) { _update_transform(); } -void Node2D::set_skew(float p_radians) { +void Node2D::set_skew(real_t p_radians) { if (_xform_dirty) { ((Node2D *)this)->_update_xform_values(); } @@ -164,11 +164,11 @@ void Node2D::set_skew(float p_radians) { _update_transform(); } -void Node2D::set_rotation_degrees(float p_degrees) { +void Node2D::set_rotation_degrees(real_t p_degrees) { set_rotation(Math::deg2rad(p_degrees)); } -void Node2D::set_skew_degrees(float p_degrees) { +void Node2D::set_skew_degrees(real_t p_degrees) { set_skew(Math::deg2rad(p_degrees)); } @@ -194,7 +194,7 @@ Point2 Node2D::get_position() const { return pos; } -float Node2D::get_rotation() const { +real_t Node2D::get_rotation() const { if (_xform_dirty) { ((Node2D *)this)->_update_xform_values(); } @@ -202,7 +202,7 @@ float Node2D::get_rotation() const { return angle; } -float Node2D::get_skew() const { +real_t Node2D::get_skew() const { if (_xform_dirty) { ((Node2D *)this)->_update_xform_values(); } @@ -210,11 +210,11 @@ float Node2D::get_skew() const { return skew; } -float Node2D::get_rotation_degrees() const { +real_t Node2D::get_rotation_degrees() const { return Math::rad2deg(get_rotation()); } -float Node2D::get_skew_degrees() const { +real_t Node2D::get_skew_degrees() const { return Math::rad2deg(get_skew()); } @@ -230,7 +230,7 @@ Transform2D Node2D::get_transform() const { return _mat; } -void Node2D::rotate(float p_radians) { +void Node2D::rotate(real_t p_radians) { set_rotation(get_rotation() + p_radians); } @@ -246,7 +246,7 @@ void Node2D::apply_scale(const Size2 &p_amount) { set_scale(get_scale() * p_amount); } -void Node2D::move_x(float p_delta, bool p_scaled) { +void Node2D::move_x(real_t p_delta, bool p_scaled) { Transform2D t = get_transform(); Vector2 m = t[0]; if (!p_scaled) { @@ -255,7 +255,7 @@ void Node2D::move_x(float p_delta, bool p_scaled) { set_position(t[2] + m * p_delta); } -void Node2D::move_y(float p_delta, bool p_scaled) { +void Node2D::move_y(real_t p_delta, bool p_scaled) { Transform2D t = get_transform(); Vector2 m = t[1]; if (!p_scaled) { @@ -279,25 +279,25 @@ void Node2D::set_global_position(const Point2 &p_pos) { } } -float Node2D::get_global_rotation() const { +real_t Node2D::get_global_rotation() const { return get_global_transform().get_rotation(); } -void Node2D::set_global_rotation(float p_radians) { +void Node2D::set_global_rotation(real_t p_radians) { CanvasItem *pi = get_parent_item(); if (pi) { - const float parent_global_rot = pi->get_global_transform().get_rotation(); + const real_t parent_global_rot = pi->get_global_transform().get_rotation(); set_rotation(p_radians - parent_global_rot); } else { set_rotation(p_radians); } } -float Node2D::get_global_rotation_degrees() const { +real_t Node2D::get_global_rotation_degrees() const { return Math::rad2deg(get_global_rotation()); } -void Node2D::set_global_rotation_degrees(float p_degrees) { +void Node2D::set_global_rotation_degrees(real_t p_degrees) { set_global_rotation(Math::deg2rad(p_degrees)); } @@ -379,7 +379,7 @@ void Node2D::look_at(const Vector2 &p_pos) { rotate(get_angle_to(p_pos)); } -float Node2D::get_angle_to(const Vector2 &p_pos) const { +real_t Node2D::get_angle_to(const Vector2 &p_pos) const { return (to_local(p_pos) * get_scale()).angle(); } diff --git a/scene/2d/node_2d.h b/scene/2d/node_2d.h index c27d740b8a..358b7e6520 100644 --- a/scene/2d/node_2d.h +++ b/scene/2d/node_2d.h @@ -37,9 +37,9 @@ class Node2D : public CanvasItem { GDCLASS(Node2D, CanvasItem); Point2 pos; - float angle = 0.0; + real_t angle = 0.0; Size2 _scale = Vector2(1, 1); - float skew = 0.0; + real_t skew = 0.0; int z_index = 0; bool z_relative = true; @@ -65,51 +65,51 @@ public: virtual void _edit_set_scale(const Size2 &p_scale) override; virtual Size2 _edit_get_scale() const override; - virtual void _edit_set_rotation(float p_rotation) override; - virtual float _edit_get_rotation() const override; + virtual void _edit_set_rotation(real_t p_rotation) override; + virtual real_t _edit_get_rotation() const override; virtual bool _edit_use_rotation() const override; virtual void _edit_set_rect(const Rect2 &p_edit_rect) override; #endif void set_position(const Point2 &p_pos); - void set_rotation(float p_radians); - void set_rotation_degrees(float p_degrees); - void set_skew(float p_radians); - void set_skew_degrees(float p_radians); + void set_rotation(real_t p_radians); + void set_rotation_degrees(real_t p_degrees); + void set_skew(real_t p_radians); + void set_skew_degrees(real_t p_radians); void set_scale(const Size2 &p_scale); - void rotate(float p_radians); - void move_x(float p_delta, bool p_scaled = false); - void move_y(float p_delta, bool p_scaled = false); + void rotate(real_t p_radians); + void move_x(real_t p_delta, bool p_scaled = false); + void move_y(real_t p_delta, bool p_scaled = false); void translate(const Vector2 &p_amount); void global_translate(const Vector2 &p_amount); void apply_scale(const Size2 &p_amount); Point2 get_position() const; - float get_rotation() const; - float get_skew() const; - float get_rotation_degrees() const; - float get_skew_degrees() const; + real_t get_rotation() const; + real_t get_skew() const; + real_t get_rotation_degrees() const; + real_t get_skew_degrees() const; Size2 get_scale() const; Point2 get_global_position() const; - float get_global_rotation() const; - float get_global_rotation_degrees() const; + real_t get_global_rotation() const; + real_t get_global_rotation_degrees() const; Size2 get_global_scale() const; void set_transform(const Transform2D &p_transform); void set_global_transform(const Transform2D &p_transform); void set_global_position(const Point2 &p_pos); - void set_global_rotation(float p_radians); - void set_global_rotation_degrees(float p_degrees); + void set_global_rotation(real_t p_radians); + void set_global_rotation_degrees(real_t p_degrees); void set_global_scale(const Size2 &p_scale); void set_z_index(int p_z); int get_z_index() const; void look_at(const Vector2 &p_pos); - float get_angle_to(const Vector2 &p_pos) const; + real_t get_angle_to(const Vector2 &p_pos) const; Point2 to_local(Point2 p_global) const; Point2 to_global(Point2 p_local) const; diff --git a/scene/2d/parallax_background.cpp b/scene/2d/parallax_background.cpp index c93915d1bc..4870ae614b 100644 --- a/scene/2d/parallax_background.cpp +++ b/scene/2d/parallax_background.cpp @@ -51,11 +51,11 @@ void ParallaxBackground::_camera_moved(const Transform2D &p_transform, const Poi set_scroll_offset(p_transform.get_origin()); } -void ParallaxBackground::set_scroll_scale(float p_scale) { +void ParallaxBackground::set_scroll_scale(real_t p_scale) { scale = p_scale; } -float ParallaxBackground::get_scroll_scale() const { +real_t ParallaxBackground::get_scroll_scale() const { return scale; } diff --git a/scene/2d/parallax_background.h b/scene/2d/parallax_background.h index c9991efc9d..27134dab29 100644 --- a/scene/2d/parallax_background.h +++ b/scene/2d/parallax_background.h @@ -39,7 +39,7 @@ class ParallaxBackground : public CanvasLayer { GDCLASS(ParallaxBackground, CanvasLayer); Point2 offset; - float scale = 1.0; + real_t scale = 1.0; Point2 base_offset; Point2 base_scale = Vector2(1, 1); Point2 screen_offset; @@ -61,8 +61,8 @@ public: void set_scroll_offset(const Point2 &p_ofs); Point2 get_scroll_offset() const; - void set_scroll_scale(float p_scale); - float get_scroll_scale() const; + void set_scroll_scale(real_t p_scale); + real_t get_scroll_scale() const; void set_scroll_base_offset(const Point2 &p_ofs); Point2 get_scroll_base_offset() const; diff --git a/scene/2d/parallax_layer.cpp b/scene/2d/parallax_layer.cpp index a38338e1e3..228020d383 100644 --- a/scene/2d/parallax_layer.cpp +++ b/scene/2d/parallax_layer.cpp @@ -39,7 +39,7 @@ void ParallaxLayer::set_motion_scale(const Size2 &p_scale) { ParallaxBackground *pb = Object::cast_to<ParallaxBackground>(get_parent()); if (pb && is_inside_tree()) { Vector2 ofs = pb->get_final_offset(); - float scale = pb->get_scroll_scale(); + real_t scale = pb->get_scroll_scale(); set_base_offset_and_scale(ofs, scale, screen_offset); } } @@ -54,7 +54,7 @@ void ParallaxLayer::set_motion_offset(const Size2 &p_offset) { ParallaxBackground *pb = Object::cast_to<ParallaxBackground>(get_parent()); if (pb && is_inside_tree()) { Vector2 ofs = pb->get_final_offset(); - float scale = pb->get_scroll_scale(); + real_t scale = pb->get_scroll_scale(); set_base_offset_and_scale(ofs, scale, screen_offset); } } @@ -107,7 +107,7 @@ void ParallaxLayer::_notification(int p_what) { } } -void ParallaxLayer::set_base_offset_and_scale(const Point2 &p_offset, float p_scale, const Point2 &p_screen_offset) { +void ParallaxLayer::set_base_offset_and_scale(const Point2 &p_offset, real_t p_scale, const Point2 &p_screen_offset) { screen_offset = p_screen_offset; if (!is_inside_tree()) { @@ -135,17 +135,14 @@ void ParallaxLayer::set_base_offset_and_scale(const Point2 &p_offset, float p_sc _update_mirroring(); } -String ParallaxLayer::get_configuration_warning() const { - String warning = Node2D::get_configuration_warning(); +TypedArray<String> ParallaxLayer::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); if (!Object::cast_to<ParallaxBackground>(get_parent())) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("ParallaxLayer node only works when set as child of a ParallaxBackground node."); + warnings.push_back(TTR("ParallaxLayer node only works when set as child of a ParallaxBackground node.")); } - return warning; + return warnings; } void ParallaxLayer::_bind_methods() { diff --git a/scene/2d/parallax_layer.h b/scene/2d/parallax_layer.h index 86694c7724..cc2d2e096e 100644 --- a/scene/2d/parallax_layer.h +++ b/scene/2d/parallax_layer.h @@ -59,9 +59,9 @@ public: void set_mirroring(const Size2 &p_mirroring); Size2 get_mirroring() const; - void set_base_offset_and_scale(const Point2 &p_offset, float p_scale, const Point2 &p_screen_offset); + void set_base_offset_and_scale(const Point2 &p_offset, real_t p_scale, const Point2 &p_screen_offset); - virtual String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; ParallaxLayer(); }; diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp index 724998641f..9912612c4f 100644 --- a/scene/2d/path_2d.cpp +++ b/scene/2d/path_2d.cpp @@ -96,9 +96,9 @@ void Path2D::_notification(int p_what) { } #ifdef TOOLS_ENABLED - const float line_width = 2 * EDSCALE; + const real_t line_width = 2 * EDSCALE; #else - const float line_width = 2; + const real_t line_width = 2; #endif const Color color = Color(0.5, 0.6, 1.0, 0.7); @@ -164,14 +164,14 @@ void PathFollow2D::_update_transform() { return; } - float path_length = c->get_baked_length(); + real_t path_length = c->get_baked_length(); if (path_length == 0) { return; } Vector2 pos = c->interpolate_baked(offset, cubic); if (rotates) { - float ahead = offset + lookahead; + real_t ahead = offset + lookahead; if (loop && ahead >= path_length) { // If our lookahead will loop, we need to check if the path is closed. @@ -240,7 +240,7 @@ bool PathFollow2D::get_cubic_interpolation() const { void PathFollow2D::_validate_property(PropertyInfo &property) const { if (property.name == "offset") { - float max = 10000.0; + real_t max = 10000.0; if (path && path->get_curve().is_valid()) { max = path->get_curve()->get_baked_length(); } @@ -249,21 +249,16 @@ void PathFollow2D::_validate_property(PropertyInfo &property) const { } } -String PathFollow2D::get_configuration_warning() const { - if (!is_visible_in_tree() || !is_inside_tree()) { - return String(); - } - - String warning = Node2D::get_configuration_warning(); +TypedArray<String> PathFollow2D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); - if (!Object::cast_to<Path2D>(get_parent())) { - if (!warning.is_empty()) { - warning += "\n\n"; + if (is_visible_in_tree() && is_inside_tree()) { + if (!Object::cast_to<Path2D>(get_parent())) { + warnings.push_back(TTR("PathFollow2D only works when set as a child of a Path2D node.")); } - warning += TTR("PathFollow2D only works when set as a child of a Path2D node."); } - return warning; + return warnings; } void PathFollow2D::_bind_methods() { @@ -301,11 +296,11 @@ void PathFollow2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lookahead", PROPERTY_HINT_RANGE, "0.001,1024.0,0.001"), "set_lookahead", "get_lookahead"); } -void PathFollow2D::set_offset(float p_offset) { +void PathFollow2D::set_offset(real_t p_offset) { offset = p_offset; if (path) { if (path->get_curve().is_valid()) { - float path_length = path->get_curve()->get_baked_length(); + real_t path_length = path->get_curve()->get_baked_length(); if (loop) { offset = Math::fposmod(offset, path_length); @@ -321,39 +316,39 @@ void PathFollow2D::set_offset(float p_offset) { } } -void PathFollow2D::set_h_offset(float p_h_offset) { +void PathFollow2D::set_h_offset(real_t p_h_offset) { h_offset = p_h_offset; if (path) { _update_transform(); } } -float PathFollow2D::get_h_offset() const { +real_t PathFollow2D::get_h_offset() const { return h_offset; } -void PathFollow2D::set_v_offset(float p_v_offset) { +void PathFollow2D::set_v_offset(real_t p_v_offset) { v_offset = p_v_offset; if (path) { _update_transform(); } } -float PathFollow2D::get_v_offset() const { +real_t PathFollow2D::get_v_offset() const { return v_offset; } -float PathFollow2D::get_offset() const { +real_t PathFollow2D::get_offset() const { return offset; } -void PathFollow2D::set_unit_offset(float p_unit_offset) { +void PathFollow2D::set_unit_offset(real_t p_unit_offset) { if (path && path->get_curve().is_valid() && path->get_curve()->get_baked_length()) { set_offset(p_unit_offset * path->get_curve()->get_baked_length()); } } -float PathFollow2D::get_unit_offset() const { +real_t PathFollow2D::get_unit_offset() const { if (path && path->get_curve().is_valid() && path->get_curve()->get_baked_length()) { return get_offset() / path->get_curve()->get_baked_length(); } else { @@ -361,11 +356,11 @@ float PathFollow2D::get_unit_offset() const { } } -void PathFollow2D::set_lookahead(float p_lookahead) { +void PathFollow2D::set_lookahead(real_t p_lookahead) { lookahead = p_lookahead; } -float PathFollow2D::get_lookahead() const { +real_t PathFollow2D::get_lookahead() const { return lookahead; } diff --git a/scene/2d/path_2d.h b/scene/2d/path_2d.h index a748817555..3b12f025fc 100644 --- a/scene/2d/path_2d.h +++ b/scene/2d/path_2d.h @@ -81,20 +81,20 @@ protected: static void _bind_methods(); public: - void set_offset(float p_offset); - float get_offset() const; + void set_offset(real_t p_offset); + real_t get_offset() const; - void set_h_offset(float p_h_offset); - float get_h_offset() const; + void set_h_offset(real_t p_h_offset); + real_t get_h_offset() const; - void set_v_offset(float p_v_offset); - float get_v_offset() const; + void set_v_offset(real_t p_v_offset); + real_t get_v_offset() const; - void set_unit_offset(float p_unit_offset); - float get_unit_offset() const; + void set_unit_offset(real_t p_unit_offset); + real_t get_unit_offset() const; - void set_lookahead(float p_lookahead); - float get_lookahead() const; + void set_lookahead(real_t p_lookahead); + real_t get_lookahead() const; void set_loop(bool p_loop); bool has_loop() const; @@ -105,7 +105,7 @@ public: void set_cubic_interpolation(bool p_enable); bool get_cubic_interpolation() const; - String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; PathFollow2D() {} }; diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index 2241f45534..4f52f62e99 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -42,70 +42,9 @@ void PhysicsBody2D::_notification(int p_what) { } void PhysicsBody2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_collision_layer", "layer"), &PhysicsBody2D::set_collision_layer); - ClassDB::bind_method(D_METHOD("get_collision_layer"), &PhysicsBody2D::get_collision_layer); - ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &PhysicsBody2D::set_collision_mask); - ClassDB::bind_method(D_METHOD("get_collision_mask"), &PhysicsBody2D::get_collision_mask); - - ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &PhysicsBody2D::set_collision_mask_bit); - ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &PhysicsBody2D::get_collision_mask_bit); - - ClassDB::bind_method(D_METHOD("set_collision_layer_bit", "bit", "value"), &PhysicsBody2D::set_collision_layer_bit); - ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &PhysicsBody2D::get_collision_layer_bit); - ClassDB::bind_method(D_METHOD("get_collision_exceptions"), &PhysicsBody2D::get_collision_exceptions); ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body"), &PhysicsBody2D::add_collision_exception_with); ClassDB::bind_method(D_METHOD("remove_collision_exception_with", "body"), &PhysicsBody2D::remove_collision_exception_with); - - ADD_GROUP("Collision", "collision_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_layer", "get_collision_layer"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_mask", "get_collision_mask"); -} - -void PhysicsBody2D::set_collision_layer(uint32_t p_layer) { - collision_layer = p_layer; - PhysicsServer2D::get_singleton()->body_set_collision_layer(get_rid(), p_layer); -} - -uint32_t PhysicsBody2D::get_collision_layer() const { - return collision_layer; -} - -void PhysicsBody2D::set_collision_mask(uint32_t p_mask) { - collision_mask = p_mask; - PhysicsServer2D::get_singleton()->body_set_collision_mask(get_rid(), p_mask); -} - -uint32_t PhysicsBody2D::get_collision_mask() const { - return collision_mask; -} - -void PhysicsBody2D::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 PhysicsBody2D::get_collision_mask_bit(int p_bit) const { - return get_collision_mask() & (1 << p_bit); -} - -void PhysicsBody2D::set_collision_layer_bit(int p_bit, bool p_value) { - uint32_t collision_layer = get_collision_layer(); - if (p_value) { - collision_layer |= 1 << p_bit; - } else { - collision_layer &= ~(1 << p_bit); - } - set_collision_layer(collision_layer); -} - -bool PhysicsBody2D::get_collision_layer_bit(int p_bit) const { - return get_collision_layer() & (1 << p_bit); } PhysicsBody2D::PhysicsBody2D(PhysicsServer2D::BodyMode p_mode) : @@ -334,6 +273,7 @@ bool RigidBody2D::_test_motion(const Vector2 &p_motion, bool p_infinite_inertia, void RigidBody2D::_direct_state_changed(Object *p_state) { #ifdef DEBUG_ENABLED state = Object::cast_to<PhysicsDirectBodyState2D>(p_state); + ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState2D object as argument"); #else state = (PhysicsDirectBodyState2D *)p_state; //trust it #endif @@ -417,13 +357,13 @@ void RigidBody2D::_direct_state_changed(Object *p_state) { } } - //process remotions + //process removals for (int i = 0; i < toremove_count; i++) { _body_inout(0, toremove[i].rid, toremove[i].body_id, toremove[i].pair.body_shape, toremove[i].pair.local_shape); } - //process aditions + //process additions for (int i = 0; i < toadd_count; i++) { _body_inout(1, toadd[i].rid, toadd[i].id, toadd[i].shape, toadd[i].local_shape); @@ -714,26 +654,23 @@ void RigidBody2D::_notification(int p_what) { if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) { if (Engine::get_singleton()->is_editor_hint()) { - update_configuration_warning(); + update_configuration_warnings(); } } #endif } -String RigidBody2D::get_configuration_warning() const { +TypedArray<String> RigidBody2D::get_configuration_warnings() const { Transform2D t = get_transform(); - String warning = CollisionObject2D::get_configuration_warning(); + TypedArray<String> warnings = CollisionObject2D::get_configuration_warnings(); if ((get_mode() == MODE_RIGID || get_mode() == MODE_CHARACTER) && (ABS(t.elements[0].length() - 1.0) > 0.05 || ABS(t.elements[1].length() - 1.0) > 0.05)) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("Size changes to RigidBody2D (in character or rigid modes) will be overridden by the physics engine when running.\nChange the size in children collision shapes instead."); + warnings.push_back(TTR("Size changes to RigidBody2D (in character or rigid modes) will be overridden by the physics engine when running.\nChange the size in children collision shapes instead.")); } - return warning; + return warnings; } void RigidBody2D::_bind_methods() { @@ -799,8 +736,6 @@ void RigidBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("test_motion", "motion", "infinite_inertia", "margin", "result"), &RigidBody2D::_test_motion, DEFVAL(true), DEFVAL(0.08), DEFVAL(Variant())); - ClassDB::bind_method(D_METHOD("_direct_state_changed"), &RigidBody2D::_direct_state_changed); - ClassDB::bind_method(D_METHOD("get_colliding_bodies"), &RigidBody2D::get_colliding_bodies); BIND_VMETHOD(MethodInfo("_integrate_forces", PropertyInfo(Variant::OBJECT, "state", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsDirectBodyState2D"))); @@ -844,7 +779,7 @@ void RigidBody2D::_bind_methods() { RigidBody2D::RigidBody2D() : PhysicsBody2D(PhysicsServer2D::BODY_MODE_RIGID) { - PhysicsServer2D::get_singleton()->body_set_force_integration_callback(get_rid(), this, "_direct_state_changed"); + PhysicsServer2D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &RigidBody2D::_direct_state_changed)); } RigidBody2D::~RigidBody2D() { @@ -1150,11 +1085,11 @@ void KinematicBody2D::set_sync_to_physics(bool p_enable) { } if (p_enable) { - PhysicsServer2D::get_singleton()->body_set_force_integration_callback(get_rid(), this, "_direct_state_changed"); + PhysicsServer2D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &KinematicBody2D::_direct_state_changed)); set_only_update_transform_changes(true); set_notify_local_transform(true); } else { - PhysicsServer2D::get_singleton()->body_set_force_integration_callback(get_rid(), nullptr, ""); + PhysicsServer2D::get_singleton()->body_set_force_integration_callback(get_rid(), Callable()); set_only_update_transform_changes(false); set_notify_local_transform(false); } @@ -1170,6 +1105,7 @@ void KinematicBody2D::_direct_state_changed(Object *p_state) { } PhysicsDirectBodyState2D *state = Object::cast_to<PhysicsDirectBodyState2D>(p_state); + ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState2D object as argument"); last_valid_transform = state->get_transform(); set_notify_local_transform(false); @@ -1223,8 +1159,6 @@ void KinematicBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_sync_to_physics", "enable"), &KinematicBody2D::set_sync_to_physics); ClassDB::bind_method(D_METHOD("is_sync_to_physics_enabled"), &KinematicBody2D::is_sync_to_physics_enabled); - ClassDB::bind_method(D_METHOD("_direct_state_changed"), &KinematicBody2D::_direct_state_changed); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "motion/sync_to_physics"), "set_sync_to_physics", "is_sync_to_physics_enabled"); } diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h index fda70ac557..47d55d11fa 100644 --- a/scene/2d/physics_body_2d.h +++ b/scene/2d/physics_body_2d.h @@ -41,9 +41,6 @@ class KinematicCollision2D; class PhysicsBody2D : public CollisionObject2D { GDCLASS(PhysicsBody2D, CollisionObject2D); - uint32_t collision_layer = 1; - uint32_t collision_mask = 1; - protected: void _notification(int p_what); PhysicsBody2D(PhysicsServer2D::BodyMode p_mode); @@ -51,18 +48,6 @@ protected: static void _bind_methods(); public: - void set_collision_layer(uint32_t p_layer); - uint32_t get_collision_layer() 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_collision_layer_bit(int p_bit, bool p_value); - bool get_collision_layer_bit(int p_bit) const; - TypedArray<PhysicsBody2D> get_collision_exceptions(); void add_collision_exception_with(Node *p_node); //must be physicsbody void remove_collision_exception_with(Node *p_node); @@ -248,7 +233,7 @@ public: TypedArray<Node2D> get_colliding_bodies() const; //function for script - virtual String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; RigidBody2D(); ~RigidBody2D(); diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp index a60a32f1d2..21083e6a4b 100644 --- a/scene/2d/polygon_2d.cpp +++ b/scene/2d/polygon_2d.cpp @@ -88,13 +88,13 @@ bool Polygon2D::_edit_is_selected_on_click(const Point2 &p_point, double p_toler } return Geometry2D::is_point_in_polygon(p_point - get_offset(), polygon2d); } +#endif void Polygon2D::_validate_property(PropertyInfo &property) const { if (!invert && property.name == "invert_border") { property.usage = PROPERTY_USAGE_NOEDITOR; } } -#endif void Polygon2D::_skeleton_bone_setup_changed() { update(); @@ -160,8 +160,8 @@ void Polygon2D::_notification(int p_what) { if (invert) { Rect2 bounds; int highest_idx = -1; - float highest_y = -1e20; - float sum = 0.0; + real_t highest_y = -1e20; + real_t sum = 0.0; for (int i = 0; i < len; i++) { if (i == 0) { @@ -279,7 +279,7 @@ void Polygon2D::_notification(int p_what) { //normalize the weights for (int i = 0; i < vc; i++) { - float tw = 0.0; + real_t tw = 0.0; for (int j = 0; j < 4; j++) { tw += weightsw[i * 4 + j]; } @@ -302,17 +302,18 @@ void Polygon2D::_notification(int p_what) { colors.write[i] = color_r[i]; } } else { - colors.push_back(color); + colors.resize(len); + for (int i = 0; i < len; i++) { + colors.write[i] = color; + } } + Vector<int> index_array; + if (invert || polygons.size() == 0) { - Vector<int> indices = Geometry2D::triangulate_polygon(points); - if (indices.size()) { - RS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), indices, points, colors, uvs, bones, weights, texture.is_valid() ? texture->get_rid() : RID(), -1); - } + index_array = Geometry2D::triangulate_polygon(points); } else { //draw individual polygons - Vector<int> total_indices; for (int i = 0; i < polygons.size(); i++) { Vector<int> src_indices = polygons[i]; int ic = src_indices.size(); @@ -333,18 +334,38 @@ void Polygon2D::_notification(int p_what) { int ic2 = indices.size(); const int *r2 = indices.ptr(); - int bic = total_indices.size(); - total_indices.resize(bic + ic2); - int *w2 = total_indices.ptrw(); + int bic = index_array.size(); + index_array.resize(bic + ic2); + int *w2 = index_array.ptrw(); for (int j = 0; j < ic2; j++) { w2[j + bic] = r[r2[j]]; } } + } + + RS::get_singleton()->mesh_clear(mesh); + + if (index_array.size()) { + Array arr; + arr.resize(RS::ARRAY_MAX); + arr[RS::ARRAY_VERTEX] = points; + if (uvs.size() == points.size()) { + arr[RS::ARRAY_TEX_UV] = uvs; + } + if (colors.size() == points.size()) { + arr[RS::ARRAY_COLOR] = colors; + } - if (total_indices.size()) { - RS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), total_indices, points, colors, uvs, bones, weights, texture.is_valid() ? texture->get_rid() : RID()); + if (bones.size() == points.size() * 4) { + arr[RS::ARRAY_BONES] = bones; + arr[RS::ARRAY_WEIGHTS] = weights; } + + arr[RS::ARRAY_INDEX] = index_array; + + RS::get_singleton()->mesh_add_surface_from_arrays(mesh, RS::PRIMITIVE_TRIANGLES, arr, Array(), Dictionary(), RS::ARRAY_FLAG_USE_2D_VERTICES); + RS::get_singleton()->canvas_item_add_mesh(get_canvas_item(), mesh, Transform2D(), Color(), texture.is_valid() ? texture->get_rid() : RID()); } } break; @@ -432,20 +453,20 @@ Vector2 Polygon2D::get_texture_offset() const { return tex_ofs; } -void Polygon2D::set_texture_rotation(float p_rot) { +void Polygon2D::set_texture_rotation(real_t p_rot) { tex_rot = p_rot; update(); } -float Polygon2D::get_texture_rotation() const { +real_t Polygon2D::get_texture_rotation() const { return tex_rot; } -void Polygon2D::set_texture_rotation_degrees(float p_rot) { +void Polygon2D::set_texture_rotation_degrees(real_t p_rot) { set_texture_rotation(Math::deg2rad(p_rot)); } -float Polygon2D::get_texture_rotation_degrees() const { +real_t Polygon2D::get_texture_rotation_degrees() const { return Math::rad2deg(get_texture_rotation()); } @@ -477,12 +498,12 @@ bool Polygon2D::get_antialiased() const { return antialiased; } -void Polygon2D::set_invert_border(float p_invert_border) { +void Polygon2D::set_invert_border(real_t p_invert_border) { invert_border = p_invert_border; update(); } -float Polygon2D::get_invert_border() const { +real_t Polygon2D::get_invert_border() const { return invert_border; } @@ -655,4 +676,9 @@ void Polygon2D::_bind_methods() { } Polygon2D::Polygon2D() { + mesh = RS::get_singleton()->mesh_create(); +} + +Polygon2D::~Polygon2D() { + RS::get_singleton()->free(mesh); } diff --git a/scene/2d/polygon_2d.h b/scene/2d/polygon_2d.h index 43a66aad13..f9f36ff9a2 100644 --- a/scene/2d/polygon_2d.h +++ b/scene/2d/polygon_2d.h @@ -55,9 +55,9 @@ class Polygon2D : public Node2D { Size2 tex_scale = Vector2(1, 1); Vector2 tex_ofs; bool tex_tile = true; - float tex_rot = 0.0; + real_t tex_rot = 0.0; bool invert = false; - float invert_border = 100.0; + real_t invert_border = 100.0; bool antialiased = false; Vector2 offset; @@ -72,13 +72,12 @@ class Polygon2D : public Node2D { void _skeleton_bone_setup_changed(); -#ifdef TOOLS_ENABLED - void _validate_property(PropertyInfo &property) const override; -#endif + RID mesh; protected: void _notification(int p_what); static void _bind_methods(); + void _validate_property(PropertyInfo &property) const override; public: #ifdef TOOLS_ENABLED @@ -118,11 +117,11 @@ public: void set_texture_offset(const Vector2 &p_offset); Vector2 get_texture_offset() const; - void set_texture_rotation(float p_rot); - float get_texture_rotation() const; + void set_texture_rotation(real_t p_rot); + real_t get_texture_rotation() const; - void set_texture_rotation_degrees(float p_rot); - float get_texture_rotation_degrees() const; + void set_texture_rotation_degrees(real_t p_rot); + real_t get_texture_rotation_degrees() const; void set_texture_scale(const Size2 &p_scale); Size2 get_texture_scale() const; @@ -133,8 +132,8 @@ public: void set_antialiased(bool p_antialiased); bool get_antialiased() const; - void set_invert_border(float p_invert_border); - float get_invert_border() const; + void set_invert_border(real_t p_invert_border); + real_t get_invert_border() const; void set_offset(const Vector2 &p_offset); Vector2 get_offset() const; @@ -152,6 +151,7 @@ public: NodePath get_skeleton() const; Polygon2D(); + ~Polygon2D(); }; #endif // POLYGON_2D_H diff --git a/scene/2d/position_2d.cpp b/scene/2d/position_2d.cpp index ff7a0dbac3..5c7d65e3e0 100644 --- a/scene/2d/position_2d.cpp +++ b/scene/2d/position_2d.cpp @@ -33,10 +33,10 @@ #include "core/config/engine.h" #include "scene/resources/texture.h" -const float DEFAULT_GIZMO_EXTENTS = 10.0; +const real_t DEFAULT_GIZMO_EXTENTS = 10.0; void Position2D::_draw_cross() { - float extents = get_gizmo_extents(); + real_t extents = get_gizmo_extents(); // Colors taken from `axis_x_color` and `axis_y_color` (defined in `editor/editor_themes.cpp`) draw_line(Point2(-extents, 0), Point2(+extents, 0), Color(0.96, 0.20, 0.32)); draw_line(Point2(0, -extents), Point2(0, +extents), Color(0.53, 0.84, 0.01)); @@ -44,7 +44,7 @@ void Position2D::_draw_cross() { #ifdef TOOLS_ENABLED Rect2 Position2D::_edit_get_rect() const { - float extents = get_gizmo_extents(); + real_t extents = get_gizmo_extents(); return Rect2(Point2(-extents, -extents), Size2(extents * 2, extents * 2)); } @@ -70,7 +70,7 @@ void Position2D::_notification(int p_what) { } } -void Position2D::set_gizmo_extents(float p_extents) { +void Position2D::set_gizmo_extents(real_t p_extents) { if (p_extents == DEFAULT_GIZMO_EXTENTS) { set_meta("_gizmo_extents_", Variant()); } else { @@ -80,7 +80,7 @@ void Position2D::set_gizmo_extents(float p_extents) { update(); } -float Position2D::get_gizmo_extents() const { +real_t Position2D::get_gizmo_extents() const { if (has_meta("_gizmo_extents_")) { return get_meta("_gizmo_extents_"); } else { diff --git a/scene/2d/position_2d.h b/scene/2d/position_2d.h index fcaef0e6a3..9ed622c8f6 100644 --- a/scene/2d/position_2d.h +++ b/scene/2d/position_2d.h @@ -48,8 +48,8 @@ public: virtual bool _edit_use_rect() const override; #endif - void set_gizmo_extents(float p_extents); - float get_gizmo_extents() const; + void set_gizmo_extents(real_t p_extents); + real_t get_gizmo_extents() const; Position2D(); }; diff --git a/scene/2d/ray_cast_2d.cpp b/scene/2d/ray_cast_2d.cpp index 50625a0f39..f6740040c1 100644 --- a/scene/2d/ray_cast_2d.cpp +++ b/scene/2d/ray_cast_2d.cpp @@ -55,6 +55,7 @@ uint32_t RayCast2D::get_collision_mask() const { } void RayCast2D::set_collision_mask_bit(int p_bit, bool p_value) { + ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision mask bit must be between 0 and 31 inclusive."); uint32_t mask = get_collision_mask(); if (p_value) { mask |= 1 << p_bit; @@ -65,6 +66,7 @@ void RayCast2D::set_collision_mask_bit(int p_bit, bool p_value) { } bool RayCast2D::get_collision_mask_bit(int p_bit) const { + ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision mask bit must be between 0 and 31 inclusive."); return get_collision_mask() & (1 << p_bit); } diff --git a/scene/2d/remote_transform_2d.cpp b/scene/2d/remote_transform_2d.cpp index f10714e28a..a7613dc009 100644 --- a/scene/2d/remote_transform_2d.cpp +++ b/scene/2d/remote_transform_2d.cpp @@ -138,7 +138,7 @@ void RemoteTransform2D::set_remote_node(const NodePath &p_remote_node) { _update_remote(); } - update_configuration_warning(); + update_configuration_warnings(); } NodePath RemoteTransform2D::get_remote_node() const { @@ -185,17 +185,14 @@ void RemoteTransform2D::force_update_cache() { _update_cache(); } -String RemoteTransform2D::get_configuration_warning() const { - String warning = Node2D::get_configuration_warning(); +TypedArray<String> RemoteTransform2D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); if (!has_node(remote_node) || !Object::cast_to<Node2D>(get_node(remote_node))) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("Path property must point to a valid Node2D node to work."); + warnings.push_back(TTR("Path property must point to a valid Node2D node to work.")); } - return warning; + return warnings; } void RemoteTransform2D::_bind_methods() { diff --git a/scene/2d/remote_transform_2d.h b/scene/2d/remote_transform_2d.h index 4a26d7b339..36fddb58c7 100644 --- a/scene/2d/remote_transform_2d.h +++ b/scene/2d/remote_transform_2d.h @@ -70,7 +70,7 @@ public: void force_update_cache(); - virtual String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; RemoteTransform2D(); }; diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index 5728230a8c..22180797f0 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -100,7 +100,7 @@ void Bone2D::set_rest(const Transform2D &p_rest) { skeleton->_make_bone_setup_dirty(); } - update_configuration_warning(); + update_configuration_warnings(); } Transform2D Bone2D::get_rest() const { @@ -119,11 +119,11 @@ void Bone2D::apply_rest() { set_transform(rest); } -void Bone2D::set_default_length(float p_length) { +void Bone2D::set_default_length(real_t p_length) { default_length = p_length; } -float Bone2D::get_default_length() const { +real_t Bone2D::get_default_length() const { return default_length; } @@ -133,27 +133,21 @@ int Bone2D::get_index_in_skeleton() const { return skeleton_index; } -String Bone2D::get_configuration_warning() const { - String warning = Node2D::get_configuration_warning(); +TypedArray<String> Bone2D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); if (!skeleton) { - if (!warning.is_empty()) { - warning += "\n\n"; - } if (parent_bone) { - warning += TTR("This Bone2D chain should end at a Skeleton2D node."); + warnings.push_back(TTR("This Bone2D chain should end at a Skeleton2D node.")); } else { - warning += TTR("A Bone2D only works with a Skeleton2D or another Bone2D as parent node."); + warnings.push_back(TTR("A Bone2D only works with a Skeleton2D or another Bone2D as parent node.")); } } if (rest == Transform2D(0, 0, 0, 0, 0, 0)) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("This bone lacks a proper REST pose. Go to the Skeleton2D node and set one."); + warnings.push_back(TTR("This bone lacks a proper REST pose. Go to the Skeleton2D node and set one.")); } - return warning; + return warnings; } Bone2D::Bone2D() { @@ -184,7 +178,7 @@ void Skeleton2D::_update_bone_setup() { bone_setup_dirty = false; RS::get_singleton()->skeleton_allocate_data(skeleton, bones.size(), true); - bones.sort(); //sorty so they are always in the same order/index + bones.sort(); //sorting so that they are always in the same order/index for (int i = 0; i < bones.size(); i++) { bones.write[i].rest_inverse = bones[i].bone->get_skeleton_rest().affine_inverse(); //bind pose diff --git a/scene/2d/skeleton_2d.h b/scene/2d/skeleton_2d.h index 80ca8c80ac..fd62b87bde 100644 --- a/scene/2d/skeleton_2d.h +++ b/scene/2d/skeleton_2d.h @@ -46,7 +46,7 @@ class Bone2D : public Node2D { Bone2D *parent_bone = nullptr; Skeleton2D *skeleton = nullptr; Transform2D rest; - float default_length = 16.0; + real_t default_length = 16.0; int skeleton_index = -1; @@ -60,10 +60,10 @@ public: void apply_rest(); Transform2D get_skeleton_rest() const; - String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; - void set_default_length(float p_length); - float get_default_length() const; + void set_default_length(real_t p_length); + real_t get_default_length() const; int get_index_in_skeleton() const; diff --git a/scene/2d/sprite_2d.cpp b/scene/2d/sprite_2d.cpp index 31040020dd..7c93edbff9 100644 --- a/scene/2d/sprite_2d.cpp +++ b/scene/2d/sprite_2d.cpp @@ -77,14 +77,14 @@ Rect2 Sprite2D::get_anchorable_rect() const { return get_rect(); } -void Sprite2D::_get_rects(Rect2 &r_src_rect, Rect2 &r_dst_rect, bool &r_filter_clip) const { +void Sprite2D::_get_rects(Rect2 &r_src_rect, Rect2 &r_dst_rect, bool &r_filter_clip_enabled) const { Rect2 base_rect; - if (region) { - r_filter_clip = region_filter_clip; + if (region_enabled) { + r_filter_clip_enabled = region_filter_clip_enabled; base_rect = region_rect; } else { - r_filter_clip = false; + r_filter_clip_enabled = false; base_rect = Rect2(0, 0, texture->get_width(), texture->get_height()); } @@ -129,10 +129,10 @@ void Sprite2D::_notification(int p_what) { */ Rect2 src_rect, dst_rect; - bool filter_clip; - _get_rects(src_rect, dst_rect, filter_clip); + bool filter_clip_enabled; + _get_rects(src_rect, dst_rect, filter_clip_enabled); - texture->draw_rect_region(ci, dst_rect, src_rect, Color(1, 1, 1), false, filter_clip); + texture->draw_rect_region(ci, dst_rect, src_rect, Color(1, 1, 1), false, filter_clip_enabled); } break; } } @@ -199,18 +199,18 @@ bool Sprite2D::is_flipped_v() const { return vflip; } -void Sprite2D::set_region(bool p_region) { - if (p_region == region) { +void Sprite2D::set_region_enabled(bool p_region_enabled) { + if (p_region_enabled == region_enabled) { return; } - region = p_region; + region_enabled = p_region_enabled; update(); notify_property_list_changed(); } -bool Sprite2D::is_region() const { - return region; +bool Sprite2D::is_region_enabled() const { + return region_enabled; } void Sprite2D::set_region_rect(const Rect2 &p_region_rect) { @@ -220,7 +220,7 @@ void Sprite2D::set_region_rect(const Rect2 &p_region_rect) { region_rect = p_region_rect; - if (region) { + if (region_enabled) { item_rect_changed(); } } @@ -229,13 +229,13 @@ Rect2 Sprite2D::get_region_rect() const { return region_rect; } -void Sprite2D::set_region_filter_clip(bool p_enable) { - region_filter_clip = p_enable; +void Sprite2D::set_region_filter_clip_enabled(bool p_region_filter_clip_enabled) { + region_filter_clip_enabled = p_region_filter_clip_enabled; update(); } bool Sprite2D::is_region_filter_clip_enabled() const { - return region_filter_clip; + return region_filter_clip_enabled; } void Sprite2D::set_frame(int p_frame) { @@ -299,8 +299,8 @@ bool Sprite2D::is_pixel_opaque(const Point2 &p_point) const { } Rect2 src_rect, dst_rect; - bool filter_clip; - _get_rects(src_rect, dst_rect, filter_clip); + bool filter_clip_enabled; + _get_rects(src_rect, dst_rect, filter_clip_enabled); dst_rect.size = dst_rect.size.abs(); if (!dst_rect.has_point(p_point)) { @@ -350,7 +350,7 @@ Rect2 Sprite2D::get_rect() const { Size2i s; - if (region) { + if (region_enabled) { s = region_rect.size; } else { s = texture->get_size(); @@ -385,7 +385,7 @@ void Sprite2D::_validate_property(PropertyInfo &property) const { property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS; } - if (!region && (property.name == "region_rect" || property.name == "region_filter_clip")) { + if (!region_enabled && (property.name == "region_rect" || property.name == "region_filter_clip")) { property.usage = PROPERTY_USAGE_NOEDITOR; } } @@ -414,15 +414,15 @@ void Sprite2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_flip_v", "flip_v"), &Sprite2D::set_flip_v); ClassDB::bind_method(D_METHOD("is_flipped_v"), &Sprite2D::is_flipped_v); - ClassDB::bind_method(D_METHOD("set_region", "enabled"), &Sprite2D::set_region); - ClassDB::bind_method(D_METHOD("is_region"), &Sprite2D::is_region); + ClassDB::bind_method(D_METHOD("set_region_enabled", "enabled"), &Sprite2D::set_region_enabled); + ClassDB::bind_method(D_METHOD("is_region_enabled"), &Sprite2D::is_region_enabled); ClassDB::bind_method(D_METHOD("is_pixel_opaque", "pos"), &Sprite2D::is_pixel_opaque); ClassDB::bind_method(D_METHOD("set_region_rect", "rect"), &Sprite2D::set_region_rect); ClassDB::bind_method(D_METHOD("get_region_rect"), &Sprite2D::get_region_rect); - ClassDB::bind_method(D_METHOD("set_region_filter_clip", "enabled"), &Sprite2D::set_region_filter_clip); + ClassDB::bind_method(D_METHOD("set_region_filter_clip_enabled", "enabled"), &Sprite2D::set_region_filter_clip_enabled); ClassDB::bind_method(D_METHOD("is_region_filter_clip_enabled"), &Sprite2D::is_region_filter_clip_enabled); ClassDB::bind_method(D_METHOD("set_frame", "frame"), &Sprite2D::set_frame); @@ -455,9 +455,9 @@ void Sprite2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "frame_coords", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_frame_coords", "get_frame_coords"); ADD_GROUP("Region", "region_"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "region_enabled"), "set_region", "is_region"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "region_enabled"), "set_region_enabled", "is_region_enabled"); ADD_PROPERTY(PropertyInfo(Variant::RECT2, "region_rect"), "set_region_rect", "get_region_rect"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "region_filter_clip"), "set_region_filter_clip", "is_region_filter_clip_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "region_filter_clip_enabled"), "set_region_filter_clip_enabled", "is_region_filter_clip_enabled"); } Sprite2D::Sprite2D() { diff --git a/scene/2d/sprite_2d.h b/scene/2d/sprite_2d.h index fa765f457d..9db74cfe26 100644 --- a/scene/2d/sprite_2d.h +++ b/scene/2d/sprite_2d.h @@ -39,23 +39,23 @@ class Sprite2D : public Node2D { Ref<Texture2D> texture; Color specular_color; - float shininess = 0.0; + real_t shininess = 0.0; bool centered = true; Point2 offset; bool hflip = false; bool vflip = false; - bool region = false; + bool region_enabled = false; Rect2 region_rect; - bool region_filter_clip = false; + bool region_filter_clip_enabled = false; int frame = 0; int vframes = 1; int hframes = 1; - void _get_rects(Rect2 &r_src_rect, Rect2 &r_dst_rect, bool &r_filter_clip) const; + void _get_rects(Rect2 &r_src_rect, Rect2 &r_dst_rect, bool &r_filter_clip_enabled) const; void _texture_changed(); @@ -97,10 +97,10 @@ public: void set_flip_v(bool p_flip); bool is_flipped_v() const; - void set_region(bool p_region); - bool is_region() const; + void set_region_enabled(bool p_enabled); + bool is_region_enabled() const; - void set_region_filter_clip(bool p_enable); + void set_region_filter_clip_enabled(bool p_enabled); bool is_region_filter_clip_enabled() const; void set_region_rect(const Rect2 &p_region_rect); diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index d868ebae25..0afead0863 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -30,279 +30,323 @@ #include "tile_map.h" -#include "collision_object_2d.h" #include "core/io/marshalls.h" +#include "core/math/geometry_2d.h" #include "core/os/os.h" -#include "scene/2d/area_2d.h" -#include "servers/navigation_server_2d.h" -#include "servers/physics_server_2d.h" -int TileMap::_get_quadrant_size() const { - if (use_y_sort) { - return 1; - } else { - return quadrant_size; - } +void TileMapPattern::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i p_atlas_coords, int p_alternative_tile) { + ERR_FAIL_COND_MSG(p_coords.x < 0 || p_coords.y < 0, vformat("Cannot set cell with negative coords in a TileMapPattern. Wrong coords: %s", p_coords)); + + size = size.max(p_coords + Vector2i(1, 1)); + pattern[p_coords] = TileMapCell(p_source_id, p_atlas_coords, p_alternative_tile); } -void TileMap::_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_ENTER_TREE: { - Node2D *c = this; - while (c) { - navigation = Object::cast_to<Navigation2D>(c); - if (navigation) { - break; - } +bool TileMapPattern::has_cell(const Vector2i &p_coords) const { + return pattern.has(p_coords); +} - c = Object::cast_to<Node2D>(c->get_parent()); - } +void TileMapPattern::remove_cell(const Vector2i &p_coords, bool p_update_size) { + ERR_FAIL_COND(!pattern.has(p_coords)); - if (use_parent) { - _clear_quadrants(); - collision_parent = Object::cast_to<CollisionObject2D>(get_parent()); - } + pattern.erase(p_coords); + if (p_update_size) { + size = Vector2i(); + for (Map<Vector2i, TileMapCell>::Element *E = pattern.front(); E; E = E->next()) { + size = size.max(E->key() + Vector2i(1, 1)); + } + } +} - pending_update = true; - _recreate_quadrants(); - update_dirty_quadrants(); - RID space = get_world_2d()->get_space(); - _update_quadrant_transform(); - _update_quadrant_space(space); - update_configuration_warning(); +int TileMapPattern::get_cell_source_id(const Vector2i &p_coords) const { + ERR_FAIL_COND_V(!pattern.has(p_coords), -1); - } break; + return pattern[p_coords].source_id; +} - case NOTIFICATION_EXIT_TREE: { - _update_quadrant_space(RID()); - for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) { - Quadrant &q = E->get(); - if (navigation) { - for (Map<PosKey, Quadrant::NavPoly>::Element *F = q.navpoly_ids.front(); F; F = F->next()) { - NavigationServer2D::get_singleton()->region_set_map(F->get().region, RID()); - } - q.navpoly_ids.clear(); - } +Vector2i TileMapPattern::get_cell_atlas_coords(const Vector2i &p_coords) const { + ERR_FAIL_COND_V(!pattern.has(p_coords), TileSetAtlasSource::INVALID_ATLAS_COORDS); - if (collision_parent) { - collision_parent->remove_shape_owner(q.shape_owner_id); - q.shape_owner_id = -1; - } + return pattern[p_coords].get_atlas_coords(); +} - for (Map<PosKey, Quadrant::Occluder>::Element *F = q.occluder_instances.front(); F; F = F->next()) { - RS::get_singleton()->free(F->get().id); - } - q.occluder_instances.clear(); - } +int TileMapPattern::get_cell_alternative_tile(const Vector2i &p_coords) const { + ERR_FAIL_COND_V(!pattern.has(p_coords), TileSetAtlasSource::INVALID_TILE_ALTERNATIVE); - collision_parent = nullptr; - navigation = nullptr; + return pattern[p_coords].alternative_tile; +} - } break; +TypedArray<Vector2i> TileMapPattern::get_used_cells() const { + // Returns the cells used in the tilemap. + TypedArray<Vector2i> a; + a.resize(pattern.size()); + int i = 0; + for (Map<Vector2i, TileMapCell>::Element *E = pattern.front(); E; E = E->next()) { + Vector2i p(E->key().x, E->key().y); + a[i++] = p; + } - case NOTIFICATION_TRANSFORM_CHANGED: { - //move stuff - _update_quadrant_transform(); + return a; +} - } break; - case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { - if (use_parent) { - _recreate_quadrants(); - } +Vector2i TileMapPattern::get_size() const { + return size; +} - } break; +void TileMapPattern::set_size(const Vector2i &p_size) { + for (Map<Vector2i, TileMapCell>::Element *E = pattern.front(); E; E = E->next()) { + Vector2i coords = E->key(); + if (p_size.x <= coords.x || p_size.y <= coords.y) { + ERR_FAIL_MSG(vformat("Cannot set pattern size to %s, it contains a tile at %s. Size can only be increased.", p_size, coords)); + }; } + + size = p_size; } -void TileMap::_update_quadrant_space(const RID &p_space) { - if (!use_parent) { - for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) { - Quadrant &q = E->get(); - PhysicsServer2D::get_singleton()->body_set_space(q.body, p_space); - } - } +bool TileMapPattern::is_empty() const { + return pattern.is_empty(); +}; + +void TileMapPattern::clear() { + size = Vector2i(); + pattern.clear(); +}; + +void TileMapPattern::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_cell", "coords", "source_id", "atlas_coords", "alternative_tile"), &TileMapPattern::set_cell, DEFVAL(-1), DEFVAL(TileSetAtlasSource::INVALID_ATLAS_COORDS), DEFVAL(TileSetAtlasSource::INVALID_TILE_ALTERNATIVE)); + ClassDB::bind_method(D_METHOD("has_cell", "coords"), &TileMapPattern::has_cell); + ClassDB::bind_method(D_METHOD("remove_cell", "coords"), &TileMapPattern::remove_cell); + ClassDB::bind_method(D_METHOD("get_cell_source_id", "coords"), &TileMapPattern::get_cell_source_id); + ClassDB::bind_method(D_METHOD("get_cell_atlas_coords", "coords"), &TileMapPattern::get_cell_atlas_coords); + ClassDB::bind_method(D_METHOD("get_cell_alternative_tile", "coords"), &TileMapPattern::get_cell_alternative_tile); + + ClassDB::bind_method(D_METHOD("get_used_cells"), &TileMapPattern::get_used_cells); + ClassDB::bind_method(D_METHOD("get_size"), &TileMapPattern::get_size); + ClassDB::bind_method(D_METHOD("set_size", "size"), &TileMapPattern::set_size); + ClassDB::bind_method(D_METHOD("is_empty"), &TileMapPattern::is_empty); } -void TileMap::_update_quadrant_transform() { - if (!is_inside_tree()) { - return; +Vector2i TileMap::transform_coords_layout(Vector2i p_coords, TileSet::TileOffsetAxis p_offset_axis, TileSet::TileLayout p_from_layout, TileSet::TileLayout p_to_layout) { + // Transform to stacked layout. + Vector2i output = p_coords; + if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) { + SWAP(output.x, output.y); + } + switch (p_from_layout) { + case TileSet::TILE_LAYOUT_STACKED: + break; + case TileSet::TILE_LAYOUT_STACKED_OFFSET: + if (output.y % 2) { + output.x -= 1; + } + break; + case TileSet::TILE_LAYOUT_STAIRS_RIGHT: + case TileSet::TILE_LAYOUT_STAIRS_DOWN: + if ((p_from_layout == TileSet::TILE_LAYOUT_STAIRS_RIGHT) ^ (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL)) { + if (output.y < 0 && bool(output.y % 2)) { + output = Vector2i(output.x + output.y / 2 - 1, output.y); + } else { + output = Vector2i(output.x + output.y / 2, output.y); + } + } else { + if (output.x < 0 && bool(output.x % 2)) { + output = Vector2i(output.x / 2 - 1, output.x + output.y * 2); + } else { + output = Vector2i(output.x / 2, output.x + output.y * 2); + } + } + break; + case TileSet::TILE_LAYOUT_DIAMOND_RIGHT: + case TileSet::TILE_LAYOUT_DIAMOND_DOWN: + if ((p_from_layout == TileSet::TILE_LAYOUT_DIAMOND_RIGHT) ^ (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL)) { + if ((output.x + output.y) < 0 && (output.x - output.y) % 2) { + output = Vector2i((output.x + output.y) / 2 - 1, output.y - output.x); + } else { + output = Vector2i((output.x + output.y) / 2, -output.x + output.y); + } + } else { + if ((output.x - output.y) < 0 && (output.x + output.y) % 2) { + output = Vector2i((output.x - output.y) / 2 - 1, output.x + output.y); + } else { + output = Vector2i((output.x - output.y) / 2, output.x + output.y); + } + } + break; } - Transform2D global_transform = get_global_transform(); + switch (p_to_layout) { + case TileSet::TILE_LAYOUT_STACKED: + break; + case TileSet::TILE_LAYOUT_STACKED_OFFSET: + if (output.y % 2) { + output.x += 1; + } + break; + case TileSet::TILE_LAYOUT_STAIRS_RIGHT: + case TileSet::TILE_LAYOUT_STAIRS_DOWN: + if ((p_to_layout == TileSet::TILE_LAYOUT_STAIRS_RIGHT) ^ (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL)) { + if (output.y < 0 && (output.y % 2)) { + output = Vector2i(output.x - output.y / 2 + 1, output.y); + } else { + output = Vector2i(output.x - output.y / 2, output.y); + } + } else { + if (output.y % 2) { + if (output.y < 0) { + output = Vector2i(2 * output.x + 1, -output.x + output.y / 2 - 1); + } else { + output = Vector2i(2 * output.x + 1, -output.x + output.y / 2); + } + } else { + output = Vector2i(2 * output.x, -output.x + output.y / 2); + } + } + break; + case TileSet::TILE_LAYOUT_DIAMOND_RIGHT: + case TileSet::TILE_LAYOUT_DIAMOND_DOWN: + if ((p_to_layout == TileSet::TILE_LAYOUT_DIAMOND_RIGHT) ^ (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL)) { + if (output.y % 2) { + if (output.y > 0) { + output = Vector2i(output.x - output.y / 2, output.x + output.y / 2 + 1); + } else { + output = Vector2i(output.x - output.y / 2 + 1, output.x + output.y / 2); + } + } else { + output = Vector2i(output.x - output.y / 2, output.x + output.y / 2); + } + } else { + if (output.y % 2) { + if (output.y < 0) { + output = Vector2i(output.x + output.y / 2, -output.x + output.y / 2 - 1); + } else { + output = Vector2i(output.x + output.y / 2 + 1, -output.x + output.y / 2); + } + } else { + output = Vector2i(output.x + output.y / 2, -output.x + output.y / 2); + } + } + break; + } - Transform2D local_transform; - if (collision_parent) { - local_transform = get_transform(); + if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) { + SWAP(output.x, output.y); } - Transform2D nav_rel; - if (navigation) { - nav_rel = get_relative_transform_to_parent(navigation); + return output; +} + +int TileMap::get_effective_quadrant_size() const { + // When using YSort, the quadrant size is reduced to 1 to have one CanvasItem per quadrant + if (tile_set.is_valid() && tile_set->is_y_sorting()) { + return 1; + } else { + return quadrant_size; } +} - for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) { - Quadrant &q = E->get(); - Transform2D xform; - xform.set_origin(q.pos); +Vector2i TileMap::_coords_to_quadrant_coords(const Vector2i &p_coords) const { + int quadrant_size = get_effective_quadrant_size(); - if (!use_parent) { - xform = global_transform * xform; - PhysicsServer2D::get_singleton()->body_set_state(q.body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform); - } + // Rounding down, instead of simply rounding towards zero (truncating) + return Vector2i( + p_coords.x > 0 ? p_coords.x / quadrant_size : (p_coords.x - (quadrant_size - 1)) / quadrant_size, + p_coords.y > 0 ? p_coords.y / quadrant_size : (p_coords.y - (quadrant_size - 1)) / quadrant_size); +} - if (navigation) { - for (Map<PosKey, Quadrant::NavPoly>::Element *F = q.navpoly_ids.front(); F; F = F->next()) { - NavigationServer2D::get_singleton()->region_set_transform(F->get().region, nav_rel * F->get().xform); - } - } +void TileMap::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + pending_update = true; + _recreate_quadrants(); + } break; + case NOTIFICATION_EXIT_TREE: { + _clear_quadrants(); + } break; + } - for (Map<PosKey, Quadrant::Occluder>::Element *F = q.occluder_instances.front(); F; F = F->next()) { - RS::get_singleton()->canvas_light_occluder_set_transform(F->get().id, global_transform * F->get().xform); + // Transfers the notification to tileset plugins. + if (tile_set.is_valid()) { + for (int i = 0; i < tile_set->get_tile_set_atlas_plugins().size(); i++) { + tile_set->get_tile_set_atlas_plugins()[i]->tilemap_notification(this, p_what); } } } +Ref<TileSet> TileMap::get_tileset() const { + return tile_set; +} + void TileMap::set_tileset(const Ref<TileSet> &p_tileset) { - if (tile_set.is_valid()) { - tile_set->disconnect("changed", callable_mp(this, &TileMap::_recreate_quadrants)); + if (p_tileset == tile_set) { + return; } - _clear_quadrants(); - tile_set = p_tileset; - + // Set the tileset, registering to its changes. if (tile_set.is_valid()) { - tile_set->connect("changed", callable_mp(this, &TileMap::_recreate_quadrants)); - } else { - clear(); + tile_set->disconnect("changed", callable_mp(this, &TileMap::_make_all_quadrants_dirty)); + tile_set->disconnect("changed", callable_mp(this, &TileMap::_tile_set_changed)); } - _recreate_quadrants(); - emit_signal("settings_changed"); -} + if (!p_tileset.is_valid()) { + _clear_quadrants(); + } -Ref<TileSet> TileMap::get_tileset() const { - return tile_set; -} + tile_set = p_tileset; -void TileMap::set_cell_size(Size2 p_size) { - ERR_FAIL_COND(p_size.x < 1 || p_size.y < 1); + if (tile_set.is_valid()) { + tile_set->connect("changed", callable_mp(this, &TileMap::_make_all_quadrants_dirty), varray(true)); + tile_set->connect("changed", callable_mp(this, &TileMap::_tile_set_changed)); + _recreate_quadrants(); + } - _clear_quadrants(); - cell_size = p_size; - _recreate_quadrants(); - emit_signal("settings_changed"); + emit_signal("changed"); } -Size2 TileMap::get_cell_size() const { - return cell_size; +int TileMap::get_quadrant_size() const { + return quadrant_size; } void TileMap::set_quadrant_size(int p_size) { - ERR_FAIL_COND_MSG(p_size < 1, "Quadrant size cannot be smaller than 1."); + ERR_FAIL_COND_MSG(p_size < 1, "TileMapQuadrant size cannot be smaller than 1."); - _clear_quadrants(); quadrant_size = p_size; _recreate_quadrants(); - emit_signal("settings_changed"); + emit_signal("changed"); } -int TileMap::get_quadrant_size() const { - return quadrant_size; -} - -void TileMap::_fix_cell_transform(Transform2D &xform, const Cell &p_cell, const Vector2 &p_offset, const Size2 &p_sc) { +void TileMap::_fix_cell_transform(Transform2D &xform, const TileMapCell &p_cell, const Vector2 &p_offset, const Size2 &p_sc) { Size2 s = p_sc; Vector2 offset = p_offset; - if (compatibility_mode && !centered_textures) { - 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; - } - } + // Flip/transpose: update the tile transform. + TileSetSource *source = *tile_set->get_source(p_cell.source_id); + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (!atlas_source) { + return; } - - if (p_cell.transpose) { + TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(p_cell.get_atlas_coords(), p_cell.alternative_tile)); + if (tile_data->get_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) { + if (tile_data->get_flip_h()) { xform.elements[0].x = -xform.elements[0].x; xform.elements[1].x = -xform.elements[1].x; - if (compatibility_mode && !centered_textures) { - 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; - } - } else { - offset.x = s.x - offset.x; - } + offset.x = s.x - offset.x; } - if (p_cell.flip_v) { + if (tile_data->get_flip_v()) { xform.elements[0].y = -xform.elements[0].y; xform.elements[1].y = -xform.elements[1].y; - if (compatibility_mode && !centered_textures) { - 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; - } - } else { - offset.y = s.y - offset.y; - } + offset.y = s.y - offset.y; } - if (centered_textures) { - offset += cell_size / 2 - s / 2; - } xform.elements[2] += offset; } -void TileMap::_add_shape(int &shape_idx, const Quadrant &p_q, const Ref<Shape2D> &p_shape, const TileSet::ShapeData &p_shape_data, const Transform2D &p_xform, const Vector2 &p_metadata) { - PhysicsServer2D *ps = PhysicsServer2D::get_singleton(); - - if (!use_parent) { - ps->body_add_shape(p_q.body, p_shape->get_rid(), p_xform); - ps->body_set_shape_metadata(p_q.body, shape_idx, p_metadata); - ps->body_set_shape_as_one_way_collision(p_q.body, shape_idx, p_shape_data.one_way_collision, p_shape_data.one_way_collision_margin); - - } else if (collision_parent) { - Transform2D xform = p_xform; - xform.set_origin(xform.get_origin() + p_q.pos); - - collision_parent->shape_owner_add_shape(p_q.shape_owner_id, p_shape); - - int real_index = collision_parent->shape_owner_get_shape_index(p_q.shape_owner_id, shape_idx); - RID rid = collision_parent->get_rid(); - - if (Object::cast_to<Area2D>(collision_parent) != nullptr) { - ps->area_set_shape_transform(rid, real_index, get_transform() * xform); - } else { - ps->body_set_shape_transform(rid, real_index, get_transform() * xform); - ps->body_set_shape_metadata(rid, real_index, p_metadata); - ps->body_set_shape_as_one_way_collision(rid, real_index, p_shape_data.one_way_collision, p_shape_data.one_way_collision_margin); - } - } - shape_idx++; -} - void TileMap::update_dirty_quadrants() { if (!pending_update) { return; @@ -312,394 +356,47 @@ void TileMap::update_dirty_quadrants() { return; } - RenderingServer *vs = RenderingServer::get_singleton(); - PhysicsServer2D *ps = PhysicsServer2D::get_singleton(); - Vector2 tofs = get_cell_draw_offset(); - Transform2D nav_rel; - if (navigation) { - nav_rel = get_relative_transform_to_parent(navigation); + // Update the coords cache. + for (SelfList<TileMapQuadrant> *q = dirty_quadrant_list.first(); q; q = q->next()) { + q->self()->map_to_world.clear(); + q->self()->world_to_map.clear(); + for (Set<Vector2i>::Element *E = q->self()->cells.front(); E; E = E->next()) { + Vector2i pk = E->get(); + Vector2i pk_world_coords = map_to_world(pk); + q->self()->map_to_world[pk] = pk_world_coords; + q->self()->world_to_map[pk_world_coords] = pk; + } } - Vector2 qofs; - - SceneTree *st = SceneTree::get_singleton(); - Color debug_collision_color; - Color debug_navigation_color; - - bool debug_shapes = st && st->is_debugging_collisions_hint(); - if (debug_shapes) { - debug_collision_color = st->get_debug_collisions_color(); + // Call the update_dirty_quadrant method on plugins. + for (int i = 0; i < tile_set->get_tile_set_atlas_plugins().size(); i++) { + tile_set->get_tile_set_atlas_plugins()[i]->update_dirty_quadrants(this, dirty_quadrant_list); } - bool debug_navigation = st && st->is_debugging_navigation_hint(); - if (debug_navigation) { - debug_navigation_color = st->get_debug_navigation_color(); + // Redraw the debug canvas_items. + RenderingServer *rs = RenderingServer::get_singleton(); + for (SelfList<TileMapQuadrant> *q = dirty_quadrant_list.first(); q; q = q->next()) { + rs->canvas_item_clear(q->self()->debug_canvas_item); + Transform2D xform; + xform.set_origin(map_to_world(q->self()->coords * get_effective_quadrant_size())); + rs->canvas_item_set_transform(q->self()->debug_canvas_item, xform); + for (int i = 0; i < tile_set->get_tile_set_atlas_plugins().size(); i++) { + tile_set->get_tile_set_atlas_plugins()[i]->draw_quadrant_debug(this, q->self()); + } } + // Clear the list while (dirty_quadrant_list.first()) { - Quadrant &q = *dirty_quadrant_list.first()->self(); - - for (List<RID>::Element *E = q.canvas_items.front(); E; E = E->next()) { - vs->free(E->get()); - } - - q.canvas_items.clear(); - - if (!use_parent) { - ps->body_clear_shapes(q.body); - } else if (collision_parent) { - collision_parent->shape_owner_clear_shapes(q.shape_owner_id); - } - int shape_idx = 0; - - if (navigation) { - for (Map<PosKey, Quadrant::NavPoly>::Element *E = q.navpoly_ids.front(); E; E = E->next()) { - NavigationServer2D::get_singleton()->region_set_map(E->get().region, RID()); - } - q.navpoly_ids.clear(); - } - - for (Map<PosKey, Quadrant::Occluder>::Element *E = q.occluder_instances.front(); E; E = E->next()) { - RS::get_singleton()->free(E->get().id); - } - q.occluder_instances.clear(); - Ref<ShaderMaterial> prev_material; - int prev_z_index = 0; - RID prev_canvas_item; - RID prev_debug_canvas_item; - - for (int i = 0; i < q.cells.size(); i++) { - Map<PosKey, Cell>::Element *E = tile_map.find(q.cells[i]); - Cell &c = E->get(); - //moment of truth - if (!tile_set->has_tile(c.id)) { - continue; - } - Ref<Texture2D> tex = tile_set->tile_get_texture(c.id); - Vector2 tile_ofs = tile_set->tile_get_texture_offset(c.id); - - Vector2 wofs = _map_to_world(E->key().x, E->key().y); - Vector2 offset = wofs - q.pos + tofs; - - if (!tex.is_valid()) { - continue; - } - - Ref<ShaderMaterial> mat = tile_set->tile_get_material(c.id); - int z_index = tile_set->tile_get_z_index(c.id); - - if (tile_set->tile_get_tile_mode(c.id) == TileSet::AUTO_TILE || - tile_set->tile_get_tile_mode(c.id) == TileSet::ATLAS_TILE) { - z_index += tile_set->autotile_get_z_index(c.id, Vector2(c.autotile_coord_x, c.autotile_coord_y)); - } - - RID canvas_item; - RID debug_canvas_item; - - if (prev_canvas_item == RID() || prev_material != mat || prev_z_index != z_index) { - canvas_item = vs->canvas_item_create(); - if (mat.is_valid()) { - vs->canvas_item_set_material(canvas_item, mat->get_rid()); - } - vs->canvas_item_set_parent(canvas_item, get_canvas_item()); - _update_item_material_state(canvas_item); - Transform2D xform; - xform.set_origin(q.pos); - vs->canvas_item_set_transform(canvas_item, xform); - vs->canvas_item_set_light_mask(canvas_item, get_light_mask()); - vs->canvas_item_set_z_index(canvas_item, z_index); - - vs->canvas_item_set_default_texture_filter(canvas_item, RS::CanvasItemTextureFilter(CanvasItem::get_texture_filter())); - vs->canvas_item_set_default_texture_repeat(canvas_item, RS::CanvasItemTextureRepeat(CanvasItem::get_texture_repeat())); - - q.canvas_items.push_back(canvas_item); - - if (debug_shapes) { - debug_canvas_item = vs->canvas_item_create(); - vs->canvas_item_set_parent(debug_canvas_item, canvas_item); - vs->canvas_item_set_z_as_relative_to_parent(debug_canvas_item, false); - vs->canvas_item_set_z_index(debug_canvas_item, RS::CANVAS_ITEM_Z_MAX - 1); - q.canvas_items.push_back(debug_canvas_item); - prev_debug_canvas_item = debug_canvas_item; - } - - prev_canvas_item = canvas_item; - prev_material = mat; - prev_z_index = z_index; - - } else { - canvas_item = prev_canvas_item; - if (debug_shapes) { - debug_canvas_item = prev_debug_canvas_item; - } - } - - Rect2 r = tile_set->tile_get_region(c.id); - if (tile_set->tile_get_tile_mode(c.id) == TileSet::AUTO_TILE || tile_set->tile_get_tile_mode(c.id) == TileSet::ATLAS_TILE) { - int spacing = tile_set->autotile_get_spacing(c.id); - r.size = tile_set->autotile_get_size(c.id); - r.position += (r.size + Vector2(spacing, spacing)) * Vector2(c.autotile_coord_x, c.autotile_coord_y); - } - - Size2 s; - if (r == Rect2()) { - s = tex->get_size(); - } else { - s = r.size; - } - - Rect2 rect; - rect.position = offset.floor(); - rect.size = s; - rect.size.x += fp_adjust; - rect.size.y += fp_adjust; - - if (compatibility_mode && !centered_textures) { - 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; - } - } - } - - if (c.transpose) { - SWAP(tile_ofs.x, tile_ofs.y); - if (centered_textures) { - rect.position.x += cell_size.x / 2 - rect.size.y / 2; - rect.position.y += cell_size.y / 2 - rect.size.x / 2; - } - } else if (centered_textures) { - 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; - } - - if (compatibility_mode && !centered_textures) { - 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; - } - } - } else { - rect.position += tile_ofs; - } - - Color modulate = tile_set->tile_get_modulate(c.id); - Color self_modulate = get_self_modulate(); - modulate = Color(modulate.r * self_modulate.r, modulate.g * self_modulate.g, - modulate.b * self_modulate.b, modulate.a * self_modulate.a); - if (r == Rect2()) { - tex->draw_rect(canvas_item, rect, false, modulate, c.transpose); - } else { - tex->draw_rect_region(canvas_item, rect, r, modulate, c.transpose, clip_uv); - } - - Vector<TileSet::ShapeData> shapes = tile_set->tile_get_shapes(c.id); - - for (int j = 0; j < shapes.size(); j++) { - Ref<Shape2D> shape = shapes[j].shape; - if (shape.is_valid()) { - if (tile_set->tile_get_tile_mode(c.id) == TileSet::SINGLE_TILE || (shapes[j].autotile_coord.x == c.autotile_coord_x && shapes[j].autotile_coord.y == c.autotile_coord_y)) { - Transform2D xform; - xform.set_origin(offset.floor()); - - Vector2 shape_ofs = shapes[j].shape_transform.get_origin(); - - _fix_cell_transform(xform, c, shape_ofs, s); - - xform *= shapes[j].shape_transform.untranslated(); - - if (debug_canvas_item.is_valid()) { - vs->canvas_item_add_set_transform(debug_canvas_item, xform); - shape->draw(debug_canvas_item, debug_collision_color); - } - - if (shape->has_meta("decomposed")) { - Array _shapes = shape->get_meta("decomposed"); - for (int k = 0; k < _shapes.size(); k++) { - Ref<ConvexPolygonShape2D> convex = _shapes[k]; - if (convex.is_valid()) { - _add_shape(shape_idx, q, convex, shapes[j], xform, Vector2(E->key().x, E->key().y)); -#ifdef DEBUG_ENABLED - } else { - print_error("The TileSet assigned to the TileMap " + get_name() + " has an invalid convex shape."); -#endif - } - } - } else { - _add_shape(shape_idx, q, shape, shapes[j], xform, Vector2(E->key().x, E->key().y)); - } - } - } - } - - if (debug_canvas_item.is_valid()) { - vs->canvas_item_add_set_transform(debug_canvas_item, Transform2D()); - } - - if (navigation) { - Ref<NavigationPolygon> navpoly; - Vector2 npoly_ofs; - if (tile_set->tile_get_tile_mode(c.id) == TileSet::AUTO_TILE || tile_set->tile_get_tile_mode(c.id) == TileSet::ATLAS_TILE) { - navpoly = tile_set->autotile_get_navigation_polygon(c.id, Vector2(c.autotile_coord_x, c.autotile_coord_y)); - npoly_ofs = Vector2(); - } else { - navpoly = tile_set->tile_get_navigation_polygon(c.id); - npoly_ofs = tile_set->tile_get_navigation_polygon_offset(c.id); - } - - if (navpoly.is_valid()) { - Transform2D xform; - xform.set_origin(offset.floor() + q.pos); - _fix_cell_transform(xform, c, npoly_ofs, s); - - RID region = NavigationServer2D::get_singleton()->region_create(); - NavigationServer2D::get_singleton()->region_set_map(region, navigation->get_rid()); - NavigationServer2D::get_singleton()->region_set_transform(region, nav_rel * xform); - NavigationServer2D::get_singleton()->region_set_navpoly(region, navpoly); - - Quadrant::NavPoly np; - np.region = region; - np.xform = xform; - q.navpoly_ids[E->key()] = np; - - if (debug_navigation) { - RID debug_navigation_item = vs->canvas_item_create(); - vs->canvas_item_set_parent(debug_navigation_item, canvas_item); - vs->canvas_item_set_z_as_relative_to_parent(debug_navigation_item, false); - vs->canvas_item_set_z_index(debug_navigation_item, RS::CANVAS_ITEM_Z_MAX - 2); // Display one below collision debug - - if (debug_navigation_item.is_valid()) { - Vector<Vector2> navigation_polygon_vertices = navpoly->get_vertices(); - int vsize = navigation_polygon_vertices.size(); - - if (vsize > 2) { - Vector<Color> colors; - Vector<Vector2> vertices; - vertices.resize(vsize); - colors.resize(vsize); - { - const Vector2 *vr = navigation_polygon_vertices.ptr(); - for (int j = 0; j < vsize; j++) { - vertices.write[j] = vr[j]; - colors.write[j] = debug_navigation_color; - } - } - - Vector<int> indices; - - for (int j = 0; j < navpoly->get_polygon_count(); j++) { - Vector<int> polygon = navpoly->get_polygon(j); - - for (int k = 2; k < polygon.size(); k++) { - int kofs[3] = { 0, k - 1, k }; - for (int l = 0; l < 3; l++) { - int idx = polygon[kofs[l]]; - ERR_FAIL_INDEX(idx, vsize); - indices.push_back(idx); - } - } - } - Transform2D navxform; - navxform.set_origin(offset.floor()); - _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); - } - } - } - } - } - - Ref<OccluderPolygon2D> occluder; - if (tile_set->tile_get_tile_mode(c.id) == TileSet::AUTO_TILE || tile_set->tile_get_tile_mode(c.id) == TileSet::ATLAS_TILE) { - occluder = tile_set->autotile_get_light_occluder(c.id, Vector2(c.autotile_coord_x, c.autotile_coord_y)); - } else { - occluder = tile_set->tile_get_light_occluder(c.id); - } - if (occluder.is_valid()) { - 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, s); - - RID orid = RS::get_singleton()->canvas_light_occluder_create(); - RS::get_singleton()->canvas_light_occluder_set_transform(orid, get_global_transform() * xform); - RS::get_singleton()->canvas_light_occluder_set_polygon(orid, occluder->get_rid()); - RS::get_singleton()->canvas_light_occluder_attach_to_canvas(orid, get_canvas()); - RS::get_singleton()->canvas_light_occluder_set_light_mask(orid, occluder_light_mask); - Quadrant::Occluder oc; - oc.xform = xform; - oc.id = orid; - q.occluder_instances[E->key()] = oc; - } - } - dirty_quadrant_list.remove(dirty_quadrant_list.first()); - quadrant_order_dirty = true; } pending_update = false; - if (quadrant_order_dirty) { - int index = -(int64_t)0x80000000; //always must be drawn below children - for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) { - Quadrant &q = E->get(); - for (List<RID>::Element *F = q.canvas_items.front(); F; F = F->next()) { - RS::get_singleton()->canvas_item_set_draw_index(F->get(), index++); - } - } - - quadrant_order_dirty = false; - } - _recompute_rect_cache(); } void TileMap::_recompute_rect_cache() { + // Compute the displayed area of the tilemap. #ifdef DEBUG_ENABLED if (!rect_cache_dirty) { @@ -707,12 +404,12 @@ void TileMap::_recompute_rect_cache() { } Rect2 r_total; - for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) { + for (Map<Vector2i, TileMapQuadrant>::Element *E = quadrant_map.front(); E; E = E->next()) { Rect2 r; - r.position = _map_to_world(E->key().x * _get_quadrant_size(), E->key().y * _get_quadrant_size()); - r.expand_to(_map_to_world(E->key().x * _get_quadrant_size() + _get_quadrant_size(), E->key().y * _get_quadrant_size())); - r.expand_to(_map_to_world(E->key().x * _get_quadrant_size() + _get_quadrant_size(), E->key().y * _get_quadrant_size() + _get_quadrant_size())); - r.expand_to(_map_to_world(E->key().x * _get_quadrant_size(), E->key().y * _get_quadrant_size() + _get_quadrant_size())); + r.position = map_to_world(E->key() * get_effective_quadrant_size()); + r.expand_to(map_to_world((E->key() + Vector2i(1, 0)) * get_effective_quadrant_size())); + r.expand_to(map_to_world((E->key() + Vector2i(1, 1)) * get_effective_quadrant_size())); + r.expand_to(map_to_world((E->key() + Vector2i(0, 1)) * get_effective_quadrant_size())); if (E == quadrant_map.front()) { r_total = r; } else { @@ -728,85 +425,58 @@ void TileMap::_recompute_rect_cache() { #endif } -Map<TileMap::PosKey, TileMap::Quadrant>::Element *TileMap::_create_quadrant(const PosKey &p_qk) { - Transform2D xform; - //xform.set_origin(Point2(p_qk.x,p_qk.y)*cell_size*quadrant_size); - Quadrant q; - q.pos = _map_to_world(p_qk.x * _get_quadrant_size(), p_qk.y * _get_quadrant_size()); - q.pos += get_cell_draw_offset(); - if (tile_origin == TILE_ORIGIN_CENTER) { - q.pos += cell_size / 2; - } else if (tile_origin == TILE_ORIGIN_BOTTOM_LEFT) { - q.pos.y += cell_size.y; - } - - xform.set_origin(q.pos); - //q.canvas_item = RenderingServer::get_singleton()->canvas_item_create(); - if (!use_parent) { - q.body = PhysicsServer2D::get_singleton()->body_create(); - PhysicsServer2D::get_singleton()->body_set_mode(q.body, use_kinematic ? PhysicsServer2D::BODY_MODE_KINEMATIC : PhysicsServer2D::BODY_MODE_STATIC); - - PhysicsServer2D::get_singleton()->body_attach_object_instance_id(q.body, get_instance_id()); - PhysicsServer2D::get_singleton()->body_set_collision_layer(q.body, collision_layer); - PhysicsServer2D::get_singleton()->body_set_collision_mask(q.body, collision_mask); - PhysicsServer2D::get_singleton()->body_set_param(q.body, PhysicsServer2D::BODY_PARAM_FRICTION, friction); - PhysicsServer2D::get_singleton()->body_set_param(q.body, PhysicsServer2D::BODY_PARAM_BOUNCE, bounce); - - if (is_inside_tree()) { - xform = get_global_transform() * xform; - RID space = get_world_2d()->get_space(); - PhysicsServer2D::get_singleton()->body_set_space(q.body, space); - } +Map<Vector2i, TileMapQuadrant>::Element *TileMap::_create_quadrant(const Vector2i &p_qk) { + TileMapQuadrant q; + q.coords = p_qk; - PhysicsServer2D::get_singleton()->body_set_state(q.body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform); - } else if (collision_parent) { - xform = get_transform() * xform; - q.shape_owner_id = collision_parent->create_shape_owner(this); - } else { - q.shape_owner_id = -1; + rect_cache_dirty = true; + + // Create the debug canvas item. + RenderingServer *rs = RenderingServer::get_singleton(); + q.debug_canvas_item = rs->canvas_item_create(); + rs->canvas_item_set_z_index(q.debug_canvas_item, RS::CANVAS_ITEM_Z_MAX - 1); + rs->canvas_item_set_parent(q.debug_canvas_item, get_canvas_item()); + + // Call the create_quadrant method on plugins + if (tile_set.is_valid()) { + for (int i = 0; i < tile_set->get_tile_set_atlas_plugins().size(); i++) { + tile_set->get_tile_set_atlas_plugins()[i]->create_quadrant(this, &q); + } } - rect_cache_dirty = true; - quadrant_order_dirty = true; return quadrant_map.insert(p_qk, q); } -void TileMap::_erase_quadrant(Map<PosKey, Quadrant>::Element *Q) { - Quadrant &q = Q->get(); - if (!use_parent) { - PhysicsServer2D::get_singleton()->free(q.body); - } else if (collision_parent) { - collision_parent->remove_shape_owner(q.shape_owner_id); - } - - for (List<RID>::Element *E = q.canvas_items.front(); E; E = E->next()) { - RenderingServer::get_singleton()->free(E->get()); - } - q.canvas_items.clear(); - if (q.dirty_list.in_list()) { - dirty_quadrant_list.remove(&q.dirty_list); - } +void TileMap::_erase_quadrant(Map<Vector2i, TileMapQuadrant>::Element *Q) { + // Remove a quadrant. + TileMapQuadrant *q = &(Q->get()); - if (navigation) { - for (Map<PosKey, Quadrant::NavPoly>::Element *E = q.navpoly_ids.front(); E; E = E->next()) { - NavigationServer2D::get_singleton()->region_set_map(E->get().region, RID()); + // Call the cleanup_quadrant method on plugins. + if (tile_set.is_valid()) { + for (int i = 0; i < tile_set->get_tile_set_atlas_plugins().size(); i++) { + tile_set->get_tile_set_atlas_plugins()[i]->cleanup_quadrant(this, q); } - q.navpoly_ids.clear(); } - for (Map<PosKey, Quadrant::Occluder>::Element *E = q.occluder_instances.front(); E; E = E->next()) { - RS::get_singleton()->free(E->get().id); + // Remove the quadrant from the dirty_list if it is there. + if (q->dirty_list_element.in_list()) { + dirty_quadrant_list.remove(&(q->dirty_list_element)); } - q.occluder_instances.clear(); + + // Free the debug canvas item. + RenderingServer *rs = RenderingServer::get_singleton(); + rs->free(q->debug_canvas_item); quadrant_map.erase(Q); rect_cache_dirty = true; } -void TileMap::_make_quadrant_dirty(Map<PosKey, Quadrant>::Element *Q, bool update) { - Quadrant &q = Q->get(); - if (!q.dirty_list.in_list()) { - dirty_quadrant_list.add(&q.dirty_list); +void TileMap::_make_all_quadrants_dirty(bool p_update) { + // Make all quandrants dirty, then trigger an update later. + for (Map<Vector2i, TileMapQuadrant>::Element *E = quadrant_map.front(); E; E = E->next()) { + if (!E->value().dirty_list_element.in_list()) { + dirty_quadrant_list.add(&E->value().dirty_list_element); + } } if (pending_update) { @@ -816,39 +486,68 @@ void TileMap::_make_quadrant_dirty(Map<PosKey, Quadrant>::Element *Q, bool updat if (!is_inside_tree()) { return; } - - if (update) { + if (p_update) { call_deferred("update_dirty_quadrants"); } } -void TileMap::set_cellv(const Vector2 &p_pos, int p_tile, bool p_flip_x, bool p_flip_y, bool p_transpose) { - set_cell(p_pos.x, p_pos.y, p_tile, p_flip_x, p_flip_y, p_transpose); -} +void TileMap::_make_quadrant_dirty(Map<Vector2i, TileMapQuadrant>::Element *Q, bool p_update) { + // Make the given quadrant dirty, then trigger an update later. + TileMapQuadrant &q = Q->get(); + if (!q.dirty_list_element.in_list()) { + dirty_quadrant_list.add(&q.dirty_list_element); + } -void TileMap::_set_celld(const Vector2 &p_pos, const Dictionary &p_data) { - Variant v_pos_x = p_pos.x, v_pos_y = p_pos.y, v_tile = p_data["id"], v_flip_h = p_data["flip_h"], v_flip_v = p_data["flip_y"], v_transpose = p_data["transpose"], v_autotile_coord = p_data["auto_coord"]; - const Variant *args[7] = { &v_pos_x, &v_pos_y, &v_tile, &v_flip_h, &v_flip_v, &v_transpose, &v_autotile_coord }; - Callable::CallError ce; - call("set_cell", args, 7, ce); + if (pending_update) { + return; + } + pending_update = true; + if (!is_inside_tree()) { + return; + } + + if (p_update) { + call_deferred("update_dirty_quadrants"); + } } -void TileMap::set_cell(int p_x, int p_y, int p_tile, bool p_flip_x, bool p_flip_y, bool p_transpose, Vector2 p_autotile_coord) { - PosKey pk(p_x, p_y); +void TileMap::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i p_atlas_coords, int p_alternative_tile) { + // Set the current cell tile (using integer position). + Vector2i pk(p_coords); + Map<Vector2i, TileMapCell>::Element *E = tile_map.find(pk); + + int source_id = p_source_id; + Vector2i atlas_coords = p_atlas_coords; + int alternative_tile = p_alternative_tile; - Map<PosKey, Cell>::Element *E = tile_map.find(pk); - if (!E && p_tile == INVALID_CELL) { - return; //nothing to do + if ((source_id == -1 || atlas_coords == TileSetAtlasSource::INVALID_ATLAS_COORDS || alternative_tile == TileSetAtlasSource::INVALID_TILE_ALTERNATIVE) && + (source_id != -1 || atlas_coords != TileSetAtlasSource::INVALID_ATLAS_COORDS || alternative_tile != TileSetAtlasSource::INVALID_TILE_ALTERNATIVE)) { + WARN_PRINT("Setting a cell a cell as empty requires both source_id, atlas_coord and alternative_tile to be set to their respective \"invalid\" values. Values were thus changes accordingly."); + source_id = -1; + atlas_coords = TileSetAtlasSource::INVALID_ATLAS_COORDS; + alternative_tile = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE; } - PosKey qk = pk.to_quadrant(_get_quadrant_size()); - if (p_tile == INVALID_CELL) { - //erase existing + if (!E && source_id == -1) { + return; // Nothing to do, the tile is already empty. + } + + // Get the quadrant + Vector2i qk = _coords_to_quadrant_coords(pk); + + Map<Vector2i, TileMapQuadrant>::Element *Q = quadrant_map.find(qk); + + if (source_id == -1) { + // Erase existing cell in the tile map. tile_map.erase(pk); - Map<PosKey, Quadrant>::Element *Q = quadrant_map.find(qk); + + // Erase existing cell in the quadrant. ERR_FAIL_COND(!Q); - Quadrant &q = Q->get(); + TileMapQuadrant &q = Q->get(); + q.cells.erase(pk); + + // Remove or make the quadrant dirty. if (q.cells.size() == 0) { _erase_quadrant(Q); } else { @@ -856,331 +555,232 @@ void TileMap::set_cell(int p_x, int p_y, int p_tile, bool p_flip_x, bool p_flip_ } used_size_cache_dirty = true; - return; - } - - Map<PosKey, Quadrant>::Element *Q = quadrant_map.find(qk); - - if (!E) { - E = tile_map.insert(pk, Cell()); - if (!Q) { - Q = _create_quadrant(qk); - } - Quadrant &q = Q->get(); - q.cells.insert(pk); } else { - ERR_FAIL_COND(!Q); // quadrant should exist... - - if (E->get().id == p_tile && E->get().flip_h == p_flip_x && E->get().flip_v == p_flip_y && E->get().transpose == p_transpose && E->get().autotile_coord_x == (uint16_t)p_autotile_coord.x && E->get().autotile_coord_y == (uint16_t)p_autotile_coord.y) { - return; //nothing changed - } - } + if (!E) { + // Insert a new cell in the tile map. + E = tile_map.insert(pk, TileMapCell()); - Cell &c = E->get(); - - c.id = p_tile; - c.flip_h = p_flip_x; - c.flip_v = p_flip_y; - c.transpose = p_transpose; - c.autotile_coord_x = (uint16_t)p_autotile_coord.x; - c.autotile_coord_y = (uint16_t)p_autotile_coord.y; - - _make_quadrant_dirty(Q); - used_size_cache_dirty = true; -} - -int TileMap::get_cellv(const Vector2 &p_pos) const { - return get_cell(p_pos.x, p_pos.y); -} - -void TileMap::make_bitmask_area_dirty(const Vector2 &p_pos) { - for (int x = p_pos.x - 1; x <= p_pos.x + 1; x++) { - for (int y = p_pos.y - 1; y <= p_pos.y + 1; y++) { - PosKey p(x, y); - if (dirty_bitmask.find(p) == nullptr) { - dirty_bitmask.push_back(p); + // Create a new quadrant if needed, then insert the cell if needed. + if (!Q) { + Q = _create_quadrant(qk); } - } - } -} + TileMapQuadrant &q = Q->get(); + q.cells.insert(pk); -void TileMap::update_bitmask_area(const Vector2 &p_pos) { - for (int x = p_pos.x - 1; x <= p_pos.x + 1; x++) { - for (int y = p_pos.y - 1; y <= p_pos.y + 1; y++) { - update_cell_bitmask(x, y); - } - } -} - -void TileMap::update_bitmask_region(const Vector2 &p_start, const Vector2 &p_end) { - if ((p_end.x < p_start.x || p_end.y < p_start.y) || (p_end.x == p_start.x && p_end.y == p_start.y)) { - Array a = get_used_cells(); - for (int i = 0; i < a.size(); i++) { - Vector2 vector = (Vector2)a[i]; - update_cell_bitmask(vector.x, vector.y); - } - return; - } - for (int x = p_start.x - 1; x <= p_end.x + 1; x++) { - for (int y = p_start.y - 1; y <= p_end.y + 1; y++) { - update_cell_bitmask(x, y); - } - } -} - -void TileMap::update_cell_bitmask(int p_x, int p_y) { - ERR_FAIL_COND_MSG(tile_set.is_null(), "Cannot update cell bitmask if Tileset is not open."); - PosKey p(p_x, p_y); - Map<PosKey, Cell>::Element *E = tile_map.find(p); - if (E != nullptr) { - int id = get_cell(p_x, p_y); - if (tile_set->tile_get_tile_mode(id) == TileSet::AUTO_TILE) { - uint16_t mask = 0; - if (tile_set->autotile_get_bitmask_mode(id) == TileSet::BITMASK_2X2) { - if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y - 1)) && tile_set->is_tile_bound(id, get_cell(p_x, p_y - 1)) && tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y))) { - mask |= TileSet::BIND_TOPLEFT; - } - if (tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y - 1)) && tile_set->is_tile_bound(id, get_cell(p_x, p_y - 1)) && tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y))) { - mask |= TileSet::BIND_TOPRIGHT; - } - if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y + 1)) && tile_set->is_tile_bound(id, get_cell(p_x, p_y + 1)) && tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y))) { - mask |= TileSet::BIND_BOTTOMLEFT; - } - if (tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y + 1)) && tile_set->is_tile_bound(id, get_cell(p_x, p_y + 1)) && tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y))) { - mask |= TileSet::BIND_BOTTOMRIGHT; - } - } else { - if (tile_set->autotile_get_bitmask_mode(id) == TileSet::BITMASK_3X3_MINIMAL) { - if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y - 1)) && tile_set->is_tile_bound(id, get_cell(p_x, p_y - 1)) && tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y))) { - mask |= TileSet::BIND_TOPLEFT; - } - if (tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y - 1)) && tile_set->is_tile_bound(id, get_cell(p_x, p_y - 1)) && tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y))) { - mask |= TileSet::BIND_TOPRIGHT; - } - if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y + 1)) && tile_set->is_tile_bound(id, get_cell(p_x, p_y + 1)) && tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y))) { - mask |= TileSet::BIND_BOTTOMLEFT; - } - if (tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y + 1)) && tile_set->is_tile_bound(id, get_cell(p_x, p_y + 1)) && tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y))) { - mask |= TileSet::BIND_BOTTOMRIGHT; - } - } else { - if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y - 1))) { - mask |= TileSet::BIND_TOPLEFT; - } - if (tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y - 1))) { - mask |= TileSet::BIND_TOPRIGHT; - } - if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y + 1))) { - mask |= TileSet::BIND_BOTTOMLEFT; - } - if (tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y + 1))) { - mask |= TileSet::BIND_BOTTOMRIGHT; - } - } - if (tile_set->is_tile_bound(id, get_cell(p_x, p_y - 1))) { - mask |= TileSet::BIND_TOP; - } - if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y))) { - mask |= TileSet::BIND_LEFT; - } - mask |= TileSet::BIND_CENTER; - if (tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y))) { - mask |= TileSet::BIND_RIGHT; - } - if (tile_set->is_tile_bound(id, get_cell(p_x, p_y + 1))) { - mask |= TileSet::BIND_BOTTOM; - } - } - Vector2 coord = tile_set->autotile_get_subtile_for_bitmask(id, mask, this, Vector2(p_x, p_y)); - E->get().autotile_coord_x = (int)coord.x; - E->get().autotile_coord_y = (int)coord.y; - - PosKey qk = p.to_quadrant(_get_quadrant_size()); - Map<PosKey, Quadrant>::Element *Q = quadrant_map.find(qk); - _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)); + } else { + ERR_FAIL_COND(!Q); // TileMapQuadrant should exist... - E->get().autotile_coord_x = (int)coord.x; - E->get().autotile_coord_y = (int)coord.y; + if (E->get().source_id == source_id && E->get().get_atlas_coords() == atlas_coords && E->get().alternative_tile == alternative_tile) { + return; // Nothing changed. } } - } -} -void TileMap::update_dirty_bitmask() { - while (dirty_bitmask.size() > 0) { - update_cell_bitmask(dirty_bitmask[0].x, dirty_bitmask[0].y); - dirty_bitmask.pop_front(); - } -} + TileMapCell &c = E->get(); -void TileMap::fix_invalid_tiles() { - ERR_FAIL_COND_MSG(tile_set.is_null(), "Cannot fix invalid tiles if Tileset is not open."); + c.source_id = source_id; + c.set_atlas_coords(atlas_coords); + c.alternative_tile = alternative_tile; - Map<PosKey, Cell> temp_tile_map = tile_map; - for (Map<PosKey, Cell>::Element *E = temp_tile_map.front(); E; E = E->next()) { - if (!tile_set->has_tile(get_cell(E->key().x, E->key().y))) { - set_cell(E->key().x, E->key().y, INVALID_CELL); - } + _make_quadrant_dirty(Q); + used_size_cache_dirty = true; } } -int TileMap::get_cell(int p_x, int p_y) const { - PosKey pk(p_x, p_y); - - const Map<PosKey, Cell>::Element *E = tile_map.find(pk); +int TileMap::get_cell_source_id(const Vector2i &p_coords) const { + // Get a cell source id from position + const Map<Vector2i, TileMapCell>::Element *E = tile_map.find(p_coords); if (!E) { - return INVALID_CELL; + return -1; } - return E->get().id; + return E->get().source_id; } -bool TileMap::is_cell_x_flipped(int p_x, int p_y) const { - PosKey pk(p_x, p_y); - - const Map<PosKey, Cell>::Element *E = tile_map.find(pk); +Vector2i TileMap::get_cell_atlas_coords(const Vector2i &p_coords) const { + // Get a cell source id from position + const Map<Vector2i, TileMapCell>::Element *E = tile_map.find(p_coords); if (!E) { - return false; + return TileSetAtlasSource::INVALID_ATLAS_COORDS; } - return E->get().flip_h; + return E->get().get_atlas_coords(); } -bool TileMap::is_cell_y_flipped(int p_x, int p_y) const { - PosKey pk(p_x, p_y); - - const Map<PosKey, Cell>::Element *E = tile_map.find(pk); +int TileMap::get_cell_alternative_tile(const Vector2i &p_coords) const { + // Get a cell source id from position + const Map<Vector2i, TileMapCell>::Element *E = tile_map.find(p_coords); if (!E) { - return false; + return TileSetAtlasSource::INVALID_TILE_ALTERNATIVE; } - return E->get().flip_v; + return E->get().alternative_tile; } -bool TileMap::is_cell_transposed(int p_x, int p_y) const { - PosKey pk(p_x, p_y); - - const Map<PosKey, Cell>::Element *E = tile_map.find(pk); +TileMapPattern *TileMap::get_pattern(TypedArray<Vector2i> p_coords_array) { + ERR_FAIL_COND_V(!tile_set.is_valid(), nullptr); - if (!E) { - return false; + TileMapPattern *output = memnew(TileMapPattern); + if (p_coords_array.is_empty()) { + return output; } - return E->get().transpose; -} - -void TileMap::set_cell_autotile_coord(int p_x, int p_y, const Vector2 &p_coord) { - PosKey pk(p_x, p_y); + Vector2i min = Vector2i(p_coords_array[0]); + for (int i = 1; i < p_coords_array.size(); i++) { + min = min.min(p_coords_array[i]); + } - const Map<PosKey, Cell>::Element *E = tile_map.find(pk); + Vector<Vector2i> coords_in_pattern_array; + coords_in_pattern_array.resize(p_coords_array.size()); + Vector2i ensure_positive_offset; + for (int i = 0; i < p_coords_array.size(); i++) { + Vector2i coords = p_coords_array[i]; + Vector2i coords_in_pattern = coords - min; + if (tile_set->get_tile_shape() != TileSet::TILE_SHAPE_SQUARE) { + if (tile_set->get_tile_layout() == TileSet::TILE_LAYOUT_STACKED) { + if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL && bool(min.y % 2) && bool(coords_in_pattern.y % 2)) { + coords_in_pattern.x -= 1; + if (coords_in_pattern.x < 0) { + ensure_positive_offset.x = 1; + } + } else if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL && bool(min.x % 2) && bool(coords_in_pattern.x % 2)) { + coords_in_pattern.y -= 1; + if (coords_in_pattern.y < 0) { + ensure_positive_offset.y = 1; + } + } + } else if (tile_set->get_tile_layout() == TileSet::TILE_LAYOUT_STACKED_OFFSET) { + if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL && bool(min.y % 2) && bool(coords_in_pattern.y % 2)) { + coords_in_pattern.x += 1; + } else if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL && bool(min.x % 2) && bool(coords_in_pattern.x % 2)) { + coords_in_pattern.y += 1; + } + } + } + coords_in_pattern_array.write[i] = coords_in_pattern; + } - if (!E) { - return; + for (int i = 0; i < coords_in_pattern_array.size(); i++) { + Vector2i coords = p_coords_array[i]; + Vector2i coords_in_pattern = coords_in_pattern_array[i]; + output->set_cell(coords_in_pattern + ensure_positive_offset, get_cell_source_id(coords), get_cell_atlas_coords(coords), get_cell_alternative_tile(coords)); } - Cell c = E->get(); - c.autotile_coord_x = p_coord.x; - c.autotile_coord_y = p_coord.y; - tile_map[pk] = c; + return output; +} - PosKey qk = pk.to_quadrant(_get_quadrant_size()); - Map<PosKey, Quadrant>::Element *Q = quadrant_map.find(qk); +Vector2i TileMap::map_pattern(Vector2i p_position_in_tilemap, Vector2i p_coords_in_pattern, const TileMapPattern *p_pattern) { + ERR_FAIL_COND_V(!p_pattern->has_cell(p_coords_in_pattern), Vector2i()); - if (!Q) { - return; + Vector2i output = p_position_in_tilemap + p_coords_in_pattern; + if (tile_set->get_tile_shape() != TileSet::TILE_SHAPE_SQUARE) { + if (tile_set->get_tile_layout() == TileSet::TILE_LAYOUT_STACKED) { + if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL && bool(p_position_in_tilemap.y % 2) && bool(p_coords_in_pattern.y % 2)) { + output.x += 1; + } else if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL && bool(p_position_in_tilemap.x % 2) && bool(p_coords_in_pattern.x % 2)) { + output.y += 1; + } + } else if (tile_set->get_tile_layout() == TileSet::TILE_LAYOUT_STACKED_OFFSET) { + if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL && bool(p_position_in_tilemap.y % 2) && bool(p_coords_in_pattern.y % 2)) { + output.x -= 1; + } else if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL && bool(p_position_in_tilemap.x % 2) && bool(p_coords_in_pattern.x % 2)) { + output.y -= 1; + } + } } - _make_quadrant_dirty(Q); + return output; } -Vector2 TileMap::get_cell_autotile_coord(int p_x, int p_y) const { - PosKey pk(p_x, p_y); +void TileMap::set_pattern(Vector2i p_position, const TileMapPattern *p_pattern) { + ERR_FAIL_COND(!tile_set.is_valid()); - const Map<PosKey, Cell>::Element *E = tile_map.find(pk); + TypedArray<Vector2i> used_cells = p_pattern->get_used_cells(); + for (int i = 0; i < used_cells.size(); i++) { + Vector2i coords = map_pattern(p_position, used_cells[i], p_pattern); + set_cell(coords, p_pattern->get_cell_source_id(coords), p_pattern->get_cell_atlas_coords(coords), p_pattern->get_cell_alternative_tile(coords)); + } +} - if (!E) { - return Vector2(); +TileMapCell TileMap::get_cell(const Vector2i &p_coords) const { + if (!tile_map.has(p_coords)) { + return TileMapCell(); + } else { + return tile_map.find(p_coords)->get(); } +} + +Map<Vector2i, TileMapQuadrant> &TileMap::get_quadrant_map() { + return quadrant_map; +} - return Vector2(E->get().autotile_coord_x, E->get().autotile_coord_y); +void TileMap::fix_invalid_tiles() { + ERR_FAIL_COND_MSG(tile_set.is_null(), "Cannot fix invalid tiles if Tileset is not open."); + for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) { + TileSetSource *source = *tile_set->get_source(E->get().source_id); + if (!source || !source->has_tile(E->get().get_atlas_coords()) || !source->has_alternative_tile(E->get().get_atlas_coords(), E->get().alternative_tile)) { + set_cell(E->key(), -1, TileSetAtlasSource::INVALID_ATLAS_COORDS, TileSetAtlasSource::INVALID_TILE_ALTERNATIVE); + } + } } void TileMap::_recreate_quadrants() { + // Clear then recreate all quadrants. _clear_quadrants(); - for (Map<PosKey, Cell>::Element *E = tile_map.front(); E; E = E->next()) { - PosKey qk = PosKey(E->key().x, E->key().y).to_quadrant(_get_quadrant_size()); + for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) { + Vector2i qk = _coords_to_quadrant_coords(Vector2i(E->key().x, E->key().y)); - Map<PosKey, Quadrant>::Element *Q = quadrant_map.find(qk); + Map<Vector2i, TileMapQuadrant>::Element *Q = quadrant_map.find(qk); if (!Q) { Q = _create_quadrant(qk); - dirty_quadrant_list.add(&Q->get().dirty_list); + dirty_quadrant_list.add(&Q->get().dirty_list_element); } - Q->get().cells.insert(E->key()); + Vector2i pk = E->key(); + Q->get().cells.insert(pk); + _make_quadrant_dirty(Q, false); } + update_dirty_quadrants(); } void TileMap::_clear_quadrants() { + // Clear quadrants. while (quadrant_map.size()) { _erase_quadrant(quadrant_map.front()); } -} - -void TileMap::set_material(const Ref<Material> &p_material) { - CanvasItem::set_material(p_material); - _update_all_items_material_state(); -} -void TileMap::set_use_parent_material(bool p_use_parent_material) { - CanvasItem::set_use_parent_material(p_use_parent_material); - _update_all_items_material_state(); -} - -void TileMap::_update_all_items_material_state() { - for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) { - Quadrant &q = E->get(); - for (List<RID>::Element *F = q.canvas_items.front(); F; F = F->next()) { - _update_item_material_state(F->get()); - } + // Clear the dirty quadrants list. + while (dirty_quadrant_list.first()) { + dirty_quadrant_list.remove(dirty_quadrant_list.first()); } } -void TileMap::_update_item_material_state(const RID &p_canvas_item) { - RS::get_singleton()->canvas_item_set_use_parent_material(p_canvas_item, get_use_parent_material() || get_material().is_valid()); -} - void TileMap::clear() { + // Remove all tiles. _clear_quadrants(); tile_map.clear(); used_size_cache_dirty = true; } void TileMap::_set_tile_data(const Vector<int> &p_data) { - ERR_FAIL_COND(format > FORMAT_2); + // Set data for a given tile from raw data. + ERR_FAIL_COND(format > FORMAT_3); int c = p_data.size(); const int *r = p_data.ptr(); - int offset = (format == FORMAT_2) ? 3 : 2; + int offset = (format >= FORMAT_2) ? 3 : 2; clear(); for (int i = 0; i < c; i += offset) { const uint8_t *ptr = (const uint8_t *)&r[i]; uint8_t local[12]; - for (int j = 0; j < ((format == FORMAT_2) ? 12 : 8); j++) { + for (int j = 0; j < ((format >= FORMAT_2) ? 12 : 8); j++) { local[j] = ptr[j]; } @@ -1191,31 +791,49 @@ void TileMap::_set_tile_data(const Vector<int> &p_data) { SWAP(local[4], local[7]); SWAP(local[5], local[6]); //TODO: ask someone to check this... - if (FORMAT == FORMAT_2) { + if (FORMAT >= FORMAT_2) { SWAP(local[8], local[11]); SWAP(local[9], local[10]); } #endif + int16_t x = decode_uint16(&local[0]); + int16_t y = decode_uint16(&local[2]); + + if (format == FORMAT_3) { + uint16_t source_id = decode_uint16(&local[4]); + uint16_t atlas_coords_x = decode_uint16(&local[6]); + uint16_t atlas_coords_y = decode_uint32(&local[8]); + uint16_t alternative_tile = decode_uint16(&local[10]); + set_cell(Vector2i(x, y), source_id, Vector2i(atlas_coords_x, atlas_coords_y), alternative_tile); + } else { + uint32_t v = decode_uint32(&local[4]); + v &= (1 << 29) - 1; + + // We generate an alternative tile number out of the the flags + // An option should create the alternative in the tileset for compatibility + bool flip_h = v & (1 << 29); + bool flip_v = v & (1 << 30); + bool transpose = v & (1 << 31); + int16_t coord_x = 0; + int16_t coord_y = 0; + if (format == FORMAT_2) { + coord_x = decode_uint16(&local[8]); + coord_y = decode_uint16(&local[10]); + } - uint16_t x = decode_uint16(&local[0]); - uint16_t y = decode_uint16(&local[2]); - uint32_t v = decode_uint32(&local[4]); - bool flip_h = v & (1 << 29); - bool flip_v = v & (1 << 30); - bool transpose = v & (1 << 31); - v &= (1 << 29) - 1; - int16_t coord_x = 0; - int16_t coord_y = 0; - if (format == FORMAT_2) { - coord_x = decode_uint16(&local[8]); - coord_y = decode_uint16(&local[10]); - } + int compatibility_alternative_tile = ((int)flip_h) + ((int)flip_v << 1) + ((int)transpose << 2); + + if (tile_set.is_valid()) { + v = tile_set->compatibility_get_source_for_tile_id(v); + } - set_cell(x, y, v, flip_h, flip_v, transpose, Vector2(coord_x, coord_y)); + set_cell(Vector2i(x, y), v, Vector2i(coord_x, coord_y), compatibility_alternative_tile); + } } } Vector<int> TileMap::_get_tile_data() const { + // Export tile data to raw format Vector<int> data; data.resize(tile_map.size() * 3); int *w = data.ptrw(); @@ -1223,23 +841,14 @@ Vector<int> TileMap::_get_tile_data() const { // Save in highest format int idx = 0; - for (const Map<PosKey, Cell>::Element *E = tile_map.front(); E; E = E->next()) { + for (const Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) { uint8_t *ptr = (uint8_t *)&w[idx]; - encode_uint16(E->key().x, &ptr[0]); - encode_uint16(E->key().y, &ptr[2]); - uint32_t val = E->get().id; - if (E->get().flip_h) { - val |= (1 << 29); - } - if (E->get().flip_v) { - val |= (1 << 30); - } - if (E->get().transpose) { - val |= (1 << 31); - } - encode_uint32(val, &ptr[4]); - encode_uint16(E->get().autotile_coord_x, &ptr[8]); - encode_uint16(E->get().autotile_coord_y, &ptr[10]); + encode_uint16((int16_t)(E->key().x), &ptr[0]); + encode_uint16((int16_t)(E->key().y), &ptr[2]); + encode_uint16(E->get().source_id, &ptr[4]); + encode_uint16(E->get().coord_x, &ptr[6]); + encode_uint16(E->get().coord_y, &ptr[8]); + encode_uint16(E->get().alternative_tile, &ptr[10]); idx += 3; } @@ -1248,6 +857,7 @@ Vector<int> TileMap::_get_tile_data() const { #ifdef TOOLS_ENABLED Rect2 TileMap::_edit_get_rect() const { + // Return the visible rect of the tilemap if (pending_update) { const_cast<TileMap *>(this)->update_dirty_quadrants(); } else { @@ -1257,240 +867,6 @@ Rect2 TileMap::_edit_get_rect() const { } #endif -void TileMap::set_collision_layer(uint32_t p_layer) { - collision_layer = p_layer; - if (!use_parent) { - for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) { - Quadrant &q = E->get(); - PhysicsServer2D::get_singleton()->body_set_collision_layer(q.body, collision_layer); - } - } -} - -void TileMap::set_collision_mask(uint32_t p_mask) { - collision_mask = p_mask; - if (!use_parent) { - for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) { - Quadrant &q = E->get(); - PhysicsServer2D::get_singleton()->body_set_collision_mask(q.body, collision_mask); - } - } -} - -void TileMap::set_collision_layer_bit(int p_bit, bool p_value) { - uint32_t layer = get_collision_layer(); - if (p_value) { - layer |= 1 << p_bit; - } else { - layer &= ~(1 << p_bit); - } - set_collision_layer(layer); -} - -void TileMap::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 TileMap::get_collision_use_kinematic() const { - return use_kinematic; -} - -void TileMap::set_collision_use_kinematic(bool p_use_kinematic) { - _clear_quadrants(); - use_kinematic = p_use_kinematic; - _recreate_quadrants(); -} - -bool TileMap::get_collision_use_parent() const { - return use_parent; -} - -void TileMap::set_collision_use_parent(bool p_use_parent) { - if (use_parent == p_use_parent) { - return; - } - - _clear_quadrants(); - - use_parent = p_use_parent; - set_notify_local_transform(use_parent); - - if (use_parent && is_inside_tree()) { - collision_parent = Object::cast_to<CollisionObject2D>(get_parent()); - } else { - collision_parent = nullptr; - } - - _recreate_quadrants(); - notify_property_list_changed(); - update_configuration_warning(); -} - -void TileMap::set_collision_friction(float p_friction) { - friction = p_friction; - if (!use_parent) { - for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) { - Quadrant &q = E->get(); - PhysicsServer2D::get_singleton()->body_set_param(q.body, PhysicsServer2D::BODY_PARAM_FRICTION, p_friction); - } - } -} - -float TileMap::get_collision_friction() const { - return friction; -} - -void TileMap::set_collision_bounce(float p_bounce) { - bounce = p_bounce; - if (!use_parent) { - for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) { - Quadrant &q = E->get(); - PhysicsServer2D::get_singleton()->body_set_param(q.body, PhysicsServer2D::BODY_PARAM_BOUNCE, p_bounce); - } - } -} - -float TileMap::get_collision_bounce() const { - return bounce; -} - -uint32_t TileMap::get_collision_layer() const { - return collision_layer; -} - -uint32_t TileMap::get_collision_mask() const { - return collision_mask; -} - -bool TileMap::get_collision_layer_bit(int p_bit) const { - return get_collision_layer() & (1 << p_bit); -} - -bool TileMap::get_collision_mask_bit(int p_bit) const { - return get_collision_mask() & (1 << p_bit); -} - -void TileMap::set_mode(Mode p_mode) { - _clear_quadrants(); - mode = p_mode; - _recreate_quadrants(); - emit_signal("settings_changed"); -} - -TileMap::Mode TileMap::get_mode() const { - return mode; -} - -void TileMap::set_half_offset(HalfOffset p_half_offset) { - _clear_quadrants(); - half_offset = p_half_offset; - _recreate_quadrants(); - emit_signal("settings_changed"); -} - -void TileMap::set_tile_origin(TileOrigin p_tile_origin) { - _clear_quadrants(); - tile_origin = p_tile_origin; - _recreate_quadrants(); - emit_signal("settings_changed"); -} - -TileMap::TileOrigin TileMap::get_tile_origin() const { - return tile_origin; -} - -Vector2 TileMap::get_cell_draw_offset() const { - switch (mode) { - case MODE_SQUARE: { - return Vector2(); - } break; - case MODE_ISOMETRIC: { - return Vector2(-cell_size.x * 0.5, 0); - - } break; - case MODE_CUSTOM: { - Vector2 min; - min.x = MIN(custom_transform[0].x, min.x); - min.y = MIN(custom_transform[0].y, min.y); - min.x = MIN(custom_transform[1].x, min.x); - min.y = MIN(custom_transform[1].y, min.y); - return min; - } break; - } - - return Vector2(); -} - -TileMap::HalfOffset TileMap::get_half_offset() const { - return half_offset; -} - -Transform2D TileMap::get_cell_transform() const { - switch (mode) { - case MODE_SQUARE: { - Transform2D m; - m[0] *= cell_size.x; - m[1] *= cell_size.y; - return m; - } break; - case MODE_ISOMETRIC: { - //isometric only makes sense when y is positive in both x and y vectors, otherwise - //the drawing of tiles will overlap - Transform2D m; - m[0] = Vector2(cell_size.x * 0.5, cell_size.y * 0.5); - m[1] = Vector2(-cell_size.x * 0.5, cell_size.y * 0.5); - return m; - - } break; - case MODE_CUSTOM: { - return custom_transform; - } break; - } - - return Transform2D(); -} - -void TileMap::set_custom_transform(const Transform2D &p_xform) { - _clear_quadrants(); - custom_transform = p_xform; - _recreate_quadrants(); - emit_signal("settings_changed"); -} - -Transform2D TileMap::get_custom_transform() const { - return custom_transform; -} - -Vector2 TileMap::_map_to_world(int p_x, int p_y, bool p_ignore_ofs) const { - Vector2 ret = get_cell_transform().xform(Vector2(p_x, p_y)); - if (!p_ignore_ofs) { - switch (half_offset) { - case HALF_OFFSET_X: - case HALF_OFFSET_NEGATIVE_X: { - if (ABS(p_y) & 1) { - ret += get_cell_transform()[0] * (half_offset == HALF_OFFSET_X ? 0.5 : -0.5); - } - } break; - case HALF_OFFSET_Y: - case HALF_OFFSET_NEGATIVE_Y: { - if (ABS(p_x) & 1) { - ret += get_cell_transform()[1] * (half_offset == HALF_OFFSET_Y ? 0.5 : -0.5); - } - } break; - case HALF_OFFSET_DISABLED: { - // Nothing to do. - } - } - } - return ret; -} - bool TileMap::_set(const StringName &p_name, const Variant &p_value) { if (p_name == "format") { if (p_value.get_type() == Variant::INT) { @@ -1509,7 +885,7 @@ bool TileMap::_set(const StringName &p_name, const Variant &p_value) { bool TileMap::_get(const StringName &p_name, Variant &r_ret) const { if (p_name == "format") { - r_ret = FORMAT_2; // When saving, always save highest format + r_ret = FORMAT_3; // When saving, always save highest format return true; } else if (p_name == "tile_data") { r_ret = _get_tile_data(); @@ -1526,92 +902,632 @@ void TileMap::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(p); } -void TileMap::_validate_property(PropertyInfo &property) const { - if (use_parent && property.name != "collision_use_parent" && property.name.begins_with("collision_")) { - property.usage = PROPERTY_USAGE_NOEDITOR; +Vector2 TileMap::map_to_world(const Vector2i &p_pos) const { + // SHOULD RETURN THE CENTER OF THE TILE + ERR_FAIL_COND_V(!tile_set.is_valid(), Vector2()); + + Vector2 ret = p_pos; + TileSet::TileShape tile_shape = tile_set->get_tile_shape(); + TileSet::TileOffsetAxis tile_offset_axis = tile_set->get_tile_offset_axis(); + + if (tile_shape == TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE || tile_shape == TileSet::TILE_SHAPE_HEXAGON || tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) { + // Technically, those 3 shapes are equivalent, as they are basically half-offset, but with different levels or overlap. + // square = no overlap, hexagon = 0.25 overlap, isometric = 0.5 overlap + if (tile_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { + switch (tile_set->get_tile_layout()) { + case TileSet::TILE_LAYOUT_STACKED: + ret = Vector2(ret.x + (Math::posmod(ret.y, 2) == 0 ? 0.0 : 0.5), ret.y); + break; + case TileSet::TILE_LAYOUT_STACKED_OFFSET: + ret = Vector2(ret.x + (Math::posmod(ret.y, 2) == 1 ? 0.0 : 0.5), ret.y); + break; + case TileSet::TILE_LAYOUT_STAIRS_RIGHT: + ret = Vector2(ret.x + ret.y / 2, ret.y); + break; + case TileSet::TILE_LAYOUT_STAIRS_DOWN: + ret = Vector2(ret.x / 2, ret.y * 2 + ret.x); + break; + case TileSet::TILE_LAYOUT_DIAMOND_RIGHT: + ret = Vector2((ret.x + ret.y) / 2, ret.y - ret.x); + break; + case TileSet::TILE_LAYOUT_DIAMOND_DOWN: + ret = Vector2((ret.x - ret.y) / 2, ret.y + ret.x); + break; + } + } else { // TILE_OFFSET_AXIS_VERTICAL + switch (tile_set->get_tile_layout()) { + case TileSet::TILE_LAYOUT_STACKED: + ret = Vector2(ret.x, ret.y + (Math::posmod(ret.x, 2) == 0 ? 0.0 : 0.5)); + break; + case TileSet::TILE_LAYOUT_STACKED_OFFSET: + ret = Vector2(ret.x, ret.y + (Math::posmod(ret.x, 2) == 1 ? 0.0 : 0.5)); + break; + case TileSet::TILE_LAYOUT_STAIRS_RIGHT: + ret = Vector2(ret.x * 2 + ret.y, ret.y / 2); + break; + case TileSet::TILE_LAYOUT_STAIRS_DOWN: + ret = Vector2(ret.x, ret.y + ret.x / 2); + break; + case TileSet::TILE_LAYOUT_DIAMOND_RIGHT: + ret = Vector2(ret.x + ret.y, (ret.y - ret.x) / 2); + break; + case TileSet::TILE_LAYOUT_DIAMOND_DOWN: + ret = Vector2(ret.x - ret.y, (ret.y + ret.x) / 2); + break; + } + } } -} -Vector2 TileMap::map_to_world(const Vector2 &p_pos, bool p_ignore_ofs) const { - return _map_to_world(p_pos.x, p_pos.y, p_ignore_ofs); + // Multiply by the overlapping ratio + double overlapping_ratio = 1.0; + if (tile_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { + if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) { + overlapping_ratio = 0.5; + } else if (tile_shape == TileSet::TILE_SHAPE_HEXAGON) { + overlapping_ratio = 0.75; + } + ret.y *= overlapping_ratio; + } else { // TILE_OFFSET_AXIS_VERTICAL + if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) { + overlapping_ratio = 0.5; + } else if (tile_shape == TileSet::TILE_SHAPE_HEXAGON) { + overlapping_ratio = 0.75; + } + ret.x *= overlapping_ratio; + } + + return (ret + Vector2(0.5, 0.5)) * tile_set->get_tile_size(); } -Vector2 TileMap::world_to_map(const Vector2 &p_pos) const { - Vector2 ret = get_cell_transform().affine_inverse().xform(p_pos); +Vector2i TileMap::world_to_map(const Vector2 &p_pos) const { + ERR_FAIL_COND_V(!tile_set.is_valid(), Vector2i()); - switch (half_offset) { - case HALF_OFFSET_X: { - if (int(floor(ret.y)) & 1) { - ret.x -= 0.5; + Vector2 ret = p_pos; + ret /= tile_set->get_tile_size(); + + TileSet::TileShape tile_shape = tile_set->get_tile_shape(); + TileSet::TileOffsetAxis tile_offset_axis = tile_set->get_tile_offset_axis(); + TileSet::TileLayout tile_layout = tile_set->get_tile_layout(); + + // Divide by the overlapping ratio + double overlapping_ratio = 1.0; + if (tile_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { + if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) { + overlapping_ratio = 0.5; + } else if (tile_shape == TileSet::TILE_SHAPE_HEXAGON) { + overlapping_ratio = 0.75; + } + ret.y /= overlapping_ratio; + } else { // TILE_OFFSET_AXIS_VERTICAL + if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) { + overlapping_ratio = 0.5; + } else if (tile_shape == TileSet::TILE_SHAPE_HEXAGON) { + overlapping_ratio = 0.75; + } + ret.x /= overlapping_ratio; + } + + // For each half-offset shape, we check if we are in the corner of the tile, and thus should correct the world position accordingly. + if (tile_shape == TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE || tile_shape == TileSet::TILE_SHAPE_HEXAGON || tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) { + // Technically, those 3 shapes are equivalent, as they are basically half-offset, but with different levels or overlap. + // square = no overlap, hexagon = 0.25 overlap, isometric = 0.5 overlap + if (tile_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { + // Smart floor of the position + Vector2 raw_pos = ret; + if (Math::posmod(Math::floor(ret.y), 2) ^ (tile_layout == TileSet::TILE_LAYOUT_STACKED_OFFSET)) { + ret = Vector2(Math::floor(ret.x + 0.5) - 0.5, Math::floor(ret.y)); + } else { + ret = ret.floor(); } - } break; - case HALF_OFFSET_NEGATIVE_X: { - if (int(floor(ret.y)) & 1) { - ret.x += 0.5; + + // Compute the tile offset, and if we might the output for a neighbour top tile + Vector2 in_tile_pos = raw_pos - ret; + bool in_top_left_triangle = (in_tile_pos - Vector2(0.5, 0.0)).cross(Vector2(-0.5, 1.0 / overlapping_ratio - 1)) <= 0; + bool in_top_right_triangle = (in_tile_pos - Vector2(0.5, 0.0)).cross(Vector2(0.5, 1.0 / overlapping_ratio - 1)) > 0; + + switch (tile_layout) { + case TileSet::TILE_LAYOUT_STACKED: + ret = ret.floor(); + if (in_top_left_triangle) { + ret += Vector2i(Math::posmod(Math::floor(ret.y), 2) ? 0 : -1, -1); + } else if (in_top_right_triangle) { + ret += Vector2i(Math::posmod(Math::floor(ret.y), 2) ? 1 : 0, -1); + } + break; + case TileSet::TILE_LAYOUT_STACKED_OFFSET: + ret = ret.floor(); + if (in_top_left_triangle) { + ret += Vector2i(Math::posmod(Math::floor(ret.y), 2) ? -1 : 0, -1); + } else if (in_top_right_triangle) { + ret += Vector2i(Math::posmod(Math::floor(ret.y), 2) ? 0 : 1, -1); + } + break; + case TileSet::TILE_LAYOUT_STAIRS_RIGHT: + ret = Vector2(ret.x - ret.y / 2, ret.y).floor(); + if (in_top_left_triangle) { + ret += Vector2i(0, -1); + } else if (in_top_right_triangle) { + ret += Vector2i(1, -1); + } + break; + case TileSet::TILE_LAYOUT_STAIRS_DOWN: + ret = Vector2(ret.x * 2, ret.y / 2 - ret.x).floor(); + if (in_top_left_triangle) { + ret += Vector2i(-1, 0); + } else if (in_top_right_triangle) { + ret += Vector2i(1, -1); + } + break; + case TileSet::TILE_LAYOUT_DIAMOND_RIGHT: + ret = Vector2(ret.x - ret.y / 2, ret.y / 2 + ret.x).floor(); + if (in_top_left_triangle) { + ret += Vector2i(0, -1); + } else if (in_top_right_triangle) { + ret += Vector2i(1, 0); + } + break; + case TileSet::TILE_LAYOUT_DIAMOND_DOWN: + ret = Vector2(ret.x + ret.y / 2, ret.y / 2 - ret.x).floor(); + if (in_top_left_triangle) { + ret += Vector2i(-1, 0); + } else if (in_top_right_triangle) { + ret += Vector2i(0, -1); + } + break; } - } break; - case HALF_OFFSET_Y: { - if (int(floor(ret.x)) & 1) { - ret.y -= 0.5; + } else { // TILE_OFFSET_AXIS_VERTICAL + // Smart floor of the position + Vector2 raw_pos = ret; + if (Math::posmod(Math::floor(ret.x), 2) ^ (tile_layout == TileSet::TILE_LAYOUT_STACKED_OFFSET)) { + ret = Vector2(Math::floor(ret.x), Math::floor(ret.y + 0.5) - 0.5); + } else { + ret = ret.floor(); } - } break; - case HALF_OFFSET_NEGATIVE_Y: { - if (int(floor(ret.x)) & 1) { - ret.y += 0.5; + + // Compute the tile offset, and if we might the output for a neighbour top tile + Vector2 in_tile_pos = raw_pos - ret; + bool in_top_left_triangle = (in_tile_pos - Vector2(0.0, 0.5)).cross(Vector2(1.0 / overlapping_ratio - 1, -0.5)) > 0; + bool in_bottom_left_triangle = (in_tile_pos - Vector2(0.0, 0.5)).cross(Vector2(1.0 / overlapping_ratio - 1, 0.5)) <= 0; + + switch (tile_layout) { + case TileSet::TILE_LAYOUT_STACKED: + ret = ret.floor(); + if (in_top_left_triangle) { + ret += Vector2i(-1, Math::posmod(Math::floor(ret.x), 2) ? 0 : -1); + } else if (in_bottom_left_triangle) { + ret += Vector2i(-1, Math::posmod(Math::floor(ret.x), 2) ? 1 : 0); + } + break; + case TileSet::TILE_LAYOUT_STACKED_OFFSET: + ret = ret.floor(); + if (in_top_left_triangle) { + ret += Vector2i(-1, Math::posmod(Math::floor(ret.x), 2) ? -1 : 0); + } else if (in_bottom_left_triangle) { + ret += Vector2i(-1, Math::posmod(Math::floor(ret.x), 2) ? 0 : 1); + } + break; + case TileSet::TILE_LAYOUT_STAIRS_RIGHT: + ret = Vector2(ret.x / 2 - ret.y, ret.y * 2).floor(); + if (in_top_left_triangle) { + ret += Vector2i(0, -1); + } else if (in_bottom_left_triangle) { + ret += Vector2i(-1, 1); + } + break; + case TileSet::TILE_LAYOUT_STAIRS_DOWN: + ret = Vector2(ret.x, ret.y - ret.x / 2).floor(); + if (in_top_left_triangle) { + ret += Vector2i(-1, 0); + } else if (in_bottom_left_triangle) { + ret += Vector2i(-1, 1); + } + break; + case TileSet::TILE_LAYOUT_DIAMOND_RIGHT: + ret = Vector2(ret.x / 2 - ret.y, ret.y + ret.x / 2).floor(); + if (in_top_left_triangle) { + ret += Vector2i(0, -1); + } else if (in_bottom_left_triangle) { + ret += Vector2i(-1, 0); + } + break; + case TileSet::TILE_LAYOUT_DIAMOND_DOWN: + ret = Vector2(ret.x / 2 + ret.y, ret.y - ret.x / 2).floor(); + if (in_top_left_triangle) { + ret += Vector2i(-1, 0); + } else if (in_bottom_left_triangle) { + ret += Vector2i(0, 1); + } + break; } - } break; - case HALF_OFFSET_DISABLED: { - // Nothing to do. + } + } else { + ret = (ret + Vector2(0.00005, 0.00005)).floor(); + } + return Vector2i(ret); +} + +bool TileMap::is_existing_neighbor(TileSet::CellNeighbor p_cell_neighbor) const { + ERR_FAIL_COND_V(!tile_set.is_valid(), false); + + TileSet::TileShape shape = tile_set->get_tile_shape(); + if (shape == TileSet::TILE_SHAPE_SQUARE) { + return p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_SIDE || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER; + + } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) { + return p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE; + } else { + if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { + return p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_SIDE || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE; + } else { + return p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE || + p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE; } } - - // Account for precision errors on the border (GH-23250). - // 0.00005 is 5*CMP_EPSILON, results would start being unpredictable if - // cell size is > 15,000, but we can hardly have more precision anyway with - // floating point. - ret += Vector2(0.00005, 0.00005); - return ret.floor(); } -void TileMap::set_y_sort_enabled(bool p_enable) { - _clear_quadrants(); - use_y_sort = p_enable; - RS::get_singleton()->canvas_item_set_sort_children_by_y(get_canvas_item(), use_y_sort); - _recreate_quadrants(); - emit_signal("settings_changed"); -} - -bool TileMap::is_y_sort_enabled() const { - return use_y_sort; -} +Vector2i TileMap::get_neighbor_cell(const Vector2i &p_coords, TileSet::CellNeighbor p_cell_neighbor) const { + ERR_FAIL_COND_V(!tile_set.is_valid(), p_coords); + + TileSet::TileShape shape = tile_set->get_tile_shape(); + if (shape == TileSet::TILE_SHAPE_SQUARE) { + switch (p_cell_neighbor) { + case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: + return p_coords + Vector2i(1, 0); + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + return p_coords + Vector2i(1, 1); + case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: + return p_coords + Vector2i(0, 1); + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + return p_coords + Vector2i(-1, 1); + case TileSet::CELL_NEIGHBOR_LEFT_SIDE: + return p_coords + Vector2i(-1, 0); + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + return p_coords + Vector2i(-1, -1); + case TileSet::CELL_NEIGHBOR_TOP_SIDE: + return p_coords + Vector2i(0, -1); + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + return p_coords + Vector2i(1, -1); + default: + ERR_FAIL_V(p_coords); + } + } else { // Half-offset shapes (square and hexagon) + switch (tile_set->get_tile_layout()) { + case TileSet::TILE_LAYOUT_STACKED: { + if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { + bool is_offset = p_coords.y % 2; + if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER) || + (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_SIDE)) { + return p_coords + Vector2i(1, 0); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) { + return p_coords + Vector2i(is_offset ? 1 : 0, 1); + } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) { + return p_coords + Vector2i(0, 2); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) { + return p_coords + Vector2i(is_offset ? 0 : -1, 1); + } else if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) || + (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE)) { + return p_coords + Vector2i(-1, 0); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) { + return p_coords + Vector2i(is_offset ? 0 : -1, -1); + } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) { + return p_coords + Vector2i(0, -2); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) { + return p_coords + Vector2i(is_offset ? 1 : 0, -1); + } else { + ERR_FAIL_V(p_coords); + } + } else { + bool is_offset = p_coords.x % 2; + + if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) || + (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)) { + return p_coords + Vector2i(0, 1); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) { + return p_coords + Vector2i(1, is_offset ? 1 : 0); + } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER) { + return p_coords + Vector2i(2, 0); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) { + return p_coords + Vector2i(1, is_offset ? 0 : -1); + } else if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) || + (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE)) { + return p_coords + Vector2i(0, -1); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) { + return p_coords + Vector2i(-1, is_offset ? 0 : -1); + } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) { + return p_coords + Vector2i(-2, 0); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) { + return p_coords + Vector2i(-1, is_offset ? 1 : 0); + } else { + ERR_FAIL_V(p_coords); + } + } + } break; + case TileSet::TILE_LAYOUT_STACKED_OFFSET: { + if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { + bool is_offset = p_coords.y % 2; + + if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER) || + (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_SIDE)) { + return p_coords + Vector2i(1, 0); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) { + return p_coords + Vector2i(is_offset ? 0 : 1, 1); + } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) { + return p_coords + Vector2i(0, 2); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) { + return p_coords + Vector2i(is_offset ? -1 : 0, 1); + } else if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) || + (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE)) { + return p_coords + Vector2i(-1, 0); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) { + return p_coords + Vector2i(is_offset ? -1 : 0, -1); + } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) { + return p_coords + Vector2i(0, -2); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) { + return p_coords + Vector2i(is_offset ? 0 : 1, -1); + } else { + ERR_FAIL_V(p_coords); + } + } else { + bool is_offset = p_coords.x % 2; + + if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) || + (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)) { + return p_coords + Vector2i(0, 1); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) { + return p_coords + Vector2i(1, is_offset ? 0 : 1); + } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER) { + return p_coords + Vector2i(2, 0); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) { + return p_coords + Vector2i(1, is_offset ? -1 : 0); + } else if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) || + (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE)) { + return p_coords + Vector2i(0, -1); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) { + return p_coords + Vector2i(-1, is_offset ? -1 : 0); + } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) { + return p_coords + Vector2i(-2, 0); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) { + return p_coords + Vector2i(-1, is_offset ? 0 : 1); + } else { + ERR_FAIL_V(p_coords); + } + } + } break; + case TileSet::TILE_LAYOUT_STAIRS_RIGHT: + case TileSet::TILE_LAYOUT_STAIRS_DOWN: { + if ((tile_set->get_tile_layout() == TileSet::TILE_LAYOUT_STAIRS_RIGHT) ^ (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL)) { + if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { + if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER) || + (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_SIDE)) { + return p_coords + Vector2i(1, 0); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) { + return p_coords + Vector2i(0, 1); + } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) { + return p_coords + Vector2i(-1, 2); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) { + return p_coords + Vector2i(-1, 1); + } else if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) || + (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE)) { + return p_coords + Vector2i(-1, 0); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) { + return p_coords + Vector2i(0, -1); + } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) { + return p_coords + Vector2i(1, -2); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) { + return p_coords + Vector2i(1, -1); + } else { + ERR_FAIL_V(p_coords); + } -void TileMap::set_compatibility_mode(bool p_enable) { - _clear_quadrants(); - compatibility_mode = p_enable; - _recreate_quadrants(); - emit_signal("settings_changed"); -} + } else { + if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) || + (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)) { + return p_coords + Vector2i(0, 1); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) { + return p_coords + Vector2i(1, 0); + } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER) { + return p_coords + Vector2i(2, -1); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) { + return p_coords + Vector2i(1, -1); + } else if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) || + (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE)) { + return p_coords + Vector2i(0, -1); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) { + return p_coords + Vector2i(-1, 0); + } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) { + return p_coords + Vector2i(-2, 1); + + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) { + return p_coords + Vector2i(-1, 1); + } else { + ERR_FAIL_V(p_coords); + } + } + } else { + if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { + if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER) || + (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_SIDE)) { + return p_coords + Vector2i(2, -1); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) { + return p_coords + Vector2i(1, 0); + } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) { + return p_coords + Vector2i(0, 1); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) { + return p_coords + Vector2i(-1, 1); + } else if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) || + (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE)) { + return p_coords + Vector2i(-2, 1); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) { + return p_coords + Vector2i(-1, 0); + } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) { + return p_coords + Vector2i(0, -1); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) { + return p_coords + Vector2i(1, -1); + } else { + ERR_FAIL_V(p_coords); + } -bool TileMap::is_compatibility_mode_enabled() const { - return compatibility_mode; -} + } else { + if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) || + (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)) { + return p_coords + Vector2i(-1, 2); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) { + return p_coords + Vector2i(0, 1); + } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER) { + return p_coords + Vector2i(1, 0); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) { + return p_coords + Vector2i(1, -1); + } else if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) || + (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE)) { + return p_coords + Vector2i(1, -2); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) { + return p_coords + Vector2i(0, -1); + } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) { + return p_coords + Vector2i(-1, 0); + + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) { + return p_coords + Vector2i(-1, 1); + } else { + ERR_FAIL_V(p_coords); + } + } + } + } break; + case TileSet::TILE_LAYOUT_DIAMOND_RIGHT: + case TileSet::TILE_LAYOUT_DIAMOND_DOWN: { + if ((tile_set->get_tile_layout() == TileSet::TILE_LAYOUT_DIAMOND_RIGHT) ^ (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL)) { + if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { + if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER) || + (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_SIDE)) { + return p_coords + Vector2i(1, 1); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) { + return p_coords + Vector2i(0, 1); + } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) { + return p_coords + Vector2i(-1, 1); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) { + return p_coords + Vector2i(-1, 0); + } else if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) || + (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE)) { + return p_coords + Vector2i(-1, -1); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) { + return p_coords + Vector2i(0, -1); + } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) { + return p_coords + Vector2i(1, -1); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) { + return p_coords + Vector2i(1, 0); + } else { + ERR_FAIL_V(p_coords); + } -void TileMap::set_centered_textures(bool p_enable) { - _clear_quadrants(); - centered_textures = p_enable; - _recreate_quadrants(); - emit_signal("settings_changed"); -} + } else { + if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) || + (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)) { + return p_coords + Vector2i(1, 1); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) { + return p_coords + Vector2i(1, 0); + } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER) { + return p_coords + Vector2i(1, -1); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) { + return p_coords + Vector2i(0, -1); + } else if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) || + (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE)) { + return p_coords + Vector2i(-1, -1); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) { + return p_coords + Vector2i(-1, 0); + } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) { + return p_coords + Vector2i(-1, 1); + + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) { + return p_coords + Vector2i(0, 1); + } else { + ERR_FAIL_V(p_coords); + } + } + } else { + if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { + if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER) || + (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_SIDE)) { + return p_coords + Vector2i(1, -1); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) { + return p_coords + Vector2i(1, 0); + } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) { + return p_coords + Vector2i(1, 1); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) { + return p_coords + Vector2i(0, 1); + } else if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) || + (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE)) { + return p_coords + Vector2i(-1, 1); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) { + return p_coords + Vector2i(-1, 0); + } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) { + return p_coords + Vector2i(-1, -1); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) { + return p_coords + Vector2i(0, -1); + } else { + ERR_FAIL_V(p_coords); + } -bool TileMap::is_centered_textures_enabled() const { - return centered_textures; + } else { + if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) || + (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)) { + return p_coords + Vector2i(-1, 1); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) { + return p_coords + Vector2i(0, 1); + } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER) { + return p_coords + Vector2i(1, 1); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) { + return p_coords + Vector2i(1, 0); + } else if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) || + (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE)) { + return p_coords + Vector2i(1, -1); + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) { + return p_coords + Vector2i(0, -1); + } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) { + return p_coords + Vector2i(-1, -1); + + } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) { + return p_coords + Vector2i(-1, 0); + } else { + ERR_FAIL_V(p_coords); + } + } + } + } break; + default: + ERR_FAIL_V(p_coords); + } + } } TypedArray<Vector2i> TileMap::get_used_cells() const { + // Returns the cells used in the tilemap. TypedArray<Vector2i> a; a.resize(tile_map.size()); int i = 0; - for (Map<PosKey, Cell>::Element *E = tile_map.front(); E; E = E->next()) { + for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) { Vector2i p(E->key().x, E->key().y); a[i++] = p; } @@ -1619,25 +1535,13 @@ TypedArray<Vector2i> TileMap::get_used_cells() const { return a; } -TypedArray<Vector2i> TileMap::get_used_cells_by_index(int p_id) const { - TypedArray<Vector2i> a; - for (Map<PosKey, Cell>::Element *E = tile_map.front(); E; E = E->next()) { - if (E->value().id == p_id) { - Vector2i p(E->key().x, E->key().y); - a.push_back(p); - } - } - - return a; -} - Rect2 TileMap::get_used_rect() { // Not const because of cache - + // Return the rect of the currently used area if (used_size_cache_dirty) { if (tile_map.size() > 0) { used_size_cache = Rect2(tile_map.front()->key().x, tile_map.front()->key().y, 0, 0); - for (Map<PosKey, Cell>::Element *E = tile_map.front(); E; E = E->next()) { + for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) { used_size_cache.expand_to(Vector2(E->key().x, E->key().y)); } @@ -1652,46 +1556,49 @@ Rect2 TileMap::get_used_rect() { // Not const because of cache return used_size_cache; } -void TileMap::set_occluder_light_mask(int p_mask) { - occluder_light_mask = p_mask; - for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) { - for (Map<PosKey, Quadrant::Occluder>::Element *F = E->get().occluder_instances.front(); F; F = F->next()) { - RenderingServer::get_singleton()->canvas_light_occluder_set_light_mask(F->get().id, occluder_light_mask); - } - } -} - -int TileMap::get_occluder_light_mask() const { - return occluder_light_mask; -} +// --- Override some methods of the CanvasItem class to pass the changes to the quadrants CanvasItems --- void TileMap::set_light_mask(int p_light_mask) { + // Occlusion: set light mask. CanvasItem::set_light_mask(p_light_mask); - for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) { + for (Map<Vector2i, TileMapQuadrant>::Element *E = quadrant_map.front(); E; E = E->next()) { for (List<RID>::Element *F = E->get().canvas_items.front(); F; F = F->next()) { RenderingServer::get_singleton()->canvas_item_set_light_mask(F->get(), get_light_mask()); } } } -void TileMap::set_clip_uv(bool p_enable) { - if (clip_uv == p_enable) { - return; - } +void TileMap::set_material(const Ref<Material> &p_material) { + // Set material for the whole tilemap. + CanvasItem::set_material(p_material); - _clear_quadrants(); - clip_uv = p_enable; - _recreate_quadrants(); + // Update material for the whole tilemap. + for (Map<Vector2i, TileMapQuadrant>::Element *E = quadrant_map.front(); E; E = E->next()) { + TileMapQuadrant &q = E->get(); + for (List<RID>::Element *F = q.canvas_items.front(); F; F = F->next()) { + RS::get_singleton()->canvas_item_set_use_parent_material(F->get(), get_use_parent_material() || get_material().is_valid()); + } + } } -bool TileMap::get_clip_uv() const { - return clip_uv; +void TileMap::set_use_parent_material(bool p_use_parent_material) { + // Set use_parent_material for the whole tilemap. + CanvasItem::set_use_parent_material(p_use_parent_material); + + // Update use_parent_material for the whole tilemap. + for (Map<Vector2i, TileMapQuadrant>::Element *E = quadrant_map.front(); E; E = E->next()) { + TileMapQuadrant &q = E->get(); + for (List<RID>::Element *F = q.canvas_items.front(); F; F = F->next()) { + RS::get_singleton()->canvas_item_set_use_parent_material(F->get(), get_use_parent_material() || get_material().is_valid()); + } + } } void TileMap::set_texture_filter(TextureFilter p_texture_filter) { + // Set a default texture filter for the whole tilemap CanvasItem::set_texture_filter(p_texture_filter); - for (Map<PosKey, Quadrant>::Element *F = quadrant_map.front(); F; F = F->next()) { - Quadrant &q = F->get(); + for (Map<Vector2i, TileMapQuadrant>::Element *F = quadrant_map.front(); F; F = F->next()) { + TileMapQuadrant &q = F->get(); for (List<RID>::Element *E = q.canvas_items.front(); E; E = E->next()) { RenderingServer::get_singleton()->canvas_item_set_default_texture_filter(E->get(), RS::CanvasItemTextureFilter(p_texture_filter)); _make_quadrant_dirty(F); @@ -1700,9 +1607,10 @@ void TileMap::set_texture_filter(TextureFilter p_texture_filter) { } void TileMap::set_texture_repeat(CanvasItem::TextureRepeat p_texture_repeat) { + // Set a default texture repeat for the whole tilemap CanvasItem::set_texture_repeat(p_texture_repeat); - for (Map<PosKey, Quadrant>::Element *F = quadrant_map.front(); F; F = F->next()) { - Quadrant &q = F->get(); + for (Map<Vector2i, TileMapQuadrant>::Element *F = quadrant_map.front(); F; F = F->next()) { + TileMapQuadrant &q = F->get(); for (List<RID>::Element *E = q.canvas_items.front(); E; E = E->next()) { RenderingServer::get_singleton()->canvas_item_set_default_texture_repeat(E->get(), RS::CanvasItemTextureRepeat(p_texture_repeat)); _make_quadrant_dirty(F); @@ -1710,164 +1618,153 @@ void TileMap::set_texture_repeat(CanvasItem::TextureRepeat p_texture_repeat) { } } -String TileMap::get_configuration_warning() const { - String warning = Node2D::get_configuration_warning(); +TypedArray<Vector2i> TileMap::get_surrounding_tiles(Vector2i coords) { + if (!tile_set.is_valid()) { + return TypedArray<Vector2i>(); + } - if (use_parent && !collision_parent) { - if (!warning.is_empty()) { - warning += "\n\n"; + TypedArray<Vector2i> around; + TileSet::TileShape shape = tile_set->get_tile_shape(); + if (shape == TileSet::TILE_SHAPE_SQUARE) { + around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)); + around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)); + around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_LEFT_SIDE)); + around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_TOP_SIDE)); + } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) { + around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)); + around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)); + around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE)); + around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE)); + } else { + if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { + around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)); + around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)); + around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)); + around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_LEFT_SIDE)); + around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE)); + around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE)); + } else { + around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)); + around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)); + around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)); + around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE)); + around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_TOP_SIDE)); + around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE)); } - return TTR("TileMap with Use Parent on needs a parent CollisionObject2D to give shapes to. Please use it as a child of Area2D, StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape."); } - return warning; + return around; } -void TileMap::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_tileset", "tileset"), &TileMap::set_tileset); - ClassDB::bind_method(D_METHOD("get_tileset"), &TileMap::get_tileset); - - ClassDB::bind_method(D_METHOD("set_mode", "mode"), &TileMap::set_mode); - ClassDB::bind_method(D_METHOD("get_mode"), &TileMap::get_mode); +void TileMap::draw_cells_outline(Control *p_control, Set<Vector2i> p_cells, Color p_color, Transform2D p_transform) { + if (!tile_set.is_valid()) { + return; + } - ClassDB::bind_method(D_METHOD("set_half_offset", "half_offset"), &TileMap::set_half_offset); - ClassDB::bind_method(D_METHOD("get_half_offset"), &TileMap::get_half_offset); + // Create a set. + Vector2i tile_size = tile_set->get_tile_size(); + Vector<Vector2> uvs; - ClassDB::bind_method(D_METHOD("set_custom_transform", "custom_transform"), &TileMap::set_custom_transform); - ClassDB::bind_method(D_METHOD("get_custom_transform"), &TileMap::get_custom_transform); + if (tile_set->get_tile_shape() == TileSet::TILE_SHAPE_SQUARE) { + uvs.append(Vector2(1.0, 0.0)); + uvs.append(Vector2(1.0, 1.0)); + uvs.append(Vector2(0.0, 1.0)); + uvs.append(Vector2(0.0, 0.0)); + } else { + float overlap = 0.0; + switch (tile_set->get_tile_shape()) { + case TileSet::TILE_SHAPE_ISOMETRIC: + overlap = 0.5; + break; + case TileSet::TILE_SHAPE_HEXAGON: + overlap = 0.25; + break; + case TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE: + overlap = 0.0; + break; + default: + break; + } + uvs.append(Vector2(1.0, overlap)); + uvs.append(Vector2(1.0, 1.0 - overlap)); + uvs.append(Vector2(0.5, 1.0)); + uvs.append(Vector2(0.0, 1.0 - overlap)); + uvs.append(Vector2(0.0, overlap)); + uvs.append(Vector2(0.5, 0.0)); + if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL) { + for (int i = 0; i < uvs.size(); i++) { + uvs.write[i] = Vector2(uvs[i].y, uvs[i].x); + } + } + } - ClassDB::bind_method(D_METHOD("set_cell_size", "size"), &TileMap::set_cell_size); - ClassDB::bind_method(D_METHOD("get_cell_size"), &TileMap::get_cell_size); + for (Set<Vector2i>::Element *E = p_cells.front(); E; E = E->next()) { + Vector2 top_left = map_to_world(E->get()) - tile_size / 2; + TypedArray<Vector2i> surrounding_tiles = get_surrounding_tiles(E->get()); + for (int i = 0; i < surrounding_tiles.size(); i++) { + if (!p_cells.has(surrounding_tiles[i])) { + p_control->draw_line(p_transform.xform(top_left + uvs[i] * tile_size), p_transform.xform(top_left + uvs[(i + 1) % uvs.size()] * tile_size), p_color); + } + } + } +} - ClassDB::bind_method(D_METHOD("_set_old_cell_size", "size"), &TileMap::_set_old_cell_size); - ClassDB::bind_method(D_METHOD("_get_old_cell_size"), &TileMap::_get_old_cell_size); +void TileMap::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_tileset", "tileset"), &TileMap::set_tileset); + ClassDB::bind_method(D_METHOD("get_tileset"), &TileMap::get_tileset); ClassDB::bind_method(D_METHOD("set_quadrant_size", "size"), &TileMap::set_quadrant_size); ClassDB::bind_method(D_METHOD("get_quadrant_size"), &TileMap::get_quadrant_size); - ClassDB::bind_method(D_METHOD("set_tile_origin", "origin"), &TileMap::set_tile_origin); - ClassDB::bind_method(D_METHOD("get_tile_origin"), &TileMap::get_tile_origin); - - ClassDB::bind_method(D_METHOD("set_clip_uv", "enable"), &TileMap::set_clip_uv); - ClassDB::bind_method(D_METHOD("get_clip_uv"), &TileMap::get_clip_uv); - - ClassDB::bind_method(D_METHOD("set_y_sort_enabled", "enable"), &TileMap::set_y_sort_enabled); - ClassDB::bind_method(D_METHOD("is_y_sort_enabled"), &TileMap::is_y_sort_enabled); - - ClassDB::bind_method(D_METHOD("set_compatibility_mode", "enable"), &TileMap::set_compatibility_mode); - ClassDB::bind_method(D_METHOD("is_compatibility_mode_enabled"), &TileMap::is_compatibility_mode_enabled); - - ClassDB::bind_method(D_METHOD("set_centered_textures", "enable"), &TileMap::set_centered_textures); - ClassDB::bind_method(D_METHOD("is_centered_textures_enabled"), &TileMap::is_centered_textures_enabled); - - ClassDB::bind_method(D_METHOD("set_collision_use_kinematic", "use_kinematic"), &TileMap::set_collision_use_kinematic); - ClassDB::bind_method(D_METHOD("get_collision_use_kinematic"), &TileMap::get_collision_use_kinematic); - - ClassDB::bind_method(D_METHOD("set_collision_use_parent", "use_parent"), &TileMap::set_collision_use_parent); - ClassDB::bind_method(D_METHOD("get_collision_use_parent"), &TileMap::get_collision_use_parent); - - ClassDB::bind_method(D_METHOD("set_collision_layer", "layer"), &TileMap::set_collision_layer); - ClassDB::bind_method(D_METHOD("get_collision_layer"), &TileMap::get_collision_layer); - - ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &TileMap::set_collision_mask); - ClassDB::bind_method(D_METHOD("get_collision_mask"), &TileMap::get_collision_mask); - - ClassDB::bind_method(D_METHOD("set_collision_layer_bit", "bit", "value"), &TileMap::set_collision_layer_bit); - ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &TileMap::get_collision_layer_bit); - - ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &TileMap::set_collision_mask_bit); - ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &TileMap::get_collision_mask_bit); - - ClassDB::bind_method(D_METHOD("set_collision_friction", "value"), &TileMap::set_collision_friction); - ClassDB::bind_method(D_METHOD("get_collision_friction"), &TileMap::get_collision_friction); - - ClassDB::bind_method(D_METHOD("set_collision_bounce", "value"), &TileMap::set_collision_bounce); - ClassDB::bind_method(D_METHOD("get_collision_bounce"), &TileMap::get_collision_bounce); - - ClassDB::bind_method(D_METHOD("set_occluder_light_mask", "mask"), &TileMap::set_occluder_light_mask); - ClassDB::bind_method(D_METHOD("get_occluder_light_mask"), &TileMap::get_occluder_light_mask); - - ClassDB::bind_method(D_METHOD("set_cell", "x", "y", "tile", "flip_x", "flip_y", "transpose", "autotile_coord"), &TileMap::set_cell, DEFVAL(false), DEFVAL(false), DEFVAL(false), DEFVAL(Vector2())); - ClassDB::bind_method(D_METHOD("set_cellv", "position", "tile", "flip_x", "flip_y", "transpose"), &TileMap::set_cellv, DEFVAL(false), DEFVAL(false), DEFVAL(false)); - ClassDB::bind_method(D_METHOD("_set_celld", "position", "data"), &TileMap::_set_celld); - ClassDB::bind_method(D_METHOD("get_cell", "x", "y"), &TileMap::get_cell); - ClassDB::bind_method(D_METHOD("get_cellv", "position"), &TileMap::get_cellv); - ClassDB::bind_method(D_METHOD("is_cell_x_flipped", "x", "y"), &TileMap::is_cell_x_flipped); - ClassDB::bind_method(D_METHOD("is_cell_y_flipped", "x", "y"), &TileMap::is_cell_y_flipped); - ClassDB::bind_method(D_METHOD("is_cell_transposed", "x", "y"), &TileMap::is_cell_transposed); - - ClassDB::bind_method(D_METHOD("get_cell_autotile_coord", "x", "y"), &TileMap::get_cell_autotile_coord); + ClassDB::bind_method(D_METHOD("set_cell", "coords", "source_id", "atlas_coords", "alternative_tile"), &TileMap::set_cell, DEFVAL(-1), DEFVAL(TileSetAtlasSource::INVALID_ATLAS_COORDS), DEFVAL(TileSetAtlasSource::INVALID_TILE_ALTERNATIVE)); + ClassDB::bind_method(D_METHOD("get_cell_source_id", "coords"), &TileMap::get_cell_source_id); + ClassDB::bind_method(D_METHOD("get_cell_atlas_coords", "coords"), &TileMap::get_cell_atlas_coords); + ClassDB::bind_method(D_METHOD("get_cell_alternative_tile", "coords"), &TileMap::get_cell_alternative_tile); ClassDB::bind_method(D_METHOD("fix_invalid_tiles"), &TileMap::fix_invalid_tiles); + ClassDB::bind_method(D_METHOD("get_surrounding_tiles", "coords"), &TileMap::get_surrounding_tiles); ClassDB::bind_method(D_METHOD("clear"), &TileMap::clear); ClassDB::bind_method(D_METHOD("get_used_cells"), &TileMap::get_used_cells); - ClassDB::bind_method(D_METHOD("get_used_cells_by_index", "index"), &TileMap::get_used_cells_by_index); ClassDB::bind_method(D_METHOD("get_used_rect"), &TileMap::get_used_rect); - ClassDB::bind_method(D_METHOD("map_to_world", "map_position", "ignore_half_ofs"), &TileMap::map_to_world, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("map_to_world", "map_position"), &TileMap::map_to_world); ClassDB::bind_method(D_METHOD("world_to_map", "world_position"), &TileMap::world_to_map); - ClassDB::bind_method(D_METHOD("_clear_quadrants"), &TileMap::_clear_quadrants); - ClassDB::bind_method(D_METHOD("update_dirty_quadrants"), &TileMap::update_dirty_quadrants); + ClassDB::bind_method(D_METHOD("get_neighbor_cell", "coords", "neighbor"), &TileMap::get_neighbor_cell); - ClassDB::bind_method(D_METHOD("update_bitmask_area", "position"), &TileMap::update_bitmask_area); - ClassDB::bind_method(D_METHOD("update_bitmask_region", "start", "end"), &TileMap::update_bitmask_region, DEFVAL(Vector2()), DEFVAL(Vector2())); + ClassDB::bind_method(D_METHOD("update_dirty_quadrants"), &TileMap::update_dirty_quadrants); ClassDB::bind_method(D_METHOD("_set_tile_data"), &TileMap::_set_tile_data); ClassDB::bind_method(D_METHOD("_get_tile_data"), &TileMap::_get_tile_data); - ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Square,Isometric,Custom"), "set_mode", "get_mode"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tile_set", PROPERTY_HINT_RESOURCE_TYPE, "TileSet"), "set_tileset", "get_tileset"); - - ADD_GROUP("Cell", "cell_"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "cell_size", PROPERTY_HINT_RANGE, "1,8192,1"), "set_cell_size", "get_cell_size"); ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_quadrant_size", PROPERTY_HINT_RANGE, "1,128,1"), "set_quadrant_size", "get_quadrant_size"); - ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "cell_custom_transform"), "set_custom_transform", "get_custom_transform"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_half_offset", PROPERTY_HINT_ENUM, "Offset X,Offset Y,Disabled,Offset Negative X,Offset Negative Y"), "set_half_offset", "get_half_offset"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_tile_origin", PROPERTY_HINT_ENUM, "Top Left,Center,Bottom Left"), "set_tile_origin", "get_tile_origin"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cell_y_sort"), "set_y_sort_enabled", "is_y_sort_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "compatibility_mode"), "set_compatibility_mode", "is_compatibility_mode_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "centered_textures"), "set_centered_textures", "is_centered_textures_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cell_clip_uv"), "set_clip_uv", "get_clip_uv"); - - ADD_GROUP("Collision", "collision_"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collision_use_parent", PROPERTY_HINT_NONE, ""), "set_collision_use_parent", "get_collision_use_parent"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collision_use_kinematic", PROPERTY_HINT_NONE, ""), "set_collision_use_kinematic", "get_collision_use_kinematic"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_collision_friction", "get_collision_friction"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_collision_bounce", "get_collision_bounce"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_layer", "get_collision_layer"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_mask", "get_collision_mask"); - - ADD_GROUP("Occluder", "occluder_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "occluder_light_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_occluder_light_mask", "get_occluder_light_mask"); ADD_PROPERTY_DEFAULT("format", FORMAT_1); - ADD_SIGNAL(MethodInfo("settings_changed")); - - BIND_CONSTANT(INVALID_CELL); - - BIND_ENUM_CONSTANT(MODE_SQUARE); - BIND_ENUM_CONSTANT(MODE_ISOMETRIC); - BIND_ENUM_CONSTANT(MODE_CUSTOM); - - BIND_ENUM_CONSTANT(HALF_OFFSET_X); - BIND_ENUM_CONSTANT(HALF_OFFSET_Y); - BIND_ENUM_CONSTANT(HALF_OFFSET_DISABLED); - BIND_ENUM_CONSTANT(HALF_OFFSET_NEGATIVE_X); - BIND_ENUM_CONSTANT(HALF_OFFSET_NEGATIVE_Y); + ADD_SIGNAL(MethodInfo("changed")); +} - BIND_ENUM_CONSTANT(TILE_ORIGIN_TOP_LEFT); - BIND_ENUM_CONSTANT(TILE_ORIGIN_CENTER); - BIND_ENUM_CONSTANT(TILE_ORIGIN_BOTTOM_LEFT); +void TileMap::_tile_set_changed() { + emit_signal("changed"); + _make_all_quadrants_dirty(true); } TileMap::TileMap() { + rect_cache_dirty = true; + used_size_cache_dirty = true; + pending_update = false; + quadrant_size = 16; + format = FORMAT_1; // Assume lowest possible format if none is present + set_notify_transform(true); set_notify_local_transform(false); } TileMap::~TileMap() { - clear(); + if (tile_set.is_valid()) { + tile_set->disconnect("changed", callable_mp(this, &TileMap::_tile_set_changed)); + } + _clear_quadrants(); } diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index 3bf4587921..e9dbccbdb9 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -33,195 +33,195 @@ #include "core/templates/self_list.h" #include "core/templates/vset.h" -#include "scene/2d/navigation_2d.h" #include "scene/2d/node_2d.h" +#include "scene/gui/control.h" #include "scene/resources/tile_set.h" -class CollisionObject2D; +class TileSetAtlasSource; -class TileMap : public Node2D { - GDCLASS(TileMap, Node2D); - -public: - enum Mode { - MODE_SQUARE, - MODE_ISOMETRIC, - MODE_CUSTOM +union TileMapCell { + struct { + int32_t source_id : 16; + int16_t coord_x : 16; + int16_t coord_y : 16; + int32_t alternative_tile : 16; }; - enum HalfOffset { - HALF_OFFSET_X, - HALF_OFFSET_Y, - HALF_OFFSET_DISABLED, - HALF_OFFSET_NEGATIVE_X, - HALF_OFFSET_NEGATIVE_Y, - }; + uint64_t _u64t; + TileMapCell(int p_source_id = -1, Vector2i p_atlas_coords = TileSetAtlasSource::INVALID_ATLAS_COORDS, int p_alternative_tile = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE) { + source_id = p_source_id; + set_atlas_coords(p_atlas_coords); + alternative_tile = p_alternative_tile; + } + + Vector2i get_atlas_coords() const { + return Vector2i(coord_x, coord_y); + } + + void set_atlas_coords(const Vector2i &r_coords) { + coord_x = r_coords.x; + coord_y = r_coords.y; + } + + bool operator<(const TileMapCell &p_other) const { + if (source_id == p_other.source_id) { + if (coord_x == p_other.coord_x) { + if (coord_y == p_other.coord_y) { + return alternative_tile < p_other.alternative_tile; + } else { + return coord_y < p_other.coord_y; + } + } else { + return coord_x < p_other.coord_x; + } + } else { + return source_id < p_other.source_id; + } + } - enum TileOrigin { - TILE_ORIGIN_TOP_LEFT, - TILE_ORIGIN_CENTER, - TILE_ORIGIN_BOTTOM_LEFT - }; + bool operator!=(const TileMapCell &p_other) const { + return !(source_id == p_other.source_id && coord_x == p_other.coord_x && coord_y == p_other.coord_y && alternative_tile == p_other.alternative_tile); + } +}; -private: - enum DataFormat { - FORMAT_1 = 0, - FORMAT_2 +struct TileMapQuadrant { + struct CoordsWorldComparator { + _ALWAYS_INLINE_ bool operator()(const Vector2i &p_a, const Vector2i &p_b) const { + // We sort the cells by their world coords, as it is needed by rendering. + if (p_a.y == p_b.y) { + return p_a.x > p_b.x; + } else { + return p_a.y < p_b.y; + } + } }; - Ref<TileSet> tile_set; - Size2i cell_size = Size2(64, 64); - int quadrant_size = 16; - Mode mode = MODE_SQUARE; - Transform2D custom_transform = Transform2D(64, 0, 0, 64, 0, 0); - HalfOffset half_offset = HALF_OFFSET_DISABLED; - bool use_parent = false; - CollisionObject2D *collision_parent = nullptr; - bool use_kinematic = false; - Navigation2D *navigation = nullptr; - - union PosKey { - struct { - int16_t x; - int16_t y; - }; - uint32_t key = 0; - - //using a more precise comparison so the regions can be sorted later - bool operator<(const PosKey &p_k) const { return (y == p_k.y) ? x < p_k.x : y < p_k.y; } - - bool operator==(const PosKey &p_k) const { return (y == p_k.y && x == p_k.x); } - - PosKey to_quadrant(const int &p_quadrant_size) const { - // rounding down, instead of simply rounding towards zero (truncating) - return PosKey( - x > 0 ? x / p_quadrant_size : (x - (p_quadrant_size - 1)) / p_quadrant_size, - y > 0 ? y / p_quadrant_size : (y - (p_quadrant_size - 1)) / p_quadrant_size); - } + // Dirty list element + SelfList<TileMapQuadrant> dirty_list_element; + + // Quadrant coords. + Vector2i coords; + + // TileMapCells + Set<Vector2i> cells; + // We need those two maps to sort by world position for rendering + // This is kind of workaround, it would be better to sort the cells directly in the "cells" set instead. + Map<Vector2i, Vector2i> map_to_world; + Map<Vector2i, Vector2i, CoordsWorldComparator> world_to_map; + + // Debug. + RID debug_canvas_item; + + // Rendering + List<RID> canvas_items; + List<RID> occluders; + + // Physics. + List<RID> bodies; + + // Navigation + Map<Vector2i, Vector<RID>> navigation_regions; + + void operator=(const TileMapQuadrant &q) { + coords = q.coords; + debug_canvas_item = q.debug_canvas_item; + canvas_items = q.canvas_items; + occluders = q.occluders; + bodies = q.bodies; + navigation_regions = q.navigation_regions; + } + + TileMapQuadrant(const TileMapQuadrant &q) : + dirty_list_element(this) { + coords = q.coords; + debug_canvas_item = q.debug_canvas_item; + canvas_items = q.canvas_items; + occluders = q.occluders; + bodies = q.bodies; + navigation_regions = q.navigation_regions; + } + + TileMapQuadrant() : + dirty_list_element(this) { + } +}; - PosKey(int16_t p_x, int16_t p_y) { - x = p_x; - y = p_y; - } - PosKey() { - x = 0; - y = 0; - } - }; +class TileMapPattern : public Object { + GDCLASS(TileMapPattern, Object); - union Cell { - struct { - int32_t id : 24; - bool flip_h : 1; - bool flip_v : 1; - bool transpose : 1; - int16_t autotile_coord_x : 16; - int16_t autotile_coord_y : 16; - }; - - uint64_t _u64t = 0; - }; + Vector2i size; + Map<Vector2i, TileMapCell> pattern; - Map<PosKey, Cell> tile_map; - List<PosKey> dirty_bitmask; +protected: + static void _bind_methods(); - struct Quadrant { - Vector2 pos; - List<RID> canvas_items; - RID body; - uint32_t shape_owner_id = 0; +public: + void set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i p_atlas_coords, int p_alternative_tile = 0); + bool has_cell(const Vector2i &p_coords) const; + void remove_cell(const Vector2i &p_coords, bool p_update_size = true); + int get_cell_source_id(const Vector2i &p_coords) const; + Vector2i get_cell_atlas_coords(const Vector2i &p_coords) const; + int get_cell_alternative_tile(const Vector2i &p_coords) const; - SelfList<Quadrant> dirty_list; + TypedArray<Vector2i> get_used_cells() const; - struct NavPoly { - RID region; - Transform2D xform; - }; + Vector2i get_size() const; + void set_size(const Vector2i &p_size); + bool is_empty() const; - struct Occluder { - RID id; - Transform2D xform; - }; + void clear(); +}; - Map<PosKey, NavPoly> navpoly_ids; - Map<PosKey, Occluder> occluder_instances; +class TileMap : public Node2D { + GDCLASS(TileMap, Node2D); - VSet<PosKey> cells; +public: +private: + friend class TileSetPlugin; - void operator=(const Quadrant &q) { - pos = q.pos; - canvas_items = q.canvas_items; - body = q.body; - shape_owner_id = q.shape_owner_id; - cells = q.cells; - navpoly_ids = q.navpoly_ids; - occluder_instances = q.occluder_instances; - } - Quadrant(const Quadrant &q) : - dirty_list(this) { - pos = q.pos; - canvas_items = q.canvas_items; - body = q.body; - shape_owner_id = q.shape_owner_id; - cells = q.cells; - occluder_instances = q.occluder_instances; - navpoly_ids = q.navpoly_ids; - } - Quadrant() : - dirty_list(this) {} + enum DataFormat { + FORMAT_1 = 0, + FORMAT_2, + FORMAT_3 }; - Map<PosKey, Quadrant> quadrant_map; + Ref<TileSet> tile_set; + int quadrant_size; + Transform2D custom_transform; + + // Map of cells + Map<Vector2i, TileMapCell> tile_map; - SelfList<Quadrant>::List dirty_quadrant_list; + Vector2i _coords_to_quadrant_coords(const Vector2i &p_coords) const; + + Map<Vector2i, TileMapQuadrant> quadrant_map; + + SelfList<TileMapQuadrant>::List dirty_quadrant_list; bool pending_update = false; Rect2 rect_cache; bool rect_cache_dirty = true; Rect2 used_size_cache; - bool used_size_cache_dirty = true; - bool quadrant_order_dirty = false; - bool use_y_sort = false; - bool compatibility_mode = false; - bool centered_textures = false; - bool clip_uv = false; - float fp_adjust = 0.00001; - float friction = 1.0; - float bounce = 0.0; - uint32_t collision_layer = 1; - uint32_t collision_mask = 1; - mutable DataFormat format = FORMAT_1; // Assume lowest possible format if none is present - - TileOrigin tile_origin = TILE_ORIGIN_TOP_LEFT; - - int occluder_light_mask = 1; - - void _fix_cell_transform(Transform2D &xform, const Cell &p_cell, const Vector2 &p_offset, const Size2 &p_sc); - - void _add_shape(int &shape_idx, const Quadrant &p_q, const Ref<Shape2D> &p_shape, const TileSet::ShapeData &p_shape_data, const Transform2D &p_xform, const Vector2 &p_metadata); - - Map<PosKey, Quadrant>::Element *_create_quadrant(const PosKey &p_qk); - void _erase_quadrant(Map<PosKey, Quadrant>::Element *Q); - void _make_quadrant_dirty(Map<PosKey, Quadrant>::Element *Q, bool update = true); + bool used_size_cache_dirty; + mutable DataFormat format; + + void _fix_cell_transform(Transform2D &xform, const TileMapCell &p_cell, const Vector2 &p_offset, const Size2 &p_sc); + + Map<Vector2i, TileMapQuadrant>::Element *_create_quadrant(const Vector2i &p_qk); + void _erase_quadrant(Map<Vector2i, TileMapQuadrant>::Element *Q); + void _make_all_quadrants_dirty(bool p_update = true); + void _make_quadrant_dirty(Map<Vector2i, TileMapQuadrant>::Element *Q, bool p_update = true); void _recreate_quadrants(); void _clear_quadrants(); - void _update_quadrant_space(const RID &p_space); - void _update_quadrant_transform(); void _recompute_rect_cache(); void _update_all_items_material_state(); - _FORCE_INLINE_ void _update_item_material_state(const RID &p_canvas_item); - - _FORCE_INLINE_ int _get_quadrant_size() const; void _set_tile_data(const Vector<int> &p_data); Vector<int> _get_tile_data() const; - void _set_old_cell_size(int p_size) { set_cell_size(Size2(p_size, p_size)); } - int _get_old_cell_size() const { return cell_size.x; } - - _FORCE_INLINE_ Vector2 _map_to_world(int p_x, int p_y, bool p_ignore_ofs = false) const; + void _tile_set_changed(); protected: bool _set(const StringName &p_name, const Variant &p_value); @@ -231,9 +231,9 @@ protected: void _notification(int p_what); static void _bind_methods(); - virtual void _validate_property(PropertyInfo &property) const override; - public: + static Vector2i transform_coords_layout(Vector2i p_coords, TileSet::TileOffsetAxis p_offset_axis, TileSet::TileLayout p_from_layout, TileSet::TileLayout p_to_layout); + enum { INVALID_CELL = -1 }; @@ -245,114 +245,49 @@ public: void set_tileset(const Ref<TileSet> &p_tileset); Ref<TileSet> get_tileset() const; - void set_cell_size(Size2 p_size); - Size2 get_cell_size() const; - void set_quadrant_size(int p_size); int get_quadrant_size() const; - void set_cell(int p_x, int p_y, int p_tile, bool p_flip_x = false, bool p_flip_y = false, bool p_transpose = false, Vector2 p_autotile_coord = Vector2()); - int get_cell(int p_x, int p_y) const; - bool is_cell_x_flipped(int p_x, int p_y) const; - bool is_cell_y_flipped(int p_x, int p_y) const; - bool is_cell_transposed(int p_x, int p_y) const; - void set_cell_autotile_coord(int p_x, int p_y, const Vector2 &p_coord); - Vector2 get_cell_autotile_coord(int p_x, int p_y) const; + void set_cell(const Vector2i &p_coords, int p_source_id = -1, const Vector2i p_atlas_coords = TileSetAtlasSource::INVALID_ATLAS_COORDS, int p_alternative_tile = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE); + int get_cell_source_id(const Vector2i &p_coords) const; + Vector2i get_cell_atlas_coords(const Vector2i &p_coords) const; + int get_cell_alternative_tile(const Vector2i &p_coords) const; - void _set_celld(const Vector2 &p_pos, const Dictionary &p_data); - void set_cellv(const Vector2 &p_pos, int p_tile, bool p_flip_x = false, bool p_flip_y = false, bool p_transpose = false); - int get_cellv(const Vector2 &p_pos) const; + TileMapPattern *get_pattern(TypedArray<Vector2i> p_coords_array); + Vector2i map_pattern(Vector2i p_position_in_tilemap, Vector2i p_coords_in_pattern, const TileMapPattern *p_pattern); + void set_pattern(Vector2i p_position, const TileMapPattern *p_pattern); - void make_bitmask_area_dirty(const Vector2 &p_pos); - void update_bitmask_area(const Vector2 &p_pos); - void update_bitmask_region(const Vector2 &p_start = Vector2(), const Vector2 &p_end = Vector2()); - void update_cell_bitmask(int p_x, int p_y); - void update_dirty_bitmask(); + // Not exposed to users + TileMapCell get_cell(const Vector2i &p_coords) const; + Map<Vector2i, TileMapQuadrant> &get_quadrant_map(); + int get_effective_quadrant_size() const; void update_dirty_quadrants(); - void set_collision_layer(uint32_t p_layer); - uint32_t get_collision_layer() const; - - void set_collision_mask(uint32_t p_mask); - uint32_t get_collision_mask() const; - - void set_collision_layer_bit(int p_bit, bool p_value); - bool get_collision_layer_bit(int p_bit) const; - - void set_collision_mask_bit(int p_bit, bool p_value); - bool get_collision_mask_bit(int p_bit) const; - - void set_collision_use_kinematic(bool p_use_kinematic); - bool get_collision_use_kinematic() const; - - void set_collision_use_parent(bool p_use_parent); - bool get_collision_use_parent() const; - - void set_collision_friction(float p_friction); - float get_collision_friction() const; - - void set_collision_bounce(float p_bounce); - float get_collision_bounce() const; - - void set_mode(Mode p_mode); - Mode get_mode() const; + Vector2 map_to_world(const Vector2i &p_pos) const; + Vector2i world_to_map(const Vector2 &p_pos) const; - void set_half_offset(HalfOffset p_half_offset); - HalfOffset get_half_offset() const; - - void set_tile_origin(TileOrigin p_tile_origin); - TileOrigin get_tile_origin() const; - - void set_custom_transform(const Transform2D &p_xform); - Transform2D get_custom_transform() const; - - Transform2D get_cell_transform() const; - Vector2 get_cell_draw_offset() const; - - Vector2 map_to_world(const Vector2 &p_pos, bool p_ignore_ofs = false) const; - Vector2 world_to_map(const Vector2 &p_pos) const; - - void set_y_sort_enabled(bool p_enable); - bool is_y_sort_enabled() const; - - void set_compatibility_mode(bool p_enable); - bool is_compatibility_mode_enabled() const; - - void set_centered_textures(bool p_enable); - bool is_centered_textures_enabled() const; + bool is_existing_neighbor(TileSet::CellNeighbor p_cell_neighbor) const; + Vector2i get_neighbor_cell(const Vector2i &p_coords, TileSet::CellNeighbor p_cell_neighbor) const; TypedArray<Vector2i> get_used_cells() const; - TypedArray<Vector2i> get_used_cells_by_index(int p_index) const; Rect2 get_used_rect(); // Not const because of cache - void set_occluder_light_mask(int p_mask); - int get_occluder_light_mask() const; - + // Override some methods of the CanvasItem class to pass the changes to the quadrants CanvasItems virtual void set_light_mask(int p_light_mask) override; - virtual void set_material(const Ref<Material> &p_material) override; - virtual void set_use_parent_material(bool p_use_parent_material) override; - - void set_clip_uv(bool p_enable); - bool get_clip_uv() const; - - String get_configuration_warning() const override; - virtual void set_texture_filter(CanvasItem::TextureFilter p_texture_filter) override; - virtual void set_texture_repeat(CanvasItem::TextureRepeat p_texture_repeat) override; void fix_invalid_tiles(); void clear(); + // Helpers + TypedArray<Vector2i> get_surrounding_tiles(Vector2i coords); + void draw_cells_outline(Control *p_control, Set<Vector2i> p_cells, Color p_color, Transform2D p_transform = Transform2D()); + TileMap(); ~TileMap(); }; - -VARIANT_ENUM_CAST(TileMap::Mode); -VARIANT_ENUM_CAST(TileMap::HalfOffset); -VARIANT_ENUM_CAST(TileMap::TileOrigin); - #endif // TILE_MAP_H diff --git a/scene/2d/touch_screen_button.cpp b/scene/2d/touch_screen_button.cpp index 9d6868a1b2..4e58984b37 100644 --- a/scene/2d/touch_screen_button.cpp +++ b/scene/2d/touch_screen_button.cpp @@ -189,6 +189,8 @@ String TouchScreenButton::get_action() const { } void TouchScreenButton::_input(const Ref<InputEvent> &p_event) { + ERR_FAIL_COND(p_event.is_null()); + if (!get_tree()) { return; } diff --git a/scene/2d/visibility_notifier_2d.cpp b/scene/2d/visibility_notifier_2d.cpp index 916038a1f3..8feb47f1cc 100644 --- a/scene/2d/visibility_notifier_2d.cpp +++ b/scene/2d/visibility_notifier_2d.cpp @@ -310,18 +310,15 @@ void VisibilityEnabler2D::_node_removed(Node *p_node) { nodes.erase(p_node); } -String VisibilityEnabler2D::get_configuration_warning() const { - String warning = VisibilityNotifier2D::get_configuration_warning(); +TypedArray<String> VisibilityEnabler2D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); #ifdef TOOLS_ENABLED if (is_inside_tree() && get_parent() && (get_parent()->get_filename() == String() && get_parent() != get_tree()->get_edited_scene_root())) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("VisibilityEnabler2D works best when used with the edited scene root directly as parent."); + warnings.push_back(TTR("VisibilityEnabler2D works best when used with the edited scene root directly as parent.")); } #endif - return warning; + return warnings; } void VisibilityEnabler2D::_bind_methods() { diff --git a/scene/2d/visibility_notifier_2d.h b/scene/2d/visibility_notifier_2d.h index 3d1701a1e5..7f4a5bc193 100644 --- a/scene/2d/visibility_notifier_2d.h +++ b/scene/2d/visibility_notifier_2d.h @@ -102,7 +102,7 @@ public: void set_enabler(Enabler p_enabler, bool p_enable); bool is_enabler_enabled(Enabler p_enabler) const; - String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; VisibilityEnabler2D(); }; diff --git a/scene/3d/area_3d.cpp b/scene/3d/area_3d.cpp index 35049b6478..44708cddff 100644 --- a/scene/3d/area_3d.cpp +++ b/scene/3d/area_3d.cpp @@ -212,7 +212,7 @@ void Area3D::_clear_monitoring() { Object *obj = ObjectDB::get_instance(E->key()); Node *node = Object::cast_to<Node>(obj); - if (!node) { //node may have been deleted in previous frame or at other legiminate point + if (!node) { //node may have been deleted in previous frame or at other legitimate point continue; } //ERR_CONTINUE(!node); @@ -241,7 +241,7 @@ void Area3D::_clear_monitoring() { Object *obj = ObjectDB::get_instance(E->key()); Node *node = Object::cast_to<Node>(obj); - if (!node) { //node may have been deleted in previous frame or at other legiminate point + if (!node) { //node may have been deleted in previous frame or at other legitimate point continue; } //ERR_CONTINUE(!node); @@ -453,52 +453,6 @@ bool Area3D::overlaps_body(Node *p_body) const { return E->get().in_tree; } -void Area3D::set_collision_mask(uint32_t p_mask) { - collision_mask = p_mask; - PhysicsServer3D::get_singleton()->area_set_collision_mask(get_rid(), p_mask); -} - -uint32_t Area3D::get_collision_mask() const { - return collision_mask; -} - -void Area3D::set_collision_layer(uint32_t p_layer) { - collision_layer = p_layer; - PhysicsServer3D::get_singleton()->area_set_collision_layer(get_rid(), p_layer); -} - -uint32_t Area3D::get_collision_layer() const { - return collision_layer; -} - -void Area3D::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 Area3D::get_collision_mask_bit(int p_bit) const { - return get_collision_mask() & (1 << p_bit); -} - -void Area3D::set_collision_layer_bit(int p_bit, bool p_value) { - uint32_t layer = get_collision_layer(); - if (p_value) { - layer |= 1 << p_bit; - } else { - layer &= ~(1 << p_bit); - } - set_collision_layer(layer); -} - -bool Area3D::get_collision_layer_bit(int p_bit) const { - return get_collision_layer() & (1 << p_bit); -} - void Area3D::set_audio_bus_override(bool p_override) { audio_bus_override = p_override; } @@ -597,18 +551,6 @@ void Area3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_priority", "priority"), &Area3D::set_priority); ClassDB::bind_method(D_METHOD("get_priority"), &Area3D::get_priority); - ClassDB::bind_method(D_METHOD("set_collision_mask", "collision_mask"), &Area3D::set_collision_mask); - ClassDB::bind_method(D_METHOD("get_collision_mask"), &Area3D::get_collision_mask); - - ClassDB::bind_method(D_METHOD("set_collision_layer", "collision_layer"), &Area3D::set_collision_layer); - ClassDB::bind_method(D_METHOD("get_collision_layer"), &Area3D::get_collision_layer); - - ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &Area3D::set_collision_mask_bit); - ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &Area3D::get_collision_mask_bit); - - ClassDB::bind_method(D_METHOD("set_collision_layer_bit", "bit", "value"), &Area3D::set_collision_layer_bit); - ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &Area3D::get_collision_layer_bit); - ClassDB::bind_method(D_METHOD("set_monitorable", "enable"), &Area3D::set_monitorable); ClassDB::bind_method(D_METHOD("is_monitorable"), &Area3D::is_monitorable); @@ -652,22 +594,23 @@ void Area3D::_bind_methods() { ADD_SIGNAL(MethodInfo("area_entered", PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area3D"))); ADD_SIGNAL(MethodInfo("area_exited", PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area3D"))); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "monitoring"), "set_monitoring", "is_monitoring"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "monitorable"), "set_monitorable", "is_monitorable"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,128,1"), "set_priority", "get_priority"); + + ADD_GROUP("Physics Overrides", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "space_override", PROPERTY_HINT_ENUM, "Disabled,Combine,Combine-Replace,Replace,Replace-Combine"), "set_space_override_mode", "get_space_override_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gravity_point"), "set_gravity_is_point", "is_gravity_a_point"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_distance_scale", PROPERTY_HINT_EXP_RANGE, "0,1024,0.001,or_greater"), "set_gravity_distance_scale", "get_gravity_distance_scale"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity_vec"), "set_gravity_vector", "get_gravity_vector"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity", PROPERTY_HINT_RANGE, "-1024,1024,0.01"), "set_gravity", "get_gravity"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity", PROPERTY_HINT_RANGE, "-32,32,0.001,or_lesser,or_greater"), "set_gravity", "get_gravity"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "linear_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_linear_damp", "get_linear_damp"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_angular_damp", "get_angular_damp"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,128,1"), "set_priority", "get_priority"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "monitoring"), "set_monitoring", "is_monitoring"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "monitorable"), "set_monitorable", "is_monitorable"); - ADD_GROUP("Collision", "collision_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer", "get_collision_layer"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask"); + ADD_GROUP("Audio Bus", "audio_bus_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "audio_bus_override"), "set_audio_bus_override", "is_overriding_audio_bus"); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "audio_bus_name", PROPERTY_HINT_ENUM, ""), "set_audio_bus_name", "get_audio_bus_name"); + ADD_GROUP("Reverb Bus", "reverb_bus_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "reverb_bus_enable"), "set_use_reverb_bus", "is_using_reverb_bus"); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "reverb_bus_name", PROPERTY_HINT_ENUM, ""), "set_reverb_bus", "get_reverb_bus"); diff --git a/scene/3d/area_3d.h b/scene/3d/area_3d.h index bed4755aee..5b8d612717 100644 --- a/scene/3d/area_3d.h +++ b/scene/3d/area_3d.h @@ -54,8 +54,6 @@ private: real_t gravity_distance_scale = 0.0; real_t angular_damp = 0.1; real_t linear_damp = 0.1; - uint32_t collision_mask = 1; - uint32_t collision_layer = 1; int priority = 0; bool monitoring = false; bool monitorable = false; @@ -171,18 +169,6 @@ public: void set_monitorable(bool p_enable); bool is_monitorable() const; - void set_collision_mask(uint32_t p_mask); - uint32_t get_collision_mask() const; - - void set_collision_layer(uint32_t p_layer); - uint32_t get_collision_layer() const; - - void set_collision_mask_bit(int p_bit, bool p_value); - bool get_collision_mask_bit(int p_bit) const; - - void set_collision_layer_bit(int p_bit, bool p_value); - bool get_collision_layer_bit(int p_bit) const; - TypedArray<Node3D> get_overlapping_bodies() const; TypedArray<Area3D> get_overlapping_areas() const; //function for script diff --git a/scene/3d/baked_lightmap.cpp b/scene/3d/baked_lightmap.cpp index 402e2b8f40..ef648a126e 100644 --- a/scene/3d/baked_lightmap.cpp +++ b/scene/3d/baked_lightmap.cpp @@ -259,7 +259,7 @@ void BakedLightmap::_find_meshes_and_lights(Node *p_at_node, Vector<MeshesFound> if (all_override.is_valid()) { mf.overrides.push_back(all_override); } else { - mf.overrides.push_back(mi->get_surface_material(i)); + mf.overrides.push_back(mi->get_surface_override_material(i)); } } @@ -449,7 +449,7 @@ int32_t BakedLightmap::_compute_bsp_tree(const Vector<Vector3> &p_points, const ERR_FAIL_COND_V(p_simplex_indices.size() <= 1, 0); //should not happen, this is a bug // Failed to separate the tetrahedrons using planes - // this means Delaunay borked at some point. + // this means Delaunay broke at some point. // Luckily, because we are using tetrahedrons, we can resort to // less precise but still working ways to generate the separating plane // this will most likely look bad when interpolating, but at least it will not crash. @@ -511,7 +511,7 @@ int32_t BakedLightmap::_compute_bsp_tree(const Vector<Vector3> &p_points, const node.plane = best_plane; if (indices_under.size() == 0) { - //noting to do here + //nothing to do here node.under = BSPNode::EMPTY_LEAF; } else if (indices_under.size() == 1) { node.under = -(indices_under[0] + 1); @@ -520,7 +520,7 @@ int32_t BakedLightmap::_compute_bsp_tree(const Vector<Vector3> &p_points, const } if (indices_over.size() == 0) { - //noting to do here + //nothing to do here node.over = BSPNode::EMPTY_LEAF; } else if (indices_over.size() == 1) { node.over = -(indices_over[0] + 1); @@ -619,10 +619,6 @@ void BakedLightmap::_gen_new_positions_from_octree(const GenProbesOctree *p_cell } BakedLightmap::BakeError BakedLightmap::bake(Node *p_from_node, String p_image_data_path, Lightmapper::BakeStepFunc p_bake_step, void *p_bake_userdata) { - if (p_image_data_path == "" && (get_light_data().is_null() || !get_light_data()->get_path().is_resource_file())) { - return BAKE_ERROR_NO_SAVE_PATH; - } - if (p_image_data_path == "") { if (get_light_data().is_null()) { return BAKE_ERROR_NO_SAVE_PATH; @@ -660,7 +656,7 @@ BakedLightmap::BakeError BakedLightmap::bake(Node *p_from_node, String p_image_d } // create mesh data for insert - //get the base material textures, help compute altlas size and bounds + //get the base material textures, help compute atlas size and bounds for (int m_i = 0; m_i < meshes_found.size(); m_i++) { if (p_bake_step) { float p = (float)(m_i) / meshes_found.size(); @@ -974,7 +970,7 @@ BakedLightmap::BakeError BakedLightmap::bake(Node *p_from_node, String p_image_d for (int i = 0; i < lightmapper->get_bake_texture_count(); i++) { images.push_back(lightmapper->get_bake_texture(i)); } - //we assume they are all the same, so lets create a large one for saving + //we assume they are all the same, so let's create a large one for saving Ref<Image> large_image; large_image.instance(); diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp index f0623c625e..041da4f6ff 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -102,7 +102,7 @@ void Camera3D::_update_camera() { void Camera3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_WORLD: { - // Needs to track the Viewport because it's needed on NOTIFICATION_EXIT_WORLD + // 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(); @@ -715,7 +715,7 @@ void ClippedCamera3D::_notification(int p_what) { Vector3 ray_from = parent_plane.project(cam_pos); - clip_offset = 0; //reset by defau;t + clip_offset = 0; //reset by default { //check if points changed Vector<Vector3> local_points = get_near_plane_points(); @@ -761,6 +761,7 @@ uint32_t ClippedCamera3D::get_collision_mask() const { } void ClippedCamera3D::set_collision_mask_bit(int p_bit, bool p_value) { + ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision layer bit must be between 0 and 31 inclusive."); uint32_t mask = get_collision_mask(); if (p_value) { mask |= 1 << p_bit; @@ -771,6 +772,7 @@ void ClippedCamera3D::set_collision_mask_bit(int p_bit, bool p_value) { } bool ClippedCamera3D::get_collision_mask_bit(int p_bit) const { + ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision mask bit must be between 0 and 31 inclusive."); return get_collision_mask() & (1 << p_bit); } diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp index 849ef7a2bf..914b3ad816 100644 --- a/scene/3d/collision_object_3d.cpp +++ b/scene/3d/collision_object_3d.cpp @@ -30,12 +30,28 @@ #include "collision_object_3d.h" -#include "mesh_instance_3d.h" +#include "core/config/engine.h" #include "scene/scene_string_names.h" #include "servers/physics_server_3d.h" void CollisionObject3D::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + if (_are_collision_shapes_visible()) { + debug_shape_old_transform = get_global_transform(); + for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) { + debug_shapes_to_update.insert(E->key()); + } + _update_debug_shapes(); + } + } break; + + case NOTIFICATION_EXIT_TREE: { + if (debug_shapes_count > 0) { + _clear_debug_shapes(); + } + } break; + case NOTIFICATION_ENTER_WORLD: { if (area) { PhysicsServer3D::get_singleton()->area_set_transform(rid, get_global_transform()); @@ -61,6 +77,8 @@ void CollisionObject3D::_notification(int p_what) { PhysicsServer3D::get_singleton()->body_set_state(rid, PhysicsServer3D::BODY_STATE_TRANSFORM, get_global_transform()); } + _on_transform_changed(); + } break; case NOTIFICATION_VISIBILITY_CHANGED: { _update_pickable(); @@ -77,6 +95,64 @@ void CollisionObject3D::_notification(int p_what) { } } +void CollisionObject3D::set_collision_layer(uint32_t p_layer) { + collision_layer = p_layer; + if (area) { + PhysicsServer3D::get_singleton()->area_set_collision_layer(get_rid(), p_layer); + } else { + PhysicsServer3D::get_singleton()->body_set_collision_layer(get_rid(), p_layer); + } +} + +uint32_t CollisionObject3D::get_collision_layer() const { + return collision_layer; +} + +void CollisionObject3D::set_collision_mask(uint32_t p_mask) { + collision_mask = p_mask; + if (area) { + PhysicsServer3D::get_singleton()->area_set_collision_mask(get_rid(), p_mask); + } else { + PhysicsServer3D::get_singleton()->body_set_collision_mask(get_rid(), p_mask); + } +} + +uint32_t CollisionObject3D::get_collision_mask() const { + return collision_mask; +} + +void CollisionObject3D::set_collision_layer_bit(int p_bit, bool p_value) { + ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision layer bit must be between 0 and 31 inclusive."); + uint32_t collision_layer = get_collision_layer(); + if (p_value) { + collision_layer |= 1 << p_bit; + } else { + collision_layer &= ~(1 << p_bit); + } + set_collision_layer(collision_layer); +} + +bool CollisionObject3D::get_collision_layer_bit(int p_bit) const { + ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision layer bit must be between 0 and 31 inclusive."); + return get_collision_layer() & (1 << p_bit); +} + +void CollisionObject3D::set_collision_mask_bit(int p_bit, bool p_value) { + ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision mask bit must be between 0 and 31 inclusive."); + uint32_t mask = get_collision_mask(); + if (p_value) { + mask |= 1 << p_bit; + } else { + mask &= ~(1 << p_bit); + } + set_collision_mask(mask); +} + +bool CollisionObject3D::get_collision_mask_bit(int p_bit) const { + ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision mask bit must be between 0 and 31 inclusive."); + return get_collision_mask() & (1 << p_bit); +} + void CollisionObject3D::_input_event(Node *p_camera, const Ref<InputEvent> &p_input_event, const Vector3 &p_pos, const Vector3 &p_normal, int p_shape) { if (get_script_instance()) { get_script_instance()->call(SceneStringNames::get_singleton()->_input_event, p_camera, p_input_event, p_pos, p_normal, p_shape); @@ -111,39 +187,98 @@ void CollisionObject3D::_update_pickable() { } } +bool CollisionObject3D::_are_collision_shapes_visible() { + return is_inside_tree() && get_tree()->is_debugging_collisions_hint() && !Engine::get_singleton()->is_editor_hint(); +} + +void CollisionObject3D::_update_shape_data(uint32_t p_owner) { + if (_are_collision_shapes_visible()) { + if (debug_shapes_to_update.is_empty()) { + callable_mp(this, &CollisionObject3D::_update_debug_shapes).call_deferred({}, 0); + } + debug_shapes_to_update.insert(p_owner); + } +} + +void CollisionObject3D::_shape_changed(const Ref<Shape3D> &p_shape) { + for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) { + ShapeData &shapedata = E->get(); + ShapeData::ShapeBase *shapes = shapedata.shapes.ptrw(); + for (int i = 0; i < shapedata.shapes.size(); i++) { + ShapeData::ShapeBase &s = shapes[i]; + if (s.shape == p_shape && s.debug_shape.is_valid()) { + Ref<Mesh> mesh = s.shape->get_debug_mesh(); + RS::get_singleton()->instance_set_base(s.debug_shape, mesh->get_rid()); + } + } + } +} + void CollisionObject3D::_update_debug_shapes() { for (Set<uint32_t>::Element *shapedata_idx = debug_shapes_to_update.front(); shapedata_idx; shapedata_idx = shapedata_idx->next()) { if (shapes.has(shapedata_idx->get())) { ShapeData &shapedata = shapes[shapedata_idx->get()]; + ShapeData::ShapeBase *shapes = shapedata.shapes.ptrw(); for (int i = 0; i < shapedata.shapes.size(); i++) { - ShapeData::ShapeBase &s = shapedata.shapes.write[i]; - if (s.debug_shape) { - s.debug_shape->queue_delete(); - s.debug_shape = nullptr; - } + ShapeData::ShapeBase &s = shapes[i]; if (s.shape.is_null() || shapedata.disabled) { + if (s.debug_shape.is_valid()) { + RS::get_singleton()->free(s.debug_shape); + s.debug_shape = RID(); + --debug_shapes_count; + } continue; } + if (s.debug_shape.is_null()) { + s.debug_shape = RS::get_singleton()->instance_create(); + RS::get_singleton()->instance_set_scenario(s.debug_shape, get_world_3d()->get_scenario()); + + if (!s.shape->is_connected("changed", callable_mp(this, &CollisionObject3D::_shape_changed))) { + s.shape->connect("changed", callable_mp(this, &CollisionObject3D::_shape_changed), + varray(s.shape), CONNECT_DEFERRED); + } + + ++debug_shapes_count; + } + Ref<Mesh> mesh = s.shape->get_debug_mesh(); - MeshInstance3D *mi = memnew(MeshInstance3D); - mi->set_transform(shapedata.xform); - mi->set_mesh(mesh); - add_child(mi); - mi->force_update_transform(); - s.debug_shape = mi; + RS::get_singleton()->instance_set_base(s.debug_shape, mesh->get_rid()); + RS::get_singleton()->instance_set_transform(s.debug_shape, get_global_transform() * shapedata.xform); } } } debug_shapes_to_update.clear(); } -void CollisionObject3D::_update_shape_data(uint32_t p_owner) { - if (is_inside_tree() && get_tree()->is_debugging_collisions_hint()) { - if (debug_shapes_to_update.is_empty()) { - call_deferred("_update_debug_shapes"); +void CollisionObject3D::_clear_debug_shapes() { + for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) { + ShapeData &shapedata = E->get(); + ShapeData::ShapeBase *shapes = shapedata.shapes.ptrw(); + for (int i = 0; i < shapedata.shapes.size(); i++) { + ShapeData::ShapeBase &s = shapes[i]; + if (s.debug_shape.is_valid()) { + RS::get_singleton()->free(s.debug_shape); + s.debug_shape = RID(); + if (s.shape.is_valid() && s.shape->is_connected("changed", callable_mp(this, &CollisionObject3D::_update_shape_data))) { + s.shape->disconnect("changed", callable_mp(this, &CollisionObject3D::_update_shape_data)); + } + } + } + } + debug_shapes_count = 0; +} + +void CollisionObject3D::_on_transform_changed() { + if (debug_shapes_count > 0 && !debug_shape_old_transform.is_equal_approx(get_global_transform())) { + debug_shape_old_transform = get_global_transform(); + for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) { + ShapeData &shapedata = E->get(); + const ShapeData::ShapeBase *shapes = shapedata.shapes.ptr(); + for (int i = 0; i < shapedata.shapes.size(); i++) { + RS::get_singleton()->instance_set_transform(shapes[i].debug_shape, debug_shape_old_transform * shapedata.xform); + } } - debug_shapes_to_update.insert(p_owner); } } @@ -157,6 +292,14 @@ bool CollisionObject3D::is_ray_pickable() const { } void CollisionObject3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_collision_layer", "layer"), &CollisionObject3D::set_collision_layer); + ClassDB::bind_method(D_METHOD("get_collision_layer"), &CollisionObject3D::get_collision_layer); + ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &CollisionObject3D::set_collision_mask); + ClassDB::bind_method(D_METHOD("get_collision_mask"), &CollisionObject3D::get_collision_mask); + ClassDB::bind_method(D_METHOD("set_collision_layer_bit", "bit", "value"), &CollisionObject3D::set_collision_layer_bit); + ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &CollisionObject3D::get_collision_layer_bit); + ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &CollisionObject3D::set_collision_mask_bit); + ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &CollisionObject3D::get_collision_mask_bit); ClassDB::bind_method(D_METHOD("set_ray_pickable", "ray_pickable"), &CollisionObject3D::set_ray_pickable); ClassDB::bind_method(D_METHOD("is_ray_pickable"), &CollisionObject3D::is_ray_pickable); ClassDB::bind_method(D_METHOD("set_capture_input_on_drag", "enable"), &CollisionObject3D::set_capture_input_on_drag); @@ -178,14 +321,17 @@ void CollisionObject3D::_bind_methods() { ClassDB::bind_method(D_METHOD("shape_owner_clear_shapes", "owner_id"), &CollisionObject3D::shape_owner_clear_shapes); ClassDB::bind_method(D_METHOD("shape_find_owner", "shape_index"), &CollisionObject3D::shape_find_owner); - ClassDB::bind_method(D_METHOD("_update_debug_shapes"), &CollisionObject3D::_update_debug_shapes); - BIND_VMETHOD(MethodInfo("_input_event", PropertyInfo(Variant::OBJECT, "camera"), PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"), PropertyInfo(Variant::VECTOR3, "click_position"), PropertyInfo(Variant::VECTOR3, "click_normal"), PropertyInfo(Variant::INT, "shape_idx"))); ADD_SIGNAL(MethodInfo("input_event", PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"), PropertyInfo(Variant::VECTOR3, "click_position"), PropertyInfo(Variant::VECTOR3, "click_normal"), PropertyInfo(Variant::INT, "shape_idx"))); ADD_SIGNAL(MethodInfo("mouse_entered")); ADD_SIGNAL(MethodInfo("mouse_exited")); + ADD_GROUP("Collision", "collision_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer", "get_collision_layer"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask"); + + ADD_GROUP("Input", "input_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "input_ray_pickable"), "set_ray_pickable", "is_ray_pickable"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "input_capture_on_drag"), "set_capture_input_on_drag", "get_capture_input_on_drag"); } @@ -219,7 +365,11 @@ void CollisionObject3D::shape_owner_set_disabled(uint32_t p_owner, bool p_disabl ERR_FAIL_COND(!shapes.has(p_owner)); ShapeData &sd = shapes[p_owner]; + if (sd.disabled == p_disabled) { + return; + } sd.disabled = p_disabled; + for (int i = 0; i < sd.shapes.size(); i++) { if (area) { PhysicsServer3D::get_singleton()->area_set_shape_disabled(rid, sd.shapes[i].index, p_disabled); @@ -324,7 +474,7 @@ void CollisionObject3D::shape_owner_remove_shape(uint32_t p_owner, int p_shape) ERR_FAIL_COND(!shapes.has(p_owner)); ERR_FAIL_INDEX(p_shape, shapes[p_owner].shapes.size()); - const ShapeData::ShapeBase &s = shapes[p_owner].shapes[p_shape]; + ShapeData::ShapeBase &s = shapes[p_owner].shapes.write[p_shape]; int index_to_remove = s.index; if (area) { @@ -333,8 +483,12 @@ void CollisionObject3D::shape_owner_remove_shape(uint32_t p_owner, int p_shape) PhysicsServer3D::get_singleton()->body_remove_shape(rid, index_to_remove); } - if (s.debug_shape) { - s.debug_shape->queue_delete(); + if (s.debug_shape.is_valid()) { + RS::get_singleton()->free(s.debug_shape); + if (s.shape.is_valid() && s.shape->is_connected("changed", callable_mp(this, &CollisionObject3D::_shape_changed))) { + s.shape->disconnect("changed", callable_mp(this, &CollisionObject3D::_shape_changed)); + } + --debug_shapes_count; } shapes[p_owner].shapes.remove(p_shape); @@ -394,17 +548,14 @@ bool CollisionObject3D::get_capture_input_on_drag() const { return capture_input_on_drag; } -String CollisionObject3D::get_configuration_warning() const { - String warning = Node3D::get_configuration_warning(); +TypedArray<String> CollisionObject3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); if (shapes.is_empty()) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("This node has no shape, so it can't collide or interact with other objects.\nConsider adding a CollisionShape3D or CollisionPolygon3D as a child to define its shape."); + warnings.push_back(TTR("This node has no shape, so it can't collide or interact with other objects.\nConsider adding a CollisionShape3D or CollisionPolygon3D as a child to define its shape.")); } - return warning; + return warnings; } CollisionObject3D::CollisionObject3D() { diff --git a/scene/3d/collision_object_3d.h b/scene/3d/collision_object_3d.h index fe20176984..7ff3c5efde 100644 --- a/scene/3d/collision_object_3d.h +++ b/scene/3d/collision_object_3d.h @@ -37,6 +37,9 @@ class CollisionObject3D : public Node3D { GDCLASS(CollisionObject3D, Node3D); + uint32_t collision_layer = 1; + uint32_t collision_mask = 1; + bool area = false; RID rid; @@ -45,7 +48,7 @@ class CollisionObject3D : public Node3D { Object *owner = nullptr; Transform xform; struct ShapeBase { - Node *debug_shape = nullptr; + RID debug_shape; Ref<Shape3D> shape; int index = 0; }; @@ -62,24 +65,43 @@ class CollisionObject3D : public Node3D { bool ray_pickable = true; Set<uint32_t> debug_shapes_to_update; + int debug_shapes_count = 0; + Transform debug_shape_old_transform; void _update_pickable(); + bool _are_collision_shapes_visible(); void _update_shape_data(uint32_t p_owner); + void _shape_changed(const Ref<Shape3D> &p_shape); + void _update_debug_shapes(); + void _clear_debug_shapes(); protected: CollisionObject3D(RID p_rid, bool p_area); void _notification(int p_what); static void _bind_methods(); + + void _on_transform_changed(); + friend class Viewport; virtual void _input_event(Node *p_camera, const Ref<InputEvent> &p_input_event, const Vector3 &p_pos, const Vector3 &p_normal, int p_shape); virtual void _mouse_enter(); virtual void _mouse_exit(); - void _update_debug_shapes(); - public: + void set_collision_layer(uint32_t p_layer); + uint32_t get_collision_layer() const; + + void set_collision_mask(uint32_t p_mask); + uint32_t get_collision_mask() const; + + void set_collision_layer_bit(int p_bit, bool p_value); + bool get_collision_layer_bit(int p_bit) const; + + void set_collision_mask_bit(int p_bit, bool p_value); + bool get_collision_mask_bit(int p_bit) const; + uint32_t create_shape_owner(Object *p_owner); void remove_shape_owner(uint32_t owner); void get_shape_owners(List<uint32_t> *r_owners); @@ -110,7 +132,7 @@ public: _FORCE_INLINE_ RID get_rid() const { return rid; } - virtual String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; CollisionObject3D(); ~CollisionObject3D(); diff --git a/scene/3d/collision_polygon_3d.cpp b/scene/3d/collision_polygon_3d.cpp index e3e2eb4669..ac715b22b2 100644 --- a/scene/3d/collision_polygon_3d.cpp +++ b/scene/3d/collision_polygon_3d.cpp @@ -121,7 +121,7 @@ void CollisionPolygon3D::set_polygon(const Vector<Point2> &p_polygon) { if (parent) { _build_polygon(); } - update_configuration_warning(); + update_configuration_warnings(); update_gizmo(); } @@ -167,24 +167,18 @@ void CollisionPolygon3D::set_margin(real_t p_margin) { } } -String CollisionPolygon3D::get_configuration_warning() const { - String warning = Node3D::get_configuration_warning(); +TypedArray<String> CollisionPolygon3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); if (!Object::cast_to<CollisionObject3D>(get_parent())) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("CollisionPolygon3D only serves to provide a collision shape to a CollisionObject3D derived node. Please only use it as a child of Area3D, StaticBody3D, RigidBody3D, KinematicBody3D, etc. to give them a shape."); + warnings.push_back(TTR("CollisionPolygon3D only serves to provide a collision shape to a CollisionObject3D derived node. Please only use it as a child of Area3D, StaticBody3D, RigidBody3D, KinematicBody3D, etc. to give them a shape.")); } if (polygon.is_empty()) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("An empty CollisionPolygon3D has no effect on collision."); + warnings.push_back(TTR("An empty CollisionPolygon3D has no effect on collision.")); } - return warning; + return warnings; } bool CollisionPolygon3D::_is_editable_3d_polygon() const { diff --git a/scene/3d/collision_polygon_3d.h b/scene/3d/collision_polygon_3d.h index 750751b509..73b8a8e0e3 100644 --- a/scene/3d/collision_polygon_3d.h +++ b/scene/3d/collision_polygon_3d.h @@ -74,7 +74,7 @@ public: real_t get_margin() const; void set_margin(real_t p_margin); - String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; CollisionPolygon3D(); }; diff --git a/scene/3d/collision_shape_3d.cpp b/scene/3d/collision_shape_3d.cpp index 242d82ab4c..70d9cebb83 100644 --- a/scene/3d/collision_shape_3d.cpp +++ b/scene/3d/collision_shape_3d.cpp @@ -120,34 +120,25 @@ void CollisionShape3D::resource_changed(RES res) { update_gizmo(); } -String CollisionShape3D::get_configuration_warning() const { - String warning = Node3D::get_configuration_warning(); +TypedArray<String> CollisionShape3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); if (!Object::cast_to<CollisionObject3D>(get_parent())) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("CollisionShape3D only serves to provide a collision shape to a CollisionObject3D derived node. Please only use it as a child of Area3D, StaticBody3D, RigidBody3D, KinematicBody3D, etc. to give them a shape."); + warnings.push_back(TTR("CollisionShape3D only serves to provide a collision shape to a CollisionObject3D derived node. Please only use it as a child of Area3D, StaticBody3D, RigidBody3D, KinematicBody3D, etc. to give them a shape.")); } if (!shape.is_valid()) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("A shape must be provided for CollisionShape3D to function. Please create a shape resource for it."); + warnings.push_back(TTR("A shape must be provided for CollisionShape3D to function. Please create a shape resource for it.")); } if (shape.is_valid() && Object::cast_to<RigidBody3D>(get_parent()) && Object::cast_to<ConcavePolygonShape3D>(*shape) && Object::cast_to<RigidBody3D>(get_parent())->get_mode() != RigidBody3D::MODE_STATIC) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("ConcavePolygonShape3D doesn't support RigidBody3D in another mode than static."); + warnings.push_back(TTR("ConcavePolygonShape3D doesn't support RigidBody3D in another mode than static.")); } - return warning; + return warnings; } void CollisionShape3D::_bind_methods() { @@ -170,12 +161,10 @@ void CollisionShape3D::set_shape(const Ref<Shape3D> &p_shape) { } if (!shape.is_null()) { shape->unregister_owner(this); - shape->disconnect("changed", callable_mp(this, &CollisionShape3D::_shape_changed)); } shape = p_shape; if (!shape.is_null()) { shape->register_owner(this); - shape->connect("changed", callable_mp(this, &CollisionShape3D::_shape_changed)); } update_gizmo(); if (parent) { @@ -185,10 +174,11 @@ void CollisionShape3D::set_shape(const Ref<Shape3D> &p_shape) { } } - if (is_inside_tree()) { - _shape_changed(); + if (is_inside_tree() && parent) { + // If this is a heightfield shape our center may have changed + _update_in_shape_owner(true); } - update_configuration_warning(); + update_configuration_warnings(); } Ref<Shape3D> CollisionShape3D::get_shape() const { @@ -218,10 +208,3 @@ CollisionShape3D::~CollisionShape3D() { } //RenderingServer::get_singleton()->free(indicator); } - -void CollisionShape3D::_shape_changed() { - // If this is a heightfield shape our center may have changed - if (parent) { - _update_in_shape_owner(true); - } -} diff --git a/scene/3d/collision_shape_3d.h b/scene/3d/collision_shape_3d.h index 5512417f75..f69c1e38eb 100644 --- a/scene/3d/collision_shape_3d.h +++ b/scene/3d/collision_shape_3d.h @@ -47,8 +47,6 @@ class CollisionShape3D : public Node3D { bool disabled = false; protected: - void _shape_changed(); - void _update_in_shape_owner(bool p_xform_only = false); protected: @@ -64,7 +62,7 @@ public: void set_disabled(bool p_disabled); bool is_disabled() const; - String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; CollisionShape3D(); ~CollisionShape3D(); diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp index d22d7ff3ab..aa29728c73 100644 --- a/scene/3d/cpu_particles_3d.cpp +++ b/scene/3d/cpu_particles_3d.cpp @@ -189,8 +189,8 @@ bool CPUParticles3D::get_fractional_delta() const { return fractional_delta; } -String CPUParticles3D::get_configuration_warning() const { - String warnings = GeometryInstance3D::get_configuration_warning(); +TypedArray<String> CPUParticles3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); bool mesh_found = false; bool anim_material_found = false; @@ -209,18 +209,12 @@ String CPUParticles3D::get_configuration_warning() const { anim_material_found = anim_material_found || (spat && spat->get_billboard_mode() == StandardMaterial3D::BILLBOARD_PARTICLES); if (!mesh_found) { - if (warnings != String()) { - warnings += "\n"; - } - warnings += "- " + TTR("Nothing is visible because no mesh has been assigned."); + warnings.push_back(TTR("Nothing is visible because no mesh has been assigned.")); } if (!anim_material_found && (get_param(PARAM_ANIM_SPEED) != 0.0 || get_param(PARAM_ANIM_OFFSET) != 0.0 || get_param_curve(PARAM_ANIM_SPEED).is_valid() || get_param_curve(PARAM_ANIM_OFFSET).is_valid())) { - if (warnings != String()) { - warnings += "\n"; - } - warnings += "- " + TTR("CPUParticles3D animation requires the usage of a StandardMaterial3D whose Billboard Mode is set to \"Particle Billboard\"."); + warnings.push_back(TTR("CPUParticles3D animation requires the usage of a StandardMaterial3D whose Billboard Mode is set to \"Particle Billboard\".")); } return warnings; @@ -1056,7 +1050,7 @@ void CPUParticles3D::_update_particle_data_buffer() { ptr[10] = t.basis.elements[2][2]; ptr[11] = t.origin.z; } else { - zeromem(ptr, sizeof(float) * 12); + memset(ptr, 0, sizeof(float) * 12); } Color c = r[idx].color; @@ -1161,7 +1155,7 @@ void CPUParticles3D::_notification(int p_what) { ptr[10] = t.basis.elements[2][2]; ptr[11] = t.origin.z; } else { - zeromem(ptr, sizeof(float) * 12); + memset(ptr, 0, sizeof(float) * 12); } ptr += 20; diff --git a/scene/3d/cpu_particles_3d.h b/scene/3d/cpu_particles_3d.h index 10ac32622d..c073c93c47 100644 --- a/scene/3d/cpu_particles_3d.h +++ b/scene/3d/cpu_particles_3d.h @@ -280,7 +280,7 @@ public: void set_gravity(const Vector3 &p_gravity); Vector3 get_gravity() const; - virtual String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; void restart(); diff --git a/scene/3d/decal.cpp b/scene/3d/decal.cpp index b5eab35605..7d6abe458a 100644 --- a/scene/3d/decal.cpp +++ b/scene/3d/decal.cpp @@ -154,13 +154,11 @@ Vector<Face3> Decal::get_faces(uint32_t p_usage_flags) const { return Vector<Face3>(); } -#ifdef TOOLS_ENABLED void Decal::_validate_property(PropertyInfo &property) const { if (!distance_fade_enabled && (property.name == "distance_fade_begin" || property.name == "distance_fade_length")) { property.usage = PROPERTY_USAGE_NOEDITOR; } } -#endif void Decal::_bind_methods() { ClassDB::bind_method(D_METHOD("set_extents", "extents"), &Decal::set_extents); diff --git a/scene/3d/decal.h b/scene/3d/decal.h index 20d86ee16c..ce19e76de1 100644 --- a/scene/3d/decal.h +++ b/scene/3d/decal.h @@ -62,12 +62,9 @@ private: float distance_fade_begin = 10.0; float distance_fade_length = 1.0; -#ifdef TOOLS_ENABLED - void _validate_property(PropertyInfo &property) const override; -#endif - protected: static void _bind_methods(); + void _validate_property(PropertyInfo &property) const override; public: void set_extents(const Vector3 &p_extents); diff --git a/scene/3d/gi_probe.cpp b/scene/3d/gi_probe.cpp index 43f820e5d4..6505fb1ee8 100644 --- a/scene/3d/gi_probe.cpp +++ b/scene/3d/gi_probe.cpp @@ -343,7 +343,7 @@ void GIProbe::_find_meshes(Node *p_at_node, List<PlotMesh> &plot_meshes) { pm.local_xform = xf; pm.mesh = mesh; for (int i = 0; i < mesh->get_surface_count(); i++) { - pm.instance_materials.push_back(mi->get_surface_material(i)); + pm.instance_materials.push_back(mi->get_surface_override_material(i)); } pm.override_material = mi->get_material_override(); plot_meshes.push_back(pm); @@ -454,7 +454,7 @@ void GIProbe::bake(Node *p_from_node, bool p_create_visual_debug) { mmi->set_multimesh(baker.create_debug_multimesh()); add_child(mmi); #ifdef TOOLS_ENABLED - if (get_tree()->get_edited_scene_root() == this) { + if (is_inside_tree() && get_tree()->get_edited_scene_root() == this) { mmi->set_owner(this); } else { mmi->set_owner(get_owner()); @@ -503,19 +503,15 @@ Vector<Face3> GIProbe::get_faces(uint32_t p_usage_flags) const { return Vector<Face3>(); } -String GIProbe::get_configuration_warning() const { - String warning = VisualInstance3D::get_configuration_warning(); +TypedArray<String> GIProbe::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); if (RenderingServer::get_singleton()->is_low_end()) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("GIProbes are not supported by the GLES2 video driver.\nUse a BakedLightmap instead."); + warnings.push_back(TTR("GIProbes are not supported by the GLES2 video driver.\nUse a BakedLightmap instead.")); } else if (probe_data.is_null()) { - warning += TTR("No GIProbe data set, so this node is disabled. Bake static objects to enable GI."); + warnings.push_back(TTR("No GIProbe data set, so this node is disabled. Bake static objects to enable GI.")); } - - return warning; + return warnings; } void GIProbe::_bind_methods() { diff --git a/scene/3d/gi_probe.h b/scene/3d/gi_probe.h index 534b425557..dac7dd3e17 100644 --- a/scene/3d/gi_probe.h +++ b/scene/3d/gi_probe.h @@ -165,7 +165,7 @@ public: virtual AABB get_aabb() const override; virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override; - virtual String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; GIProbe(); ~GIProbe(); diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp index 17a61b3e4d..50044ddc67 100644 --- a/scene/3d/gpu_particles_3d.cpp +++ b/scene/3d/gpu_particles_3d.cpp @@ -115,7 +115,7 @@ void GPUParticles3D::set_process_material(const Ref<Material> &p_material) { } RS::get_singleton()->particles_set_process_material(particles, material_rid); - update_configuration_warning(); + update_configuration_warnings(); } void GPUParticles3D::set_speed_scale(float p_scale) { @@ -181,12 +181,33 @@ void GPUParticles3D::set_draw_order(DrawOrder p_order) { RS::get_singleton()->particles_set_draw_order(particles, RS::ParticlesDrawOrder(p_order)); } +void GPUParticles3D::set_enable_trail(bool p_enabled) { + trail_enabled = p_enabled; + RS::get_singleton()->particles_set_trails(particles, trail_enabled, trail_length); + update_configuration_warnings(); +} +void GPUParticles3D::set_trail_length(float p_seconds) { + ERR_FAIL_COND(p_seconds < 0.001); + trail_length = p_seconds; + RS::get_singleton()->particles_set_trails(particles, trail_enabled, trail_length); +} + +bool GPUParticles3D::is_trail_enabled() const { + return trail_enabled; +} +float GPUParticles3D::get_trail_length() const { + return trail_length; +} + GPUParticles3D::DrawOrder GPUParticles3D::get_draw_order() const { return draw_order; } void GPUParticles3D::set_draw_passes(int p_count) { ERR_FAIL_COND(p_count < 1); + for (int i = p_count; i < draw_passes.size(); i++) { + set_draw_pass_mesh(i, Ref<Mesh>()); + } draw_passes.resize(p_count); RS::get_singleton()->particles_set_draw_passes(particles, p_count); notify_property_list_changed(); @@ -199,8 +220,16 @@ int GPUParticles3D::get_draw_passes() const { void GPUParticles3D::set_draw_pass_mesh(int p_pass, const Ref<Mesh> &p_mesh) { ERR_FAIL_INDEX(p_pass, draw_passes.size()); + if (Engine::get_singleton()->is_editor_hint() && draw_passes.write[p_pass].is_valid()) { + draw_passes.write[p_pass]->disconnect("changed", callable_mp((Node *)this, &Node::update_configuration_warnings)); + } + draw_passes.write[p_pass] = p_mesh; + if (Engine::get_singleton()->is_editor_hint() && draw_passes.write[p_pass].is_valid()) { + draw_passes.write[p_pass]->connect("changed", callable_mp((Node *)this, &Node::update_configuration_warnings), varray(), CONNECT_DEFERRED); + } + RID mesh_rid; if (p_mesh.is_valid()) { mesh_rid = p_mesh->get_rid(); @@ -208,7 +237,8 @@ void GPUParticles3D::set_draw_pass_mesh(int p_pass, const Ref<Mesh> &p_mesh) { RS::get_singleton()->particles_set_draw_pass_mesh(particles, p_pass, mesh_rid); - update_configuration_warning(); + _skinning_changed(); + update_configuration_warnings(); } Ref<Mesh> GPUParticles3D::get_draw_pass_mesh(int p_pass) const { @@ -235,13 +265,22 @@ bool GPUParticles3D::get_fractional_delta() const { return fractional_delta; } -String GPUParticles3D::get_configuration_warning() const { +void GPUParticles3D::set_interpolate(bool p_enable) { + interpolate = p_enable; + RS::get_singleton()->particles_set_interpolate(particles, p_enable); +} + +bool GPUParticles3D::get_interpolate() const { + return interpolate; +} + +TypedArray<String> GPUParticles3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); + if (RenderingServer::get_singleton()->is_low_end()) { - return TTR("GPU-based particles are not supported by the GLES2 video driver.\nUse the CPUParticles3D node instead. You can use the \"Convert to CPUParticles3D\" option for this purpose."); + warnings.push_back(TTR("GPU-based particles are not supported by the GLES2 video driver.\nUse the CPUParticles3D node instead. You can use the \"Convert to CPUParticles3D\" option for this purpose.")); } - String warnings = GeometryInstance3D::get_configuration_warning(); - bool meshes_found = false; bool anim_material_found = false; @@ -250,7 +289,7 @@ String GPUParticles3D::get_configuration_warning() const { meshes_found = true; for (int j = 0; j < draw_passes[i]->get_surface_count(); j++) { anim_material_found = Object::cast_to<ShaderMaterial>(draw_passes[i]->surface_get_material(j).ptr()) != nullptr; - StandardMaterial3D *spat = Object::cast_to<StandardMaterial3D>(draw_passes[i]->surface_get_material(j).ptr()); + BaseMaterial3D *spat = Object::cast_to<BaseMaterial3D>(draw_passes[i]->surface_get_material(j).ptr()); anim_material_found = anim_material_found || (spat && spat->get_billboard_mode() == StandardMaterial3D::BILLBOARD_PARTICLES); } if (anim_material_found) { @@ -260,30 +299,73 @@ String GPUParticles3D::get_configuration_warning() const { } anim_material_found = anim_material_found || Object::cast_to<ShaderMaterial>(get_material_override().ptr()) != nullptr; - StandardMaterial3D *spat = Object::cast_to<StandardMaterial3D>(get_material_override().ptr()); - anim_material_found = anim_material_found || (spat && spat->get_billboard_mode() == StandardMaterial3D::BILLBOARD_PARTICLES); + { + BaseMaterial3D *spat = Object::cast_to<BaseMaterial3D>(get_material_override().ptr()); + anim_material_found = anim_material_found || (spat && spat->get_billboard_mode() == BaseMaterial3D::BILLBOARD_PARTICLES); + } if (!meshes_found) { - if (warnings != String()) { - warnings += "\n"; - } - warnings += "- " + TTR("Nothing is visible because meshes have not been assigned to draw passes."); + warnings.push_back(TTR("Nothing is visible because meshes have not been assigned to draw passes.")); } if (process_material.is_null()) { - if (warnings != String()) { - warnings += "\n"; - } - warnings += "- " + TTR("A material to process the particles is not assigned, so no behavior is imprinted."); + warnings.push_back(TTR("A material to process the particles is not assigned, so no behavior is imprinted.")); } else { const ParticlesMaterial *process = Object::cast_to<ParticlesMaterial>(process_material.ptr()); if (!anim_material_found && process && (process->get_param(ParticlesMaterial::PARAM_ANIM_SPEED) != 0.0 || process->get_param(ParticlesMaterial::PARAM_ANIM_OFFSET) != 0.0 || process->get_param_texture(ParticlesMaterial::PARAM_ANIM_SPEED).is_valid() || process->get_param_texture(ParticlesMaterial::PARAM_ANIM_OFFSET).is_valid())) { - if (warnings != String()) { - warnings += "\n"; + warnings.push_back(TTR("Particles animation requires the usage of a BaseMaterial3D whose Billboard Mode is set to \"Particle Billboard\".")); + } + } + + if (trail_enabled) { + int dp_count = 0; + bool missing_trails = false; + bool no_materials = false; + + for (int i = 0; i < draw_passes.size(); i++) { + Ref<Mesh> draw_pass = draw_passes[i]; + if (draw_pass.is_valid() && draw_pass->get_builtin_bind_pose_count() > 0) { + dp_count++; + } + + if (draw_pass.is_valid()) { + int mats_found = 0; + for (int j = 0; j < draw_passes[i]->get_surface_count(); j++) { + BaseMaterial3D *spat = Object::cast_to<BaseMaterial3D>(draw_passes[i]->surface_get_material(j).ptr()); + if (spat) { + mats_found++; + } + if (spat && !spat->get_flag(BaseMaterial3D::FLAG_PARTICLE_TRAILS_MODE)) { + missing_trails = true; + } + } + + if (mats_found != draw_passes[i]->get_surface_count()) { + no_materials = true; + } } - warnings += "- " + TTR("Particles animation requires the usage of a StandardMaterial3D whose Billboard Mode is set to \"Particle Billboard\"."); + } + + BaseMaterial3D *spat = Object::cast_to<BaseMaterial3D>(get_material_override().ptr()); + if (spat) { + no_materials = false; + } + if (spat && !spat->get_flag(BaseMaterial3D::FLAG_PARTICLE_TRAILS_MODE)) { + missing_trails = true; + } + + if (dp_count && skin.is_valid()) { + warnings.push_back(TTR("Using Trail meshes with a skin causes Skin to override Trail poses. Suggest removing the Skin.")); + } else if (dp_count == 0 && skin.is_null()) { + warnings.push_back(TTR("Trails active, but neither Trail meshes or a Skin were found.")); + } else if (dp_count > 1) { + warnings.push_back(TTR("Only one Trail mesh is supported. If you want to use more than a single mesh, a Skin is needed (see documentation).")); + } + + if ((dp_count || !skin.is_null()) && (missing_trails || no_materials)) { + warnings.push_back(TTR("Trails enabled, but one or more mesh materials are either missing or not set for trails rendering.")); } } @@ -348,7 +430,7 @@ void GPUParticles3D::_notification(int p_what) { } } - // Use internal process when emitting and one_shot are on so that when + // Use internal process when emitting and one_shot is on so that when // the shot ends the editor can properly update if (p_what == NOTIFICATION_INTERNAL_PROCESS) { if (one_shot && !is_emitting()) { @@ -375,6 +457,47 @@ void GPUParticles3D::_notification(int p_what) { } } +void GPUParticles3D::_skinning_changed() { + Vector<Transform> xforms; + if (skin.is_valid()) { + xforms.resize(skin->get_bind_count()); + for (int i = 0; i < skin->get_bind_count(); i++) { + xforms.write[i] = skin->get_bind_pose(i); + } + } else { + for (int i = 0; i < draw_passes.size(); i++) { + Ref<Mesh> draw_pass = draw_passes[i]; + if (draw_pass.is_valid() && draw_pass->get_builtin_bind_pose_count() > 0) { + xforms.resize(draw_pass->get_builtin_bind_pose_count()); + for (int j = 0; j < draw_pass->get_builtin_bind_pose_count(); j++) { + xforms.write[i] = draw_pass->get_builtin_bind_pose(j); + } + break; + } + } + } + + RS::get_singleton()->particles_set_trail_bind_poses(particles, xforms); + update_configuration_warnings(); +} + +void GPUParticles3D::set_skin(const Ref<Skin> &p_skin) { + skin = p_skin; + _skinning_changed(); +} +Ref<Skin> GPUParticles3D::get_skin() const { + return skin; +} + +void GPUParticles3D::set_transform_align(TransformAlign p_align) { + ERR_FAIL_INDEX(uint32_t(p_align), 4); + transform_align = p_align; + RS::get_singleton()->particles_set_transform_align(particles, RS::ParticlesTransformAlign(transform_align)); +} +GPUParticles3D::TransformAlign GPUParticles3D::get_transform_align() const { + return transform_align; +} + void GPUParticles3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_emitting", "emitting"), &GPUParticles3D::set_emitting); ClassDB::bind_method(D_METHOD("set_amount", "amount"), &GPUParticles3D::set_amount); @@ -387,6 +510,7 @@ void GPUParticles3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_use_local_coordinates", "enable"), &GPUParticles3D::set_use_local_coordinates); ClassDB::bind_method(D_METHOD("set_fixed_fps", "fps"), &GPUParticles3D::set_fixed_fps); ClassDB::bind_method(D_METHOD("set_fractional_delta", "enable"), &GPUParticles3D::set_fractional_delta); + ClassDB::bind_method(D_METHOD("set_interpolate", "enable"), &GPUParticles3D::set_interpolate); ClassDB::bind_method(D_METHOD("set_process_material", "material"), &GPUParticles3D::set_process_material); ClassDB::bind_method(D_METHOD("set_speed_scale", "scale"), &GPUParticles3D::set_speed_scale); ClassDB::bind_method(D_METHOD("set_collision_base_size", "size"), &GPUParticles3D::set_collision_base_size); @@ -402,6 +526,7 @@ void GPUParticles3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_use_local_coordinates"), &GPUParticles3D::get_use_local_coordinates); ClassDB::bind_method(D_METHOD("get_fixed_fps"), &GPUParticles3D::get_fixed_fps); ClassDB::bind_method(D_METHOD("get_fractional_delta"), &GPUParticles3D::get_fractional_delta); + ClassDB::bind_method(D_METHOD("get_interpolate"), &GPUParticles3D::get_interpolate); ClassDB::bind_method(D_METHOD("get_process_material"), &GPUParticles3D::get_process_material); ClassDB::bind_method(D_METHOD("get_speed_scale"), &GPUParticles3D::get_speed_scale); ClassDB::bind_method(D_METHOD("get_collision_base_size"), &GPUParticles3D::get_collision_base_size); @@ -416,6 +541,9 @@ void GPUParticles3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_draw_passes"), &GPUParticles3D::get_draw_passes); ClassDB::bind_method(D_METHOD("get_draw_pass_mesh", "pass"), &GPUParticles3D::get_draw_pass_mesh); + ClassDB::bind_method(D_METHOD("set_skin", "skin"), &GPUParticles3D::set_skin); + ClassDB::bind_method(D_METHOD("get_skin"), &GPUParticles3D::get_skin); + ClassDB::bind_method(D_METHOD("restart"), &GPUParticles3D::restart); ClassDB::bind_method(D_METHOD("capture_aabb"), &GPUParticles3D::capture_aabb); @@ -424,6 +552,15 @@ void GPUParticles3D::_bind_methods() { ClassDB::bind_method(D_METHOD("emit_particle", "xform", "velocity", "color", "custom", "flags"), &GPUParticles3D::emit_particle); + ClassDB::bind_method(D_METHOD("set_enable_trail", "enabled"), &GPUParticles3D::set_enable_trail); + ClassDB::bind_method(D_METHOD("set_trail_length", "secs"), &GPUParticles3D::set_trail_length); + + ClassDB::bind_method(D_METHOD("is_trail_enabled"), &GPUParticles3D::is_trail_enabled); + ClassDB::bind_method(D_METHOD("get_trail_length"), &GPUParticles3D::get_trail_length); + + ClassDB::bind_method(D_METHOD("set_transform_align", "align"), &GPUParticles3D::set_transform_align); + ClassDB::bind_method(D_METHOD("get_transform_align"), &GPUParticles3D::get_transform_align); + 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_PROPERTY(PropertyInfo(Variant::NODE_PATH, "sub_emitter", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "GPUParticles3D"), "set_sub_emitter", "get_sub_emitter"); @@ -435,6 +572,7 @@ void GPUParticles3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness_ratio", "get_explosiveness_ratio"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "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, "interpolate"), "set_interpolate", "get_interpolate"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fract_delta"), "set_fractional_delta", "get_fractional_delta"); ADD_GROUP("Collision", "collision_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_base_size", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater"), "set_collision_base_size", "get_collision_base_size"); @@ -442,6 +580,10 @@ void GPUParticles3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::AABB, "visibility_aabb"), "set_visibility_aabb", "get_visibility_aabb"); 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,View Depth"), "set_draw_order", "get_draw_order"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "transform_align", PROPERTY_HINT_ENUM, "Disabled,ZBillboard,YToVelocity,ZBillboardYToVelocity"), "set_transform_align", "get_transform_align"); + ADD_GROUP("Trails", "trail_"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "trail_enabled"), "set_enable_trail", "is_trail_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "trail_length_secs", PROPERTY_HINT_RANGE, "0.01,4,0.01"), "set_trail_length", "get_trail_length"); ADD_GROUP("Process Material", ""); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "process_material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,ParticlesMaterial"), "set_process_material", "get_process_material"); ADD_GROUP("Draw Passes", "draw_"); @@ -449,6 +591,7 @@ void GPUParticles3D::_bind_methods() { for (int i = 0; i < MAX_DRAW_PASSES; i++) { ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "draw_pass_" + itos(i + 1), PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_draw_pass_mesh", "get_draw_pass_mesh", i); } + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "draw_skin", PROPERTY_HINT_RESOURCE_TYPE, "Skin"), "set_skin", "get_skin"); BIND_ENUM_CONSTANT(DRAW_ORDER_INDEX); BIND_ENUM_CONSTANT(DRAW_ORDER_LIFETIME); @@ -461,27 +604,36 @@ void GPUParticles3D::_bind_methods() { BIND_ENUM_CONSTANT(EMIT_FLAG_CUSTOM); BIND_CONSTANT(MAX_DRAW_PASSES); + + BIND_ENUM_CONSTANT(TRANSFORM_ALIGN_DISABLED); + BIND_ENUM_CONSTANT(TRANSFORM_ALIGN_Z_BILLBOARD); + BIND_ENUM_CONSTANT(TRANSFORM_ALIGN_Y_TO_VELOCITY); + BIND_ENUM_CONSTANT(TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY); } GPUParticles3D::GPUParticles3D() { particles = RS::get_singleton()->particles_create(); + RS::get_singleton()->particles_set_mode(particles, RS::PARTICLES_MODE_3D); set_base(particles); one_shot = false; // Needed so that set_emitting doesn't access uninitialized values set_emitting(true); set_one_shot(false); set_amount(8); set_lifetime(1); - set_fixed_fps(0); + set_fixed_fps(30); set_fractional_delta(true); + set_interpolate(true); set_pre_process_time(0); set_explosiveness_ratio(0); set_randomness_ratio(0); + set_trail_length(0.3); set_visibility_aabb(AABB(Vector3(-4, -4, -4), Vector3(8, 8, 8))); set_use_local_coordinates(true); set_draw_passes(1); set_draw_order(DRAW_ORDER_INDEX); set_speed_scale(1); set_collision_base_size(0.01); + set_transform_align(TRANSFORM_ALIGN_DISABLED); } GPUParticles3D::~GPUParticles3D() { diff --git a/scene/3d/gpu_particles_3d.h b/scene/3d/gpu_particles_3d.h index 0c1a1a510c..1f9cea79b6 100644 --- a/scene/3d/gpu_particles_3d.h +++ b/scene/3d/gpu_particles_3d.h @@ -34,6 +34,7 @@ #include "core/templates/rid.h" #include "scene/3d/visual_instance_3d.h" #include "scene/resources/material.h" +#include "scene/resources/skin.h" class GPUParticles3D : public GeometryInstance3D { private: @@ -46,6 +47,13 @@ public: DRAW_ORDER_VIEW_DEPTH, }; + enum TransformAlign { + TRANSFORM_ALIGN_DISABLED, + TRANSFORM_ALIGN_Z_BILLBOARD, + TRANSFORM_ALIGN_Y_TO_VELOCITY, + TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY + }; + enum { MAX_DRAW_PASSES = 4 }; @@ -64,17 +72,26 @@ private: bool local_coords; int fixed_fps; bool fractional_delta; + bool interpolate = true; NodePath sub_emitter; float collision_base_size; + bool trail_enabled = false; + float trail_length = 0.3; + + TransformAlign transform_align = TRANSFORM_ALIGN_DISABLED; + Ref<Material> process_material; DrawOrder draw_order; Vector<Ref<Mesh>> draw_passes; + Ref<Skin> skin; void _attach_sub_emitter(); + void _skinning_changed(); + protected: static void _bind_methods(); void _notification(int p_what); @@ -96,6 +113,8 @@ public: void set_process_material(const Ref<Material> &p_material); void set_speed_scale(float p_scale); void set_collision_base_size(float p_ratio); + void set_enable_trail(bool p_enabled); + void set_trail_length(float p_seconds); bool is_emitting() const; int get_amount() const; @@ -109,6 +128,8 @@ public: Ref<Material> get_process_material() const; float get_speed_scale() const; float get_collision_base_size() const; + bool is_trail_enabled() const; + float get_trail_length() const; void set_fixed_fps(int p_count); int get_fixed_fps() const; @@ -116,6 +137,9 @@ public: void set_fractional_delta(bool p_enable); bool get_fractional_delta() const; + void set_interpolate(bool p_enable); + bool get_interpolate() const; + void set_draw_order(DrawOrder p_order); DrawOrder get_draw_order() const; @@ -125,11 +149,17 @@ public: void set_draw_pass_mesh(int p_pass, const Ref<Mesh> &p_mesh); Ref<Mesh> get_draw_pass_mesh(int p_pass) const; - virtual String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; void set_sub_emitter(const NodePath &p_path); NodePath get_sub_emitter() const; + void set_skin(const Ref<Skin> &p_skin); + Ref<Skin> get_skin() const; + + void set_transform_align(TransformAlign p_align); + TransformAlign get_transform_align() const; + void restart(); enum EmitFlags { @@ -148,6 +178,7 @@ public: }; VARIANT_ENUM_CAST(GPUParticles3D::DrawOrder) +VARIANT_ENUM_CAST(GPUParticles3D::TransformAlign) VARIANT_ENUM_CAST(GPUParticles3D::EmitFlags) #endif // PARTICLES_H diff --git a/scene/3d/gpu_particles_collision_3d.cpp b/scene/3d/gpu_particles_collision_3d.cpp index 97241be60f..628b823f89 100644 --- a/scene/3d/gpu_particles_collision_3d.cpp +++ b/scene/3d/gpu_particles_collision_3d.cpp @@ -346,7 +346,7 @@ void GPUParticlesCollisionSDF::_compute_sdf(ComputeSDFParams *params) { ThreadWorkPool work_pool; work_pool.init(); work_pool.begin_work(params->size.z, this, &GPUParticlesCollisionSDF::_compute_sdf_z, params); - while (work_pool.get_work_index() < (uint32_t)params->size.z) { + while (!work_pool.is_done_dispatching()) { OS::get_singleton()->delay_usec(10000); bake_step_function(work_pool.get_work_index() * 100 / params->size.z, "Baking SDF"); } diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp index 87f54022b3..d45749d36b 100644 --- a/scene/3d/light_3d.cpp +++ b/scene/3d/light_3d.cpp @@ -48,7 +48,7 @@ void Light3D::set_param(Param p_param, float p_value) { update_gizmo(); if (p_param == PARAM_SPOT_ANGLE) { - update_configuration_warning(); + update_configuration_warnings(); } } } @@ -63,7 +63,7 @@ void Light3D::set_shadow(bool p_enable) { RS::get_singleton()->light_set_shadow(light, p_enable); if (type == RenderingServer::LIGHT_SPOT || type == RenderingServer::LIGHT_OMNI) { - update_configuration_warning(); + update_configuration_warnings(); } notify_property_list_changed(); @@ -153,7 +153,7 @@ void Light3D::set_projector(const Ref<Texture2D> &p_texture) { projector = p_texture; RID tex_id = projector.is_valid() ? projector->get_rid() : RID(); RS::get_singleton()->light_set_projector(light, tex_id); - update_configuration_warning(); + update_configuration_warnings(); } Ref<Texture2D> Light3D::get_projector() const { @@ -204,7 +204,7 @@ bool Light3D::is_editor_only() const { } void Light3D::_validate_property(PropertyInfo &property) const { - if (!shadow && (property.name == "shadow_color" || property.name == "shadow_color" || property.name == "shadow_bias" || property.name == "shadow_normal_bias" || property.name == "shadow_reverse_cull_face" || property.name == "shadow_transmittance_bias" || property.name == "shadow_blur")) { + if (!shadow && (property.name == "shadow_color" || property.name == "shadow_bias" || property.name == "shadow_normal_bias" || property.name == "shadow_reverse_cull_face" || property.name == "shadow_transmittance_bias" || property.name == "shadow_fog_fade" || property.name == "shadow_blur")) { property.usage = PROPERTY_USAGE_NOEDITOR; } @@ -457,17 +457,14 @@ OmniLight3D::ShadowMode OmniLight3D::get_shadow_mode() const { return shadow_mode; } -String OmniLight3D::get_configuration_warning() const { - String warning = Light3D::get_configuration_warning(); +TypedArray<String> OmniLight3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); if (!has_shadow() && get_projector().is_valid()) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("Projector texture only works with shadows active."); + warnings.push_back(TTR("Projector texture only works with shadows active.")); } - return warning; + return warnings; } void OmniLight3D::_bind_methods() { @@ -491,24 +488,18 @@ OmniLight3D::OmniLight3D() : set_param(PARAM_SHADOW_NORMAL_BIAS, 2.0); } -String SpotLight3D::get_configuration_warning() const { - String warning = Light3D::get_configuration_warning(); +TypedArray<String> SpotLight3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); if (has_shadow() && get_param(PARAM_SPOT_ANGLE) >= 90.0) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("A SpotLight3D with an angle wider than 90 degrees cannot cast shadows."); + warnings.push_back(TTR("A SpotLight3D with an angle wider than 90 degrees cannot cast shadows.")); } if (!has_shadow() && get_projector().is_valid()) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("Projector texture only works with shadows active."); + warnings.push_back(TTR("Projector texture only works with shadows active.")); } - return warning; + return warnings; } void SpotLight3D::_bind_methods() { diff --git a/scene/3d/light_3d.h b/scene/3d/light_3d.h index 311db54bce..e145b08b74 100644 --- a/scene/3d/light_3d.h +++ b/scene/3d/light_3d.h @@ -202,7 +202,7 @@ public: void set_shadow_mode(ShadowMode p_mode); ShadowMode get_shadow_mode() const; - virtual String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; OmniLight3D(); }; @@ -216,7 +216,7 @@ protected: static void _bind_methods(); public: - virtual String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; SpotLight3D() : Light3D(RenderingServer::LIGHT_SPOT) {} diff --git a/scene/3d/lightmapper.cpp b/scene/3d/lightmapper.cpp index c17ac52aa2..9e5078ba95 100644 --- a/scene/3d/lightmapper.cpp +++ b/scene/3d/lightmapper.cpp @@ -39,6 +39,15 @@ Ref<LightmapDenoiser> LightmapDenoiser::create() { return Ref<LightmapDenoiser>(); } +LightmapRaycaster *(*LightmapRaycaster::create_function)() = nullptr; + +Ref<LightmapRaycaster> LightmapRaycaster::create() { + if (create_function) { + return Ref<LightmapRaycaster>(create_function()); + } + return Ref<LightmapRaycaster>(); +} + Lightmapper::CreateFunc Lightmapper::create_custom = nullptr; Lightmapper::CreateFunc Lightmapper::create_gpu = nullptr; Lightmapper::CreateFunc Lightmapper::create_cpu = nullptr; diff --git a/scene/3d/lightmapper.h b/scene/3d/lightmapper.h index a07a964c01..f63515f666 100644 --- a/scene/3d/lightmapper.h +++ b/scene/3d/lightmapper.h @@ -34,6 +34,16 @@ #include "scene/resources/mesh.h" #include "servers/rendering/rendering_device.h" +#if !defined(__aligned) + +#if defined(_WIN32) && defined(_MSC_VER) +#define __aligned(...) __declspec(align(__VA_ARGS__)) +#else +#define __aligned(...) __attribute__((aligned(__VA_ARGS__))) +#endif + +#endif + class LightmapDenoiser : public Reference { GDCLASS(LightmapDenoiser, Reference) protected: @@ -44,6 +54,73 @@ public: static Ref<LightmapDenoiser> create(); }; +class LightmapRaycaster : public Reference { + GDCLASS(LightmapRaycaster, Reference) +protected: + static LightmapRaycaster *(*create_function)(); + +public: + // compatible with embree3 rays + struct __aligned(16) Ray { + const static unsigned int INVALID_GEOMETRY_ID = ((unsigned int)-1); // from rtcore_common.h + + /*! Default construction does nothing. */ + _FORCE_INLINE_ Ray() : + geomID(INVALID_GEOMETRY_ID) {} + + /*! Constructs a ray from origin, direction, and ray segment. Near + * has to be smaller than far. */ + _FORCE_INLINE_ Ray(const Vector3 &org, + const Vector3 &dir, + float tnear = 0.0f, + float tfar = INFINITY) : + org(org), + tnear(tnear), + dir(dir), + time(0.0f), + tfar(tfar), + mask(-1), + u(0.0), + v(0.0), + primID(INVALID_GEOMETRY_ID), + geomID(INVALID_GEOMETRY_ID), + instID(INVALID_GEOMETRY_ID) {} + + /*! Tests if we hit something. */ + _FORCE_INLINE_ explicit operator bool() const { return geomID != INVALID_GEOMETRY_ID; } + + public: + Vector3 org; //!< Ray origin + tnear + float tnear; //!< Start of ray segment + Vector3 dir; //!< Ray direction + tfar + float time; //!< Time of this ray for motion blur. + float tfar; //!< End of ray segment + unsigned int mask; //!< used to mask out objects during traversal + unsigned int id; //!< ray ID + unsigned int flags; //!< ray flags + + Vector3 normal; //!< Not normalized geometry normal + float u; //!< Barycentric u coordinate of hit + float v; //!< Barycentric v coordinate of hit + unsigned int primID; //!< primitive ID + unsigned int geomID; //!< geometry ID + unsigned int instID; //!< instance ID + }; + + virtual bool intersect(Ray &p_ray) = 0; + + virtual void intersect(Vector<Ray> &r_rays) = 0; + + virtual void add_mesh(const Vector<Vector3> &p_vertices, const Vector<Vector3> &p_normals, const Vector<Vector2> &p_uv2s, unsigned int p_id) = 0; + virtual void set_mesh_alpha_texture(Ref<Image> p_alpha_texture, unsigned int p_id) = 0; + virtual void commit() = 0; + + virtual void set_mesh_filter(const Set<int> &p_mesh_ids) = 0; + virtual void clear_mesh_filter() = 0; + + static Ref<LightmapRaycaster> create(); +}; + class Lightmapper : public Reference { GDCLASS(Lightmapper, Reference) public: diff --git a/scene/3d/mesh_instance_3d.cpp b/scene/3d/mesh_instance_3d.cpp index b997c64b29..c495f68890 100644 --- a/scene/3d/mesh_instance_3d.cpp +++ b/scene/3d/mesh_instance_3d.cpp @@ -51,13 +51,13 @@ bool MeshInstance3D::_set(const StringName &p_name, const Variant &p_value) { return true; } - if (p_name.operator String().begins_with("material/")) { + if (p_name.operator String().begins_with("surface_material_override/")) { int idx = p_name.operator String().get_slicec('/', 1).to_int(); - if (idx >= materials.size() || idx < 0) { + if (idx >= surface_override_materials.size() || idx < 0) { return false; } - set_surface_material(idx, p_value); + set_surface_override_material(idx, p_value); return true; } @@ -75,12 +75,12 @@ bool MeshInstance3D::_get(const StringName &p_name, Variant &r_ret) const { return true; } - if (p_name.operator String().begins_with("material/")) { + if (p_name.operator String().begins_with("surface_material_override/")) { int idx = p_name.operator String().get_slicec('/', 1).to_int(); - if (idx >= materials.size() || idx < 0) { + if (idx >= surface_override_materials.size() || idx < 0) { return false; } - r_ret = materials[idx]; + r_ret = surface_override_materials[idx]; return true; } return false; @@ -100,7 +100,7 @@ void MeshInstance3D::_get_property_list(List<PropertyInfo> *p_list) const { if (mesh.is_valid()) { for (int i = 0; i < mesh->get_surface_count(); i++) { - p_list->push_back(PropertyInfo(Variant::OBJECT, "material/" + itos(i), PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,StandardMaterial3D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE)); + p_list->push_back(PropertyInfo(Variant::OBJECT, "surface_material_override/" + itos(i), PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,StandardMaterial3D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE)); } } } @@ -126,7 +126,7 @@ void MeshInstance3D::set_mesh(const Ref<Mesh> &p_mesh) { } mesh->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &MeshInstance3D::_mesh_changed)); - materials.resize(mesh->get_surface_count()); + surface_override_materials.resize(mesh->get_surface_count()); set_base(mesh->get_rid()); } else { @@ -271,32 +271,67 @@ void MeshInstance3D::create_convex_collision() { } } +Node *MeshInstance3D::create_multiple_convex_collisions_node() { + if (mesh.is_null()) { + return nullptr; + } + + Vector<Ref<Shape3D>> shapes = mesh->convex_decompose(); + if (!shapes.size()) { + return nullptr; + } + + StaticBody3D *static_body = memnew(StaticBody3D); + for (int i = 0; i < shapes.size(); i++) { + CollisionShape3D *cshape = memnew(CollisionShape3D); + cshape->set_shape(shapes[i]); + static_body->add_child(cshape); + } + return static_body; +} + +void MeshInstance3D::create_multiple_convex_collisions() { + StaticBody3D *static_body = Object::cast_to<StaticBody3D>(create_multiple_convex_collisions_node()); + ERR_FAIL_COND(!static_body); + static_body->set_name(String(get_name()) + "_col"); + + add_child(static_body); + if (get_owner()) { + static_body->set_owner(get_owner()); + int count = static_body->get_child_count(); + for (int i = 0; i < count; i++) { + CollisionShape3D *cshape = Object::cast_to<CollisionShape3D>(static_body->get_child(i)); + cshape->set_owner(get_owner()); + } + } +} + void MeshInstance3D::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE) { _resolve_skeleton_path(); } } -int MeshInstance3D::get_surface_material_count() const { - return materials.size(); +int MeshInstance3D::get_surface_override_material_count() const { + return surface_override_materials.size(); } -void MeshInstance3D::set_surface_material(int p_surface, const Ref<Material> &p_material) { - ERR_FAIL_INDEX(p_surface, materials.size()); +void MeshInstance3D::set_surface_override_material(int p_surface, const Ref<Material> &p_material) { + ERR_FAIL_INDEX(p_surface, surface_override_materials.size()); - materials.write[p_surface] = p_material; + surface_override_materials.write[p_surface] = p_material; - if (materials[p_surface].is_valid()) { - RS::get_singleton()->instance_set_surface_material(get_instance(), p_surface, materials[p_surface]->get_rid()); + if (surface_override_materials[p_surface].is_valid()) { + RS::get_singleton()->instance_set_surface_override_material(get_instance(), p_surface, surface_override_materials[p_surface]->get_rid()); } else { - RS::get_singleton()->instance_set_surface_material(get_instance(), p_surface, RID()); + RS::get_singleton()->instance_set_surface_override_material(get_instance(), p_surface, RID()); } } -Ref<Material> MeshInstance3D::get_surface_material(int p_surface) const { - ERR_FAIL_INDEX_V(p_surface, materials.size(), Ref<Material>()); +Ref<Material> MeshInstance3D::get_surface_override_material(int p_surface) const { + ERR_FAIL_INDEX_V(p_surface, surface_override_materials.size(), Ref<Material>()); - return materials[p_surface]; + return surface_override_materials[p_surface]; } Ref<Material> MeshInstance3D::get_active_material(int p_surface) const { @@ -305,7 +340,7 @@ Ref<Material> MeshInstance3D::get_active_material(int p_surface) const { return material_override; } - Ref<Material> surface_material = get_surface_material(p_surface); + Ref<Material> surface_material = get_surface_override_material(p_surface); if (surface_material.is_valid()) { return surface_material; } @@ -320,7 +355,8 @@ Ref<Material> MeshInstance3D::get_active_material(int p_surface) const { void MeshInstance3D::_mesh_changed() { ERR_FAIL_COND(mesh.is_null()); - materials.resize(mesh->get_surface_count()); + surface_override_materials.resize(mesh->get_surface_count()); + update_gizmo(); } void MeshInstance3D::create_debug_tangents() { @@ -391,7 +427,7 @@ void MeshInstance3D::create_debug_tangents() { add_child(mi); #ifdef TOOLS_ENABLED - if (this == get_tree()->get_edited_scene_root()) { + if (is_inside_tree() && this == get_tree()->get_edited_scene_root()) { mi->set_owner(this); } else { mi->set_owner(get_owner()); @@ -408,15 +444,17 @@ void MeshInstance3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_skin", "skin"), &MeshInstance3D::set_skin); ClassDB::bind_method(D_METHOD("get_skin"), &MeshInstance3D::get_skin); - ClassDB::bind_method(D_METHOD("get_surface_material_count"), &MeshInstance3D::get_surface_material_count); - ClassDB::bind_method(D_METHOD("set_surface_material", "surface", "material"), &MeshInstance3D::set_surface_material); - ClassDB::bind_method(D_METHOD("get_surface_material", "surface"), &MeshInstance3D::get_surface_material); + ClassDB::bind_method(D_METHOD("get_surface_override_material_count"), &MeshInstance3D::get_surface_override_material_count); + ClassDB::bind_method(D_METHOD("set_surface_override_material", "surface", "material"), &MeshInstance3D::set_surface_override_material); + ClassDB::bind_method(D_METHOD("get_surface_override_material", "surface"), &MeshInstance3D::get_surface_override_material); ClassDB::bind_method(D_METHOD("get_active_material", "surface"), &MeshInstance3D::get_active_material); ClassDB::bind_method(D_METHOD("create_trimesh_collision"), &MeshInstance3D::create_trimesh_collision); ClassDB::set_method_flags("MeshInstance3D", "create_trimesh_collision", METHOD_FLAGS_DEFAULT); ClassDB::bind_method(D_METHOD("create_convex_collision"), &MeshInstance3D::create_convex_collision); ClassDB::set_method_flags("MeshInstance3D", "create_convex_collision", METHOD_FLAGS_DEFAULT); + ClassDB::bind_method(D_METHOD("create_multiple_convex_collisions"), &MeshInstance3D::create_multiple_convex_collisions); + ClassDB::set_method_flags("MeshInstance3D", "create_multiple_convex_collisions", METHOD_FLAGS_DEFAULT); ClassDB::bind_method(D_METHOD("create_debug_tangents"), &MeshInstance3D::create_debug_tangents); ClassDB::set_method_flags("MeshInstance3D", "create_debug_tangents", METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR); diff --git a/scene/3d/mesh_instance_3d.h b/scene/3d/mesh_instance_3d.h index eb300784b1..9dea5804e0 100644 --- a/scene/3d/mesh_instance_3d.h +++ b/scene/3d/mesh_instance_3d.h @@ -52,7 +52,7 @@ protected: }; Map<StringName, BlendShapeTrack> blend_shape_tracks; - Vector<Ref<Material>> materials; + Vector<Ref<Material>> surface_override_materials; void _mesh_changed(); void _resolve_skeleton_path(); @@ -75,9 +75,9 @@ public: void set_skeleton_path(const NodePath &p_skeleton); NodePath get_skeleton_path(); - int get_surface_material_count() const; - void set_surface_material(int p_surface, const Ref<Material> &p_material); - Ref<Material> get_surface_material(int p_surface) const; + int get_surface_override_material_count() const; + void set_surface_override_material(int p_surface, const Ref<Material> &p_material); + Ref<Material> get_surface_override_material(int p_surface) const; Ref<Material> get_active_material(int p_surface) const; Node *create_trimesh_collision_node(); @@ -86,6 +86,9 @@ public: Node *create_convex_collision_node(); void create_convex_collision(); + Node *create_multiple_convex_collisions_node(); + void create_multiple_convex_collisions(); + void create_debug_tangents(); virtual AABB get_aabb() const override; diff --git a/scene/3d/navigation_3d.cpp b/scene/3d/navigation_3d.cpp deleted file mode 100644 index eaddec7601..0000000000 --- a/scene/3d/navigation_3d.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/*************************************************************************/ -/* navigation_3d.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "navigation_3d.h" - -#include "servers/navigation_server_3d.h" - -Vector<Vector3> Navigation3D::get_simple_path(const Vector3 &p_start, const Vector3 &p_end, bool p_optimize) const { - return NavigationServer3D::get_singleton()->map_get_path(map, p_start, p_end, p_optimize); -} - -Vector3 Navigation3D::get_closest_point_to_segment(const Vector3 &p_from, const Vector3 &p_to, bool p_use_collision) const { - return NavigationServer3D::get_singleton()->map_get_closest_point_to_segment(map, p_from, p_to, p_use_collision); -} - -Vector3 Navigation3D::get_closest_point(const Vector3 &p_point) const { - return NavigationServer3D::get_singleton()->map_get_closest_point(map, p_point); -} - -Vector3 Navigation3D::get_closest_point_normal(const Vector3 &p_point) const { - return NavigationServer3D::get_singleton()->map_get_closest_point_normal(map, p_point); -} - -RID Navigation3D::get_closest_point_owner(const Vector3 &p_point) const { - return NavigationServer3D::get_singleton()->map_get_closest_point_owner(map, p_point); -} - -void Navigation3D::set_up_vector(const Vector3 &p_up) { - up = p_up; - NavigationServer3D::get_singleton()->map_set_up(map, up); -} - -Vector3 Navigation3D::get_up_vector() const { - return up; -} - -void Navigation3D::set_cell_size(float p_cell_size) { - cell_size = p_cell_size; - NavigationServer3D::get_singleton()->map_set_cell_size(map, cell_size); -} - -void Navigation3D::set_edge_connection_margin(float p_edge_connection_margin) { - edge_connection_margin = p_edge_connection_margin; - NavigationServer3D::get_singleton()->map_set_edge_connection_margin(map, edge_connection_margin); -} - -void Navigation3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("get_rid"), &Navigation3D::get_rid); - - ClassDB::bind_method(D_METHOD("get_simple_path", "start", "end", "optimize"), &Navigation3D::get_simple_path, DEFVAL(true)); - ClassDB::bind_method(D_METHOD("get_closest_point_to_segment", "start", "end", "use_collision"), &Navigation3D::get_closest_point_to_segment, DEFVAL(false)); - ClassDB::bind_method(D_METHOD("get_closest_point", "to_point"), &Navigation3D::get_closest_point); - ClassDB::bind_method(D_METHOD("get_closest_point_normal", "to_point"), &Navigation3D::get_closest_point_normal); - ClassDB::bind_method(D_METHOD("get_closest_point_owner", "to_point"), &Navigation3D::get_closest_point_owner); - - ClassDB::bind_method(D_METHOD("set_up_vector", "up"), &Navigation3D::set_up_vector); - ClassDB::bind_method(D_METHOD("get_up_vector"), &Navigation3D::get_up_vector); - - ClassDB::bind_method(D_METHOD("set_cell_size", "cell_size"), &Navigation3D::set_cell_size); - ClassDB::bind_method(D_METHOD("get_cell_size"), &Navigation3D::get_cell_size); - - ClassDB::bind_method(D_METHOD("set_edge_connection_margin", "margin"), &Navigation3D::set_edge_connection_margin); - ClassDB::bind_method(D_METHOD("get_edge_connection_margin"), &Navigation3D::get_edge_connection_margin); - - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "up_vector"), "set_up_vector", "get_up_vector"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "cell_size"), "set_cell_size", "get_cell_size"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "edge_connection_margin"), "set_edge_connection_margin", "get_edge_connection_margin"); -} - -void Navigation3D::_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_READY: { - NavigationServer3D::get_singleton()->map_set_active(map, true); - } break; - case NOTIFICATION_EXIT_TREE: { - NavigationServer3D::get_singleton()->map_set_active(map, false); - } break; - } -} - -Navigation3D::Navigation3D() { - map = NavigationServer3D::get_singleton()->map_create(); - - set_cell_size(0.3); - set_edge_connection_margin(5.0); // Five meters, depends a lot on the agent's radius -} - -Navigation3D::~Navigation3D() { - NavigationServer3D::get_singleton()->free(map); -} diff --git a/scene/3d/navigation_agent_3d.cpp b/scene/3d/navigation_agent_3d.cpp index 8917cc4664..64cfe4dca7 100644 --- a/scene/3d/navigation_agent_3d.cpp +++ b/scene/3d/navigation_agent_3d.cpp @@ -31,10 +31,11 @@ #include "navigation_agent_3d.h" #include "core/config/engine.h" -#include "scene/3d/navigation_3d.h" #include "servers/navigation_server_3d.h" void NavigationAgent3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_rid"), &NavigationAgent3D::get_rid); + ClassDB::bind_method(D_METHOD("set_target_desired_distance", "desired_distance"), &NavigationAgent3D::set_target_desired_distance); ClassDB::bind_method(D_METHOD("get_target_desired_distance"), &NavigationAgent3D::get_target_desired_distance); @@ -47,9 +48,6 @@ void NavigationAgent3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_ignore_y", "ignore"), &NavigationAgent3D::set_ignore_y); ClassDB::bind_method(D_METHOD("get_ignore_y"), &NavigationAgent3D::get_ignore_y); - ClassDB::bind_method(D_METHOD("set_navigation", "navigation"), &NavigationAgent3D::set_navigation_node); - ClassDB::bind_method(D_METHOD("get_navigation"), &NavigationAgent3D::get_navigation_node); - ClassDB::bind_method(D_METHOD("set_neighbor_dist", "neighbor_dist"), &NavigationAgent3D::set_neighbor_dist); ClassDB::bind_method(D_METHOD("get_neighbor_dist"), &NavigationAgent3D::get_neighbor_dist); @@ -99,41 +97,21 @@ void NavigationAgent3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_READY: { agent_parent = Object::cast_to<Node3D>(get_parent()); - - NavigationServer3D::get_singleton()->agent_set_callback(agent, this, "_avoidance_done"); - - // Search the navigation node and set it - { - Navigation3D *nav = nullptr; - Node *p = get_parent(); - while (p != nullptr) { - nav = Object::cast_to<Navigation3D>(p); - if (nav != nullptr) { - p = nullptr; - } else { - p = p->get_parent(); - } - } - - set_navigation(nav); + if (agent_parent != nullptr) { + // place agent on navigation map first or else the RVO agent callback creation fails silently later + NavigationServer3D::get_singleton()->agent_set_map(get_rid(), agent_parent->get_world_3d()->get_navigation_map()); + NavigationServer3D::get_singleton()->agent_set_callback(agent, this, "_avoidance_done"); } - set_physics_process_internal(true); } break; case NOTIFICATION_EXIT_TREE: { agent_parent = nullptr; - set_navigation(nullptr); set_physics_process_internal(false); } break; case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { if (agent_parent) { NavigationServer3D::get_singleton()->agent_set_position(agent, agent_parent->get_global_transform().origin); - if (!target_reached) { - if (distance_to_target() < target_desired_distance) { - emit_signal("target_reached"); - target_reached = true; - } - } + _check_distance_to_target(); } } break; } @@ -154,25 +132,6 @@ NavigationAgent3D::~NavigationAgent3D() { agent = RID(); // Pointless } -void NavigationAgent3D::set_navigation(Navigation3D *p_nav) { - if (navigation == p_nav) { - return; // Pointless - } - - navigation = p_nav; - NavigationServer3D::get_singleton()->agent_set_map(agent, navigation == nullptr ? RID() : navigation->get_rid()); -} - -void NavigationAgent3D::set_navigation_node(Node *p_nav) { - Navigation3D *nav = Object::cast_to<Navigation3D>(p_nav); - ERR_FAIL_COND(nav == nullptr); - set_navigation(nav); -} - -Node *NavigationAgent3D::get_navigation_node() const { - return Object::cast_to<Node>(navigation); -} - void NavigationAgent3D::set_target_desired_distance(real_t p_dd) { target_desired_distance = p_dd; } @@ -286,24 +245,21 @@ void NavigationAgent3D::_avoidance_done(Vector3 p_new_velocity) { emit_signal("velocity_computed", p_new_velocity); } -String NavigationAgent3D::get_configuration_warning() const { - String warning = Node::get_configuration_warning(); +TypedArray<String> NavigationAgent3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); if (!Object::cast_to<Node3D>(get_parent())) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("The NavigationAgent3D can be used only under a spatial node."); + warnings.push_back(TTR("The NavigationAgent3D can be used only under a spatial node.")); } - return warning; + return warnings; } void NavigationAgent3D::update_navigation() { if (agent_parent == nullptr) { return; } - if (navigation == nullptr) { + if (!agent_parent->is_inside_tree()) { return; } if (update_frame_id == Engine::get_singleton()->get_physics_frames()) { @@ -337,7 +293,7 @@ void NavigationAgent3D::update_navigation() { } if (reload_path) { - navigation_path = NavigationServer3D::get_singleton()->map_get_path(navigation->get_rid(), o, target_location, true); + navigation_path = NavigationServer3D::get_singleton()->map_get_path(agent_parent->get_world_3d()->get_navigation_map(), o, target_location, true); navigation_finished = false; nav_path_index = 0; emit_signal("path_changed"); @@ -353,6 +309,7 @@ void NavigationAgent3D::update_navigation() { while (o.distance_to(navigation_path[nav_path_index] - Vector3(0, navigation_height_offset, 0)) < target_desired_distance) { nav_path_index += 1; if (nav_path_index == navigation_path.size()) { + _check_distance_to_target(); nav_path_index -= 1; navigation_finished = true; emit_signal("navigation_finished"); @@ -361,3 +318,12 @@ void NavigationAgent3D::update_navigation() { } } } + +void NavigationAgent3D::_check_distance_to_target() { + if (!target_reached) { + if (distance_to_target() < target_desired_distance) { + emit_signal("target_reached"); + target_reached = true; + } + } +} diff --git a/scene/3d/navigation_agent_3d.h b/scene/3d/navigation_agent_3d.h index bd890a051b..56da2d1acf 100644 --- a/scene/3d/navigation_agent_3d.h +++ b/scene/3d/navigation_agent_3d.h @@ -35,13 +35,11 @@ #include "scene/main/node.h" class Node3D; -class Navigation3D; class NavigationAgent3D : public Node { GDCLASS(NavigationAgent3D, Node); Node3D *agent_parent = nullptr; - Navigation3D *navigation = nullptr; RID agent; @@ -76,14 +74,6 @@ public: NavigationAgent3D(); virtual ~NavigationAgent3D(); - void set_navigation(Navigation3D *p_nav); - const Navigation3D *get_navigation() const { - return navigation; - } - - void set_navigation_node(Node *p_nav); - Node *get_navigation_node() const; - RID get_rid() const { return agent; } @@ -153,10 +143,11 @@ public: void set_velocity(Vector3 p_velocity); void _avoidance_done(Vector3 p_new_velocity); - virtual String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; private: void update_navigation(); + void _check_distance_to_target(); }; #endif diff --git a/scene/3d/navigation_obstacle_3d.cpp b/scene/3d/navigation_obstacle_3d.cpp index 01bf7de913..20ffc3b00e 100644 --- a/scene/3d/navigation_obstacle_3d.cpp +++ b/scene/3d/navigation_obstacle_3d.cpp @@ -31,55 +31,38 @@ #include "navigation_obstacle_3d.h" #include "scene/3d/collision_shape_3d.h" -#include "scene/3d/navigation_3d.h" #include "scene/3d/physics_body_3d.h" #include "servers/navigation_server_3d.h" void NavigationObstacle3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_navigation", "navigation"), &NavigationObstacle3D::set_navigation_node); - ClassDB::bind_method(D_METHOD("get_navigation"), &NavigationObstacle3D::get_navigation_node); } void NavigationObstacle3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_READY: { - update_agent_shape(); - - // Search the navigation node and set it - { - Navigation3D *nav = nullptr; - Node *p = get_parent(); - while (p != nullptr) { - nav = Object::cast_to<Navigation3D>(p); - if (nav != nullptr) { - p = nullptr; - } else { - p = p->get_parent(); - } - } - - set_navigation(nav); - } - set_physics_process_internal(true); } break; case NOTIFICATION_EXIT_TREE: { - set_navigation(nullptr); set_physics_process_internal(false); } break; + case NOTIFICATION_PARENTED: { + parent_node3d = Object::cast_to<Node3D>(get_parent()); + update_agent_shape(); + } break; + case NOTIFICATION_UNPARENTED: { + parent_node3d = nullptr; + } break; case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { - Node3D *spatial = Object::cast_to<Node3D>(get_parent()); - if (spatial) { - NavigationServer3D::get_singleton()->agent_set_position(agent, spatial->get_global_transform().origin); - } - - PhysicsBody3D *rigid = Object::cast_to<PhysicsBody3D>(get_parent()); - if (rigid) { - Vector3 v = rigid->get_linear_velocity(); - NavigationServer3D::get_singleton()->agent_set_velocity(agent, v); - NavigationServer3D::get_singleton()->agent_set_target_velocity(agent, v); + if (parent_node3d) { + NavigationServer3D::get_singleton()->agent_set_position(agent, parent_node3d->get_global_transform().origin); + + PhysicsBody3D *rigid = Object::cast_to<PhysicsBody3D>(get_parent()); + if (rigid) { + Vector3 v = rigid->get_linear_velocity(); + NavigationServer3D::get_singleton()->agent_set_velocity(agent, v); + NavigationServer3D::get_singleton()->agent_set_target_velocity(agent, v); + } } - } break; } } @@ -93,73 +76,49 @@ NavigationObstacle3D::~NavigationObstacle3D() { agent = RID(); // Pointless } -void NavigationObstacle3D::set_navigation(Navigation3D *p_nav) { - if (navigation == p_nav) { - return; // Pointless - } - - navigation = p_nav; - NavigationServer3D::get_singleton()->agent_set_map(agent, navigation == nullptr ? RID() : navigation->get_rid()); -} - -void NavigationObstacle3D::set_navigation_node(Node *p_nav) { - Navigation3D *nav = Object::cast_to<Navigation3D>(p_nav); - ERR_FAIL_COND(nav == nullptr); - set_navigation(nav); -} - -Node *NavigationObstacle3D::get_navigation_node() const { - return Object::cast_to<Node>(navigation); -} - -String NavigationObstacle3D::get_configuration_warning() const { - String warning = Node::get_configuration_warning(); +TypedArray<String> NavigationObstacle3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); if (!Object::cast_to<Node3D>(get_parent())) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("The NavigationObstacle3D only serves to provide collision avoidance to a spatial object."); + warnings.push_back(TTR("The NavigationObstacle3D only serves to provide collision avoidance to a spatial object.")); } - return warning; + return warnings; } void NavigationObstacle3D::update_agent_shape() { - Node *node = get_parent(); - - // Estimate the radius of this physics body - real_t radius = 0.0; - for (int i(0); i < node->get_child_count(); i++) { - // For each collision shape - CollisionShape3D *cs = Object::cast_to<CollisionShape3D>(node->get_child(i)); - if (cs) { - // Take the distance between the Body center to the shape center - real_t r = cs->get_transform().origin.length(); - if (cs->get_shape().is_valid()) { - // and add the enclosing shape radius - r += cs->get_shape()->get_enclosing_radius(); + if (parent_node3d) { + // Estimate the radius of this physics body + real_t radius = 0.0; + for (int i(0); i < parent_node3d->get_child_count(); i++) { + // For each collision shape + CollisionShape3D *cs = Object::cast_to<CollisionShape3D>(parent_node3d->get_child(i)); + if (cs) { + // Take the distance between the Body center to the shape center + real_t r = cs->get_transform().origin.length(); + if (cs->get_shape().is_valid()) { + // and add the enclosing shape radius + r += cs->get_shape()->get_enclosing_radius(); + } + Vector3 s = cs->get_global_transform().basis.get_scale(); + r *= MAX(s.x, MAX(s.y, s.z)); + // Takes the biggest radius + radius = MAX(radius, r); } - Vector3 s = cs->get_global_transform().basis.get_scale(); - r *= MAX(s.x, MAX(s.y, s.z)); - // Takes the biggest radius - radius = MAX(radius, r); } - } - Node3D *spa = Object::cast_to<Node3D>(node); - if (spa) { - Vector3 s = spa->get_global_transform().basis.get_scale(); + + Vector3 s = parent_node3d->get_global_transform().basis.get_scale(); radius *= MAX(s.x, MAX(s.y, s.z)); - } - if (radius == 0.0) { - radius = 1.0; // Never a 0 radius - } + if (radius == 0.0) { + radius = 1.0; // Never a 0 radius + } - // Initialize the Agent as an object - NavigationServer3D::get_singleton()->agent_set_neighbor_dist(agent, 0.0); - NavigationServer3D::get_singleton()->agent_set_max_neighbors(agent, 0); - NavigationServer3D::get_singleton()->agent_set_time_horizon(agent, 0.0); - NavigationServer3D::get_singleton()->agent_set_radius(agent, radius); - NavigationServer3D::get_singleton()->agent_set_max_speed(agent, 0.0); + // Initialize the Agent as an object + NavigationServer3D::get_singleton()->agent_set_neighbor_dist(agent, 0.0); + NavigationServer3D::get_singleton()->agent_set_max_neighbors(agent, 0); + NavigationServer3D::get_singleton()->agent_set_time_horizon(agent, 0.0); + NavigationServer3D::get_singleton()->agent_set_radius(agent, radius); + NavigationServer3D::get_singleton()->agent_set_max_speed(agent, 0.0); + } } diff --git a/scene/3d/navigation_obstacle_3d.h b/scene/3d/navigation_obstacle_3d.h index b8d05b8a87..2f78f624a4 100644 --- a/scene/3d/navigation_obstacle_3d.h +++ b/scene/3d/navigation_obstacle_3d.h @@ -31,15 +31,13 @@ #ifndef NAVIGATION_OBSTACLE_H #define NAVIGATION_OBSTACLE_H +#include "scene/3d/node_3d.h" #include "scene/main/node.h" -class Navigation3D; - class NavigationObstacle3D : public Node { GDCLASS(NavigationObstacle3D, Node); - Navigation3D *navigation = nullptr; - + Node3D *parent_node3d = nullptr; RID agent; protected: @@ -50,19 +48,11 @@ public: NavigationObstacle3D(); virtual ~NavigationObstacle3D(); - void set_navigation(Navigation3D *p_nav); - const Navigation3D *get_navigation() const { - return navigation; - } - - void set_navigation_node(Node *p_nav); - Node *get_navigation_node() const; - RID get_rid() const { return agent; } - virtual String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; private: void update_agent_shape(); diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp index 19bde94222..0afad62404 100644 --- a/scene/3d/navigation_region_3d.cpp +++ b/scene/3d/navigation_region_3d.cpp @@ -32,7 +32,6 @@ #include "core/os/thread.h" #include "mesh_instance_3d.h" -#include "navigation_3d.h" #include "servers/navigation_server_3d.h" void NavigationRegion3D::set_enabled(bool p_enabled) { @@ -48,9 +47,7 @@ void NavigationRegion3D::set_enabled(bool p_enabled) { if (!enabled) { NavigationServer3D::get_singleton()->region_set_map(region, RID()); } else { - if (navigation) { - NavigationServer3D::get_singleton()->region_set_map(region, navigation->get_rid()); - } + NavigationServer3D::get_singleton()->region_set_map(region, get_world_3d()->get_navigation_map()); } if (debug_view) { @@ -69,22 +66,21 @@ bool NavigationRegion3D::is_enabled() const { return enabled; } +void NavigationRegion3D::set_layers(uint32_t p_layers) { + NavigationServer3D::get_singleton()->region_set_layers(region, p_layers); +} + +uint32_t NavigationRegion3D::get_layers() const { + return NavigationServer3D::get_singleton()->region_get_layers(region); +} + ///////////////////////////// void NavigationRegion3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { - Node3D *c = this; - while (c) { - navigation = Object::cast_to<Navigation3D>(c); - if (navigation) { - if (enabled) { - NavigationServer3D::get_singleton()->region_set_map(region, navigation->get_rid()); - } - break; - } - - c = c->get_parent_spatial(); + if (enabled) { + NavigationServer3D::get_singleton()->region_set_map(region, get_world_3d()->get_navigation_map()); } if (navmesh.is_valid() && get_tree()->is_debugging_navigation_hint()) { @@ -105,15 +101,12 @@ void NavigationRegion3D::_notification(int p_what) { } break; case NOTIFICATION_EXIT_TREE: { - if (navigation) { - NavigationServer3D::get_singleton()->region_set_map(region, RID()); - } + NavigationServer3D::get_singleton()->region_set_map(region, RID()); if (debug_view) { debug_view->queue_delete(); debug_view = nullptr; } - navigation = nullptr; } break; } } @@ -142,7 +135,7 @@ void NavigationRegion3D::set_navigation_mesh(const Ref<NavigationMesh> &p_navmes emit_signal("navigation_mesh_changed"); update_gizmo(); - update_configuration_warning(); + update_configuration_warnings(); } Ref<NavigationMesh> NavigationRegion3D::get_navigation_mesh() const { @@ -184,33 +177,16 @@ void NavigationRegion3D::_bake_finished(Ref<NavigationMesh> p_nav_mesh) { emit_signal("bake_finished"); } -String NavigationRegion3D::get_configuration_warning() const { - if (!is_visible_in_tree() || !is_inside_tree()) { - return String(); - } - - String warning = Node3D::get_configuration_warning(); - - if (!navmesh.is_valid()) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("A NavigationMesh resource must be set or created for this node to work."); - } +TypedArray<String> NavigationRegion3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); - const Node3D *c = this; - while (c) { - if (Object::cast_to<Navigation3D>(c)) { - return warning; + if (is_visible_in_tree() && is_inside_tree()) { + if (!navmesh.is_valid()) { + warnings.push_back(TTR("A NavigationMesh resource must be set or created for this node to work.")); } - - c = Object::cast_to<Node3D>(c->get_parent()); } - if (!warning.is_empty()) { - warning += "\n\n"; - } - return warning + TTR("NavigationRegion3D must be a child or grandchild to a Navigation3D node. It only provides navigation data."); + return warnings; } void NavigationRegion3D::_bind_methods() { @@ -220,11 +196,15 @@ void NavigationRegion3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &NavigationRegion3D::set_enabled); ClassDB::bind_method(D_METHOD("is_enabled"), &NavigationRegion3D::is_enabled); + ClassDB::bind_method(D_METHOD("set_layers", "layers"), &NavigationRegion3D::set_layers); + ClassDB::bind_method(D_METHOD("get_layers"), &NavigationRegion3D::get_layers); + ClassDB::bind_method(D_METHOD("bake_navigation_mesh"), &NavigationRegion3D::bake_navigation_mesh); ClassDB::bind_method(D_METHOD("_bake_finished", "nav_mesh"), &NavigationRegion3D::_bake_finished); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navmesh", PROPERTY_HINT_RESOURCE_TYPE, "NavigationMesh"), "set_navigation_mesh", "get_navigation_mesh"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "layers", PROPERTY_HINT_LAYERS_3D_NAVIGATION), "set_layers", "get_layers"); ADD_SIGNAL(MethodInfo("navigation_mesh_changed")); ADD_SIGNAL(MethodInfo("bake_finished")); @@ -232,7 +212,7 @@ void NavigationRegion3D::_bind_methods() { void NavigationRegion3D::_navigation_changed() { update_gizmo(); - update_configuration_warning(); + update_configuration_warnings(); } NavigationRegion3D::NavigationRegion3D() { diff --git a/scene/3d/navigation_region_3d.h b/scene/3d/navigation_region_3d.h index 6ae15c9360..c2045215b1 100644 --- a/scene/3d/navigation_region_3d.h +++ b/scene/3d/navigation_region_3d.h @@ -35,8 +35,6 @@ #include "scene/resources/mesh.h" #include "scene/resources/navigation_mesh.h" -class Navigation3D; - class NavigationRegion3D : public Node3D { GDCLASS(NavigationRegion3D, Node3D); @@ -44,7 +42,6 @@ class NavigationRegion3D : public Node3D { RID region; Ref<NavigationMesh> navmesh; - Navigation3D *navigation = nullptr; Node *debug_view = nullptr; Thread bake_thread; @@ -58,6 +55,9 @@ public: void set_enabled(bool p_enabled); bool is_enabled() const; + void set_layers(uint32_t p_layers); + uint32_t get_layers() const; + void set_navigation_mesh(const Ref<NavigationMesh> &p_navmesh); Ref<NavigationMesh> get_navigation_mesh() const; @@ -66,7 +66,7 @@ public: void bake_navigation_mesh(); void _bake_finished(Ref<NavigationMesh> p_nav_mesh); - String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; NavigationRegion3D(); ~NavigationRegion3D(); diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp index 4575716f7a..ba0f8cc870 100644 --- a/scene/3d/node_3d.cpp +++ b/scene/3d/node_3d.cpp @@ -48,7 +48,7 @@ a) If above is invalid, don't keep invalidating upwards 2) If a node sets a GLOBAL, it is converted to LOCAL (and forces validation of everything pending below) - drawback: setting/reading globals is useful and used very very often, and using affine inverses is slow + drawback: setting/reading globals is useful and used very often, and using affine inverses is slow --- diff --git a/scene/3d/occluder_instance_3d.cpp b/scene/3d/occluder_instance_3d.cpp new file mode 100644 index 0000000000..d3a256db34 --- /dev/null +++ b/scene/3d/occluder_instance_3d.cpp @@ -0,0 +1,335 @@ +/*************************************************************************/ +/* occluder_instance_3d.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (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 "occluder_instance_3d.h" +#include "core/core_string_names.h" +#include "scene/3d/mesh_instance_3d.h" + +RID Occluder3D::get_rid() const { + if (!occluder.is_valid()) { + occluder = RS::get_singleton()->occluder_create(); + RS::get_singleton()->occluder_set_mesh(occluder, vertices, indices); + } + return occluder; +} + +void Occluder3D::set_vertices(PackedVector3Array p_vertices) { + vertices = p_vertices; + if (occluder.is_valid()) { + RS::get_singleton()->occluder_set_mesh(occluder, vertices, indices); + } + _update_changes(); +} + +PackedVector3Array Occluder3D::get_vertices() const { + return vertices; +} + +void Occluder3D::set_indices(PackedInt32Array p_indices) { + indices = p_indices; + if (occluder.is_valid()) { + RS::get_singleton()->occluder_set_mesh(occluder, vertices, indices); + } + _update_changes(); +} + +PackedInt32Array Occluder3D::get_indices() const { + return indices; +} + +void Occluder3D::_update_changes() { + aabb = AABB(); + + const Vector3 *ptr = vertices.ptr(); + for (int i = 0; i < vertices.size(); i++) { + aabb.expand_to(ptr[i]); + } + + debug_lines.clear(); + debug_mesh.unref(); + + emit_changed(); +} + +Vector<Vector3> Occluder3D::get_debug_lines() const { + if (!debug_lines.is_empty()) { + return debug_lines; + } + + if (indices.size() % 3 != 0) { + return Vector<Vector3>(); + } + + for (int i = 0; i < indices.size() / 3; i++) { + for (int j = 0; j < 3; j++) { + int a = indices[i * 3 + j]; + int b = indices[i * 3 + (j + 1) % 3]; + ERR_FAIL_INDEX_V_MSG(a, vertices.size(), Vector<Vector3>(), "Occluder indices are out of range."); + ERR_FAIL_INDEX_V_MSG(b, vertices.size(), Vector<Vector3>(), "Occluder indices are out of range."); + debug_lines.push_back(vertices[a]); + debug_lines.push_back(vertices[b]); + } + } + return debug_lines; +} + +Ref<ArrayMesh> Occluder3D::get_debug_mesh() const { + if (debug_mesh.is_valid()) { + return debug_mesh; + } + + if (indices.size() % 3 != 0) { + return debug_mesh; + } + + Array arrays; + arrays.resize(Mesh::ARRAY_MAX); + arrays[Mesh::ARRAY_VERTEX] = vertices; + arrays[Mesh::ARRAY_INDEX] = indices; + + debug_mesh.instance(); + debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arrays); + return debug_mesh; +} + +AABB Occluder3D::get_aabb() const { + return aabb; +} + +void Occluder3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_vertices", "vertices"), &Occluder3D::set_vertices); + ClassDB::bind_method(D_METHOD("get_vertices"), &Occluder3D::get_vertices); + + ClassDB::bind_method(D_METHOD("set_indices", "indices"), &Occluder3D::set_indices); + ClassDB::bind_method(D_METHOD("get_indices"), &Occluder3D::get_indices); + + ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_vertices", "get_vertices"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "indices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_indices", "get_indices"); +} + +Occluder3D::Occluder3D() { +} + +Occluder3D::~Occluder3D() { + if (occluder.is_valid()) { + RS::get_singleton()->free(occluder); + } +} +///////////////////////////////////////////////// + +AABB OccluderInstance3D::get_aabb() const { + if (occluder.is_valid()) { + return occluder->get_aabb(); + } + return AABB(); +} + +Vector<Face3> OccluderInstance3D::get_faces(uint32_t p_usage_flags) const { + return Vector<Face3>(); +} + +void OccluderInstance3D::set_occluder(const Ref<Occluder3D> &p_occluder) { + if (occluder == p_occluder) { + return; + } + + if (occluder.is_valid()) { + occluder->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &OccluderInstance3D::_occluder_changed)); + } + + occluder = p_occluder; + + if (occluder.is_valid()) { + set_base(occluder->get_rid()); + occluder->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &OccluderInstance3D::_occluder_changed)); + } else { + set_base(RID()); + } + + update_gizmo(); +} + +void OccluderInstance3D::_occluder_changed() { + update_gizmo(); +} + +Ref<Occluder3D> OccluderInstance3D::get_occluder() const { + return occluder; +} + +void OccluderInstance3D::set_bake_mask(uint32_t p_mask) { + bake_mask = p_mask; +} + +uint32_t OccluderInstance3D::get_bake_mask() const { + return bake_mask; +} + +void OccluderInstance3D::set_bake_mask_bit(int p_layer, bool p_enable) { + ERR_FAIL_INDEX(p_layer, 32); + if (p_enable) { + set_bake_mask(bake_mask | (1 << p_layer)); + } else { + set_bake_mask(bake_mask & (~(1 << p_layer))); + } +} + +bool OccluderInstance3D::get_bake_mask_bit(int p_layer) const { + ERR_FAIL_INDEX_V(p_layer, 32, false); + return (bake_mask & (1 << p_layer)); +} + +bool OccluderInstance3D::_bake_material_check(Ref<Material> p_material) { + StandardMaterial3D *standard_mat = Object::cast_to<StandardMaterial3D>(p_material.ptr()); + if (standard_mat && standard_mat->get_transparency() != StandardMaterial3D::TRANSPARENCY_DISABLED) { + return false; + } + return true; +} + +void OccluderInstance3D::_bake_node(Node *p_node, PackedVector3Array &r_vertices, PackedInt32Array &r_indices) { + MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_node); + if (mi && mi->is_visible_in_tree()) { + Ref<Mesh> mesh = mi->get_mesh(); + bool valid = true; + + if (mesh.is_null()) { + valid = false; + } + + if (valid && !_bake_material_check(mi->get_material_override())) { + valid = false; + } + + if ((mi->get_layer_mask() & bake_mask) == 0) { + valid = false; + } + + if (valid) { + Transform global_to_local = get_global_transform().affine_inverse() * mi->get_global_transform(); + + for (int i = 0; i < mesh->get_surface_count(); i++) { + if (mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) { + continue; + } + + if (mi->get_surface_override_material(i).is_valid()) { + if (!_bake_material_check(mi->get_surface_override_material(i))) { + continue; + } + } else { + if (!_bake_material_check(mesh->surface_get_material(i))) { + continue; + } + } + + Array arrays = mesh->surface_get_arrays(i); + + int vertex_offset = r_vertices.size(); + PackedVector3Array vertices = arrays[Mesh::ARRAY_VERTEX]; + r_vertices.resize(r_vertices.size() + vertices.size()); + + Vector3 *vtx_ptr = r_vertices.ptrw(); + for (int j = 0; j < vertices.size(); j++) { + vtx_ptr[vertex_offset + j] = global_to_local.xform(vertices[j]); + } + + int index_offset = r_indices.size(); + PackedInt32Array indices = arrays[Mesh::ARRAY_INDEX]; + r_indices.resize(r_indices.size() + indices.size()); + + int *idx_ptr = r_indices.ptrw(); + for (int j = 0; j < indices.size(); j++) { + idx_ptr[index_offset + j] = vertex_offset + indices[j]; + } + } + } + } + + for (int i = 0; i < p_node->get_child_count(); i++) { + Node *child = p_node->get_child(i); + if (!child->get_owner()) { + continue; //maybe a helper + } + + _bake_node(child, r_vertices, r_indices); + } +} + +OccluderInstance3D::BakeError OccluderInstance3D::bake(Node *p_from_node, String p_occluder_path) { + if (p_occluder_path == "") { + if (get_occluder().is_null()) { + return BAKE_ERROR_NO_SAVE_PATH; + } + } + + PackedVector3Array vertices; + PackedInt32Array indices; + + _bake_node(p_from_node, vertices, indices); + + if (vertices.is_empty() || indices.is_empty()) { + return BAKE_ERROR_NO_MESHES; + } + + Ref<Occluder3D> occ; + if (get_occluder().is_valid()) { + occ = get_occluder(); + } else { + occ.instance(); + occ->set_path(p_occluder_path); + } + + occ->set_vertices(vertices); + occ->set_indices(indices); + set_occluder(occ); + + return BAKE_ERROR_OK; +} + +void OccluderInstance3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_bake_mask", "mask"), &OccluderInstance3D::set_bake_mask); + ClassDB::bind_method(D_METHOD("get_bake_mask"), &OccluderInstance3D::get_bake_mask); + ClassDB::bind_method(D_METHOD("set_bake_mask_bit", "layer", "enabled"), &OccluderInstance3D::set_bake_mask_bit); + ClassDB::bind_method(D_METHOD("get_bake_mask_bit", "layer"), &OccluderInstance3D::get_bake_mask_bit); + + ClassDB::bind_method(D_METHOD("set_occluder", "occluder"), &OccluderInstance3D::set_occluder); + ClassDB::bind_method(D_METHOD("get_occluder"), &OccluderInstance3D::get_occluder); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "occluder", PROPERTY_HINT_RESOURCE_TYPE, "Occluder3D"), "set_occluder", "get_occluder"); + ADD_GROUP("Bake", "bake_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "bake_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_bake_mask", "get_bake_mask"); +} + +OccluderInstance3D::OccluderInstance3D() { +} + +OccluderInstance3D::~OccluderInstance3D() { +} diff --git a/scene/3d/navigation_3d.h b/scene/3d/occluder_instance_3d.h index b89725a3f5..4bb468274d 100644 --- a/scene/3d/navigation_3d.h +++ b/scene/3d/occluder_instance_3d.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* navigation_3d.h */ +/* occluder_instance_3d.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,51 +28,81 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef NAVIGATION_3D_H -#define NAVIGATION_3D_H +#ifndef OCCLUDER_INSTANCE_3D_H +#define OCCLUDER_INSTANCE_3D_H -#include "scene/3d/navigation_region_3d.h" -#include "scene/3d/node_3d.h" +#include "scene/3d/visual_instance_3d.h" -class Navigation3D : public Node3D { - GDCLASS(Navigation3D, Node3D); +class Occluder3D : public Resource { + GDCLASS(Occluder3D, Resource); + RES_BASE_EXTENSION("occ"); - RID map; + mutable RID occluder; + mutable Ref<ArrayMesh> debug_mesh; + mutable Vector<Vector3> debug_lines; + AABB aabb; - Vector3 up = Vector3(0, 1, 0); - real_t cell_size; - real_t edge_connection_margin; + PackedVector3Array vertices; + PackedInt32Array indices; + + void _update_changes(); protected: static void _bind_methods(); - void _notification(int p_what); public: - RID get_rid() const { - return map; - } - - void set_up_vector(const Vector3 &p_up); - Vector3 get_up_vector() const; - - void set_cell_size(float p_cell_size); - float get_cell_size() const { - return cell_size; - } - - void set_edge_connection_margin(float p_edge_connection_margin); - float get_edge_connection_margin() const { - return edge_connection_margin; - } - - Vector<Vector3> get_simple_path(const Vector3 &p_start, const Vector3 &p_end, bool p_optimize = true) const; - Vector3 get_closest_point_to_segment(const Vector3 &p_from, const Vector3 &p_to, bool p_use_collision = false) const; - Vector3 get_closest_point(const Vector3 &p_point) const; - Vector3 get_closest_point_normal(const Vector3 &p_point) const; - RID get_closest_point_owner(const Vector3 &p_point) const; - - Navigation3D(); - ~Navigation3D(); + void set_vertices(PackedVector3Array p_vertices); + PackedVector3Array get_vertices() const; + + void set_indices(PackedInt32Array p_indices); + PackedInt32Array get_indices() const; + + Vector<Vector3> get_debug_lines() const; + Ref<ArrayMesh> get_debug_mesh() const; + AABB get_aabb() const; + + virtual RID get_rid() const override; + Occluder3D(); + ~Occluder3D(); +}; + +class OccluderInstance3D : public VisualInstance3D { + GDCLASS(OccluderInstance3D, Node3D); + +private: + Ref<Occluder3D> occluder; + uint32_t bake_mask = 0xFFFFFFFF; + + void _occluder_changed(); + + bool _bake_material_check(Ref<Material> p_material); + void _bake_node(Node *p_node, PackedVector3Array &r_vertices, PackedInt32Array &r_indices); + +protected: + static void _bind_methods(); + +public: + enum BakeError { + BAKE_ERROR_OK, + BAKE_ERROR_NO_SAVE_PATH, + BAKE_ERROR_NO_MESHES, + }; + + void set_occluder(const Ref<Occluder3D> &p_occluder); + Ref<Occluder3D> get_occluder() const; + + virtual AABB get_aabb() const override; + virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override; + + void set_bake_mask(uint32_t p_mask); + uint32_t get_bake_mask() const; + + void set_bake_mask_bit(int p_layer, bool p_enable); + bool get_bake_mask_bit(int p_layer) const; + BakeError bake(Node *p_from_node, String p_occluder_path = ""); + + OccluderInstance3D(); + ~OccluderInstance3D(); }; -#endif // NAVIGATION_H +#endif diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp index 7e2601902b..4ec4ee6207 100644 --- a/scene/3d/path_3d.cpp +++ b/scene/3d/path_3d.cpp @@ -50,7 +50,7 @@ void Path3D::_curve_changed() { for (int i = 0; i < get_child_count(); i++) { PathFollow3D *child = Object::cast_to<PathFollow3D>(get_child(i)); if (child) { - child->update_configuration_warning(); + child->update_configuration_warnings(); } } } @@ -241,29 +241,21 @@ void PathFollow3D::_validate_property(PropertyInfo &property) const { } } -String PathFollow3D::get_configuration_warning() const { - if (!is_visible_in_tree() || !is_inside_tree()) { - return String(); - } - - String warning = Node3D::get_configuration_warning(); +TypedArray<String> PathFollow3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); - if (!Object::cast_to<Path3D>(get_parent())) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("PathFollow3D only works when set as a child of a Path3D node."); - } else { - Path3D *path = Object::cast_to<Path3D>(get_parent()); - if (path->get_curve().is_valid() && !path->get_curve()->is_up_vector_enabled() && rotation_mode == ROTATION_ORIENTED) { - if (!warning.is_empty()) { - warning += "\n\n"; + if (is_visible_in_tree() && is_inside_tree()) { + if (!Object::cast_to<Path3D>(get_parent())) { + warnings.push_back(TTR("PathFollow3D only works when set as a child of a Path3D node.")); + } else { + Path3D *path = Object::cast_to<Path3D>(get_parent()); + if (path->get_curve().is_valid() && !path->get_curve()->is_up_vector_enabled() && rotation_mode == ROTATION_ORIENTED) { + warnings.push_back(TTR("PathFollow3D's ROTATION_ORIENTED requires \"Up Vector\" to be enabled in its parent Path3D's Curve resource.")); } - warning += TTR("PathFollow3D's ROTATION_ORIENTED requires \"Up Vector\" to be enabled in its parent Path3D's Curve resource."); } } - return warning; + return warnings; } void PathFollow3D::_bind_methods() { @@ -368,7 +360,7 @@ float PathFollow3D::get_unit_offset() const { void PathFollow3D::set_rotation_mode(RotationMode p_rotation_mode) { rotation_mode = p_rotation_mode; - update_configuration_warning(); + update_configuration_warnings(); _update_transform(); } diff --git a/scene/3d/path_3d.h b/scene/3d/path_3d.h index 17ee47593e..8545370a4a 100644 --- a/scene/3d/path_3d.h +++ b/scene/3d/path_3d.h @@ -104,7 +104,7 @@ public: void set_cubic_interpolation(bool p_enable); bool get_cubic_interpolation() const; - String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; PathFollow3D() {} }; diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp index 6d93b996f1..30c5a75bca 100644 --- a/scene/3d/physics_body_3d.cpp +++ b/scene/3d/physics_body_3d.cpp @@ -55,52 +55,6 @@ real_t PhysicsBody3D::get_inverse_mass() const { return 0; } -void PhysicsBody3D::set_collision_layer(uint32_t p_layer) { - collision_layer = p_layer; - PhysicsServer3D::get_singleton()->body_set_collision_layer(get_rid(), p_layer); -} - -uint32_t PhysicsBody3D::get_collision_layer() const { - return collision_layer; -} - -void PhysicsBody3D::set_collision_mask(uint32_t p_mask) { - collision_mask = p_mask; - PhysicsServer3D::get_singleton()->body_set_collision_mask(get_rid(), p_mask); -} - -uint32_t PhysicsBody3D::get_collision_mask() const { - return collision_mask; -} - -void PhysicsBody3D::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 PhysicsBody3D::get_collision_mask_bit(int p_bit) const { - return get_collision_mask() & (1 << p_bit); -} - -void PhysicsBody3D::set_collision_layer_bit(int p_bit, bool p_value) { - uint32_t mask = get_collision_layer(); - if (p_value) { - mask |= 1 << p_bit; - } else { - mask &= ~(1 << p_bit); - } - set_collision_layer(mask); -} - -bool PhysicsBody3D::get_collision_layer_bit(int p_bit) const { - return get_collision_layer() & (1 << p_bit); -} - TypedArray<PhysicsBody3D> PhysicsBody3D::get_collision_exceptions() { List<RID> exceptions; PhysicsServer3D::get_singleton()->body_get_collision_exceptions(get_rid(), &exceptions); @@ -129,29 +83,11 @@ void PhysicsBody3D::remove_collision_exception_with(Node *p_node) { PhysicsServer3D::get_singleton()->body_remove_collision_exception(get_rid(), collision_object->get_rid()); } -void PhysicsBody3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_collision_layer", "layer"), &PhysicsBody3D::set_collision_layer); - ClassDB::bind_method(D_METHOD("get_collision_layer"), &PhysicsBody3D::get_collision_layer); - - ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &PhysicsBody3D::set_collision_mask); - ClassDB::bind_method(D_METHOD("get_collision_mask"), &PhysicsBody3D::get_collision_mask); - - ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &PhysicsBody3D::set_collision_mask_bit); - ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &PhysicsBody3D::get_collision_mask_bit); - - ClassDB::bind_method(D_METHOD("set_collision_layer_bit", "bit", "value"), &PhysicsBody3D::set_collision_layer_bit); - ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &PhysicsBody3D::get_collision_layer_bit); - - ADD_GROUP("Collision", "collision_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer", "get_collision_layer"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask"); -} +void PhysicsBody3D::_bind_methods() {} PhysicsBody3D::PhysicsBody3D(PhysicsServer3D::BodyMode p_mode) : CollisionObject3D(PhysicsServer3D::get_singleton()->body_create(), false) { PhysicsServer3D::get_singleton()->body_set_mode(get_rid(), p_mode); - collision_layer = 1; - collision_mask = 1; } void StaticBody3D::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) { @@ -340,6 +276,7 @@ struct _RigidBodyInOut { void RigidBody3D::_direct_state_changed(Object *p_state) { #ifdef DEBUG_ENABLED state = Object::cast_to<PhysicsDirectBodyState3D>(p_state); + ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState3D object as argument"); #else state = (PhysicsDirectBodyState3D *)p_state; //trust it #endif @@ -357,6 +294,7 @@ void RigidBody3D::_direct_state_changed(Object *p_state) { get_script_instance()->call("_integrate_forces", state); } set_ignore_transform_notification(false); + _on_transform_changed(); if (contact_monitor) { contact_monitor->locked = true; @@ -422,13 +360,13 @@ void RigidBody3D::_direct_state_changed(Object *p_state) { } } - //process remotions + //process removals for (int i = 0; i < toremove_count; i++) { _body_inout(0, toremove[i].rid, toremove[i].body_id, toremove[i].pair.body_shape, toremove[i].pair.local_shape); } - //process aditions + //process additions for (int i = 0; i < toadd_count; i++) { _body_inout(1, toremove[i].rid, toadd[i].id, toadd[i].shape, toadd[i].local_shape); @@ -450,7 +388,7 @@ void RigidBody3D::_notification(int p_what) { if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) { if (Engine::get_singleton()->is_editor_hint()) { - update_configuration_warning(); + update_configuration_warnings(); } } @@ -475,7 +413,7 @@ void RigidBody3D::set_mode(Mode p_mode) { PhysicsServer3D::get_singleton()->body_set_mode(get_rid(), PhysicsServer3D::BODY_MODE_KINEMATIC); } break; } - update_configuration_warning(); + update_configuration_warnings(); } RigidBody3D::Mode RigidBody3D::get_mode() const { @@ -715,19 +653,16 @@ Array RigidBody3D::get_colliding_bodies() const { return ret; } -String RigidBody3D::get_configuration_warning() const { +TypedArray<String> RigidBody3D::get_configuration_warnings() const { Transform t = get_transform(); - String warning = CollisionObject3D::get_configuration_warning(); + TypedArray<String> warnings = Node::get_configuration_warnings(); if ((get_mode() == MODE_RIGID || get_mode() == MODE_CHARACTER) && (ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(2).length() - 1.0) > 0.05)) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("Size changes to RigidBody3D (in character or rigid modes) will be overridden by the physics engine when running.\nChange the size in children collision shapes instead."); + warnings.push_back(TTR("Size changes to RigidBody3D (in character or rigid modes) will be overridden by the physics engine when running.\nChange the size in children collision shapes instead.")); } - return warning; + return warnings; } void RigidBody3D::_bind_methods() { @@ -785,8 +720,6 @@ void RigidBody3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_can_sleep", "able_to_sleep"), &RigidBody3D::set_can_sleep); ClassDB::bind_method(D_METHOD("is_able_to_sleep"), &RigidBody3D::is_able_to_sleep); - ClassDB::bind_method(D_METHOD("_direct_state_changed"), &RigidBody3D::_direct_state_changed); - ClassDB::bind_method(D_METHOD("set_axis_lock", "axis", "lock"), &RigidBody3D::set_axis_lock); ClassDB::bind_method(D_METHOD("get_axis_lock", "axis"), &RigidBody3D::get_axis_lock); @@ -832,7 +765,7 @@ void RigidBody3D::_bind_methods() { RigidBody3D::RigidBody3D() : PhysicsBody3D(PhysicsServer3D::BODY_MODE_RIGID) { - PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), this, "_direct_state_changed"); + PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &RigidBody3D::_direct_state_changed)); } RigidBody3D::~RigidBody3D() { @@ -1166,8 +1099,6 @@ void KinematicBody3D::_notification(int p_what) { } void KinematicBody3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("_direct_state_changed"), &KinematicBody3D::_direct_state_changed); - ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "infinite_inertia", "exclude_raycast_shapes", "test_only"), &KinematicBody3D::_move, DEFVAL(true), DEFVAL(true), DEFVAL(false)); ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity", "up_direction", "stop_on_slope", "max_slides", "floor_max_angle", "infinite_inertia"), &KinematicBody3D::move_and_slide, DEFVAL(Vector3(0, 0, 0)), DEFVAL(false), DEFVAL(4), DEFVAL(Math::deg2rad((real_t)45.0)), DEFVAL(true)); ClassDB::bind_method(D_METHOD("move_and_slide_with_snap", "linear_velocity", "snap", "up_direction", "stop_on_slope", "max_slides", "floor_max_angle", "infinite_inertia"), &KinematicBody3D::move_and_slide_with_snap, DEFVAL(Vector3(0, 0, 0)), DEFVAL(false), DEFVAL(4), DEFVAL(Math::deg2rad((real_t)45.0)), DEFVAL(true)); @@ -1200,6 +1131,7 @@ void KinematicBody3D::_bind_methods() { void KinematicBody3D::_direct_state_changed(Object *p_state) { #ifdef DEBUG_ENABLED PhysicsDirectBodyState3D *state = Object::cast_to<PhysicsDirectBodyState3D>(p_state); + ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState3D object as argument"); #else PhysicsDirectBodyState3D *state = (PhysicsDirectBodyState3D *)p_state; //trust it #endif @@ -1211,7 +1143,7 @@ void KinematicBody3D::_direct_state_changed(Object *p_state) { KinematicBody3D::KinematicBody3D() : PhysicsBody3D(PhysicsServer3D::BODY_MODE_KINEMATIC) { set_safe_margin(0.001); - PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), this, "_direct_state_changed"); + PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &KinematicBody3D::_direct_state_changed)); } KinematicBody3D::~KinematicBody3D() { @@ -2050,6 +1982,7 @@ void PhysicalBone3D::_direct_state_changed(Object *p_state) { #ifdef DEBUG_ENABLED state = Object::cast_to<PhysicsDirectBodyState3D>(p_state); + ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState3D object as argument"); #else state = (PhysicsDirectBodyState3D *)p_state; //trust it #endif @@ -2059,6 +1992,7 @@ void PhysicalBone3D::_direct_state_changed(Object *p_state) { set_ignore_transform_notification(true); set_global_transform(global_transform); set_ignore_transform_notification(false); + _on_transform_changed(); // Update skeleton if (parent_skeleton) { @@ -2072,8 +2006,6 @@ void PhysicalBone3D::_bind_methods() { ClassDB::bind_method(D_METHOD("apply_central_impulse", "impulse"), &PhysicalBone3D::apply_central_impulse); ClassDB::bind_method(D_METHOD("apply_impulse", "impulse", "position"), &PhysicalBone3D::apply_impulse, Vector3()); - ClassDB::bind_method(D_METHOD("_direct_state_changed"), &PhysicalBone3D::_direct_state_changed); - ClassDB::bind_method(D_METHOD("set_joint_type", "joint_type"), &PhysicalBone3D::set_joint_type); ClassDB::bind_method(D_METHOD("get_joint_type"), &PhysicalBone3D::get_joint_type); @@ -2552,7 +2484,7 @@ void PhysicalBone3D::_start_physics_simulation() { PhysicsServer3D::get_singleton()->body_set_mode(get_rid(), PhysicsServer3D::BODY_MODE_RIGID); PhysicsServer3D::get_singleton()->body_set_collision_layer(get_rid(), get_collision_layer()); PhysicsServer3D::get_singleton()->body_set_collision_mask(get_rid(), get_collision_mask()); - PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), this, "_direct_state_changed"); + PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &PhysicalBone3D::_direct_state_changed)); set_as_top_level(true); _internal_simulate_physics = true; } @@ -2571,7 +2503,7 @@ void PhysicalBone3D::_stop_physics_simulation() { PhysicsServer3D::get_singleton()->body_set_collision_mask(get_rid(), 0); } if (_internal_simulate_physics) { - PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), nullptr, ""); + PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), Callable()); parent_skeleton->set_bone_global_pose_override(bone_id, Transform(), 0.0, false); set_as_top_level(false); _internal_simulate_physics = false; diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h index 16d70226b1..cf18b8ca82 100644 --- a/scene/3d/physics_body_3d.h +++ b/scene/3d/physics_body_3d.h @@ -40,9 +40,6 @@ class PhysicsBody3D : public CollisionObject3D { GDCLASS(PhysicsBody3D, CollisionObject3D); - uint32_t collision_layer; - uint32_t collision_mask; - protected: static void _bind_methods(); PhysicsBody3D(PhysicsServer3D::BodyMode p_mode); @@ -52,18 +49,6 @@ public: virtual Vector3 get_angular_velocity() const; virtual real_t get_inverse_mass() const; - void set_collision_layer(uint32_t p_layer); - uint32_t get_collision_layer() const; - - void set_collision_mask(uint32_t p_mask); - uint32_t get_collision_mask() const; - - void set_collision_layer_bit(int p_bit, bool p_value); - bool get_collision_layer_bit(int p_bit) const; - - void set_collision_mask_bit(int p_bit, bool p_value); - bool get_collision_mask_bit(int p_bit) const; - TypedArray<PhysicsBody3D> get_collision_exceptions(); void add_collision_exception_with(Node *p_node); //must be physicsbody void remove_collision_exception_with(Node *p_node); @@ -240,7 +225,7 @@ public: void apply_impulse(const Vector3 &p_impulse, const Vector3 &p_position = Vector3()); void apply_torque_impulse(const Vector3 &p_impulse); - virtual String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; RigidBody3D(); ~RigidBody3D(); diff --git a/scene/3d/physics_joint_3d.cpp b/scene/3d/physics_joint_3d.cpp index de9c75621b..3d58d1c10e 100644 --- a/scene/3d/physics_joint_3d.cpp +++ b/scene/3d/physics_joint_3d.cpp @@ -65,6 +65,7 @@ void Joint3D::_update_joint(bool p_only_free) { if (p_only_free || !is_inside_tree()) { PhysicsServer3D::get_singleton()->joint_clear(joint); warning = String(); + update_configuration_warnings(); return; } @@ -75,43 +76,26 @@ void Joint3D::_update_joint(bool p_only_free) { PhysicsBody3D *body_b = Object::cast_to<PhysicsBody3D>(node_b); if (node_a && !body_a && node_b && !body_b) { - PhysicsServer3D::get_singleton()->joint_clear(joint); warning = TTR("Node A and Node B must be PhysicsBody3Ds"); - update_configuration_warning(); - return; - } - - if (node_a && !body_a) { - PhysicsServer3D::get_singleton()->joint_clear(joint); + } else if (node_a && !body_a) { warning = TTR("Node A must be a PhysicsBody3D"); - update_configuration_warning(); - return; - } - - if (node_b && !body_b) { - PhysicsServer3D::get_singleton()->joint_clear(joint); + } else if (node_b && !body_b) { warning = TTR("Node B must be a PhysicsBody3D"); - update_configuration_warning(); - return; - } - - if (!body_a && !body_b) { - PhysicsServer3D::get_singleton()->joint_clear(joint); + } else if (!body_a && !body_b) { warning = TTR("Joint is not connected to any PhysicsBody3Ds"); - update_configuration_warning(); - return; + } else if (body_a == body_b) { + warning = TTR("Node A and Node B must be different PhysicsBody3Ds"); + } else { + warning = String(); } - if (body_a == body_b) { + update_configuration_warnings(); + + if (!warning.is_empty()) { PhysicsServer3D::get_singleton()->joint_clear(joint); - warning = TTR("Node A and Node B must be different PhysicsBody3Ds"); - update_configuration_warning(); return; } - warning = String(); - update_configuration_warning(); - configured = true; if (body_a) { @@ -206,17 +190,14 @@ bool Joint3D::get_exclude_nodes_from_collision() const { return exclude_from_collision; } -String Joint3D::get_configuration_warning() const { - String node_warning = Node3D::get_configuration_warning(); +TypedArray<String> Joint3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node3D::get_configuration_warnings(); if (!warning.is_empty()) { - if (!node_warning.is_empty()) { - node_warning += "\n\n"; - } - node_warning += warning; + warnings.push_back(warning); } - return node_warning; + return warnings; } void Joint3D::_bind_methods() { diff --git a/scene/3d/physics_joint_3d.h b/scene/3d/physics_joint_3d.h index f624ba602b..3e0ea38a5c 100644 --- a/scene/3d/physics_joint_3d.h +++ b/scene/3d/physics_joint_3d.h @@ -63,7 +63,7 @@ protected: _FORCE_INLINE_ bool is_configured() const { return configured; } public: - virtual String get_configuration_warning() const override; + virtual TypedArray<String> get_configuration_warnings() const override; void set_node_a(const NodePath &p_node_a); NodePath get_node_a() const; diff --git a/scene/3d/ray_cast_3d.cpp b/scene/3d/ray_cast_3d.cpp index 465de2cb47..475f8c07fd 100644 --- a/scene/3d/ray_cast_3d.cpp +++ b/scene/3d/ray_cast_3d.cpp @@ -61,6 +61,7 @@ uint32_t RayCast3D::get_collision_mask() const { } void RayCast3D::set_collision_mask_bit(int p_bit, bool p_value) { + ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision mask bit must be between 0 and 31 inclusive."); uint32_t mask = get_collision_mask(); if (p_value) { mask |= 1 << p_bit; @@ -71,6 +72,7 @@ void RayCast3D::set_collision_mask_bit(int p_bit, bool p_value) { } bool RayCast3D::get_collision_mask_bit(int p_bit) const { + ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision mask bit must be between 0 and 31 inclusive."); return get_collision_mask() & (1 << p_bit); } @@ -426,12 +428,12 @@ void RayCast3D::_update_debug_shape_material(bool p_check_collision) { color = get_tree()->get_debug_collisions_color(); } - if (p_check_collision) { + if (p_check_collision && collided) { if ((color.get_h() < 0.055 || color.get_h() > 0.945) && color.get_s() > 0.5 && color.get_v() > 0.5) { - // If base color is already quite reddish, hightlight collision with green color + // If base color is already quite reddish, highlight collision with green color color = Color(0.0, 1.0, 0.0, color.a); } else { - // Else, hightlight collision with red color + // Else, highlight collision with red color color = Color(1.0, 0, 0, color.a); } } diff --git a/scene/3d/remote_transform_3d.cpp b/scene/3d/remote_transform_3d.cpp index 83ac813c53..29a407905b 100644 --- a/scene/3d/remote_transform_3d.cpp +++ b/scene/3d/remote_transform_3d.cpp @@ -133,7 +133,7 @@ void RemoteTransform3D::set_remote_node(const NodePath &p_remote_node) { _update_remote(); } - update_configuration_warning(); + update_configuration_warnings(); } NodePath RemoteTransform3D::get_remote_node() const { @@ -179,17 +179,14 @@ void RemoteTransform3D::force_update_cache() { _update_cache(); } -String RemoteTransform3D::get_configuration_warning() const { - String warning = Node3D::get_configuration_warning(); +TypedArray<String> RemoteTransform3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); if (!has_node(remote_node) || !Object::cast_to<Node3D>(get_node(remote_node))) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("The \"Remote Path\" property must point to a valid Node3D or Node3D-derived node to work."); + warnings.push_back(TTR("The \"Remote Path\" property must point to a valid Node3D or Node3D-derived node to work.")); } - return warning; + return warnings; } void RemoteTransform3D::_bind_methods() { diff --git a/scene/3d/remote_transform_3d.h b/scene/3d/remote_transform_3d.h index 21005d92d1..321bd3b51e 100644 --- a/scene/3d/remote_transform_3d.h +++ b/scene/3d/remote_transform_3d.h @@ -70,7 +70,7 @@ public: void force_update_cache(); - virtual String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; RemoteTransform3D(); }; diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp index be62fe801f..59233708f6 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -237,53 +237,57 @@ void Skeleton3D::_notification(int p_what) { for (int i = 0; i < len; i++) { Bone &b = bonesptr[order[i]]; - if (b.global_pose_override_amount >= 0.999) { - b.pose_global = b.global_pose_override; - } else { - if (b.disable_rest) { - if (b.enabled) { - Transform pose = b.pose; - if (b.custom_pose_enable) { - pose = b.custom_pose * pose; - } - if (b.parent >= 0) { - b.pose_global = bonesptr[b.parent].pose_global * pose; - } else { - b.pose_global = pose; - } + if (b.disable_rest) { + if (b.enabled) { + Transform pose = b.pose; + if (b.custom_pose_enable) { + pose = b.custom_pose * pose; + } + if (b.parent >= 0) { + b.pose_global = bonesptr[b.parent].pose_global * pose; + b.pose_global_no_override = bonesptr[b.parent].pose_global * pose; } else { - if (b.parent >= 0) { - b.pose_global = bonesptr[b.parent].pose_global; - } else { - b.pose_global = Transform(); - } + b.pose_global = pose; + b.pose_global_no_override = pose; } - } else { - if (b.enabled) { - Transform pose = b.pose; - if (b.custom_pose_enable) { - pose = b.custom_pose * pose; - } - if (b.parent >= 0) { - b.pose_global = bonesptr[b.parent].pose_global * (b.rest * pose); - } else { - b.pose_global = b.rest * pose; - } + if (b.parent >= 0) { + b.pose_global = bonesptr[b.parent].pose_global; + b.pose_global_no_override = bonesptr[b.parent].pose_global; } else { - if (b.parent >= 0) { - b.pose_global = bonesptr[b.parent].pose_global * b.rest; - } else { - b.pose_global = b.rest; - } + b.pose_global = Transform(); + b.pose_global_no_override = Transform(); } } - if (b.global_pose_override_amount >= CMP_EPSILON) { - b.pose_global = b.pose_global.interpolate_with(b.global_pose_override, b.global_pose_override_amount); + } else { + if (b.enabled) { + Transform pose = b.pose; + if (b.custom_pose_enable) { + pose = b.custom_pose * pose; + } + if (b.parent >= 0) { + b.pose_global = bonesptr[b.parent].pose_global * (b.rest * pose); + b.pose_global_no_override = bonesptr[b.parent].pose_global * (b.rest * pose); + } else { + b.pose_global = b.rest * pose; + b.pose_global_no_override = b.rest * pose; + } + } else { + if (b.parent >= 0) { + b.pose_global = bonesptr[b.parent].pose_global * b.rest; + b.pose_global_no_override = bonesptr[b.parent].pose_global * b.rest; + } else { + b.pose_global = b.rest; + b.pose_global_no_override = b.rest; + } } } + if (b.global_pose_override_amount >= CMP_EPSILON) { + b.pose_global = b.pose_global.interpolate_with(b.global_pose_override, b.global_pose_override_amount); + } + if (b.global_pose_override_reset) { b.global_pose_override_amount = 0.0; } @@ -387,6 +391,7 @@ void Skeleton3D::_notification(int p_what) { void Skeleton3D::clear_bones_global_pose_override() { for (int i = 0; i < bones.size(); i += 1) { bones.write[i].global_pose_override_amount = 0; + bones.write[i].global_pose_override_reset = true; } _make_dirty(); } @@ -407,6 +412,14 @@ Transform Skeleton3D::get_bone_global_pose(int p_bone) const { return bones[p_bone].pose_global; } +Transform Skeleton3D::get_bone_global_pose_no_override(int p_bone) const { + ERR_FAIL_INDEX_V(p_bone, bones.size(), Transform()); + if (dirty) { + const_cast<Skeleton3D *>(this)->notification(NOTIFICATION_UPDATE_SKELETON); + } + return bones[p_bone].pose_global_no_override; +} + // skeleton creation api void Skeleton3D::add_bone(const String &p_name) { ERR_FAIL_COND(p_name == "" || p_name.find(":") != -1 || p_name.find("/") != -1); @@ -439,6 +452,17 @@ String Skeleton3D::get_bone_name(int p_bone) const { return bones[p_bone].name; } +void Skeleton3D::set_bone_name(int p_bone, const String &p_name) { + ERR_FAIL_INDEX(p_bone, bones.size()); + + for (int i = 0; i < bones.size(); i++) { + if (i != p_bone) { + ERR_FAIL_COND(bones[i].name == p_name); + } + } + + bones.write[p_bone].name = p_name; +} bool Skeleton3D::is_bone_parent_of(int p_bone, int p_parent_bone_id) const { int parent_of_bone = get_bone_parent(p_bone); @@ -869,6 +893,7 @@ void Skeleton3D::_bind_methods() { ClassDB::bind_method(D_METHOD("add_bone", "name"), &Skeleton3D::add_bone); ClassDB::bind_method(D_METHOD("find_bone", "name"), &Skeleton3D::find_bone); ClassDB::bind_method(D_METHOD("get_bone_name", "bone_idx"), &Skeleton3D::get_bone_name); + ClassDB::bind_method(D_METHOD("set_bone_name", "bone_idx", "name"), &Skeleton3D::set_bone_name); ClassDB::bind_method(D_METHOD("get_bone_parent", "bone_idx"), &Skeleton3D::get_bone_parent); ClassDB::bind_method(D_METHOD("set_bone_parent", "bone_idx", "parent_idx"), &Skeleton3D::set_bone_parent); @@ -899,6 +924,7 @@ void Skeleton3D::_bind_methods() { ClassDB::bind_method(D_METHOD("clear_bones_global_pose_override"), &Skeleton3D::clear_bones_global_pose_override); ClassDB::bind_method(D_METHOD("set_bone_global_pose_override", "bone_idx", "pose", "amount", "persistent"), &Skeleton3D::set_bone_global_pose_override, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_bone_global_pose", "bone_idx"), &Skeleton3D::get_bone_global_pose); + ClassDB::bind_method(D_METHOD("get_bone_global_pose_no_override", "bone_idx"), &Skeleton3D::get_bone_global_pose_no_override); ClassDB::bind_method(D_METHOD("get_bone_custom_pose", "bone_idx"), &Skeleton3D::get_bone_custom_pose); ClassDB::bind_method(D_METHOD("set_bone_custom_pose", "bone_idx", "custom_pose"), &Skeleton3D::set_bone_custom_pose); diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h index 9772bfcc95..508cd7c329 100644 --- a/scene/3d/skeleton_3d.h +++ b/scene/3d/skeleton_3d.h @@ -83,6 +83,7 @@ private: Transform pose; Transform pose_global; + Transform pose_global_no_override; bool custom_pose_enable = false; Transform custom_pose; @@ -143,6 +144,7 @@ public: void add_bone(const String &p_name); int find_bone(const String &p_name) const; String get_bone_name(int p_bone) const; + void set_bone_name(int p_bone, const String &p_name); bool is_bone_parent_of(int p_bone_id, int p_parent_bone_id) const; @@ -159,6 +161,7 @@ public: void set_bone_rest(int p_bone, const Transform &p_rest); Transform get_bone_rest(int p_bone) const; Transform get_bone_global_pose(int p_bone) const; + Transform get_bone_global_pose_no_override(int p_bone) const; void clear_bones_global_pose_override(); void set_bone_global_pose_override(int p_bone, const Transform &p_pose, float p_amount, bool p_persistent = false); diff --git a/scene/3d/skeleton_ik_3d.cpp b/scene/3d/skeleton_ik_3d.cpp index cb486a12ae..bd1c202205 100644 --- a/scene/3d/skeleton_ik_3d.cpp +++ b/scene/3d/skeleton_ik_3d.cpp @@ -63,7 +63,6 @@ bool FabrikInverseKinematic::build_chain(Task *p_task, bool p_force_simple_chain chain.chain_root.bone = p_task->root_bone; chain.chain_root.initial_transform = p_task->skeleton->get_bone_global_pose(chain.chain_root.bone); chain.chain_root.current_pos = chain.chain_root.initial_transform.origin; - chain.chain_root.pb = p_task->skeleton->get_physical_bone(chain.chain_root.bone); chain.middle_chain_item = nullptr; // Holds all IDs that are composing a single chain in reverse order @@ -96,8 +95,6 @@ bool FabrikInverseKinematic::build_chain(Task *p_task, bool p_force_simple_chain if (!child_ci) { child_ci = sub_chain->add_child(chain_ids[i]); - child_ci->pb = p_task->skeleton->get_physical_bone(child_ci->bone); - child_ci->initial_transform = p_task->skeleton->get_bone_global_pose(child_ci->bone); child_ci->current_pos = child_ci->initial_transform.origin; @@ -123,7 +120,7 @@ bool FabrikInverseKinematic::build_chain(Task *p_task, bool p_force_simple_chain if (p_force_simple_chain) { // NOTE: - // This is an "hack" that force to create only one tip per chain since the solver of multi tip (end effector) + // This is a "hack" that force to create only one tip per chain since the solver of multi tip (end effector) // is not yet created. // Remove this code when this is done break; @@ -132,20 +129,6 @@ bool FabrikInverseKinematic::build_chain(Task *p_task, bool p_force_simple_chain return true; } -void FabrikInverseKinematic::update_chain(const Skeleton3D *p_sk, ChainItem *p_chain_item) { - if (!p_chain_item) { - return; - } - - 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; - - ChainItem *items = p_chain_item->children.ptrw(); - for (int i = 0; i < p_chain_item->children.size(); i += 1) { - update_chain(p_sk, items + i); - } -} - void FabrikInverseKinematic::solve_simple(Task *p_task, bool p_solve_magnet) { real_t distance_to_goal(1e4); real_t previous_distance_to_goal(0); @@ -263,7 +246,7 @@ void FabrikInverseKinematic::make_goal(Task *p_task, const Transform &p_inverse_ p_task->end_effectors.write[0].goal_transform = p_inverse_transf * p_task->goal_global_transform; } else { // End effector in local transform - const Transform end_effector_pose(p_task->skeleton->get_bone_global_pose(p_task->end_effectors.write[0].tip_bone)); + const Transform end_effector_pose(p_task->skeleton->get_bone_global_pose_no_override(p_task->end_effectors[0].tip_bone)); // Update the end_effector (local transform) by blending with current pose p_task->end_effectors.write[0].goal_transform = end_effector_pose.interpolate_with(p_inverse_transf * p_task->goal_global_transform, blending_delta); @@ -272,22 +255,25 @@ void FabrikInverseKinematic::make_goal(Task *p_task, const Transform &p_inverse_ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool override_tip_basis, bool p_use_magnet, const Vector3 &p_magnet_position) { if (blending_delta <= 0.01f) { - return; // Skip solving - } - - p_task->skeleton->set_bone_global_pose_override(p_task->chain.chain_root.bone, Transform(), 0.0, true); + // Before skipping, make sure we undo the global pose overrides + ChainItem *ci(&p_task->chain.chain_root); + while (ci) { + p_task->skeleton->set_bone_global_pose_override(ci->bone, ci->initial_transform, 0.0, false); - if (p_task->chain.middle_chain_item) { - p_task->skeleton->set_bone_global_pose_override(p_task->chain.middle_chain_item->bone, Transform(), 0.0, true); - } + if (!ci->children.is_empty()) { + ci = &ci->children.write[0]; + } else { + ci = nullptr; + } + } - for (int i = 0; i < p_task->chain.tips.size(); i += 1) { - p_task->skeleton->set_bone_global_pose_override(p_task->chain.tips[i].chain_item->bone, Transform(), 0.0, true); + return; // Skip solving } - make_goal(p_task, p_task->skeleton->get_global_transform().affine_inverse().scaled(p_task->skeleton->get_global_transform().get_basis().get_scale()), blending_delta); + // Update the initial root transform so its synced with any animation changes + _update_chain(p_task->skeleton, &p_task->chain.chain_root); - update_chain(p_task->skeleton, &p_task->chain.chain_root); + make_goal(p_task, p_task->skeleton->get_global_transform().affine_inverse(), blending_delta); if (p_use_magnet && p_task->chain.middle_chain_item) { p_task->chain.magnet_position = p_task->chain.middle_chain_item->initial_transform.origin.lerp(p_magnet_position, blending_delta); @@ -310,6 +296,7 @@ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool ove const real_t rot_angle(Math::acos(CLAMP(initial_ori.dot(ci->current_ori), -1, 1))); new_bone_pose.basis.rotate(rot_axis, rot_angle); } + } else { // Set target orientation to tip if (override_tip_basis) { @@ -319,6 +306,10 @@ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool ove } } + // IK should not affect scale, so undo any scaling + new_bone_pose.basis.orthonormalize(); + new_bone_pose.basis.scale(p_task->skeleton->get_bone_global_pose(ci->bone).basis.get_scale()); + p_task->skeleton->set_bone_global_pose_override(ci->bone, new_bone_pose, 1.0, true); if (!ci->children.is_empty()) { @@ -329,6 +320,20 @@ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool ove } } +void FabrikInverseKinematic::_update_chain(const Skeleton3D *p_sk, ChainItem *p_chain_item) { + if (!p_chain_item) { + return; + } + + p_chain_item->initial_transform = p_sk->get_bone_global_pose_no_override(p_chain_item->bone); + p_chain_item->current_pos = p_chain_item->initial_transform.origin; + + ChainItem *items = p_chain_item->children.ptrw(); + for (int i = 0; i < p_chain_item->children.size(); i += 1) { + _update_chain(p_sk, items + i); + } +} + void SkeletonIK3D::_validate_property(PropertyInfo &property) const { if (property.name == "root_bone" || property.name == "tip_bone") { if (skeleton) { @@ -524,6 +529,9 @@ void SkeletonIK3D::start(bool p_one_time) { void SkeletonIK3D::stop() { set_process_internal(false); + if (skeleton) { + skeleton->clear_bones_global_pose_override(); + } } Transform SkeletonIK3D::_get_target_transform() { diff --git a/scene/3d/skeleton_ik_3d.h b/scene/3d/skeleton_ik_3d.h index eefecf68bb..9255e18b72 100644 --- a/scene/3d/skeleton_ik_3d.h +++ b/scene/3d/skeleton_ik_3d.h @@ -52,7 +52,6 @@ class FabrikInverseKinematic { // Bone info BoneId bone = -1; - PhysicalBone3D *pb = nullptr; real_t length = 0.0; /// Positions relative to root bone @@ -107,8 +106,6 @@ private: /// Init a chain that starts from the root to tip static bool build_chain(Task *p_task, bool p_force_simple_chain = true); - static void update_chain(const Skeleton3D *p_sk, ChainItem *p_chain_item); - static void solve_simple(Task *p_task, bool p_solve_magnet); /// Special solvers that solve only chains with one end effector static void solve_simple_backwards(Chain &r_chain, bool p_solve_magnet); @@ -121,6 +118,8 @@ public: static void set_goal(Task *p_task, const Transform &p_goal); static void make_goal(Task *p_task, const Transform &p_inverse_transf, real_t blending_delta); static void solve(Task *p_task, real_t blending_delta, bool override_tip_basis, bool p_use_magnet, const Vector3 &p_magnet_position); + + static void _update_chain(const Skeleton3D *p_skeleton, ChainItem *p_chain_item); }; class SkeletonIK3D : public Node { diff --git a/scene/3d/soft_body_3d.cpp b/scene/3d/soft_body_3d.cpp index 2d8f22ab37..dc4deb0570 100644 --- a/scene/3d/soft_body_3d.cpp +++ b/scene/3d/soft_body_3d.cpp @@ -37,7 +37,6 @@ #include "scene/3d/collision_object_3d.h" #include "scene/3d/physics_body_3d.h" #include "scene/3d/skeleton_3d.h" -#include "servers/physics_server_3d.h" SoftBodyRenderingServerHandler::SoftBodyRenderingServerHandler() {} @@ -48,27 +47,28 @@ void SoftBodyRenderingServerHandler::prepare(RID p_mesh, int p_surface) { mesh = p_mesh; surface = p_surface; -#ifndef _MSC_VER -#warning Softbody is not working, needs to be redone considering that these functions no longer exist -#endif -#if 0 - const uint32_t surface_format = RS::get_singleton()->mesh_surface_get_format(mesh, surface); - const int surface_vertex_len = RS::get_singleton()->mesh_surface_get_array_len(mesh, p_surface); - const int surface_index_len = RS::get_singleton()->mesh_surface_get_array_index_len(mesh, p_surface); + + RS::SurfaceData surface_data = RS::get_singleton()->mesh_get_surface(mesh, surface); + uint32_t surface_offsets[RS::ARRAY_MAX]; + uint32_t vertex_stride; + uint32_t attrib_stride; + uint32_t skin_stride; + RS::get_singleton()->mesh_surface_make_offsets_from_format(surface_data.format, surface_data.vertex_count, surface_data.index_count, surface_offsets, vertex_stride, attrib_stride, skin_stride); - buffer = RS::get_singleton()->mesh_surface_get_array(mesh, surface); - stride = RS::get_singleton()->mesh_surface_make_offsets_from_format(surface_format, surface_vertex_len, surface_index_len, surface_offsets); + buffer = surface_data.vertex_data; + stride = vertex_stride; offset_vertices = surface_offsets[RS::ARRAY_VERTEX]; offset_normal = surface_offsets[RS::ARRAY_NORMAL]; -#endif } void SoftBodyRenderingServerHandler::clear() { - if (mesh.is_valid()) { - buffer.resize(0); - } + buffer.resize(0); + stride = 0; + offset_vertices = 0; + offset_normal = 0; + surface = 0; mesh = RID(); } @@ -77,7 +77,7 @@ void SoftBodyRenderingServerHandler::open() { } void SoftBodyRenderingServerHandler::close() { - //write_buffer.release(); + write_buffer = nullptr; } void SoftBodyRenderingServerHandler::commit_changes() { @@ -85,11 +85,11 @@ void SoftBodyRenderingServerHandler::commit_changes() { } void SoftBodyRenderingServerHandler::set_vertex(int p_vertex_id, const void *p_vector3) { - copymem(&write_buffer[p_vertex_id * stride + offset_vertices], p_vector3, sizeof(float) * 3); + memcpy(&write_buffer[p_vertex_id * stride + offset_vertices], p_vector3, sizeof(float) * 3); } void SoftBodyRenderingServerHandler::set_normal(int p_vertex_id, const void *p_vector3) { - copymem(&write_buffer[p_vertex_id * stride + offset_normal], p_vector3, sizeof(float) * 3); + memcpy(&write_buffer[p_vertex_id * stride + offset_normal], p_vector3, sizeof(float) * 3); } void SoftBodyRenderingServerHandler::set_aabb(const AABB &p_aabb) { @@ -249,7 +249,7 @@ void SoftBody3D::_softbody_changed() { prepare_physics_server(); _reset_points_offsets(); #ifdef TOOLS_ENABLED - update_configuration_warning(); + update_configuration_warnings(); #endif } @@ -301,7 +301,7 @@ void SoftBody3D::_notification(int p_what) { if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) { if (Engine::get_singleton()->is_editor_hint()) { - update_configuration_warning(); + update_configuration_warnings(); } } @@ -309,6 +309,8 @@ void SoftBody3D::_notification(int p_what) { } void SoftBody3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_physics_rid"), &SoftBody3D::get_physics_rid); + ClassDB::bind_method(D_METHOD("set_collision_mask", "collision_mask"), &SoftBody3D::set_collision_mask); ClassDB::bind_method(D_METHOD("get_collision_mask"), &SoftBody3D::get_collision_mask); @@ -337,18 +339,9 @@ void SoftBody3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_linear_stiffness", "linear_stiffness"), &SoftBody3D::set_linear_stiffness); ClassDB::bind_method(D_METHOD("get_linear_stiffness"), &SoftBody3D::get_linear_stiffness); - ClassDB::bind_method(D_METHOD("set_angular_stiffness", "angular_stiffness"), &SoftBody3D::set_angular_stiffness); - ClassDB::bind_method(D_METHOD("get_angular_stiffness"), &SoftBody3D::get_angular_stiffness); - - ClassDB::bind_method(D_METHOD("set_volume_stiffness", "volume_stiffness"), &SoftBody3D::set_volume_stiffness); - ClassDB::bind_method(D_METHOD("get_volume_stiffness"), &SoftBody3D::get_volume_stiffness); - ClassDB::bind_method(D_METHOD("set_pressure_coefficient", "pressure_coefficient"), &SoftBody3D::set_pressure_coefficient); ClassDB::bind_method(D_METHOD("get_pressure_coefficient"), &SoftBody3D::get_pressure_coefficient); - ClassDB::bind_method(D_METHOD("set_pose_matching_coefficient", "pose_matching_coefficient"), &SoftBody3D::set_pose_matching_coefficient); - ClassDB::bind_method(D_METHOD("get_pose_matching_coefficient"), &SoftBody3D::get_pose_matching_coefficient); - ClassDB::bind_method(D_METHOD("set_damping_coefficient", "damping_coefficient"), &SoftBody3D::set_damping_coefficient); ClassDB::bind_method(D_METHOD("get_damping_coefficient"), &SoftBody3D::get_damping_coefficient); @@ -366,37 +359,26 @@ void SoftBody3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "simulation_precision", PROPERTY_HINT_RANGE, "1,100,1"), "set_simulation_precision", "get_simulation_precision"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "total_mass", PROPERTY_HINT_RANGE, "0.01,10000,1"), "set_total_mass", "get_total_mass"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "linear_stiffness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_linear_stiffness", "get_linear_stiffness"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_stiffness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_angular_stiffness", "get_angular_stiffness"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volume_stiffness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_volume_stiffness", "get_volume_stiffness"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pressure_coefficient"), "set_pressure_coefficient", "get_pressure_coefficient"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "damping_coefficient", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_damping_coefficient", "get_damping_coefficient"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "drag_coefficient", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_coefficient", "get_drag_coefficient"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pose_matching_coefficient", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_pose_matching_coefficient", "get_pose_matching_coefficient"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ray_pickable"), "set_ray_pickable", "is_ray_pickable"); } -String SoftBody3D::get_configuration_warning() const { - String warning = MeshInstance3D::get_configuration_warning(); +TypedArray<String> SoftBody3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); if (get_mesh().is_null()) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - - warning += TTR("This body will be ignored until you set a mesh."); + warnings.push_back(TTR("This body will be ignored until you set a mesh.")); } Transform t = get_transform(); if ((ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(2).length() - 1.0) > 0.05)) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - - warning += TTR("Size changes to SoftBody3D will be overridden by the physics engine when running.\nChange the size in children collision shapes instead."); + warnings.push_back(TTR("Size changes to SoftBody3D will be overridden by the physics engine when running.\nChange the size in children collision shapes instead.")); } - return warning; + return warnings; } void SoftBody3D::_update_physics_server() { @@ -470,7 +452,7 @@ void SoftBody3D::become_mesh_owner() { mesh_owner = true; Vector<Ref<Material>> copy_materials; - copy_materials.append_array(materials); + copy_materials.append_array(surface_override_materials); ERR_FAIL_COND(!mesh->get_surface_count()); @@ -490,7 +472,7 @@ void SoftBody3D::become_mesh_owner() { set_mesh(soft_mesh); for (int i = copy_materials.size() - 1; 0 <= i; --i) { - set_surface_material(i, copy_materials[i]); + set_surface_override_material(i, copy_materials[i]); } } } @@ -514,6 +496,7 @@ uint32_t SoftBody3D::get_collision_layer() const { } void SoftBody3D::set_collision_mask_bit(int p_bit, bool p_value) { + ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision mask bit must be between 0 and 31 inclusive."); uint32_t mask = get_collision_mask(); if (p_value) { mask |= 1 << p_bit; @@ -524,10 +507,12 @@ void SoftBody3D::set_collision_mask_bit(int p_bit, bool p_value) { } bool SoftBody3D::get_collision_mask_bit(int p_bit) const { + ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision mask bit must be between 0 and 31 inclusive."); return get_collision_mask() & (1 << p_bit); } void SoftBody3D::set_collision_layer_bit(int p_bit, bool p_value) { + ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision layer bit must be between 0 and 31 inclusive."); uint32_t layer = get_collision_layer(); if (p_value) { layer |= 1 << p_bit; @@ -538,6 +523,7 @@ void SoftBody3D::set_collision_layer_bit(int p_bit, bool p_value) { } bool SoftBody3D::get_collision_layer_bit(int p_bit) const { + ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision layer bit must be between 0 and 31 inclusive."); return get_collision_layer() & (1 << p_bit); } @@ -612,34 +598,10 @@ real_t SoftBody3D::get_linear_stiffness() { return PhysicsServer3D::get_singleton()->soft_body_get_linear_stiffness(physics_rid); } -void SoftBody3D::set_angular_stiffness(real_t p_angular_stiffness) { - PhysicsServer3D::get_singleton()->soft_body_set_angular_stiffness(physics_rid, p_angular_stiffness); -} - -real_t SoftBody3D::get_angular_stiffness() { - return PhysicsServer3D::get_singleton()->soft_body_get_angular_stiffness(physics_rid); -} - -void SoftBody3D::set_volume_stiffness(real_t p_volume_stiffness) { - PhysicsServer3D::get_singleton()->soft_body_set_volume_stiffness(physics_rid, p_volume_stiffness); -} - -real_t SoftBody3D::get_volume_stiffness() { - return PhysicsServer3D::get_singleton()->soft_body_get_volume_stiffness(physics_rid); -} - real_t SoftBody3D::get_pressure_coefficient() { return PhysicsServer3D::get_singleton()->soft_body_get_pressure_coefficient(physics_rid); } -void SoftBody3D::set_pose_matching_coefficient(real_t p_pose_matching_coefficient) { - PhysicsServer3D::get_singleton()->soft_body_set_pose_matching_coefficient(physics_rid, p_pose_matching_coefficient); -} - -real_t SoftBody3D::get_pose_matching_coefficient() { - return PhysicsServer3D::get_singleton()->soft_body_get_pose_matching_coefficient(physics_rid); -} - void SoftBody3D::set_pressure_coefficient(real_t p_pressure_coefficient) { PhysicsServer3D::get_singleton()->soft_body_set_pressure_coefficient(physics_rid, p_pressure_coefficient); } @@ -768,7 +730,9 @@ void SoftBody3D::_reset_points_offsets() { PinnedPoint *w = pinned_points.ptrw(); for (int i = pinned_points.size() - 1; 0 <= i; --i) { if (!r[i].spatial_attachment) { - w[i].spatial_attachment = Object::cast_to<Node3D>(get_node(r[i].spatial_attachment_path)); + if (!r[i].spatial_attachment_path.is_empty() && has_node(r[i].spatial_attachment_path)) { + w[i].spatial_attachment = Object::cast_to<Node3D>(get_node(r[i].spatial_attachment_path)); + } } if (!r[i].spatial_attachment) { diff --git a/scene/3d/soft_body_3d.h b/scene/3d/soft_body_3d.h index 6e24a530bd..0d0d39d48f 100644 --- a/scene/3d/soft_body_3d.h +++ b/scene/3d/soft_body_3d.h @@ -32,10 +32,11 @@ #define SOFT_PHYSICS_BODY_H #include "scene/3d/mesh_instance_3d.h" +#include "servers/physics_server_3d.h" class SoftBody3D; -class SoftBodyRenderingServerHandler { +class SoftBodyRenderingServerHandler : public RenderingServerHandler { friend class SoftBody3D; RID mesh; @@ -57,9 +58,9 @@ private: void commit_changes(); public: - void set_vertex(int p_vertex_id, const void *p_vector3); - void set_normal(int p_vertex_id, const void *p_vector3); - void set_aabb(const AABB &p_aabb); + void set_vertex(int p_vertex_id, const void *p_vector3) override; + void set_normal(int p_vertex_id, const void *p_vector3) override; + void set_aabb(const AABB &p_aabb) override; }; class SoftBody3D : public MeshInstance3D { @@ -112,7 +113,7 @@ protected: void _notification(int p_what); static void _bind_methods(); - virtual String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; protected: void _update_physics_server(); @@ -122,6 +123,8 @@ public: void prepare_physics_server(); void become_mesh_owner(); + RID get_physics_rid() const { return physics_rid; } + void set_collision_mask(uint32_t p_mask); uint32_t get_collision_mask() const; @@ -149,18 +152,9 @@ public: void set_linear_stiffness(real_t p_linear_stiffness); real_t get_linear_stiffness(); - void set_angular_stiffness(real_t p_angular_stiffness); - real_t get_angular_stiffness(); - - void set_volume_stiffness(real_t p_volume_stiffness); - real_t get_volume_stiffness(); - void set_pressure_coefficient(real_t p_pressure_coefficient); real_t get_pressure_coefficient(); - void set_pose_matching_coefficient(real_t p_pose_matching_coefficient); - real_t get_pose_matching_coefficient(); - void set_damping_coefficient(real_t p_damping_coefficient); real_t get_damping_coefficient(); diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index f881181ccd..33b8b488c6 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -366,7 +366,7 @@ void Sprite3D::_draw() { } Rect2 base_rect; - if (region) { + if (region_enabled) { base_rect = region_rect; } else { base_rect = Rect2(0, 0, texture->get_width(), texture->get_height()); @@ -511,24 +511,24 @@ Ref<Texture2D> Sprite3D::get_texture() const { return texture; } -void Sprite3D::set_region(bool p_region) { - if (p_region == region) { +void Sprite3D::set_region_enabled(bool p_region_enabled) { + if (p_region_enabled == region_enabled) { return; } - region = p_region; + region_enabled = p_region_enabled; _queue_update(); notify_property_list_changed(); } -bool Sprite3D::is_region() const { - return region; +bool Sprite3D::is_region_enabled() const { + return region_enabled; } void Sprite3D::set_region_rect(const Rect2 &p_region_rect) { bool changed = region_rect != p_region_rect; region_rect = p_region_rect; - if (region && changed) { + if (region_enabled && changed) { _queue_update(); } } @@ -595,7 +595,7 @@ Rect2 Sprite3D::get_item_rect() const { Size2i s; - if (region) { + if (region_enabled) { s = region_rect.size; } else { s = texture->get_size(); @@ -625,19 +625,17 @@ void Sprite3D::_validate_property(PropertyInfo &property) const { property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS; } -#ifdef TOOLS_ENABLED - if (!region && property.name == "region_rect") { + if (!region_enabled && property.name == "region_rect") { property.usage = PROPERTY_USAGE_NOEDITOR; } -#endif } void Sprite3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_texture", "texture"), &Sprite3D::set_texture); ClassDB::bind_method(D_METHOD("get_texture"), &Sprite3D::get_texture); - ClassDB::bind_method(D_METHOD("set_region", "enabled"), &Sprite3D::set_region); - ClassDB::bind_method(D_METHOD("is_region"), &Sprite3D::is_region); + ClassDB::bind_method(D_METHOD("set_region_enabled", "enabled"), &Sprite3D::set_region_enabled); + ClassDB::bind_method(D_METHOD("is_region_enabled"), &Sprite3D::is_region_enabled); ClassDB::bind_method(D_METHOD("set_region_rect", "rect"), &Sprite3D::set_region_rect); ClassDB::bind_method(D_METHOD("get_region_rect"), &Sprite3D::get_region_rect); @@ -661,14 +659,14 @@ void Sprite3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "frame"), "set_frame", "get_frame"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "frame_coords", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_frame_coords", "get_frame_coords"); ADD_GROUP("Region", "region_"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "region_enabled"), "set_region", "is_region"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "region_enabled"), "set_region_enabled", "is_region_enabled"); ADD_PROPERTY(PropertyInfo(Variant::RECT2, "region_rect"), "set_region_rect", "get_region_rect"); ADD_SIGNAL(MethodInfo("frame_changed")); } Sprite3D::Sprite3D() { - region = false; + region_enabled = false; frame = 0; vframes = 1; hframes = 1; @@ -694,7 +692,7 @@ void AnimatedSprite3D::_draw() { Ref<Texture2D> texture = frames->get_frame(animation, frame); if (!texture.is_valid()) { - return; //no texuture no life + return; //no texture no life } Vector2 tsize = texture->get_size(); if (tsize.x == 0 || tsize.y == 0) { @@ -930,7 +928,7 @@ void AnimatedSprite3D::set_sprite_frames(const Ref<SpriteFrames> &p_frames) { notify_property_list_changed(); _reset_timeout(); _queue_update(); - update_configuration_warning(); + update_configuration_warnings(); } Ref<SpriteFrames> AnimatedSprite3D::get_sprite_frames() const { @@ -1060,17 +1058,14 @@ StringName AnimatedSprite3D::get_animation() const { return animation; } -String AnimatedSprite3D::get_configuration_warning() const { - String warning = SpriteBase3D::get_configuration_warning(); +TypedArray<String> AnimatedSprite3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); if (frames.is_null()) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("A SpriteFrames resource must be created or set in the \"Frames\" property in order for AnimatedSprite3D to display frames."); + warnings.push_back(TTR("A SpriteFrames resource must be created or set in the \"Frames\" property in order for AnimatedSprite3D to display frames.")); } - return warning; + return warnings; } void AnimatedSprite3D::_bind_methods() { diff --git a/scene/3d/sprite_3d.h b/scene/3d/sprite_3d.h index a9ce2d8eee..5e47e66bcb 100644 --- a/scene/3d/sprite_3d.h +++ b/scene/3d/sprite_3d.h @@ -31,8 +31,8 @@ #ifndef SPRITE_3D_H #define SPRITE_3D_H -#include "scene/2d/animated_sprite_2d.h" #include "scene/3d/visual_instance_3d.h" +#include "scene/resources/sprite_frames.h" class SpriteBase3D : public GeometryInstance3D { GDCLASS(SpriteBase3D, GeometryInstance3D); @@ -107,8 +107,8 @@ public: void set_flip_v(bool p_flip); bool is_flipped_v() const; - void set_region(bool p_region); - bool is_region() const; + void set_region_enabled(bool p_region_enabled); + bool is_region_enabled() const; void set_region_rect(const Rect2 &p_region_rect); Rect2 get_region_rect() const; @@ -147,7 +147,7 @@ class Sprite3D : public SpriteBase3D { GDCLASS(Sprite3D, SpriteBase3D); Ref<Texture2D> texture; - bool region; + bool region_enabled; Rect2 region_rect; int frame; @@ -167,8 +167,8 @@ public: void set_texture(const Ref<Texture2D> &p_texture); Ref<Texture2D> get_texture() const; - void set_region(bool p_region); - bool is_region() const; + void set_region_enabled(bool p_region_enabled); + bool is_region_enabled() const; void set_region_rect(const Rect2 &p_region_rect); Rect2 get_region_rect() const; @@ -203,8 +203,8 @@ class AnimatedSprite3D : public SpriteBase3D { float timeout = 0.0; - bool hflip = 1; - bool vflip = 1; + bool hflip = true; + bool vflip = true; Color modulate; @@ -236,7 +236,7 @@ public: virtual Rect2 get_item_rect() const override; - virtual String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; AnimatedSprite3D(); }; diff --git a/scene/3d/vehicle_body_3d.cpp b/scene/3d/vehicle_body_3d.cpp index 0d25e2f21f..9493f686c4 100644 --- a/scene/3d/vehicle_body_3d.cpp +++ b/scene/3d/vehicle_body_3d.cpp @@ -102,17 +102,14 @@ void VehicleWheel3D::_notification(int p_what) { } } -String VehicleWheel3D::get_configuration_warning() const { - String warning = Node3D::get_configuration_warning(); +TypedArray<String> VehicleWheel3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); if (!Object::cast_to<VehicleBody3D>(get_parent())) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("VehicleWheel3D serves to provide a wheel system to a VehicleBody3D. Please use it as a child of a VehicleBody3D."); + warnings.push_back(TTR("VehicleWheel3D serves to provide a wheel system to a VehicleBody3D. Please use it as a child of a VehicleBody3D.")); } - return warning; + return warnings; } void VehicleWheel3D::_update(PhysicsDirectBodyState3D *s) { @@ -407,7 +404,7 @@ real_t VehicleBody3D::_ray_cast(int p_idx, PhysicsDirectBodyState3D *s) { PhysicsDirectSpaceState3D *ss = s->get_space_state(); - bool col = ss->intersect_ray(source, target, rr, exclude); + bool col = ss->intersect_ray(source, target, rr, exclude, get_collision_mask()); wheel.m_raycastInfo.m_groundObject = nullptr; @@ -563,7 +560,7 @@ void VehicleBody3D::_resolve_single_bilateral(PhysicsDirectBodyState3D *s, const b2invmass); // FIXME: rel_vel assignment here is overwritten by the following assignment. - // What seems to be intended in the next next assignment is: rel_vel = normal.dot(rel_vel); + // What seems to be intended in the next assignment is: rel_vel = normal.dot(rel_vel); // Investigate why. real_t rel_vel = jac.getRelativeVelocity( s->get_linear_velocity(), @@ -806,6 +803,7 @@ void VehicleBody3D::_direct_state_changed(Object *p_state) { RigidBody3D::_direct_state_changed(p_state); state = Object::cast_to<PhysicsDirectBodyState3D>(p_state); + ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState3D object as argument"); real_t step = state->get_step(); @@ -925,7 +923,7 @@ void VehicleBody3D::_bind_methods() { VehicleBody3D::VehicleBody3D() { exclude.insert(get_rid()); - //PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), this, "_direct_state_changed"); + //PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &VehicleBody3D::_direct_state_changed)); set_mass(40); } diff --git a/scene/3d/vehicle_body_3d.h b/scene/3d/vehicle_body_3d.h index 860fa7e3b7..646071a363 100644 --- a/scene/3d/vehicle_body_3d.h +++ b/scene/3d/vehicle_body_3d.h @@ -145,7 +145,7 @@ public: void set_steering(real_t p_steering); real_t get_steering() const; - String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; VehicleWheel3D(); }; diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp index 394c67e873..d81b09b86c 100644 --- a/scene/3d/visual_instance_3d.cpp +++ b/scene/3d/visual_instance_3d.cpp @@ -338,6 +338,15 @@ GeometryInstance3D::GIMode GeometryInstance3D::get_gi_mode() const { return gi_mode; } +void GeometryInstance3D::set_ignore_occlusion_culling(bool p_enabled) { + ignore_occlusion_culling = p_enabled; + RS::get_singleton()->instance_geometry_set_flag(get_instance(), RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, ignore_occlusion_culling); +} + +bool GeometryInstance3D::is_ignoring_occlusion_culling() { + return ignore_occlusion_culling; +} + void GeometryInstance3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_material_override", "material"), &GeometryInstance3D::set_material_override); ClassDB::bind_method(D_METHOD("get_material_override"), &GeometryInstance3D::get_material_override); @@ -345,21 +354,24 @@ void GeometryInstance3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_cast_shadows_setting", "shadow_casting_setting"), &GeometryInstance3D::set_cast_shadows_setting); ClassDB::bind_method(D_METHOD("get_cast_shadows_setting"), &GeometryInstance3D::get_cast_shadows_setting); + ClassDB::bind_method(D_METHOD("set_lod_bias", "bias"), &GeometryInstance3D::set_lod_bias); + ClassDB::bind_method(D_METHOD("get_lod_bias"), &GeometryInstance3D::get_lod_bias); + ClassDB::bind_method(D_METHOD("set_lod_max_hysteresis", "mode"), &GeometryInstance3D::set_lod_max_hysteresis); ClassDB::bind_method(D_METHOD("get_lod_max_hysteresis"), &GeometryInstance3D::get_lod_max_hysteresis); ClassDB::bind_method(D_METHOD("set_lod_max_distance", "mode"), &GeometryInstance3D::set_lod_max_distance); ClassDB::bind_method(D_METHOD("get_lod_max_distance"), &GeometryInstance3D::get_lod_max_distance); - ClassDB::bind_method(D_METHOD("set_shader_instance_uniform", "uniform", "value"), &GeometryInstance3D::set_shader_instance_uniform); - ClassDB::bind_method(D_METHOD("get_shader_instance_uniform", "uniform"), &GeometryInstance3D::get_shader_instance_uniform); - ClassDB::bind_method(D_METHOD("set_lod_min_hysteresis", "mode"), &GeometryInstance3D::set_lod_min_hysteresis); ClassDB::bind_method(D_METHOD("get_lod_min_hysteresis"), &GeometryInstance3D::get_lod_min_hysteresis); ClassDB::bind_method(D_METHOD("set_lod_min_distance", "mode"), &GeometryInstance3D::set_lod_min_distance); ClassDB::bind_method(D_METHOD("get_lod_min_distance"), &GeometryInstance3D::get_lod_min_distance); + ClassDB::bind_method(D_METHOD("set_shader_instance_uniform", "uniform", "value"), &GeometryInstance3D::set_shader_instance_uniform); + ClassDB::bind_method(D_METHOD("get_shader_instance_uniform", "uniform"), &GeometryInstance3D::get_shader_instance_uniform); + ClassDB::bind_method(D_METHOD("set_extra_cull_margin", "margin"), &GeometryInstance3D::set_extra_cull_margin); ClassDB::bind_method(D_METHOD("get_extra_cull_margin"), &GeometryInstance3D::get_extra_cull_margin); @@ -369,8 +381,8 @@ void GeometryInstance3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_gi_mode", "mode"), &GeometryInstance3D::set_gi_mode); ClassDB::bind_method(D_METHOD("get_gi_mode"), &GeometryInstance3D::get_gi_mode); - ClassDB::bind_method(D_METHOD("set_lod_bias", "bias"), &GeometryInstance3D::set_lod_bias); - ClassDB::bind_method(D_METHOD("get_lod_bias"), &GeometryInstance3D::get_lod_bias); + ClassDB::bind_method(D_METHOD("set_ignore_occlusion_culling", "ignore_culling"), &GeometryInstance3D::set_ignore_occlusion_culling); + ClassDB::bind_method(D_METHOD("is_ignoring_occlusion_culling"), &GeometryInstance3D::is_ignoring_occlusion_culling); ClassDB::bind_method(D_METHOD("set_custom_aabb", "aabb"), &GeometryInstance3D::set_custom_aabb); @@ -381,6 +393,7 @@ void GeometryInstance3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "cast_shadow", PROPERTY_HINT_ENUM, "Off,On,Double-Sided,Shadows Only"), "set_cast_shadows_setting", "get_cast_shadows_setting"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "extra_cull_margin", PROPERTY_HINT_RANGE, "0,16384,0.01"), "set_extra_cull_margin", "get_extra_cull_margin"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lod_bias", PROPERTY_HINT_RANGE, "0.001,128,0.001"), "set_lod_bias", "get_lod_bias"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_occlusion_culling"), "set_ignore_occlusion_culling", "is_ignoring_occlusion_culling"); ADD_GROUP("Global Illumination", "gi_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_mode", PROPERTY_HINT_ENUM, "Disabled,Baked,Dynamic"), "set_gi_mode", "get_gi_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_lightmap_scale", PROPERTY_HINT_ENUM, "1x,2x,4x,8x"), "set_lightmap_scale", "get_lightmap_scale"); diff --git a/scene/3d/visual_instance_3d.h b/scene/3d/visual_instance_3d.h index 7fed8095ef..68d29ef81e 100644 --- a/scene/3d/visual_instance_3d.h +++ b/scene/3d/visual_instance_3d.h @@ -120,6 +120,7 @@ private: float extra_cull_margin = 0.0; LightmapScale lightmap_scale = LIGHTMAP_SCALE_1X; GIMode gi_mode = GI_MODE_DISABLED; + bool ignore_occlusion_culling = false; const StringName *_instance_uniform_get_remap(const StringName p_name) const; @@ -167,6 +168,9 @@ public: void set_custom_aabb(AABB aabb); + void set_ignore_occlusion_culling(bool p_enabled); + bool is_ignoring_occlusion_culling(); + GeometryInstance3D(); }; diff --git a/scene/3d/voxelizer.cpp b/scene/3d/voxelizer.cpp index 17c8596e8f..1b9ce0201f 100644 --- a/scene/3d/voxelizer.cpp +++ b/scene/3d/voxelizer.cpp @@ -151,7 +151,7 @@ void Voxelizer::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, co Vector2 uv; Vector3 lnormal; get_uv_and_normal(intersection, p_vtx, p_uv, p_normal, uv, lnormal); - if (lnormal == Vector3()) { //just in case normal as nor provided + if (lnormal == Vector3()) { //just in case normal is not provided lnormal = normal; } @@ -183,7 +183,7 @@ void Voxelizer::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, co Vector3 lnormal; Vector2 uv; get_uv_and_normal(inters, p_vtx, p_uv, p_normal, uv, normal); - if (lnormal == Vector3()) { //just in case normal as nor provided + if (lnormal == Vector3()) { //just in case normal is not provided lnormal = normal; } @@ -344,7 +344,7 @@ Voxelizer::MaterialCache Voxelizer::_get_material_cache(Ref<Material> p_material Ref<Image> img_albedo; if (albedo_tex.is_valid()) { - img_albedo = albedo_tex->get_data(); + img_albedo = albedo_tex->get_image(); mc.albedo = _get_bake_texture(img_albedo, mat->get_albedo(), Color(0, 0, 0)); // albedo texture, color is multiplicative } else { mc.albedo = _get_bake_texture(img_albedo, Color(1, 1, 1), mat->get_albedo()); // no albedo texture, color is additive @@ -358,7 +358,7 @@ Voxelizer::MaterialCache Voxelizer::_get_material_cache(Ref<Material> p_material Ref<Image> img_emission; if (emission_tex.is_valid()) { - img_emission = emission_tex->get_data(); + img_emission = emission_tex->get_image(); } if (mat->get_emission_operator() == StandardMaterial3D::EMISSION_OP_ADD) { diff --git a/scene/3d/world_environment.cpp b/scene/3d/world_environment.cpp index 214ffd6bd5..829ecc5ec2 100644 --- a/scene/3d/world_environment.cpp +++ b/scene/3d/world_environment.cpp @@ -65,7 +65,7 @@ void WorldEnvironment::_update_current_environment() { } else { get_viewport()->find_world_3d()->set_environment(Ref<Environment>()); } - get_tree()->call_group("_world_environment_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warning"); + get_tree()->call_group("_world_environment_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warnings"); } void WorldEnvironment::_update_current_camera_effects() { @@ -76,7 +76,7 @@ void WorldEnvironment::_update_current_camera_effects() { get_viewport()->find_world_3d()->set_camera_effects(Ref<CameraEffects>()); } - get_tree()->call_group("_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warning"); + get_tree()->call_group("_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warnings"); } void WorldEnvironment::set_environment(const Ref<Environment> &p_environment) { @@ -96,7 +96,7 @@ void WorldEnvironment::set_environment(const Ref<Environment> &p_environment) { if (is_inside_tree()) { _update_current_environment(); } else { - update_configuration_warning(); + update_configuration_warnings(); } } @@ -121,7 +121,7 @@ void WorldEnvironment::set_camera_effects(const Ref<CameraEffects> &p_camera_eff if (is_inside_tree()) { _update_current_camera_effects(); } else { - update_configuration_warning(); + update_configuration_warnings(); } } @@ -129,35 +129,26 @@ Ref<CameraEffects> WorldEnvironment::get_camera_effects() const { return camera_effects; } -String WorldEnvironment::get_configuration_warning() const { - String warning = Node::get_configuration_warning(); +TypedArray<String> WorldEnvironment::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); if (!environment.is_valid()) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("WorldEnvironment requires its \"Environment\" property to contain an Environment to have a visible effect."); + warnings.push_back(TTR("WorldEnvironment requires its \"Environment\" property to contain an Environment to have a visible effect.")); } if (!is_inside_tree()) { - return warning; + return warnings; } if (environment.is_valid() && get_viewport()->find_world_3d()->get_environment() != environment) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("Only the first Environment has an effect in a scene (or set of instantiated scenes)."); + warnings.push_back(("Only the first Environment has an effect in a scene (or set of instantiated scenes).")); } if (camera_effects.is_valid() && get_viewport()->find_world_3d()->get_camera_effects() != camera_effects) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("Only the first CameraEffects has an effect in a scene (or set of instantiated scenes)."); + warnings.push_back(TTR("Only one WorldEnvironment is allowed per scene (or set of instanced scenes).")); } - return warning; + return warnings; } void WorldEnvironment::_bind_methods() { diff --git a/scene/3d/world_environment.h b/scene/3d/world_environment.h index e3f28d6d6b..9e85982381 100644 --- a/scene/3d/world_environment.h +++ b/scene/3d/world_environment.h @@ -55,7 +55,7 @@ public: void set_camera_effects(const Ref<CameraEffects> &p_camera_effects); Ref<CameraEffects> get_camera_effects() const; - String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; WorldEnvironment(); }; diff --git a/scene/3d/xr_nodes.cpp b/scene/3d/xr_nodes.cpp index 7fed34c7c6..b5037f9be7 100644 --- a/scene/3d/xr_nodes.cpp +++ b/scene/3d/xr_nodes.cpp @@ -55,23 +55,18 @@ void XRCamera3D::_notification(int p_what) { }; }; -String XRCamera3D::get_configuration_warning() const { - if (!is_visible() || !is_inside_tree()) { - return String(); +TypedArray<String> XRCamera3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); + + if (is_visible() && is_inside_tree()) { + // must be child node of XROrigin3D! + XROrigin3D *origin = Object::cast_to<XROrigin3D>(get_parent()); + if (origin == nullptr) { + warnings.push_back(TTR("XRCamera3D must have an XROrigin3D node as its parent.")); + }; } - String warning = Camera3D::get_configuration_warning(); - - // must be child node of XROrigin3D! - XROrigin3D *origin = Object::cast_to<XROrigin3D>(get_parent()); - if (origin == nullptr) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("XRCamera3D must have an XROrigin3D node as its parent."); - }; - - return warning; + return warnings; }; Vector3 XRCamera3D::project_local_ray_normal(const Point2 &p_pos) const { @@ -190,8 +185,8 @@ void XRController3D::_notification(int p_what) { ERR_FAIL_NULL(xr_server); // find the tracker for our controller - XRPositionalTracker *tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, controller_id); - if (tracker == nullptr) { + Ref<XRPositionalTracker> tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, controller_id); + if (!tracker.is_valid()) { // this controller is currently turned off is_active = false; button_states = 0; @@ -265,7 +260,7 @@ void XRController3D::set_controller_id(int p_controller_id) { // We don't check any bounds here, this controller may not yet be active and just be a place holder until it is. // Note that setting this to 0 means this node is not bound to a controller yet. controller_id = p_controller_id; - update_configuration_warning(); + update_configuration_warnings(); }; int XRController3D::get_controller_id() const { @@ -277,8 +272,8 @@ String XRController3D::get_controller_name() const { XRServer *xr_server = XRServer::get_singleton(); ERR_FAIL_NULL_V(xr_server, String()); - XRPositionalTracker *tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, controller_id); - if (tracker == nullptr) { + Ref<XRPositionalTracker> tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, controller_id); + if (!tracker.is_valid()) { return String("Not connected"); }; @@ -290,8 +285,8 @@ int XRController3D::get_joystick_id() const { XRServer *xr_server = XRServer::get_singleton(); ERR_FAIL_NULL_V(xr_server, 0); - XRPositionalTracker *tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, controller_id); - if (tracker == nullptr) { + Ref<XRPositionalTracker> tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, controller_id); + if (!tracker.is_valid()) { // No tracker? no joystick id... (0 is our first joystick) return -1; }; @@ -322,8 +317,8 @@ real_t XRController3D::get_rumble() const { XRServer *xr_server = XRServer::get_singleton(); ERR_FAIL_NULL_V(xr_server, 0.0); - XRPositionalTracker *tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, controller_id); - if (tracker == nullptr) { + Ref<XRPositionalTracker> tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, controller_id); + if (!tracker.is_valid()) { return 0.0; }; @@ -335,8 +330,8 @@ void XRController3D::set_rumble(real_t p_rumble) { XRServer *xr_server = XRServer::get_singleton(); ERR_FAIL_NULL(xr_server); - XRPositionalTracker *tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, controller_id); - if (tracker != nullptr) { + Ref<XRPositionalTracker> tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, controller_id); + if (tracker.is_valid()) { tracker->set_rumble(p_rumble); }; }; @@ -354,38 +349,30 @@ XRPositionalTracker::TrackerHand XRController3D::get_tracker_hand() const { XRServer *xr_server = XRServer::get_singleton(); ERR_FAIL_NULL_V(xr_server, XRPositionalTracker::TRACKER_HAND_UNKNOWN); - XRPositionalTracker *tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, controller_id); - if (tracker == nullptr) { + Ref<XRPositionalTracker> tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, controller_id); + if (!tracker.is_valid()) { return XRPositionalTracker::TRACKER_HAND_UNKNOWN; }; return tracker->get_tracker_hand(); }; -String XRController3D::get_configuration_warning() const { - if (!is_visible() || !is_inside_tree()) { - return String(); - } - - String warning = Node3D::get_configuration_warning(); +TypedArray<String> XRController3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); - // must be child node of XROrigin! - XROrigin3D *origin = Object::cast_to<XROrigin3D>(get_parent()); - if (origin == nullptr) { - if (!warning.is_empty()) { - warning += "\n\n"; + if (is_visible() && is_inside_tree()) { + // must be child node of XROrigin! + XROrigin3D *origin = Object::cast_to<XROrigin3D>(get_parent()); + if (origin == nullptr) { + warnings.push_back(TTR("XRController3D must have an XROrigin3D node as its parent.")); } - warning += TTR("XRController3D must have an XROrigin3D node as its parent."); - }; - if (controller_id == 0) { - if (!warning.is_empty()) { - warning += "\n\n"; + if (controller_id == 0) { + warnings.push_back(TTR("The controller ID must not be 0 or this controller won't be bound to an actual controller.")); } - warning += TTR("The controller ID must not be 0 or this controller won't be bound to an actual controller."); - }; + } - return warning; + return warnings; }; //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -404,8 +391,8 @@ void XRAnchor3D::_notification(int p_what) { ERR_FAIL_NULL(xr_server); // find the tracker for our anchor - XRPositionalTracker *tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_ANCHOR, anchor_id); - if (tracker == nullptr) { + Ref<XRPositionalTracker> tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_ANCHOR, anchor_id); + if (!tracker.is_valid()) { // this anchor is currently not available is_active = false; } else { @@ -459,7 +446,7 @@ void XRAnchor3D::set_anchor_id(int p_anchor_id) { // We don't check any bounds here, this anchor may not yet be active and just be a place holder until it is. // Note that setting this to 0 means this node is not bound to an anchor yet. anchor_id = p_anchor_id; - update_configuration_warning(); + update_configuration_warnings(); }; int XRAnchor3D::get_anchor_id() const { @@ -475,8 +462,8 @@ String XRAnchor3D::get_anchor_name() const { XRServer *xr_server = XRServer::get_singleton(); ERR_FAIL_NULL_V(xr_server, String()); - XRPositionalTracker *tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_ANCHOR, anchor_id); - if (tracker == nullptr) { + Ref<XRPositionalTracker> tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_ANCHOR, anchor_id); + if (!tracker.is_valid()) { return String("Not connected"); }; @@ -487,30 +474,22 @@ bool XRAnchor3D::get_is_active() const { return is_active; }; -String XRAnchor3D::get_configuration_warning() const { - if (!is_visible() || !is_inside_tree()) { - return String(); - } - - String warning = Node3D::get_configuration_warning(); +TypedArray<String> XRAnchor3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); - // must be child node of XROrigin3D! - XROrigin3D *origin = Object::cast_to<XROrigin3D>(get_parent()); - if (origin == nullptr) { - if (!warning.is_empty()) { - warning += "\n\n"; + if (is_visible() && is_inside_tree()) { + // must be child node of XROrigin3D! + XROrigin3D *origin = Object::cast_to<XROrigin3D>(get_parent()); + if (origin == nullptr) { + warnings.push_back(TTR("XRAnchor3D must have an XROrigin3D node as its parent.")); } - warning += TTR("XRAnchor3D must have an XROrigin3D node as its parent."); - }; - if (anchor_id == 0) { - if (!warning.is_empty()) { - warning += "\n\n"; + if (anchor_id == 0) { + warnings.push_back(TTR("The anchor ID must not be 0 or this anchor won't be bound to an actual anchor.")); } - warning += TTR("The anchor ID must not be 0 or this anchor won't be bound to an actual anchor."); - }; + } - return warning; + return warnings; }; Plane XRAnchor3D::get_plane() const { @@ -528,21 +507,16 @@ Ref<Mesh> XRAnchor3D::get_mesh() const { //////////////////////////////////////////////////////////////////////////////////////////////////// -String XROrigin3D::get_configuration_warning() const { - if (!is_visible() || !is_inside_tree()) { - return String(); - } - - String warning = Node3D::get_configuration_warning(); +TypedArray<String> XROrigin3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); - if (tracked_camera == nullptr) { - if (!warning.is_empty()) { - warning += "\n\n"; + if (is_visible() && is_inside_tree()) { + if (tracked_camera == nullptr) { + warnings.push_back(TTR("XROrigin3D requires an XRCamera3D child node.")); } - warning += TTR("XROrigin3D requires an XRCamera3D child node."); } - return warning; + return warnings; }; void XROrigin3D::_bind_methods() { diff --git a/scene/3d/xr_nodes.h b/scene/3d/xr_nodes.h index 7cd6e2ac57..90079f5fe9 100644 --- a/scene/3d/xr_nodes.h +++ b/scene/3d/xr_nodes.h @@ -50,7 +50,7 @@ protected: void _notification(int p_what); public: - String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; virtual Vector3 project_local_ray_normal(const Point2 &p_pos) const override; virtual Point2 unproject_position(const Vector3 &p_pos) const override; @@ -97,7 +97,7 @@ public: Ref<Mesh> get_mesh() const; - String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; XRController3D() {} ~XRController3D() {} @@ -133,7 +133,7 @@ public: Ref<Mesh> get_mesh() const; - String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; XRAnchor3D() {} ~XRAnchor3D() {} @@ -158,7 +158,7 @@ protected: static void _bind_methods(); public: - String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; void set_tracked_camera(XRCamera3D *p_tracked_camera); void clear_tracked_camera_if(XRCamera3D *p_tracked_camera); diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp index d46f24752e..246fff6d57 100644 --- a/scene/animation/animation_node_state_machine.cpp +++ b/scene/animation/animation_node_state_machine.cpp @@ -281,7 +281,7 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_sta at = cost_map[at].prev; } - path.invert(); + path.reverse(); return true; } diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index 2c19307c52..0c1798a876 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -605,7 +605,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, float } if (p_seeked) { - //find whathever should be playing + //find whatever should be playing int idx = a->track_find_key(i, p_time); if (idx < 0) { continue; @@ -634,7 +634,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, float nc->audio_playing = true; playing_caches.insert(nc); - if (len && end_ofs > 0) { //force a end at a time + if (len && end_ofs > 0) { //force an end at a time nc->audio_len = len - start_ofs - end_ofs; } else { nc->audio_len = 0; @@ -665,7 +665,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, float nc->audio_playing = true; playing_caches.insert(nc); - if (len && end_ofs > 0) { //force a end at a time + if (len && end_ofs > 0) { //force an end at a time nc->audio_len = len - start_ofs - end_ofs; } else { nc->audio_len = 0; @@ -1343,7 +1343,7 @@ void AnimationPlayer::_stop_playing_caches() { } void AnimationPlayer::_node_removed(Node *p_node) { - clear_caches(); // nodes contained here ar being removed, clear the caches + clear_caches(); // nodes contained here are being removed, clear the caches } void AnimationPlayer::clear_caches() { diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index 26a13f33c9..2ad871ba61 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -458,7 +458,7 @@ void AnimationTree::set_tree_root(const Ref<AnimationNode> &p_root) { properties_dirty = true; - update_configuration_warning(); + update_configuration_warnings(); } Ref<AnimationNode> AnimationTree::get_tree_root() const { @@ -597,9 +597,9 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { if (path.get_subname_count() == 1 && Object::cast_to<Skeleton3D>(spatial)) { Skeleton3D *sk = Object::cast_to<Skeleton3D>(spatial); + track_xform->skeleton = sk; int bone_idx = sk->find_bone(path.get_subname(0)); if (bone_idx != -1) { - track_xform->skeleton = sk; track_xform->bone_idx = bone_idx; } } @@ -1009,7 +1009,7 @@ void AnimationTree::_process_graph(float p_delta) { TrackCacheAudio *t = static_cast<TrackCacheAudio *>(track); if (seeked) { - //find whathever should be playing + //find whatever should be playing int idx = a->track_find_key(i, time); if (idx < 0) { continue; @@ -1038,7 +1038,7 @@ void AnimationTree::_process_graph(float p_delta) { t->playing = true; playing_caches.insert(t); - if (len && end_ofs > 0) { //force a end at a time + if (len && end_ofs > 0) { //force an end at a time t->len = len - start_ofs - end_ofs; } else { t->len = 0; @@ -1069,7 +1069,7 @@ void AnimationTree::_process_graph(float p_delta) { t->playing = true; playing_caches.insert(t); - if (len && end_ofs > 0) { //force a end at a time + if (len && end_ofs > 0) { //force an end at a time t->len = len - start_ofs - end_ofs; } else { t->len = 0; @@ -1205,7 +1205,7 @@ void AnimationTree::_process_graph(float p_delta) { } else if (t->skeleton && t->bone_idx >= 0) { t->skeleton->set_bone_pose(t->bone_idx, xform); - } else { + } else if (!t->skeleton) { t->spatial->set_transform(xform); } @@ -1262,7 +1262,7 @@ void AnimationTree::_notification(int p_what) { void AnimationTree::set_animation_player(const NodePath &p_player) { animation_player = p_player; - update_configuration_warning(); + update_configuration_warnings(); } NodePath AnimationTree::get_animation_player() const { @@ -1281,38 +1281,26 @@ uint64_t AnimationTree::get_last_process_pass() const { return process_pass; } -String AnimationTree::get_configuration_warning() const { - String warning = Node::get_configuration_warning(); +TypedArray<String> AnimationTree::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); if (!root.is_valid()) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("No root AnimationNode for the graph is set."); + warnings.push_back(TTR("No root AnimationNode for the graph is set.")); } if (!has_node(animation_player)) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("Path to an AnimationPlayer node containing animations is not set."); + warnings.push_back(TTR("Path to an AnimationPlayer node containing animations is not set.")); } else { AnimationPlayer *player = Object::cast_to<AnimationPlayer>(get_node(animation_player)); if (!player) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("Path set for AnimationPlayer does not lead to an AnimationPlayer node."); + warnings.push_back(TTR("Path set for AnimationPlayer does not lead to an AnimationPlayer node.")); } else if (!player->has_node(player->get_root())) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("The AnimationPlayer root node is not a valid node."); + warnings.push_back(TTR("The AnimationPlayer root node is not a valid node.")); } } - return warning; + return warnings; } void AnimationTree::set_root_motion_track(const NodePath &p_track) { @@ -1337,6 +1325,7 @@ void AnimationTree::_tree_changed() { } void AnimationTree::_update_properties_for_node(const String &p_base_path, Ref<AnimationNode> node) { + ERR_FAIL_COND(node.is_null()); if (!property_parent_map.has(p_base_path)) { property_parent_map[p_base_path] = HashMap<StringName, StringName>(); } diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h index 1c5aec26ab..700ff1cb5b 100644 --- a/scene/animation/animation_tree.h +++ b/scene/animation/animation_tree.h @@ -300,7 +300,7 @@ public: void set_animation_player(const NodePath &p_player); NodePath get_animation_player() const; - virtual String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; bool is_state_invalid() const; String get_invalid_state_reason() const; diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp index eb35979a47..2030808724 100644 --- a/scene/animation/tween.cpp +++ b/scene/animation/tween.cpp @@ -1115,7 +1115,7 @@ real_t Tween::tell() const { // For each interpolation... for (const List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) { - // Get the data and figure out if it's position is further along than the previous ones + // Get the data and figure out if its position is further along than the previous ones const InterpolateData &data = E->get(); if (data.elapsed > pos) { // Save it if so diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp index 2853a8b9d9..1478cbf69e 100644 --- a/scene/audio/audio_stream_player.cpp +++ b/scene/audio/audio_stream_player.cpp @@ -171,7 +171,7 @@ void AudioStreamPlayer::set_stream(Ref<AudioStream> p_stream) { if (active.is_set() && stream_playback.is_valid() && !stream_paused) { //changing streams out of the blue is not a great idea, but at least - //lets try to somehow avoid a click + //let's try to somehow avoid a click AudioFrame *buffer = fadeout_buffer.ptrw(); int buffer_size = fadeout_buffer.size(); diff --git a/scene/debugger/scene_debugger.cpp b/scene/debugger/scene_debugger.cpp index 4dbe3cc1c4..a1d4adcd41 100644 --- a/scene/debugger/scene_debugger.cpp +++ b/scene/debugger/scene_debugger.cpp @@ -487,7 +487,7 @@ void LiveEditor::_send_tree() { } Array arr; - // Encoded as a flat list depth fist. + // Encoded as a flat list depth first. SceneDebuggerTree tree(scene_tree->root); tree.serialize(arr); EngineDebugger::get_singleton()->send_message("scene:scene_tree", arr); diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index db13b9b11f..ac067aa001 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -53,6 +53,8 @@ void BaseButton::_unpress_group() { } void BaseButton::_gui_input(Ref<InputEvent> p_event) { + ERR_FAIL_COND(p_event.is_null()); + if (status.disabled) { // no interaction with disabled button return; } @@ -153,6 +155,9 @@ void BaseButton::on_action_event(Ref<InputEvent> p_event) { } status.pressed = !status.pressed; _unpress_group(); + if (button_group.is_valid()) { + button_group->emit_signal("pressed", this); + } _toggled(status.pressed); _pressed(); } @@ -216,6 +221,9 @@ void BaseButton::set_pressed(bool p_pressed) { if (p_pressed) { _unpress_group(); + if (button_group.is_valid()) { + button_group->emit_signal("pressed", this); + } } _toggled(status.pressed); @@ -323,6 +331,8 @@ Ref<Shortcut> BaseButton::get_shortcut() const { } void BaseButton::_unhandled_key_input(Ref<InputEvent> p_event) { + ERR_FAIL_COND(p_event.is_null()); + if (!_is_focus_owner_in_shorcut_context()) { return; } @@ -483,6 +493,7 @@ BaseButton *ButtonGroup::get_pressed_button() { void ButtonGroup::_bind_methods() { ClassDB::bind_method(D_METHOD("get_pressed_button"), &ButtonGroup::get_pressed_button); ClassDB::bind_method(D_METHOD("get_buttons"), &ButtonGroup::_get_buttons); + ADD_SIGNAL(MethodInfo("pressed", PropertyInfo(Variant::OBJECT, "button"))); } ButtonGroup::ButtonGroup() { diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h index d54d63cc39..6c7a8f3433 100644 --- a/scene/gui/base_button.h +++ b/scene/gui/base_button.h @@ -45,7 +45,7 @@ public: }; private: - int button_mask = BUTTON_MASK_LEFT; + int button_mask = MOUSE_BUTTON_MASK_LEFT; bool toggle_mode = false; bool shortcut_in_tooltip = true; bool keep_pressed_outside = false; diff --git a/scene/gui/box_container.cpp b/scene/gui/box_container.cpp index c570438b6a..7407ad5b8f 100644 --- a/scene/gui/box_container.cpp +++ b/scene/gui/box_container.cpp @@ -313,7 +313,7 @@ BoxContainer::AlignMode BoxContainer::get_alignment() const { return align; } -void BoxContainer::add_spacer(bool p_begin) { +Control *BoxContainer::add_spacer(bool p_begin) { Control *c = memnew(Control); c->set_mouse_filter(MOUSE_FILTER_PASS); //allow spacer to pass mouse events @@ -327,6 +327,8 @@ void BoxContainer::add_spacer(bool p_begin) { if (p_begin) { move_child(c, 0); } + + return c; } BoxContainer::BoxContainer(bool p_vertical) { diff --git a/scene/gui/box_container.h b/scene/gui/box_container.h index 31050d1feb..23feea565c 100644 --- a/scene/gui/box_container.h +++ b/scene/gui/box_container.h @@ -55,7 +55,7 @@ protected: static void _bind_methods(); public: - void add_spacer(bool p_begin = false); + Control *add_spacer(bool p_begin = false); void set_alignment(AlignMode p_align); AlignMode get_alignment() const; diff --git a/scene/gui/check_box.cpp b/scene/gui/check_box.cpp index 9df328dd11..c0650a8f3f 100644 --- a/scene/gui/check_box.cpp +++ b/scene/gui/check_box.cpp @@ -34,7 +34,9 @@ Size2 CheckBox::get_icon_size() const { Ref<Texture2D> checked = Control::get_theme_icon("checked"); + Ref<Texture2D> checked_disabled = Control::get_theme_icon("checked_disabled"); Ref<Texture2D> unchecked = Control::get_theme_icon("unchecked"); + Ref<Texture2D> unchecked_disabled = Control::get_theme_icon("unchecked_disabled"); Ref<Texture2D> radio_checked = Control::get_theme_icon("radio_checked"); Ref<Texture2D> radio_unchecked = Control::get_theme_icon("radio_unchecked"); @@ -79,8 +81,8 @@ void CheckBox::_notification(int p_what) { } else if (p_what == NOTIFICATION_DRAW) { RID ci = get_canvas_item(); - Ref<Texture2D> on = Control::get_theme_icon(is_radio() ? "radio_checked" : "checked"); - Ref<Texture2D> off = Control::get_theme_icon(is_radio() ? "radio_unchecked" : "unchecked"); + Ref<Texture2D> on = Control::get_theme_icon(vformat("%s%s", is_radio() ? "radio_checked" : "checked", is_disabled() ? "_disabled" : "")); + Ref<Texture2D> off = Control::get_theme_icon(vformat("%s%s", is_radio() ? "radio_unchecked" : "unchecked", is_disabled() ? "_disabled" : "")); Ref<StyleBox> sb = get_theme_stylebox("normal"); Vector2 ofs; diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index e8586b72e9..9ccb2886e1 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -84,6 +84,57 @@ void ColorPicker::_notification(int p_what) { } } +Ref<Shader> ColorPicker::wheel_shader; +Ref<Shader> ColorPicker::circle_shader; + +void ColorPicker::init_shaders() { + wheel_shader.instance(); + wheel_shader->set_code( + "shader_type canvas_item;" + "const float TAU = 6.28318530718;" + "void fragment() {" + " float x = UV.x - 0.5;" + " float y = UV.y - 0.5;" + " float a = atan(y, x);" + " x += 0.001;" + " y += 0.001;" + " float b = float(sqrt(x * x + y * y) < 0.5) * float(sqrt(x * x + y * y) > 0.42);" + " x -= 0.002;" + " float b2 = float(sqrt(x * x + y * y) < 0.5) * float(sqrt(x * x + y * y) > 0.42);" + " y -= 0.002;" + " float b3 = float(sqrt(x * x + y * y) < 0.5) * float(sqrt(x * x + y * y) > 0.42);" + " x += 0.002;" + " float b4 = float(sqrt(x * x + y * y) < 0.5) * float(sqrt(x * x + y * y) > 0.42);" + " COLOR = vec4(clamp((abs(fract(((a - TAU) / TAU) + vec3(3.0, 2.0, 1.0) / 3.0) * 6.0 - 3.0) - 1.0), 0.0, 1.0), (b + b2 + b3 + b4) / 4.00);" + "}"); + + circle_shader.instance(); + circle_shader->set_code( + "shader_type canvas_item;" + "const float TAU = 6.28318530718;" + "uniform float v = 1.0;" + "void fragment() {" + " float x = UV.x - 0.5;" + " float y = UV.y - 0.5;" + " float a = atan(y, x);" + " x += 0.001;" + " y += 0.001;" + " float b = float(sqrt(x * x + y * y) < 0.5);" + " x -= 0.002;" + " float b2 = float(sqrt(x * x + y * y) < 0.5);" + " y -= 0.002;" + " float b3 = float(sqrt(x * x + y * y) < 0.5);" + " x += 0.002;" + " float b4 = float(sqrt(x * x + y * y) < 0.5);" + " COLOR = vec4(mix(vec3(1.0), clamp(abs(fract(vec3((a - TAU) / TAU) + vec3(1.0, 2.0 / 3.0, 1.0 / 3.0)) * 6.0 - vec3(3.0)) - vec3(1.0), 0.0, 1.0), ((float(sqrt(x * x + y * y)) * 2.0)) / 1.0) * vec3(v), (b + b2 + b3 + b4) / 4.00);" + "}"); +} + +void ColorPicker::finish_shaders() { + wheel_shader.unref(); + circle_shader.unref(); +} + void ColorPicker::set_focus_on_line_edit() { c_text->call_deferred("grab_focus"); } @@ -113,6 +164,27 @@ void ColorPicker::_update_controls() { btn_hsv->set_disabled(false); } + if (raw_mode_enabled) { + for (int i = 0; i < 3; i++) { + scroll[i]->remove_theme_icon_override("grabber"); + scroll[i]->remove_theme_icon_override("grabber_highlight"); + scroll[i]->remove_theme_style_override("slider"); + scroll[i]->remove_theme_style_override("grabber_area"); + scroll[i]->remove_theme_style_override("grabber_area_highlight"); + } + } else { + Ref<StyleBoxEmpty> style_box_empty(memnew(StyleBoxEmpty)); + Ref<Texture2D> bar_arrow = get_theme_icon("bar_arrow"); + + for (int i = 0; i < 4; i++) { + scroll[i]->add_theme_icon_override("grabber", bar_arrow); + scroll[i]->add_theme_icon_override("grabber_highlight", bar_arrow); + scroll[i]->add_theme_style_override("slider", style_box_empty); + scroll[i]->add_theme_style_override("grabber_area", style_box_empty); + scroll[i]->add_theme_style_override("grabber_area_highlight", style_box_empty); + } + } + if (edit_alpha) { values[3]->show(); scroll[3]->show(); @@ -122,6 +194,30 @@ void ColorPicker::_update_controls() { scroll[3]->hide(); labels[3]->hide(); } + + switch (picker_type) { + case SHAPE_HSV_RECTANGLE: + wheel_edit->hide(); + w_edit->show(); + uv_edit->show(); + break; + case SHAPE_HSV_WHEEL: + wheel_edit->show(); + w_edit->hide(); + uv_edit->hide(); + + wheel->set_material(wheel_mat); + break; + case SHAPE_VHS_CIRCLE: + wheel_edit->show(); + w_edit->show(); + uv_edit->hide(); + + wheel->set_material(circle_mat); + break; + default: { + } + } } void ColorPicker::_set_pick_color(const Color &p_color, bool p_update_sliders) { @@ -144,6 +240,18 @@ void ColorPicker::set_pick_color(const Color &p_color) { _set_pick_color(p_color, true); //because setters can't have more arguments } +void ColorPicker::set_old_color(const Color &p_color) { + old_color = p_color; +} + +void ColorPicker::set_display_old_color(bool p_enabled) { + display_old_color = p_enabled; +} + +bool ColorPicker::is_displaying_old_color() const { + return display_old_color; +} + void ColorPicker::set_edit_alpha(bool p_show) { edit_alpha = p_show; _update_controls(); @@ -243,6 +351,11 @@ void ColorPicker::_update_color(bool p_update_sliders) { sample->update(); uv_edit->update(); w_edit->update(); + for (int i = 0; i < 4; i++) { + scroll[i]->update(); + } + wheel->update(); + wheel_uv->update(); updating = false; } @@ -285,6 +398,18 @@ Color ColorPicker::get_pick_color() const { return color; } +void ColorPicker::set_picker_shape(PickerShapeType p_picker_type) { + ERR_FAIL_INDEX(p_picker_type, SHAPE_MAX); + picker_type = p_picker_type; + + _update_controls(); + _update_color(); +} + +ColorPicker::PickerShapeType ColorPicker::get_picker_shape() const { + return picker_type; +} + void ColorPicker::add_preset(const Color &p_color) { if (presets.find(p_color)) { presets.move_to_back(presets.find(p_color)); @@ -396,18 +521,53 @@ void ColorPicker::_update_text_value() { c_text->set_visible(visible); } +void ColorPicker::_sample_input(const Ref<InputEvent> &p_event) { + const Ref<InputEventMouseButton> mb = p_event; + if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) { + const Rect2 rect_old = Rect2(Point2(), Size2(sample->get_size().width * 0.5, sample->get_size().height * 0.95)); + if (rect_old.has_point(mb->get_position())) { + // Revert to the old color when left-clicking the old color sample. + color = old_color; + _update_color(); + emit_signal("color_changed", color); + } + } +} + void ColorPicker::_sample_draw() { - const Rect2 r = Rect2(Point2(), Size2(uv_edit->get_size().width, sample->get_size().height * 0.95)); + // Covers the right half of the sample if the old color is being displayed, + // or the whole sample if it's not being displayed. + Rect2 rect_new; + + if (display_old_color) { + rect_new = Rect2(Point2(sample->get_size().width * 0.5, 0), Size2(sample->get_size().width * 0.5, sample->get_size().height * 0.95)); + + // Draw both old and new colors for easier comparison (only if spawned from a ColorPickerButton). + const Rect2 rect_old = Rect2(Point2(), Size2(sample->get_size().width * 0.5, sample->get_size().height * 0.95)); + + if (display_old_color && old_color.a < 1.0) { + sample->draw_texture_rect(get_theme_icon("preset_bg", "ColorPicker"), rect_old, true); + } + + sample->draw_rect(rect_old, old_color); + + if (old_color.r > 1 || old_color.g > 1 || old_color.b > 1) { + // Draw an indicator to denote that the old color is "overbright" and can't be displayed accurately in the preview. + sample->draw_texture(get_theme_icon("overbright_indicator", "ColorPicker"), Point2()); + } + } else { + rect_new = Rect2(Point2(), Size2(sample->get_size().width, sample->get_size().height * 0.95)); + } if (color.a < 1.0) { - sample->draw_texture_rect(get_theme_icon("preset_bg", "ColorPicker"), r, true); + sample->draw_texture_rect(get_theme_icon("preset_bg", "ColorPicker"), rect_new, true); } - sample->draw_rect(r, color); + sample->draw_rect(rect_new, color); if (color.r > 1 || color.g > 1 || color.b > 1) { - // Draw an indicator to denote that the color is "overbright" and can't be displayed accurately in the preview - sample->draw_texture(get_theme_icon("overbright_indicator", "ColorPicker"), Point2()); + // Draw an indicator to denote that the new color is "overbright" and can't be displayed accurately in the preview. + sample->draw_texture(get_theme_icon("overbright_indicator", "ColorPicker"), Point2(uv_edit->get_size().width * 0.5, 0)); } } @@ -417,55 +577,242 @@ void ColorPicker::_hsv_draw(int p_which, Control *c) { } if (p_which == 0) { Vector<Point2> points; - points.push_back(Vector2()); - points.push_back(Vector2(c->get_size().x, 0)); - points.push_back(c->get_size()); - points.push_back(Vector2(0, c->get_size().y)); Vector<Color> colors; - colors.push_back(Color(1, 1, 1, 1)); - colors.push_back(Color(1, 1, 1, 1)); - colors.push_back(Color(0, 0, 0, 1)); - colors.push_back(Color(0, 0, 0, 1)); - c->draw_polygon(points, colors); Vector<Color> colors2; Color col = color; + Vector2 center = c->get_size() / 2.0; + + switch (picker_type) { + case SHAPE_HSV_WHEEL: { + points.resize(4); + colors.resize(4); + colors2.resize(4); + real_t ring_radius_x = Math_SQRT12 * c->get_size().width * 0.42; + real_t ring_radius_y = Math_SQRT12 * c->get_size().height * 0.42; + + points.set(0, center - Vector2(ring_radius_x, ring_radius_y)); + points.set(1, center + Vector2(ring_radius_x, -ring_radius_y)); + points.set(2, center + Vector2(ring_radius_x, ring_radius_y)); + points.set(3, center + Vector2(-ring_radius_x, ring_radius_y)); + colors.set(0, Color(1, 1, 1, 1)); + colors.set(1, Color(1, 1, 1, 1)); + colors.set(2, Color(0, 0, 0, 1)); + colors.set(3, Color(0, 0, 0, 1)); + c->draw_polygon(points, colors); + + col.set_hsv(h, 1, 1); + col.a = 0; + colors2.set(0, col); + col.a = 1; + colors2.set(1, col); + col.set_hsv(h, 1, 0); + colors2.set(2, col); + col.a = 0; + colors2.set(3, col); + c->draw_polygon(points, colors2); + break; + } + case SHAPE_HSV_RECTANGLE: { + points.resize(4); + colors.resize(4); + colors2.resize(4); + points.set(0, Vector2()); + points.set(1, Vector2(c->get_size().x, 0)); + points.set(2, c->get_size()); + points.set(3, Vector2(0, c->get_size().y)); + colors.set(0, Color(1, 1, 1, 1)); + colors.set(1, Color(1, 1, 1, 1)); + colors.set(2, Color(0, 0, 0, 1)); + colors.set(3, Color(0, 0, 0, 1)); + c->draw_polygon(points, colors); + col = color; + col.set_hsv(h, 1, 1); + col.a = 0; + colors2.set(0, col); + col.a = 1; + colors2.set(1, col); + col.set_hsv(h, 1, 0); + colors2.set(2, col); + col.a = 0; + colors2.set(3, col); + c->draw_polygon(points, colors2); + break; + } + default: { + } + } + Ref<Texture2D> cursor = get_theme_icon("picker_cursor", "ColorPicker"); + int x; + int y; + if (picker_type == SHAPE_VHS_CIRCLE) { + x = center.x + (center.x * Math::cos(h * Math_TAU) * s) - (cursor->get_width() / 2); + y = center.y + (center.y * Math::sin(h * Math_TAU) * s) - (cursor->get_height() / 2); + } else { + real_t corner_x = (c == wheel_uv) ? center.x - Math_SQRT12 * c->get_size().width * 0.42 : 0; + real_t corner_y = (c == wheel_uv) ? center.y - Math_SQRT12 * c->get_size().height * 0.42 : 0; + + Size2 real_size(c->get_size().x - corner_x * 2, c->get_size().y - corner_y * 2); + x = CLAMP(real_size.x * s, 0, real_size.x) + corner_x - (cursor->get_width() / 2); + y = CLAMP(real_size.y - real_size.y * v, 0, real_size.y) + corner_y - (cursor->get_height() / 2); + } + c->draw_texture(cursor, Point2(x, y)); + col.set_hsv(h, 1, 1); - col.a = 0; - colors2.push_back(col); - col.a = 1; - colors2.push_back(col); - col.set_hsv(h, 1, 0); - colors2.push_back(col); - col.a = 0; - colors2.push_back(col); - c->draw_polygon(points, colors2); - int x = CLAMP(c->get_size().x * s, 0, c->get_size().x); - int y = CLAMP(c->get_size().y - c->get_size().y * v, 0, c->get_size().y); - col = color; - col.a = 1; - c->draw_line(Point2(x, 0), Point2(x, c->get_size().y), col.inverted()); - c->draw_line(Point2(0, y), Point2(c->get_size().x, y), col.inverted()); - c->draw_line(Point2(x, y), Point2(x, y), Color(1, 1, 1), 2); + if (picker_type == SHAPE_HSV_WHEEL) { + points.resize(4); + double h1 = h - (0.5 / 360); + double h2 = h + (0.5 / 360); + points.set(0, Point2(center.x + (center.x * Math::cos(h1 * Math_TAU)), center.y + (center.y * Math::sin(h1 * Math_TAU)))); + points.set(1, Point2(center.x + (center.x * Math::cos(h1 * Math_TAU) * 0.84), center.y + (center.y * Math::sin(h1 * Math_TAU) * 0.84))); + points.set(2, Point2(center.x + (center.x * Math::cos(h2 * Math_TAU)), center.y + (center.y * Math::sin(h2 * Math_TAU)))); + points.set(3, Point2(center.x + (center.x * Math::cos(h2 * Math_TAU) * 0.84), center.y + (center.y * Math::sin(h2 * Math_TAU) * 0.84))); + c->draw_multiline(points, col.inverted()); + } + } else if (p_which == 1) { - Ref<Texture2D> hue = get_theme_icon("color_hue", "ColorPicker"); - c->draw_texture_rect(hue, Rect2(Point2(), c->get_size())); - int y = c->get_size().y - c->get_size().y * (1.0 - h); - Color col = Color(); - col.set_hsv(h, 1, 1); - c->draw_line(Point2(0, y), Point2(c->get_size().x, y), col.inverted()); + if (picker_type == SHAPE_HSV_RECTANGLE) { + Ref<Texture2D> hue = get_theme_icon("color_hue", "ColorPicker"); + c->draw_texture_rect(hue, Rect2(Point2(), c->get_size())); + int y = c->get_size().y - c->get_size().y * (1.0 - h); + Color col; + col.set_hsv(h, 1, 1); + c->draw_line(Point2(0, y), Point2(c->get_size().x, y), col.inverted()); + } else if (picker_type == SHAPE_VHS_CIRCLE) { + Vector<Point2> points; + Vector<Color> colors; + Color col; + col.set_hsv(h, s, 1); + points.resize(4); + colors.resize(4); + points.set(0, Vector2()); + points.set(1, Vector2(c->get_size().x, 0)); + points.set(2, c->get_size()); + points.set(3, Vector2(0, c->get_size().y)); + colors.set(0, col); + colors.set(1, col); + colors.set(2, Color(0, 0, 0)); + colors.set(3, Color(0, 0, 0)); + c->draw_polygon(points, colors); + int y = c->get_size().y - c->get_size().y * CLAMP(v, 0, 1); + col.set_hsv(h, 1, v); + c->draw_line(Point2(0, y), Point2(c->get_size().x, y), col.inverted()); + } + } else if (p_which == 2) { + c->draw_rect(Rect2(Point2(), c->get_size()), Color(1, 1, 1)); + if (picker_type == SHAPE_VHS_CIRCLE) { + circle_mat->set_shader_param("v", v); + } } } -void ColorPicker::_uv_input(const Ref<InputEvent> &p_event) { +void ColorPicker::_slider_draw(int p_which) { + Vector<Vector2> pos; + pos.resize(4); + Vector<Color> col; + col.resize(4); + Size2 size = scroll[p_which]->get_size(); + Color left_color; + Color right_color; +#ifdef TOOLS_ENABLED + const real_t margin = 4 * EDSCALE; +#else + const real_t margin = 4; +#endif + + if (p_which == 3) { + scroll[p_which]->draw_texture_rect(get_theme_icon("preset_bg", "ColorPicker"), Rect2(Point2(0, margin), Size2(size.x, margin)), true); + + left_color = color; + left_color.a = 0; + right_color = color; + right_color.a = 1; + } else { + if (raw_mode_enabled) { + return; + } + if (hsv_mode_enabled) { + if (p_which == 0) { + Ref<Texture2D> hue = get_theme_icon("color_hue", "ColorPicker"); + scroll[p_which]->draw_set_transform(Point2(), -Math_PI / 2, Size2(1.0, 1.0)); + scroll[p_which]->draw_texture_rect(hue, Rect2(Vector2(margin * -2, 0), Vector2(scroll[p_which]->get_size().x, margin)), false, Color(1, 1, 1), true); + return; + } + Color s_col; + Color v_col; + s_col.set_hsv(h, 0, v); + left_color = (p_which == 1) ? s_col : Color(0, 0, 0); + s_col.set_hsv(h, 1, v); + v_col.set_hsv(h, s, 1); + right_color = (p_which == 1) ? s_col : v_col; + } else { + left_color = Color( + p_which == 0 ? 0 : color.r, + p_which == 1 ? 0 : color.g, + p_which == 2 ? 0 : color.b); + right_color = Color( + p_which == 0 ? 1 : color.r, + p_which == 1 ? 1 : color.g, + p_which == 2 ? 1 : color.b); + } + } + + col.set(0, left_color); + col.set(1, right_color); + col.set(2, right_color); + col.set(3, left_color); + pos.set(0, Vector2(0, margin)); + pos.set(1, Vector2(size.x, margin)); + pos.set(2, Vector2(size.x, margin * 2)); + pos.set(3, Vector2(0, margin * 2)); + + scroll[p_which]->draw_polygon(pos, col); +} + +void ColorPicker::_uv_input(const Ref<InputEvent> &p_event, Control *c) { Ref<InputEventMouseButton> bev = p_event; if (bev.is_valid()) { - if (bev->is_pressed() && bev->get_button_index() == BUTTON_LEFT) { + if (bev->is_pressed() && bev->get_button_index() == MOUSE_BUTTON_LEFT) { + Vector2 center = c->get_size() / 2.0; + if (picker_type == SHAPE_VHS_CIRCLE) { + real_t dist = center.distance_to(bev->get_position()); + + if (dist <= center.x) { + real_t rad = Math::atan2(bev->get_position().y - center.y, bev->get_position().x - center.x); + h = ((rad >= 0) ? rad : (Math_TAU + rad)) / Math_TAU; + s = CLAMP(dist / center.x, 0, 1); + } else { + return; + } + } else { + real_t corner_x = (c == wheel_uv) ? center.x - Math_SQRT12 * c->get_size().width * 0.42 : 0; + real_t corner_y = (c == wheel_uv) ? center.y - Math_SQRT12 * c->get_size().height * 0.42 : 0; + Size2 real_size(c->get_size().x - corner_x * 2, c->get_size().y - corner_y * 2); + + if (bev->get_position().x < corner_x || bev->get_position().x > c->get_size().x - corner_x || + bev->get_position().y < corner_y || bev->get_position().y > c->get_size().y - corner_y) { + { + real_t dist = center.distance_to(bev->get_position()); + + if (dist >= center.x * 0.84 && dist <= center.x) { + real_t rad = Math::atan2(bev->get_position().y - center.y, bev->get_position().x - center.x); + h = ((rad >= 0) ? rad : (Math_TAU + rad)) / Math_TAU; + spinning = true; + } else { + return; + } + } + } + + if (!spinning) { + real_t x = CLAMP(bev->get_position().x, corner_x, c->get_size().x - corner_x); + real_t y = CLAMP(bev->get_position().y, corner_x, c->get_size().y - corner_y); + + s = (x - c->get_position().x - corner_x) / real_size.x; + v = 1.0 - (y - c->get_position().y - corner_y) / real_size.y; + } + } changing_color = true; - float x = CLAMP((float)bev->get_position().x, 0, uv_edit->get_size().width); - float y = CLAMP((float)bev->get_position().y, 0, uv_edit->get_size().height); - s = x / uv_edit->get_size().width; - v = 1.0 - y / uv_edit->get_size().height; color.set_hsv(h, s, v, color.a); last_hsv = color; set_pick_color(color); @@ -473,11 +820,13 @@ void ColorPicker::_uv_input(const Ref<InputEvent> &p_event) { if (!deferred_mode_enabled) { emit_signal("color_changed", color); } - } else if (deferred_mode_enabled && !bev->is_pressed() && bev->get_button_index() == BUTTON_LEFT) { + } else if (deferred_mode_enabled && !bev->is_pressed() && bev->get_button_index() == MOUSE_BUTTON_LEFT) { emit_signal("color_changed", color); changing_color = false; + spinning = false; } else { changing_color = false; + spinning = false; } } @@ -487,10 +836,30 @@ void ColorPicker::_uv_input(const Ref<InputEvent> &p_event) { if (!changing_color) { return; } - float x = CLAMP((float)mev->get_position().x, 0, uv_edit->get_size().width); - float y = CLAMP((float)mev->get_position().y, 0, uv_edit->get_size().height); - s = x / uv_edit->get_size().width; - v = 1.0 - y / uv_edit->get_size().height; + + Vector2 center = c->get_size() / 2.0; + if (picker_type == SHAPE_VHS_CIRCLE) { + real_t dist = center.distance_to(mev->get_position()); + real_t rad = Math::atan2(mev->get_position().y - center.y, mev->get_position().x - center.x); + h = ((rad >= 0) ? rad : (Math_TAU + rad)) / Math_TAU; + s = CLAMP(dist / center.x, 0, 1); + } else { + if (spinning) { + real_t rad = Math::atan2(mev->get_position().y - center.y, mev->get_position().x - center.x); + h = ((rad >= 0) ? rad : (Math_TAU + rad)) / Math_TAU; + } else { + real_t corner_x = (c == wheel_uv) ? center.x - Math_SQRT12 * c->get_size().width * 0.42 : 0; + real_t corner_y = (c == wheel_uv) ? center.y - Math_SQRT12 * c->get_size().height * 0.42 : 0; + Size2 real_size(c->get_size().x - corner_x * 2, c->get_size().y - corner_y * 2); + + real_t x = CLAMP(mev->get_position().x, corner_x, c->get_size().x - corner_x); + real_t y = CLAMP(mev->get_position().y, corner_x, c->get_size().y - corner_y); + + s = (x - corner_x) / real_size.x; + v = 1.0 - (y - corner_y) / real_size.y; + } + } + color.set_hsv(h, s, v, color.a); last_hsv = color; set_pick_color(color); @@ -505,10 +874,14 @@ void ColorPicker::_w_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseButton> bev = p_event; if (bev.is_valid()) { - if (bev->is_pressed() && bev->get_button_index() == BUTTON_LEFT) { + if (bev->is_pressed() && bev->get_button_index() == MOUSE_BUTTON_LEFT) { changing_color = true; float y = CLAMP((float)bev->get_position().y, 0, w_edit->get_size().height); - h = y / w_edit->get_size().height; + if (picker_type == SHAPE_VHS_CIRCLE) { + v = 1.0 - (y / w_edit->get_size().height); + } else { + h = y / w_edit->get_size().height; + } } else { changing_color = false; } @@ -518,7 +891,7 @@ void ColorPicker::_w_input(const Ref<InputEvent> &p_event) { _update_color(); if (!deferred_mode_enabled) { emit_signal("color_changed", color); - } else if (!bev->is_pressed() && bev->get_button_index() == BUTTON_LEFT) { + } else if (!bev->is_pressed() && bev->get_button_index() == MOUSE_BUTTON_LEFT) { emit_signal("color_changed", color); } } @@ -530,7 +903,11 @@ void ColorPicker::_w_input(const Ref<InputEvent> &p_event) { return; } float y = CLAMP((float)mev->get_position().y, 0, w_edit->get_size().height); - h = y / w_edit->get_size().height; + if (picker_type == SHAPE_VHS_CIRCLE) { + v = 1.0 - (y / w_edit->get_size().height); + } else { + h = y / w_edit->get_size().height; + } color.set_hsv(h, s, v, color.a); last_hsv = color; set_pick_color(color); @@ -546,7 +923,7 @@ void ColorPicker::_preset_input(const Ref<InputEvent> &p_event) { if (bev.is_valid()) { int index = 0; - if (bev->is_pressed() && bev->get_button_index() == BUTTON_LEFT) { + if (bev->is_pressed() && bev->get_button_index() == MOUSE_BUTTON_LEFT) { for (int i = 0; i < presets.size(); i++) { int x = (i % presets_per_row) * bt_add_preset->get_size().x; int y = (Math::floor((float)i / presets_per_row)) * bt_add_preset->get_size().y; @@ -557,7 +934,7 @@ void ColorPicker::_preset_input(const Ref<InputEvent> &p_event) { set_pick_color(presets[index]); _update_color(); emit_signal("color_changed", color); - } else if (bev->is_pressed() && bev->get_button_index() == BUTTON_RIGHT && presets_enabled) { + } else if (bev->is_pressed() && bev->get_button_index() == MOUSE_BUTTON_RIGHT && presets_enabled) { index = bev->get_position().x / (preset->get_size().x / presets.size()); Color clicked_preset = presets[index]; erase_preset(clicked_preset); @@ -586,7 +963,7 @@ void ColorPicker::_screen_input(const Ref<InputEvent> &p_event) { } Ref<InputEventMouseButton> bev = p_event; - if (bev.is_valid() && bev->get_button_index() == BUTTON_LEFT && !bev->is_pressed()) { + if (bev.is_valid() && bev->get_button_index() == MOUSE_BUTTON_LEFT && !bev->is_pressed()) { emit_signal("color_changed", color); screen->hide(); } @@ -598,7 +975,7 @@ void ColorPicker::_screen_input(const Ref<InputEvent> &p_event) { return; } - Ref<Image> img = r->get_texture()->get_data(); + Ref<Image> img = r->get_texture()->get_image(); if (img.is_valid() && !img->is_empty()) { Vector2 ofs = mev->get_global_position() - r->get_visible_rect().get_position(); Color c = img->get_pixel(ofs.x, r->get_visible_rect().size.height - ofs.y); @@ -714,18 +1091,25 @@ void ColorPicker::_bind_methods() { ClassDB::bind_method(D_METHOD("add_preset", "color"), &ColorPicker::add_preset); ClassDB::bind_method(D_METHOD("erase_preset", "color"), &ColorPicker::erase_preset); ClassDB::bind_method(D_METHOD("get_presets"), &ColorPicker::get_presets); + ClassDB::bind_method(D_METHOD("set_picker_shape", "picker"), &ColorPicker::set_picker_shape); + ClassDB::bind_method(D_METHOD("get_picker_shape"), &ColorPicker::get_picker_shape); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_pick_color", "get_pick_color"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "edit_alpha"), "set_edit_alpha", "is_editing_alpha"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hsv_mode"), "set_hsv_mode", "is_hsv_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "raw_mode"), "set_raw_mode", "is_raw_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deferred_mode"), "set_deferred_mode", "is_deferred_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "picker_shape", PROPERTY_HINT_ENUM, "HSV Rectangle,HSV Rectangle Wheel,VHS Circle"), "set_picker_shape", "get_picker_shape"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "presets_enabled"), "set_presets_enabled", "are_presets_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "presets_visible"), "set_presets_visible", "are_presets_visible"); ADD_SIGNAL(MethodInfo("color_changed", PropertyInfo(Variant::COLOR, "color"))); ADD_SIGNAL(MethodInfo("preset_added", PropertyInfo(Variant::COLOR, "color"))); ADD_SIGNAL(MethodInfo("preset_removed", PropertyInfo(Variant::COLOR, "color"))); + + BIND_ENUM_CONSTANT(SHAPE_HSV_RECTANGLE); + BIND_ENUM_CONSTANT(SHAPE_HSV_WHEEL); + BIND_ENUM_CONSTANT(SHAPE_VHS_CIRCLE); } ColorPicker::ColorPicker() : @@ -734,36 +1118,26 @@ ColorPicker::ColorPicker() : add_child(hb_edit); hb_edit->set_v_size_flags(SIZE_EXPAND_FILL); - uv_edit = memnew(Control); hb_edit->add_child(uv_edit); - uv_edit->connect("gui_input", callable_mp(this, &ColorPicker::_uv_input)); + uv_edit->connect("gui_input", callable_mp(this, &ColorPicker::_uv_input), make_binds(uv_edit)); uv_edit->set_mouse_filter(MOUSE_FILTER_PASS); uv_edit->set_h_size_flags(SIZE_EXPAND_FILL); uv_edit->set_v_size_flags(SIZE_EXPAND_FILL); uv_edit->set_custom_minimum_size(Size2(get_theme_constant("sv_width"), get_theme_constant("sv_height"))); uv_edit->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw), make_binds(0, uv_edit)); - w_edit = memnew(Control); - hb_edit->add_child(w_edit); - w_edit->set_custom_minimum_size(Size2(get_theme_constant("h_width"), 0)); - w_edit->set_h_size_flags(SIZE_FILL); - w_edit->set_v_size_flags(SIZE_EXPAND_FILL); - w_edit->connect("gui_input", callable_mp(this, &ColorPicker::_w_input)); - w_edit->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw), make_binds(1, w_edit)); - HBoxContainer *hb_smpl = memnew(HBoxContainer); add_child(hb_smpl); - sample = memnew(TextureRect); hb_smpl->add_child(sample); sample->set_h_size_flags(SIZE_EXPAND_FILL); + sample->connect("gui_input", callable_mp(this, &ColorPicker::_sample_input)); sample->connect("draw", callable_mp(this, &ColorPicker::_sample_draw)); - btn_pick = memnew(Button); btn_pick->set_flat(true); hb_smpl->add_child(btn_pick); btn_pick->set_toggle_mode(true); - btn_pick->set_tooltip(TTR("Pick a color from the editor window.")); + btn_pick->set_tooltip(RTR("Pick a color from the editor window.")); btn_pick->connect("pressed", callable_mp(this, &ColorPicker::_screen_pick_pressed)); VBoxContainer *vbl = memnew(VBoxContainer); @@ -799,25 +1173,24 @@ ColorPicker::ColorPicker() : scroll[i]->set_h_size_flags(SIZE_EXPAND_FILL); scroll[i]->connect("value_changed", callable_mp(this, &ColorPicker::_value_changed)); + scroll[i]->connect("draw", callable_mp(this, &ColorPicker::_slider_draw), make_binds(i)); vbr->add_child(hbc); } + labels[3]->set_text("A"); HBoxContainer *hhb = memnew(HBoxContainer); vbr->add_child(hhb); - btn_hsv = memnew(CheckButton); hhb->add_child(btn_hsv); - btn_hsv->set_text(TTR("HSV")); + btn_hsv->set_text(RTR("HSV")); btn_hsv->connect("toggled", callable_mp(this, &ColorPicker::set_hsv_mode)); - btn_raw = memnew(CheckButton); hhb->add_child(btn_raw); - btn_raw->set_text(TTR("Raw")); + btn_raw->set_text(RTR("Raw")); btn_raw->connect("toggled", callable_mp(this, &ColorPicker::set_raw_mode)); - text_type = memnew(Button); hhb->add_child(text_type); text_type->set_text("#"); text_type->set_tooltip(TTR("Switch between hexadecimal and code values.")); @@ -831,41 +1204,76 @@ ColorPicker::ColorPicker() : text_type->set_mouse_filter(MOUSE_FILTER_IGNORE); } - c_text = memnew(LineEdit); hhb->add_child(c_text); c_text->set_h_size_flags(SIZE_EXPAND_FILL); c_text->connect("text_entered", callable_mp(this, &ColorPicker::_html_entered)); c_text->connect("focus_entered", callable_mp(this, &ColorPicker::_focus_enter)); c_text->connect("focus_exited", callable_mp(this, &ColorPicker::_html_focus_exit)); + wheel_edit->set_h_size_flags(SIZE_EXPAND_FILL); + wheel_edit->set_v_size_flags(SIZE_EXPAND_FILL); + wheel_edit->set_custom_minimum_size(Size2(get_theme_constant("sv_width"), get_theme_constant("sv_height"))); + hb_edit->add_child(wheel_edit); + + wheel_mat.instance(); + wheel_mat->set_shader(wheel_shader); + circle_mat.instance(); + circle_mat->set_shader(circle_shader); + + MarginContainer *wheel_margin(memnew(MarginContainer)); +#ifdef TOOLS_ENABLED + wheel_margin->add_theme_constant_override("margin_bottom", 8 * EDSCALE); +#else + wheel_margin->add_theme_constant_override("margin_bottom", 8); +#endif + wheel_edit->add_child(wheel_margin); + + wheel_margin->add_child(wheel); + wheel->set_mouse_filter(MOUSE_FILTER_PASS); + wheel->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw), make_binds(2, wheel)); + + wheel_margin->add_child(wheel_uv); + wheel_uv->connect("gui_input", callable_mp(this, &ColorPicker::_uv_input), make_binds(wheel_uv)); + wheel_uv->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw), make_binds(0, wheel_uv)); + + hb_edit->add_child(w_edit); + w_edit->set_custom_minimum_size(Size2(get_theme_constant("h_width"), 0)); + w_edit->set_h_size_flags(SIZE_FILL); + w_edit->set_v_size_flags(SIZE_EXPAND_FILL); + w_edit->connect("gui_input", callable_mp(this, &ColorPicker::_w_input)); + w_edit->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw), make_binds(1, w_edit)); + + picker_type = SHAPE_HSV_RECTANGLE; _update_controls(); updating = false; set_pick_color(Color(1, 1, 1)); - preset_separator = memnew(HSeparator); add_child(preset_separator); - preset_container = memnew(HBoxContainer); preset_container->set_h_size_flags(SIZE_EXPAND_FILL); add_child(preset_container); - preset = memnew(TextureRect); preset_container->add_child(preset); preset->connect("gui_input", callable_mp(this, &ColorPicker::_preset_input)); preset->connect("draw", callable_mp(this, &ColorPicker::_update_presets)); - preset_container2 = memnew(HBoxContainer); preset_container2->set_h_size_flags(SIZE_EXPAND_FILL); add_child(preset_container2); - bt_add_preset = memnew(Button); preset_container2->add_child(bt_add_preset); - bt_add_preset->set_tooltip(TTR("Add current color as a preset.")); + bt_add_preset->set_tooltip(RTR("Add current color as a preset.")); bt_add_preset->connect("pressed", callable_mp(this, &ColorPicker::_add_preset_pressed)); } ///////////////// +void ColorPickerButton::_about_to_popup() { + set_pressed(true); + if (picker) { + picker->set_old_color(color); + } +} + void ColorPickerButton::_color_changed(const Color &p_color) { color = p_color; update(); @@ -978,10 +1386,11 @@ void ColorPickerButton::_update_picker() { popup->add_child(picker); add_child(popup); picker->connect("color_changed", callable_mp(this, &ColorPickerButton::_color_changed)); - popup->connect("about_to_popup", callable_mp((BaseButton *)this, &BaseButton::set_pressed), varray(true)); + popup->connect("about_to_popup", callable_mp(this, &ColorPickerButton::_about_to_popup)); popup->connect("popup_hide", callable_mp(this, &ColorPickerButton::_modal_closed)); picker->set_pick_color(color); picker->set_edit_alpha(edit_alpha); + picker->set_display_old_color(true); emit_signal("picker_created"); } } @@ -993,6 +1402,7 @@ void ColorPickerButton::_bind_methods() { ClassDB::bind_method(D_METHOD("get_popup"), &ColorPickerButton::get_popup); ClassDB::bind_method(D_METHOD("set_edit_alpha", "show"), &ColorPickerButton::set_edit_alpha); ClassDB::bind_method(D_METHOD("is_editing_alpha"), &ColorPickerButton::is_editing_alpha); + ClassDB::bind_method(D_METHOD("_about_to_popup"), &ColorPickerButton::_about_to_popup); ADD_SIGNAL(MethodInfo("color_changed", PropertyInfo(Variant::COLOR, "color"))); ADD_SIGNAL(MethodInfo("popup_closed")); diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h index 7915527bc0..14113467d0 100644 --- a/scene/gui/color_picker.h +++ b/scene/gui/color_picker.h @@ -31,6 +31,7 @@ #ifndef COLOR_PICKER_H #define COLOR_PICKER_H +#include "scene/gui/aspect_ratio_container.h" #include "scene/gui/box_container.h" #include "scene/gui/button.h" #include "scene/gui/check_button.h" @@ -45,36 +46,57 @@ class ColorPicker : public BoxContainer { GDCLASS(ColorPicker, BoxContainer); +public: + enum PickerShapeType { + SHAPE_HSV_RECTANGLE, + SHAPE_HSV_WHEEL, + SHAPE_VHS_CIRCLE, + + SHAPE_MAX + }; + private: + static Ref<Shader> wheel_shader; + static Ref<Shader> circle_shader; + Control *screen = nullptr; - Control *uv_edit; - Control *w_edit; - TextureRect *sample; - TextureRect *preset; - HBoxContainer *preset_container; - HBoxContainer *preset_container2; - HSeparator *preset_separator; - Button *bt_add_preset; + Control *uv_edit = memnew(Control); + Control *w_edit = memnew(Control); + AspectRatioContainer *wheel_edit = memnew(AspectRatioContainer); + Ref<ShaderMaterial> wheel_mat; + Ref<ShaderMaterial> circle_mat; + Control *wheel = memnew(Control); + Control *wheel_uv = memnew(Control); + TextureRect *sample = memnew(TextureRect); + TextureRect *preset = memnew(TextureRect); + HBoxContainer *preset_container = memnew(HBoxContainer); + HBoxContainer *preset_container2 = memnew(HBoxContainer); + HSeparator *preset_separator = memnew(HSeparator); + Button *bt_add_preset = memnew(Button); List<Color> presets; - Button *btn_pick; - CheckButton *btn_hsv; - CheckButton *btn_raw; + Button *btn_pick = memnew(Button); + CheckButton *btn_hsv = memnew(CheckButton); + CheckButton *btn_raw = memnew(CheckButton); HSlider *scroll[4]; SpinBox *values[4]; Label *labels[4]; - Button *text_type; - LineEdit *c_text; + Button *text_type = memnew(Button); + LineEdit *c_text = memnew(LineEdit); bool edit_alpha = true; Size2i ms; bool text_is_constructor = false; int presets_per_row = 0; + PickerShapeType picker_type = SHAPE_HSV_WHEEL; Color color; + Color old_color; + bool display_old_color = false; bool raw_mode_enabled = false; bool hsv_mode_enabled = false; bool deferred_mode_enabled = false; bool updating = true; bool changing_color = false; + bool spinning = false; bool presets_enabled = true; bool presets_visible = true; float h = 0.0; @@ -89,10 +111,12 @@ private: void _update_presets(); void _update_text_value(); void _text_type_toggled(); + void _sample_input(const Ref<InputEvent> &p_event); void _sample_draw(); void _hsv_draw(int p_which, Control *c); + void _slider_draw(int p_which); - void _uv_input(const Ref<InputEvent> &p_event); + void _uv_input(const Ref<InputEvent> &p_event, Control *c); void _w_input(const Ref<InputEvent> &p_event); void _preset_input(const Ref<InputEvent> &p_event); void _screen_input(const Ref<InputEvent> &p_event); @@ -107,12 +131,22 @@ protected: static void _bind_methods(); public: + static void init_shaders(); + static void finish_shaders(); + void set_edit_alpha(bool p_show); bool is_editing_alpha() const; void _set_pick_color(const Color &p_color, bool p_update_sliders); void set_pick_color(const Color &p_color); Color get_pick_color() const; + void set_old_color(const Color &p_color); + + void set_display_old_color(bool p_enabled); + bool is_displaying_old_color() const; + + void set_picker_shape(PickerShapeType p_picker_type); + PickerShapeType get_picker_shape() const; void add_preset(const Color &p_color); void erase_preset(const Color &p_color); @@ -150,6 +184,7 @@ class ColorPickerButton : public Button { Color color; bool edit_alpha = true; + void _about_to_popup(); void _color_changed(const Color &p_color); void _modal_closed(); @@ -174,4 +209,5 @@ public: ColorPickerButton(); }; +VARIANT_ENUM_CAST(ColorPicker::PickerShapeType); #endif // COLOR_PICKER_H diff --git a/scene/gui/container.cpp b/scene/gui/container.cpp index 2e6b798eea..dea69aae6b 100644 --- a/scene/gui/container.cpp +++ b/scene/gui/container.cpp @@ -159,16 +159,14 @@ void Container::_notification(int p_what) { } } -String Container::get_configuration_warning() const { - String warning = Control::get_configuration_warning(); +TypedArray<String> Container::get_configuration_warnings() const { + TypedArray<String> warnings = Control::get_configuration_warnings(); if (get_class() == "Container" && get_script().is_null()) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("Container by itself serves no purpose unless a script configures its children placement behavior.\nIf you don't intend to add a script, use a plain Control node instead."); + warnings.push_back(TTR("Container by itself serves no purpose unless a script configures its children placement behavior.\nIf you don't intend to add a script, use a plain Control node instead.")); } - return warning; + + return warnings; } void Container::_bind_methods() { diff --git a/scene/gui/container.h b/scene/gui/container.h index a4f392a3ae..bce3085f0c 100644 --- a/scene/gui/container.h +++ b/scene/gui/container.h @@ -56,7 +56,7 @@ public: void fit_child_in_rect(Control *p_child, const Rect2 &p_rect); - virtual String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; Container(); }; diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index bff3024e38..83abd02b40 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -73,6 +73,9 @@ Dictionary Control::_edit_get_state() const { } void Control::_edit_set_state(const Dictionary &p_state) { + ERR_FAIL_COND((p_state.size() <= 0) || + !p_state.has("rotation") || !p_state.has("scale") || + !p_state.has("pivot") || !p_state.has("anchors") || !p_state.has("offsets")); Dictionary state = p_state; set_rotation(state["rotation"]); @@ -93,6 +96,7 @@ void Control::_edit_set_state(const Dictionary &p_state) { void Control::_edit_set_position(const Point2 &p_position) { #ifdef TOOLS_ENABLED + ERR_FAIL_COND_MSG(!Engine::get_singleton()->is_editor_hint(), "This function can only be used from editor plugins."); set_position(p_position, CanvasItemEditor::get_singleton()->is_anchors_mode_enabled() && Object::cast_to<Control>(data.parent)); #else // Unlikely to happen. TODO: enclose all _edit_ functions into TOOLS_ENABLED @@ -114,6 +118,7 @@ Size2 Control::_edit_get_scale() const { void Control::_edit_set_rect(const Rect2 &p_edit_rect) { #ifdef TOOLS_ENABLED + ERR_FAIL_COND_MSG(!Engine::get_singleton()->is_editor_hint(), "This function can only be used from editor plugins."); 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 @@ -131,11 +136,11 @@ bool Control::_edit_use_rect() const { return true; } -void Control::_edit_set_rotation(float p_rotation) { +void Control::_edit_set_rotation(real_t p_rotation) { set_rotation(p_rotation); } -float Control::_edit_get_rotation() const { +real_t Control::_edit_get_rotation() const { return get_rotation(); } @@ -163,6 +168,12 @@ Size2 Control::_edit_get_minimum_size() const { } #endif +void Control::accept_event() { + if (is_inside_tree()) { + get_viewport()->_gui_accept_event(); + } +} + void Control::set_custom_minimum_size(const Size2 &p_custom) { if (p_custom == data.custom_minimum_size) { return; @@ -285,15 +296,11 @@ 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(); - } - data.updating_last_minimum_size = false; if (minsize != data.last_minimum_size) { data.last_minimum_size = minsize; + _size_changed(); emit_signal(SceneStringNames::get_singleton()->minimum_size_changed); } } @@ -322,7 +329,6 @@ bool Control::_get(const StringName &p_name, Variant &r_ret) const { r_ret = data.color_override.has(name) ? Variant(data.color_override[name]) : Variant(); } else if (sname.begins_with("custom_constants/")) { String name = sname.get_slicec('/', 1); - r_ret = data.constant_override.has(name) ? Variant(data.constant_override[name]) : Variant(); } else { return false; @@ -345,72 +351,72 @@ void Control::_get_property_list(List<PropertyInfo> *p_list) const { List<StringName> names; theme->get_icon_list(get_class_name(), &names); for (List<StringName>::Element *E = names.front(); E; E = E->next()) { - uint32_t hint = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CHECKABLE; + uint32_t usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CHECKABLE; if (data.icon_override.has(E->get())) { - hint |= PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_CHECKED; + usage |= PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_CHECKED; } - p_list->push_back(PropertyInfo(Variant::OBJECT, "custom_icons/" + E->get(), PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", hint)); + p_list->push_back(PropertyInfo(Variant::OBJECT, "custom_icons/" + E->get(), PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", usage)); } } { List<StringName> names; theme->get_stylebox_list(get_class_name(), &names); for (List<StringName>::Element *E = names.front(); E; E = E->next()) { - uint32_t hint = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CHECKABLE; + uint32_t usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CHECKABLE; if (data.style_override.has(E->get())) { - hint |= PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_CHECKED; + usage |= PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_CHECKED; } - p_list->push_back(PropertyInfo(Variant::OBJECT, "custom_styles/" + E->get(), PROPERTY_HINT_RESOURCE_TYPE, "StyleBox", hint)); + p_list->push_back(PropertyInfo(Variant::OBJECT, "custom_styles/" + E->get(), PROPERTY_HINT_RESOURCE_TYPE, "StyleBox", usage)); } } { List<StringName> names; theme->get_font_list(get_class_name(), &names); for (List<StringName>::Element *E = names.front(); E; E = E->next()) { - uint32_t hint = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CHECKABLE; + uint32_t usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CHECKABLE; if (data.font_override.has(E->get())) { - hint |= PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_CHECKED; + usage |= PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_CHECKED; } - p_list->push_back(PropertyInfo(Variant::OBJECT, "custom_fonts/" + E->get(), PROPERTY_HINT_RESOURCE_TYPE, "Font", hint)); + p_list->push_back(PropertyInfo(Variant::OBJECT, "custom_fonts/" + E->get(), PROPERTY_HINT_RESOURCE_TYPE, "Font", usage)); } } { List<StringName> names; theme->get_font_size_list(get_class_name(), &names); for (List<StringName>::Element *E = names.front(); E; E = E->next()) { - uint32_t hint = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CHECKABLE; + uint32_t usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CHECKABLE; if (data.font_size_override.has(E->get())) { - hint |= PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_CHECKED; + usage |= PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_CHECKED; } - p_list->push_back(PropertyInfo(Variant::INT, "custom_font_sizes/" + E->get(), PROPERTY_HINT_NONE, "", hint)); + p_list->push_back(PropertyInfo(Variant::INT, "custom_font_sizes/" + E->get(), PROPERTY_HINT_NONE, "", usage)); } } { List<StringName> names; theme->get_color_list(get_class_name(), &names); for (List<StringName>::Element *E = names.front(); E; E = E->next()) { - uint32_t hint = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CHECKABLE; + uint32_t usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CHECKABLE; if (data.color_override.has(E->get())) { - hint |= PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_CHECKED; + usage |= PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_CHECKED; } - p_list->push_back(PropertyInfo(Variant::COLOR, "custom_colors/" + E->get(), PROPERTY_HINT_NONE, "", hint)); + p_list->push_back(PropertyInfo(Variant::COLOR, "custom_colors/" + E->get(), PROPERTY_HINT_NONE, "", usage)); } } { List<StringName> names; theme->get_constant_list(get_class_name(), &names); for (List<StringName>::Element *E = names.front(); E; E = E->next()) { - uint32_t hint = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CHECKABLE; + uint32_t usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CHECKABLE; if (data.constant_override.has(E->get())) { - hint |= PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_CHECKED; + usage |= PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_CHECKED; } - p_list->push_back(PropertyInfo(Variant::INT, "custom_constants/" + E->get(), PROPERTY_HINT_RANGE, "-16384,16384", hint)); + p_list->push_back(PropertyInfo(Variant::INT, "custom_constants/" + E->get(), PROPERTY_HINT_RANGE, "-16384,16384", usage)); } } } @@ -581,7 +587,7 @@ void Control::_notification(int p_notification) { } break; case NOTIFICATION_MOVED_IN_PARENT: { - // some parents need to know the order of the childrens to draw (like TabContainer) + // some parents need to know the order of the children to draw (like TabContainer) // update if necessary if (data.parent) { data.parent->update(); @@ -765,32 +771,27 @@ Size2 Control::get_minimum_size() const { } template <class T> -bool Control::_find_theme_item(Control *p_theme_owner, Window *p_theme_owner_window, T &r_ret, T (Theme::*get_func)(const StringName &, const StringName &) const, bool (Theme::*has_func)(const StringName &, const StringName &) const, const StringName &p_name, const StringName &p_node_type) { - // try with custom themes +T Control::get_theme_item_in_types(Control *p_theme_owner, Window *p_theme_owner_window, Theme::DataType p_data_type, const StringName &p_name, List<StringName> p_theme_types) { + ERR_FAIL_COND_V_MSG(p_theme_types.size() == 0, T(), "At least one theme type must be specified."); + + // First, look through each control or window node in the branch, until no valid parent can be found. + // For each control iterate through its inheritance chain and see if p_name exists in any of them. Control *theme_owner = p_theme_owner; Window *theme_owner_window = p_theme_owner_window; while (theme_owner || theme_owner_window) { - StringName class_name = p_node_type; - - while (class_name != StringName()) { - if (theme_owner && (theme_owner->data.theme.operator->()->*has_func)(p_name, class_name)) { - r_ret = (theme_owner->data.theme.operator->()->*get_func)(p_name, class_name); - return true; + for (List<StringName>::Element *E = p_theme_types.front(); E; E = E->next()) { + if (theme_owner && theme_owner->data.theme->has_theme_item(p_data_type, p_name, E->get())) { + return theme_owner->data.theme->get_theme_item(p_data_type, p_name, E->get()); } - if (theme_owner_window && (theme_owner_window->theme.operator->()->*has_func)(p_name, class_name)) { - r_ret = (theme_owner_window->theme.operator->()->*get_func)(p_name, class_name); - return true; + if (theme_owner_window && theme_owner_window->theme->has_theme_item(p_data_type, p_name, E->get())) { + return theme_owner_window->theme->get_theme_item(p_data_type, p_name, E->get()); } - - class_name = ClassDB::get_parent_class_nocheck(class_name); } Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent(); - Control *parent_c = Object::cast_to<Control>(parent); - if (parent_c) { theme_owner = parent_c->data.theme_owner; theme_owner_window = parent_c->data.theme_owner_window; @@ -805,33 +806,47 @@ bool Control::_find_theme_item(Control *p_theme_owner, Window *p_theme_owner_win } } } - return false; + + // Secondly, check the project-defined Theme resource. + if (Theme::get_project_default().is_valid()) { + for (List<StringName>::Element *E = p_theme_types.front(); E; E = E->next()) { + if (Theme::get_project_default()->has_theme_item(p_data_type, p_name, E->get())) { + return Theme::get_project_default()->get_theme_item(p_data_type, p_name, E->get()); + } + } + } + + // Lastly, fall back on the items defined in the default Theme, if they exist. + for (List<StringName>::Element *E = p_theme_types.front(); E; E = E->next()) { + if (Theme::get_default()->has_theme_item(p_data_type, p_name, E->get())) { + return Theme::get_default()->get_theme_item(p_data_type, p_name, E->get()); + } + } + // If they don't exist, use any type to return the default/empty value. + return Theme::get_default()->get_theme_item(p_data_type, p_name, p_theme_types[0]); } -bool Control::_has_theme_item(Control *p_theme_owner, Window *p_theme_owner_window, bool (Theme::*has_func)(const StringName &, const StringName &) const, const StringName &p_name, const StringName &p_node_type) { - // try with custom themes +bool Control::has_theme_item_in_types(Control *p_theme_owner, Window *p_theme_owner_window, Theme::DataType p_data_type, const StringName &p_name, List<StringName> p_theme_types) { + ERR_FAIL_COND_V_MSG(p_theme_types.size() == 0, false, "At least one theme type must be specified."); + + // First, look through each control or window node in the branch, until no valid parent can be found. + // For each control iterate through its inheritance chain and see if p_name exists in any of them. Control *theme_owner = p_theme_owner; Window *theme_owner_window = p_theme_owner_window; while (theme_owner || theme_owner_window) { - StringName class_name = p_node_type; - - while (class_name != StringName()) { - if (theme_owner && (theme_owner->data.theme.operator->()->*has_func)(p_name, class_name)) { + for (List<StringName>::Element *E = p_theme_types.front(); E; E = E->next()) { + if (theme_owner && theme_owner->data.theme->has_theme_item(p_data_type, p_name, E->get())) { return true; } - if (theme_owner_window && (theme_owner_window->theme.operator->()->*has_func)(p_name, class_name)) { + if (theme_owner_window && theme_owner_window->theme->has_theme_item(p_data_type, p_name, E->get())) { return true; } - - class_name = ClassDB::get_parent_class_nocheck(class_name); } Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent(); - Control *parent_c = Object::cast_to<Control>(parent); - if (parent_c) { theme_owner = parent_c->data.theme_owner; theme_owner_window = parent_c->data.theme_owner_window; @@ -846,179 +861,112 @@ bool Control::_has_theme_item(Control *p_theme_owner, Window *p_theme_owner_wind } } } - return false; -} -Ref<Texture2D> Control::get_theme_icon(const StringName &p_name, const StringName &p_node_type) const { - if (p_node_type == StringName() || p_node_type == get_class_name()) { - const Ref<Texture2D> *tex = data.icon_override.getptr(p_name); - if (tex) { - return *tex; + // Secondly, check the project-defined Theme resource. + if (Theme::get_project_default().is_valid()) { + for (List<StringName>::Element *E = p_theme_types.front(); E; E = E->next()) { + if (Theme::get_project_default()->has_theme_item(p_data_type, p_name, E->get())) { + return true; + } } } - StringName type = p_node_type ? p_node_type : get_class_name(); - - return get_icons(data.theme_owner, data.theme_owner_window, p_name, type); + // Lastly, fall back on the items defined in the default Theme, if they exist. + for (List<StringName>::Element *E = p_theme_types.front(); E; E = E->next()) { + if (Theme::get_default()->has_theme_item(p_data_type, p_name, E->get())) { + return true; + } + } + return false; } -Ref<Texture2D> Control::get_icons(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type) { - Ref<Texture2D> icon; - - if (_find_theme_item(p_theme_owner, p_theme_owner_window, icon, &Theme::get_icon, &Theme::has_icon, p_name, p_node_type)) { - return icon; +void Control::_get_theme_type_dependencies(const StringName &p_theme_type, List<StringName> *p_list) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_custom_type) { + if (data.theme_custom_type != StringName()) { + p_list->push_back(data.theme_custom_type); + } + Theme::get_type_dependencies(get_class_name(), p_list); + } else { + Theme::get_type_dependencies(p_theme_type, p_list); } +} - if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_icon(p_name, p_node_type)) { - return Theme::get_project_default()->get_icon(p_name, p_node_type); +Ref<Texture2D> Control::get_theme_icon(const StringName &p_name, const StringName &p_theme_type) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_custom_type) { + const Ref<Texture2D> *tex = data.icon_override.getptr(p_name); + if (tex) { + return *tex; } } - return Theme::get_default()->get_icon(p_name, p_node_type); + List<StringName> theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return get_theme_item_in_types<Ref<Texture2D>>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_ICON, p_name, theme_types); } -Ref<StyleBox> Control::get_theme_stylebox(const StringName &p_name, const StringName &p_node_type) const { - if (p_node_type == StringName() || p_node_type == get_class_name()) { +Ref<StyleBox> Control::get_theme_stylebox(const StringName &p_name, const StringName &p_theme_type) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_custom_type) { const Ref<StyleBox> *style = data.style_override.getptr(p_name); if (style) { return *style; } } - StringName type = p_node_type ? p_node_type : get_class_name(); - - return get_styleboxs(data.theme_owner, data.theme_owner_window, p_name, type); + List<StringName> theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return get_theme_item_in_types<Ref<StyleBox>>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_STYLEBOX, p_name, theme_types); } -Ref<StyleBox> Control::get_styleboxs(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type) { - Ref<StyleBox> stylebox; - - if (_find_theme_item(p_theme_owner, p_theme_owner_window, stylebox, &Theme::get_stylebox, &Theme::has_stylebox, p_name, p_node_type)) { - return stylebox; - } - - if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_stylebox(p_name, p_node_type)) { - return Theme::get_project_default()->get_stylebox(p_name, p_node_type); - } - } - - return Theme::get_default()->get_stylebox(p_name, p_node_type); -} - -Ref<Font> Control::get_theme_font(const StringName &p_name, const StringName &p_node_type) const { - if (p_node_type == StringName() || p_node_type == get_class_name()) { +Ref<Font> Control::get_theme_font(const StringName &p_name, const StringName &p_theme_type) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_custom_type) { const Ref<Font> *font = data.font_override.getptr(p_name); if (font) { return *font; } } - StringName type = p_node_type ? p_node_type : get_class_name(); - - return get_fonts(data.theme_owner, data.theme_owner_window, p_name, type); + List<StringName> theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return get_theme_item_in_types<Ref<Font>>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_FONT, p_name, theme_types); } -int Control::get_theme_font_size(const StringName &p_name, const StringName &p_node_type) const { - if (p_node_type == StringName() || p_node_type == get_class_name()) { +int Control::get_theme_font_size(const StringName &p_name, const StringName &p_theme_type) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_custom_type) { const int *font_size = data.font_size_override.getptr(p_name); if (font_size) { return *font_size; } } - StringName type = p_node_type ? p_node_type : get_class_name(); - - return get_font_sizes(data.theme_owner, data.theme_owner_window, p_name, type); -} - -Ref<Font> Control::get_fonts(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type) { - Ref<Font> font; - - if (_find_theme_item(p_theme_owner, p_theme_owner_window, font, &Theme::get_font, &Theme::has_font, p_name, p_node_type)) { - return font; - } - - if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_font(p_name, p_node_type)) { - return Theme::get_project_default()->get_font(p_name, p_node_type); - } - } - - return Theme::get_default()->get_font(p_name, p_node_type); -} - -int Control::get_font_sizes(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type) { - int font_size; - - if (_find_theme_item(p_theme_owner, p_theme_owner_window, font_size, &Theme::get_font_size, &Theme::has_font_size, p_name, p_node_type)) { - return font_size; - } - - if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_font_size(p_name, p_node_type)) { - return Theme::get_project_default()->get_font_size(p_name, p_node_type); - } - } - - return Theme::get_default()->get_font_size(p_name, p_node_type); + List<StringName> theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return get_theme_item_in_types<int>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_FONT_SIZE, p_name, theme_types); } -Color Control::get_theme_color(const StringName &p_name, const StringName &p_node_type) const { - if (p_node_type == StringName() || p_node_type == get_class_name()) { +Color Control::get_theme_color(const StringName &p_name, const StringName &p_theme_type) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_custom_type) { const Color *color = data.color_override.getptr(p_name); if (color) { return *color; } } - StringName type = p_node_type ? p_node_type : get_class_name(); - - return get_colors(data.theme_owner, data.theme_owner_window, p_name, type); -} - -Color Control::get_colors(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type) { - Color color; - - if (_find_theme_item(p_theme_owner, p_theme_owner_window, color, &Theme::get_color, &Theme::has_color, p_name, p_node_type)) { - return color; - } - - if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_color(p_name, p_node_type)) { - return Theme::get_project_default()->get_color(p_name, p_node_type); - } - } - return Theme::get_default()->get_color(p_name, p_node_type); + List<StringName> theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return get_theme_item_in_types<Color>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_COLOR, p_name, theme_types); } -int Control::get_theme_constant(const StringName &p_name, const StringName &p_node_type) const { - if (p_node_type == StringName() || p_node_type == get_class_name()) { +int Control::get_theme_constant(const StringName &p_name, const StringName &p_theme_type) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_custom_type) { const int *constant = data.constant_override.getptr(p_name); if (constant) { return *constant; } } - StringName type = p_node_type ? p_node_type : get_class_name(); - - return get_constants(data.theme_owner, data.theme_owner_window, p_name, type); -} - -int Control::get_constants(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type) { - int constant; - - if (_find_theme_item(p_theme_owner, p_theme_owner_window, constant, &Theme::get_constant, &Theme::has_constant, p_name, p_node_type)) { - return constant; - } - - if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_constant(p_name, p_node_type)) { - return Theme::get_project_default()->get_constant(p_name, p_node_type); - } - } - return Theme::get_default()->get_constant(p_name, p_node_type); + List<StringName> theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return get_theme_item_in_types<int>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_CONSTANT, p_name, theme_types); } bool Control::has_theme_icon_override(const StringName &p_name) const { @@ -1051,154 +999,76 @@ bool Control::has_theme_constant_override(const StringName &p_name) const { return constant != nullptr; } -bool Control::has_theme_icon(const StringName &p_name, const StringName &p_node_type) const { - if (p_node_type == StringName() || p_node_type == get_class_name()) { +bool Control::has_theme_icon(const StringName &p_name, const StringName &p_theme_type) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_custom_type) { if (has_theme_icon_override(p_name)) { return true; } } - StringName type = p_node_type ? p_node_type : get_class_name(); - - return has_icons(data.theme_owner, data.theme_owner_window, p_name, type); -} - -bool Control::has_icons(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type) { - if (_has_theme_item(p_theme_owner, p_theme_owner_window, &Theme::has_icon, p_name, p_node_type)) { - return true; - } - - if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_color(p_name, p_node_type)) { - return true; - } - } - return Theme::get_default()->has_icon(p_name, p_node_type); + List<StringName> theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_ICON, p_name, theme_types); } -bool Control::has_theme_stylebox(const StringName &p_name, const StringName &p_node_type) const { - if (p_node_type == StringName() || p_node_type == get_class_name()) { +bool Control::has_theme_stylebox(const StringName &p_name, const StringName &p_theme_type) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_custom_type) { if (has_theme_stylebox_override(p_name)) { return true; } } - StringName type = p_node_type ? p_node_type : get_class_name(); - - return has_styleboxs(data.theme_owner, data.theme_owner_window, p_name, type); -} - -bool Control::has_styleboxs(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type) { - if (_has_theme_item(p_theme_owner, p_theme_owner_window, &Theme::has_stylebox, p_name, p_node_type)) { - return true; - } - - if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_stylebox(p_name, p_node_type)) { - return true; - } - } - return Theme::get_default()->has_stylebox(p_name, p_node_type); + List<StringName> theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_STYLEBOX, p_name, theme_types); } -bool Control::has_theme_font(const StringName &p_name, const StringName &p_node_type) const { - if (p_node_type == StringName() || p_node_type == get_class_name()) { +bool Control::has_theme_font(const StringName &p_name, const StringName &p_theme_type) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_custom_type) { if (has_theme_font_override(p_name)) { return true; } } - StringName type = p_node_type ? p_node_type : get_class_name(); - - return has_fonts(data.theme_owner, data.theme_owner_window, p_name, type); + List<StringName> theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_FONT, p_name, theme_types); } -bool Control::has_fonts(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type) { - if (_has_theme_item(p_theme_owner, p_theme_owner_window, &Theme::has_font, p_name, p_node_type)) { - return true; - } - - if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_font(p_name, p_node_type)) { - return true; - } - } - return Theme::get_default()->has_font(p_name, p_node_type); -} - -bool Control::has_theme_font_size(const StringName &p_name, const StringName &p_node_type) const { - if (p_node_type == StringName() || p_node_type == get_class_name()) { +bool Control::has_theme_font_size(const StringName &p_name, const StringName &p_theme_type) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_custom_type) { if (has_theme_font_size_override(p_name)) { return true; } } - StringName type = p_node_type ? p_node_type : get_class_name(); - - return has_font_sizes(data.theme_owner, data.theme_owner_window, p_name, type); + List<StringName> theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_FONT_SIZE, p_name, theme_types); } -bool Control::has_font_sizes(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type) { - if (_has_theme_item(p_theme_owner, p_theme_owner_window, &Theme::has_font_size, p_name, p_node_type)) { - return true; - } - - if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_font_size(p_name, p_node_type)) { - return true; - } - } - return Theme::get_default()->has_font_size(p_name, p_node_type); -} - -bool Control::has_theme_color(const StringName &p_name, const StringName &p_node_type) const { - if (p_node_type == StringName() || p_node_type == get_class_name()) { +bool Control::has_theme_color(const StringName &p_name, const StringName &p_theme_type) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_custom_type) { if (has_theme_color_override(p_name)) { return true; } } - StringName type = p_node_type ? p_node_type : get_class_name(); - - return has_colors(data.theme_owner, data.theme_owner_window, p_name, type); -} - -bool Control::has_colors(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type) { - if (_has_theme_item(p_theme_owner, p_theme_owner_window, &Theme::has_color, p_name, p_node_type)) { - return true; - } - - if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_color(p_name, p_node_type)) { - return true; - } - } - return Theme::get_default()->has_color(p_name, p_node_type); + List<StringName> theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_COLOR, p_name, theme_types); } -bool Control::has_theme_constant(const StringName &p_name, const StringName &p_node_type) const { - if (p_node_type == StringName() || p_node_type == get_class_name()) { +bool Control::has_theme_constant(const StringName &p_name, const StringName &p_theme_type) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_custom_type) { if (has_theme_constant_override(p_name)) { return true; } } - StringName type = p_node_type ? p_node_type : get_class_name(); - - return has_constants(data.theme_owner, data.theme_owner_window, p_name, p_node_type); -} - -bool Control::has_constants(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type) { - if (_has_theme_item(p_theme_owner, p_theme_owner_window, &Theme::has_constant, p_name, p_node_type)) { - return true; - } - - if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_constant(p_name, p_node_type)) { - return true; - } - } - return Theme::get_default()->has_constant(p_name, p_node_type); + List<StringName> theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_CONSTANT, p_name, theme_types); } Rect2 Control::get_parent_anchorable_rect() const { @@ -1233,10 +1103,10 @@ Size2 Control::get_parent_area_size() const { void Control::_size_changed() { Rect2 parent_rect = get_parent_anchorable_rect(); - float edge_pos[4]; + real_t edge_pos[4]; for (int i = 0; i < 4; i++) { - float area = parent_rect.size[i & 1]; + real_t area = parent_rect.size[i & 1]; edge_pos[i] = data.offset[i] + (data.anchor[i] * area); } @@ -1290,13 +1160,13 @@ void Control::_size_changed() { } } -void Control::set_anchor(Side p_side, float p_anchor, bool p_keep_offset, bool p_push_opposite_anchor) { +void Control::set_anchor(Side p_side, real_t p_anchor, bool p_keep_offset, bool p_push_opposite_anchor) { ERR_FAIL_INDEX((int)p_side, 4); Rect2 parent_rect = get_parent_anchorable_rect(); - float parent_range = (p_side == SIDE_LEFT || p_side == SIDE_RIGHT) ? parent_rect.size.x : parent_rect.size.y; - float previous_pos = data.offset[p_side] + data.anchor[p_side] * parent_range; - float previous_opposite_pos = data.offset[(p_side + 2) % 4] + data.anchor[(p_side + 2) % 4] * parent_range; + real_t parent_range = (p_side == SIDE_LEFT || p_side == SIDE_RIGHT) ? parent_rect.size.x : parent_rect.size.y; + real_t previous_pos = data.offset[p_side] + data.anchor[p_side] * parent_range; + real_t previous_opposite_pos = data.offset[(p_side + 2) % 4] + data.anchor[(p_side + 2) % 4] * parent_range; data.anchor[p_side] = p_anchor; @@ -1322,11 +1192,11 @@ void Control::set_anchor(Side p_side, float p_anchor, bool p_keep_offset, bool p update(); } -void Control::_set_anchor(Side p_side, float p_anchor) { +void Control::_set_anchor(Side p_side, real_t p_anchor) { set_anchor(p_side, p_anchor); } -void Control::set_anchor_and_offset(Side p_side, float p_anchor, float p_pos, bool p_push_opposite_anchor) { +void Control::set_anchor_and_offset(Side p_side, real_t p_anchor, real_t p_pos, bool p_push_opposite_anchor) { set_anchor(p_side, p_anchor, false, p_push_opposite_anchor); set_offset(p_side, p_pos); } @@ -1463,7 +1333,7 @@ void Control::set_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz Rect2 parent_rect = get_parent_anchorable_rect(); - float x = parent_rect.size.x; + real_t x = parent_rect.size.x; if (is_layout_rtl()) { x = parent_rect.size.x - x - new_size.x; } @@ -1587,13 +1457,13 @@ void Control::set_anchors_and_offsets_preset(LayoutPreset p_preset, LayoutPreset set_offsets_preset(p_preset, p_resize_mode, p_margin); } -float Control::get_anchor(Side p_side) const { +real_t Control::get_anchor(Side p_side) const { ERR_FAIL_INDEX_V(int(p_side), 4, 0.0); return data.anchor[p_side]; } -void Control::set_offset(Side p_side, float p_value) { +void Control::set_offset(Side p_side, real_t p_value) { ERR_FAIL_INDEX((int)p_side, 4); data.offset[p_side] = p_value; @@ -1612,7 +1482,7 @@ void Control::set_end(const Size2 &p_point) { _size_changed(); } -float Control::get_offset(Side p_side) const { +real_t Control::get_offset(Side p_side) const { ERR_FAIL_INDEX_V((int)p_side, 4, 0); return data.offset[p_side]; @@ -1655,12 +1525,12 @@ void Control::set_global_position(const Point2 &p_point, bool p_keep_offsets) { set_position(inv.xform(p_point), p_keep_offsets); } -void Control::_compute_anchors(Rect2 p_rect, const float p_offsets[4], float (&r_anchors)[4]) { +void Control::_compute_anchors(Rect2 p_rect, const real_t p_offsets[4], real_t (&r_anchors)[4]) { Size2 parent_rect_size = get_parent_anchorable_rect().size; ERR_FAIL_COND(parent_rect_size.x == 0.0); ERR_FAIL_COND(parent_rect_size.y == 0.0); - float x = p_rect.position.x; + real_t x = p_rect.position.x; if (is_layout_rtl()) { x = parent_rect_size.x - x - p_rect.size.x; } @@ -1670,10 +1540,10 @@ void Control::_compute_anchors(Rect2 p_rect, const float p_offsets[4], float (&r r_anchors[3] = (p_rect.position.y + p_rect.size.y - p_offsets[3]) / parent_rect_size.y; } -void Control::_compute_offsets(Rect2 p_rect, const float p_anchors[4], float (&r_offsets)[4]) { +void Control::_compute_offsets(Rect2 p_rect, const real_t p_anchors[4], real_t (&r_offsets)[4]) { Size2 parent_rect_size = get_parent_anchorable_rect().size; - float x = p_rect.position.x; + real_t x = p_rect.position.x; if (is_layout_rtl()) { x = parent_rect_size.x - x - p_rect.size.x; } @@ -1775,53 +1645,38 @@ Rect2 Control::get_anchorable_rect() const { } void Control::add_theme_icon_override(const StringName &p_name, const Ref<Texture2D> &p_icon) { + ERR_FAIL_COND(!p_icon.is_valid()); + if (data.icon_override.has(p_name)) { data.icon_override[p_name]->disconnect("changed", callable_mp(this, &Control::_override_changed)); } - // 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", callable_mp(this, &Control::_override_changed), Vector<Variant>(), CONNECT_REFERENCE_COUNTED); - } - } + data.icon_override[p_name] = p_icon; + data.icon_override[p_name]->connect("changed", callable_mp(this, &Control::_override_changed), Vector<Variant>(), CONNECT_REFERENCE_COUNTED); notification(NOTIFICATION_THEME_CHANGED); } void Control::add_theme_style_override(const StringName &p_name, const Ref<StyleBox> &p_style) { + ERR_FAIL_COND(!p_style.is_valid()); + if (data.style_override.has(p_name)) { data.style_override[p_name]->disconnect("changed", callable_mp(this, &Control::_override_changed)); } - // 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", callable_mp(this, &Control::_override_changed), Vector<Variant>(), CONNECT_REFERENCE_COUNTED); - } - } + data.style_override[p_name] = p_style; + data.style_override[p_name]->connect("changed", callable_mp(this, &Control::_override_changed), Vector<Variant>(), CONNECT_REFERENCE_COUNTED); notification(NOTIFICATION_THEME_CHANGED); } void Control::add_theme_font_override(const StringName &p_name, const Ref<Font> &p_font) { + ERR_FAIL_COND(!p_font.is_valid()); + if (data.font_override.has(p_name)) { data.font_override[p_name]->disconnect("changed", callable_mp(this, &Control::_override_changed)); } - // 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", callable_mp(this, &Control::_override_changed), Vector<Variant>(), CONNECT_REFERENCE_COUNTED); - } - } + data.font_override[p_name] = p_font; + data.font_override[p_name]->connect("changed", callable_mp(this, &Control::_override_changed), Vector<Variant>(), CONNECT_REFERENCE_COUNTED); notification(NOTIFICATION_THEME_CHANGED); } @@ -1840,6 +1695,48 @@ void Control::add_theme_constant_override(const StringName &p_name, int p_consta notification(NOTIFICATION_THEME_CHANGED); } +void Control::remove_theme_icon_override(const StringName &p_name) { + if (data.icon_override.has(p_name)) { + data.icon_override[p_name]->disconnect("changed", callable_mp(this, &Control::_override_changed)); + } + + data.icon_override.erase(p_name); + notification(NOTIFICATION_THEME_CHANGED); +} + +void Control::remove_theme_style_override(const StringName &p_name) { + if (data.style_override.has(p_name)) { + data.style_override[p_name]->disconnect("changed", callable_mp(this, &Control::_override_changed)); + } + + data.style_override.erase(p_name); + notification(NOTIFICATION_THEME_CHANGED); +} + +void Control::remove_theme_font_override(const StringName &p_name) { + if (data.font_override.has(p_name)) { + data.font_override[p_name]->disconnect("changed", callable_mp(this, &Control::_override_changed)); + } + + data.font_override.erase(p_name); + notification(NOTIFICATION_THEME_CHANGED); +} + +void Control::remove_theme_font_size_override(const StringName &p_name) { + data.font_size_override.erase(p_name); + notification(NOTIFICATION_THEME_CHANGED); +} + +void Control::remove_theme_color_override(const StringName &p_name) { + data.color_override.erase(p_name); + notification(NOTIFICATION_THEME_CHANGED); +} + +void Control::remove_theme_constant_override(const StringName &p_name) { + data.constant_override.erase(p_name); + notification(NOTIFICATION_THEME_CHANGED); +} + void Control::set_focus_mode(FocusMode p_focus_mode) { ERR_FAIL_INDEX((int)p_focus_mode, 3); @@ -2144,19 +2041,22 @@ void Control::set_theme(const Ref<Theme> &p_theme) { } } -void Control::accept_event() { - if (is_inside_tree()) { - get_viewport()->_gui_accept_event(); - } -} - Ref<Theme> Control::get_theme() const { return data.theme; } +void Control::set_theme_custom_type(const StringName &p_theme_type) { + data.theme_custom_type = p_theme_type; + _propagate_theme_changed(this, data.theme_owner, data.theme_owner_window); +} + +StringName Control::get_theme_custom_type() const { + return data.theme_custom_type; +} + void Control::set_tooltip(const String &p_tooltip) { data.tooltip = p_tooltip; - update_configuration_warning(); + update_configuration_warnings(); } String Control::get_tooltip(const Point2 &p_pos) const { @@ -2252,7 +2152,7 @@ Control *Control::_get_focus_neighbor(Side p_side, int p_count) { return c; } - float dist = 1e7; + real_t dist = 1e7; Control *result = nullptr; Point2 points[4]; @@ -2273,10 +2173,10 @@ Control *Control::_get_focus_neighbor(Side p_side, int p_count) { Vector2 vdir = dir[p_side]; - float maxd = -1e7; + real_t maxd = -1e7; for (int i = 0; i < 4; i++) { - float d = vdir.dot(points[i]); + real_t d = vdir.dot(points[i]); if (d > maxd) { maxd = d; } @@ -2303,7 +2203,7 @@ Control *Control::_get_focus_neighbor(Side p_side, int p_count) { return result; } -void Control::_window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, const Point2 *p_points, float p_min, float &r_closest_dist, Control **r_closest) { +void Control::_window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, const Point2 *p_points, real_t p_min, real_t &r_closest_dist, Control **r_closest) { if (Object::cast_to<Viewport>(p_at)) { return; //bye } @@ -2320,10 +2220,10 @@ void Control::_window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, cons points[2] = xform.xform(c->get_size()); points[3] = xform.xform(Point2(0, c->get_size().y)); - float min = 1e7; + real_t min = 1e7; for (int i = 0; i < 4; i++) { - float d = p_dir.dot(points[i]); + real_t d = p_dir.dot(points[i]); if (d < min) { min = d; } @@ -2339,8 +2239,8 @@ void Control::_window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, cons Vector2 fb = points[(j + 1) % 4]; Vector2 pa, pb; - float d = Geometry2D::get_closest_points_between_segments(la, lb, fa, fb, pa, pb); - //float d = Geometry2D::get_closest_distance_between_segments(Vector3(la.x,la.y,0),Vector3(lb.x,lb.y,0),Vector3(fa.x,fa.y,0),Vector3(fb.x,fb.y,0)); + real_t d = Geometry2D::get_closest_points_between_segments(la, lb, fa, fb, pa, pb); + //real_t d = Geometry2D::get_closest_distance_between_segments(Vector3(la.x,la.y,0),Vector3(lb.x,lb.y,0),Vector3(fa.x,fa.y,0),Vector3(fb.x,fb.y,0)); if (d < r_closest_dist) { r_closest_dist = d; *r_closest = c; @@ -2380,7 +2280,7 @@ void Control::set_v_size_flags(int p_flags) { emit_signal(SceneStringNames::get_singleton()->size_flags_changed); } -void Control::set_stretch_ratio(float p_ratio) { +void Control::set_stretch_ratio(real_t p_ratio) { if (data.expand == p_ratio) { return; } @@ -2389,7 +2289,7 @@ void Control::set_stretch_ratio(float p_ratio) { emit_signal(SceneStringNames::get_singleton()->size_flags_changed); } -float Control::get_stretch_ratio() const { +real_t Control::get_stretch_ratio() const { return data.expand; } @@ -2441,7 +2341,7 @@ int Control::get_v_size_flags() const { void Control::set_mouse_filter(MouseFilter p_filter) { ERR_FAIL_INDEX(p_filter, 3); data.mouse_filter = p_filter; - update_configuration_warning(); + update_configuration_warnings(); } Control::MouseFilter Control::get_mouse_filter() const { @@ -2472,9 +2372,9 @@ bool Control::is_text_field() const { return false; } -Vector<Vector2i> Control::structured_text_parser(StructuredTextParser p_node_type, const Array &p_args, const String p_text) const { +Vector<Vector2i> Control::structured_text_parser(StructuredTextParser p_theme_type, const Array &p_args, const String p_text) const { Vector<Vector2i> ret; - switch (p_node_type) { + switch (p_theme_type) { case STRUCTURED_TEXT_URI: { int prev = 0; for (int i = 0; i < p_text.length(); i++) { @@ -2561,21 +2461,21 @@ Vector<Vector2i> Control::structured_text_parser(StructuredTextParser p_node_typ return ret; } -void Control::set_rotation(float p_radians) { +void Control::set_rotation(real_t p_radians) { data.rotation = p_radians; update(); _notify_transform(); } -float Control::get_rotation() const { +real_t Control::get_rotation() const { return data.rotation; } -void Control::set_rotation_degrees(float p_degrees) { +void Control::set_rotation_degrees(real_t p_degrees) { set_rotation(Math::deg2rad(p_degrees)); } -float Control::get_rotation_degrees() const { +real_t Control::get_rotation_degrees() const { return Math::rad2deg(get_rotation()); } @@ -2661,15 +2561,15 @@ void Control::get_argument_options(const StringName &p_function, int p_idx, List if (p_idx == 0) { List<StringName> sn; String pf = p_function; - if (pf == "add_color_override" || pf == "has_color" || pf == "has_color_override" || pf == "get_color") { + if (pf == "add_theme_color_override" || pf == "has_theme_color" || pf == "has_theme_color_override" || pf == "get_theme_color") { Theme::get_default()->get_color_list(get_class(), &sn); - } else if (pf == "add_style_override" || pf == "has_style" || pf == "has_style_override" || pf == "get_style") { + } else if (pf == "add_theme_style_override" || pf == "has_theme_style" || pf == "has_theme_style_override" || pf == "get_theme_style") { Theme::get_default()->get_stylebox_list(get_class(), &sn); - } else if (pf == "add_font_override" || pf == "has_font" || pf == "has_font_override" || pf == "get_font") { + } else if (pf == "add_theme_font_override" || pf == "has_theme_font" || pf == "has_theme_font_override" || pf == "get_theme_font") { Theme::get_default()->get_font_list(get_class(), &sn); - } else if (pf == "add_font_size_override" || pf == "has_font_size" || pf == "has_font_size_override" || pf == "get_font_size") { + } else if (pf == "add_theme_font_size_override" || pf == "has_theme_font_size" || pf == "has_theme_font_size_override" || pf == "get_theme_font_size") { Theme::get_default()->get_font_size_list(get_class(), &sn); - } else if (pf == "add_constant_override" || pf == "has_constant" || pf == "has_constant_override" || pf == "get_constant") { + } else if (pf == "add_theme_constant_override" || pf == "has_theme_constant" || pf == "has_theme_constant_override" || pf == "get_theme_constant") { Theme::get_default()->get_constant_list(get_class(), &sn); } @@ -2680,17 +2580,14 @@ void Control::get_argument_options(const StringName &p_function, int p_idx, List } } -String Control::get_configuration_warning() const { - String warning = CanvasItem::get_configuration_warning(); +TypedArray<String> Control::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); if (data.mouse_filter == MOUSE_FILTER_IGNORE && data.tooltip != "") { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("The Hint Tooltip won't be displayed as the control's Mouse Filter is set to \"Ignore\". To solve this, set the Mouse Filter to \"Stop\" or \"Pass\"."); + warnings.push_back(TTR("The Hint Tooltip won't be displayed as the control's Mouse Filter is set to \"Ignore\". To solve this, set the Mouse Filter to \"Stop\" or \"Pass\".")); } - return warning; + return warnings; } void Control::set_clip_contents(bool p_clip) { @@ -2787,6 +2684,9 @@ void Control::_bind_methods() { ClassDB::bind_method(D_METHOD("set_theme", "theme"), &Control::set_theme); ClassDB::bind_method(D_METHOD("get_theme"), &Control::get_theme); + ClassDB::bind_method(D_METHOD("set_theme_custom_type", "theme_type"), &Control::set_theme_custom_type); + ClassDB::bind_method(D_METHOD("get_theme_custom_type"), &Control::get_theme_custom_type); + ClassDB::bind_method(D_METHOD("add_theme_icon_override", "name", "texture"), &Control::add_theme_icon_override); ClassDB::bind_method(D_METHOD("add_theme_stylebox_override", "name", "stylebox"), &Control::add_theme_style_override); ClassDB::bind_method(D_METHOD("add_theme_font_override", "name", "font"), &Control::add_theme_font_override); @@ -2794,12 +2694,19 @@ void Control::_bind_methods() { ClassDB::bind_method(D_METHOD("add_theme_color_override", "name", "color"), &Control::add_theme_color_override); ClassDB::bind_method(D_METHOD("add_theme_constant_override", "name", "constant"), &Control::add_theme_constant_override); - ClassDB::bind_method(D_METHOD("get_theme_icon", "name", "node_type"), &Control::get_theme_icon, DEFVAL("")); - ClassDB::bind_method(D_METHOD("get_theme_stylebox", "name", "node_type"), &Control::get_theme_stylebox, DEFVAL("")); - ClassDB::bind_method(D_METHOD("get_theme_font", "name", "node_type"), &Control::get_theme_font, DEFVAL("")); - ClassDB::bind_method(D_METHOD("get_theme_font_size", "name", "node_type"), &Control::get_theme_font_size, DEFVAL("")); - ClassDB::bind_method(D_METHOD("get_theme_color", "name", "node_type"), &Control::get_theme_color, DEFVAL("")); - ClassDB::bind_method(D_METHOD("get_theme_constant", "name", "node_type"), &Control::get_theme_constant, DEFVAL("")); + ClassDB::bind_method(D_METHOD("remove_theme_icon_override", "name"), &Control::remove_theme_icon_override); + ClassDB::bind_method(D_METHOD("remove_theme_stylebox_override", "name"), &Control::remove_theme_style_override); + ClassDB::bind_method(D_METHOD("remove_theme_font_override", "name"), &Control::remove_theme_font_override); + ClassDB::bind_method(D_METHOD("remove_theme_font_size_override", "name"), &Control::remove_theme_font_size_override); + ClassDB::bind_method(D_METHOD("remove_theme_color_override", "name"), &Control::remove_theme_color_override); + ClassDB::bind_method(D_METHOD("remove_theme_constant_override", "name"), &Control::remove_theme_constant_override); + + ClassDB::bind_method(D_METHOD("get_theme_icon", "name", "theme_type"), &Control::get_theme_icon, DEFVAL("")); + ClassDB::bind_method(D_METHOD("get_theme_stylebox", "name", "theme_type"), &Control::get_theme_stylebox, DEFVAL("")); + ClassDB::bind_method(D_METHOD("get_theme_font", "name", "theme_type"), &Control::get_theme_font, DEFVAL("")); + ClassDB::bind_method(D_METHOD("get_theme_font_size", "name", "theme_type"), &Control::get_theme_font_size, DEFVAL("")); + ClassDB::bind_method(D_METHOD("get_theme_color", "name", "theme_type"), &Control::get_theme_color, DEFVAL("")); + ClassDB::bind_method(D_METHOD("get_theme_constant", "name", "theme_type"), &Control::get_theme_constant, DEFVAL("")); ClassDB::bind_method(D_METHOD("has_theme_icon_override", "name"), &Control::has_theme_icon_override); ClassDB::bind_method(D_METHOD("has_theme_stylebox_override", "name"), &Control::has_theme_stylebox_override); @@ -2808,12 +2715,12 @@ void Control::_bind_methods() { ClassDB::bind_method(D_METHOD("has_theme_color_override", "name"), &Control::has_theme_color_override); ClassDB::bind_method(D_METHOD("has_theme_constant_override", "name"), &Control::has_theme_constant_override); - ClassDB::bind_method(D_METHOD("has_theme_icon", "name", "node_type"), &Control::has_theme_icon, DEFVAL("")); - ClassDB::bind_method(D_METHOD("has_theme_stylebox", "name", "node_type"), &Control::has_theme_stylebox, DEFVAL("")); - ClassDB::bind_method(D_METHOD("has_theme_font", "name", "node_type"), &Control::has_theme_font, DEFVAL("")); - ClassDB::bind_method(D_METHOD("has_theme_font_size", "name", "node_type"), &Control::has_theme_font_size, DEFVAL("")); - ClassDB::bind_method(D_METHOD("has_theme_color", "name", "node_type"), &Control::has_theme_color, DEFVAL("")); - ClassDB::bind_method(D_METHOD("has_theme_constant", "name", "node_type"), &Control::has_theme_constant, DEFVAL("")); + ClassDB::bind_method(D_METHOD("has_theme_icon", "name", "theme_type"), &Control::has_theme_icon, DEFVAL("")); + ClassDB::bind_method(D_METHOD("has_theme_stylebox", "name", "theme_type"), &Control::has_theme_stylebox, DEFVAL("")); + ClassDB::bind_method(D_METHOD("has_theme_font", "name", "theme_type"), &Control::has_theme_font, DEFVAL("")); + ClassDB::bind_method(D_METHOD("has_theme_font_size", "name", "theme_type"), &Control::has_theme_font_size, DEFVAL("")); + ClassDB::bind_method(D_METHOD("has_theme_color", "name", "theme_type"), &Control::has_theme_color, DEFVAL("")); + ClassDB::bind_method(D_METHOD("has_theme_constant", "name", "theme_type"), &Control::has_theme_constant, DEFVAL("")); ClassDB::bind_method(D_METHOD("get_parent_control"), &Control::get_parent_control); @@ -2927,8 +2834,9 @@ void Control::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "size_flags_horizontal", PROPERTY_HINT_FLAGS, "Fill,Expand,Shrink Center,Shrink End"), "set_h_size_flags", "get_h_size_flags"); ADD_PROPERTY(PropertyInfo(Variant::INT, "size_flags_vertical", PROPERTY_HINT_FLAGS, "Fill,Expand,Shrink Center,Shrink End"), "set_v_size_flags", "get_v_size_flags"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size_flags_stretch_ratio", PROPERTY_HINT_RANGE, "0,20,0.01,or_greater"), "set_stretch_ratio", "get_stretch_ratio"); - ADD_GROUP("Theme", ""); + ADD_GROUP("Theme", "theme_"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "theme", PROPERTY_HINT_RESOURCE_TYPE, "Theme"), "set_theme", "get_theme"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "theme_custom_type"), "set_theme_custom_type", "get_theme_custom_type"); ADD_GROUP("", ""); BIND_ENUM_CONSTANT(FOCUS_NONE); diff --git a/scene/gui/control.h b/scene/gui/control.h index 8981e05872..a05025c32d 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -171,22 +171,22 @@ private: Size2 last_minimum_size; bool updating_last_minimum_size = false; - float offset[4] = { 0.0, 0.0, 0.0, 0.0 }; - float anchor[4] = { ANCHOR_BEGIN, ANCHOR_BEGIN, ANCHOR_BEGIN, ANCHOR_BEGIN }; + real_t offset[4] = { 0.0, 0.0, 0.0, 0.0 }; + real_t anchor[4] = { ANCHOR_BEGIN, ANCHOR_BEGIN, ANCHOR_BEGIN, ANCHOR_BEGIN }; FocusMode focus_mode = FOCUS_NONE; GrowDirection h_grow = GROW_DIRECTION_END; GrowDirection v_grow = GROW_DIRECTION_END; LayoutDirection layout_dir = LAYOUT_DIRECTION_INHERITED; - float rotation = 0.0; + real_t rotation = 0.0; Vector2 scale = Vector2(1, 1); Vector2 pivot_offset; bool size_warning = true; int h_size_flags = SIZE_FILL; int v_size_flags = SIZE_FILL; - float expand = 1.0; + real_t expand = 1.0; Point2 custom_minimum_size; MouseFilter mouse_filter = MOUSE_FILTER_STOP; @@ -201,6 +201,8 @@ private: Ref<Theme> theme; Control *theme_owner = nullptr; Window *theme_owner_window = nullptr; + StringName theme_custom_type; + String tooltip; CursorShape default_cursor = CURSOR_ARROW; @@ -224,10 +226,10 @@ private: // used internally Control *_find_control_at_pos(CanvasItem *p_node, const Point2 &p_pos, const Transform2D &p_xform, Transform2D &r_inv_xform); - void _window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, const Point2 *p_points, float p_min, float &r_closest_dist, Control **r_closest); + void _window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, const Point2 *p_points, real_t p_min, real_t &r_closest_dist, Control **r_closest); Control *_get_focus_neighbor(Side p_side, int p_count = 0); - void _set_anchor(Side p_side, float p_anchor); + void _set_anchor(Side p_side, real_t p_anchor); void _set_position(const Point2 &p_point); void _set_global_position(const Point2 &p_point); void _set_size(const Size2 &p_size); @@ -239,8 +241,8 @@ private: void _clear_size_warning(); void _update_scroll(); - void _compute_offsets(Rect2 p_rect, const float p_anchors[4], float (&r_offsets)[4]); - void _compute_anchors(Rect2 p_rect, const float p_offsets[4], float (&r_anchors)[4]); + void _compute_offsets(Rect2 p_rect, const real_t p_anchors[4], real_t (&r_offsets)[4]); + void _compute_anchors(Rect2 p_rect, const real_t p_offsets[4], real_t (&r_anchors)[4]); void _size_changed(); String _get_tooltip() const; @@ -258,23 +260,9 @@ private: static void _propagate_theme_changed(Node *p_at, Control *p_owner, Window *p_owner_window, bool p_assign = true); template <class T> - _FORCE_INLINE_ static bool _find_theme_item(Control *p_theme_owner, Window *p_theme_owner_window, T &, T (Theme::*get_func)(const StringName &, const StringName &) const, bool (Theme::*has_func)(const StringName &, const StringName &) const, const StringName &p_name, const StringName &p_node_type); - - _FORCE_INLINE_ static bool _has_theme_item(Control *p_theme_owner, Window *p_theme_owner_window, bool (Theme::*has_func)(const StringName &, const StringName &) const, const StringName &p_name, const StringName &p_node_type); - - static Ref<Texture2D> get_icons(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type = StringName()); - static Ref<StyleBox> get_styleboxs(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type = StringName()); - static Ref<Font> get_fonts(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type = StringName()); - static int get_font_sizes(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type = StringName()); - static Color get_colors(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type = StringName()); - static int get_constants(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type = StringName()); - - static bool has_icons(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type = StringName()); - static bool has_styleboxs(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type = StringName()); - static bool has_fonts(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type = StringName()); - static bool has_font_sizes(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type = StringName()); - static bool has_colors(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type = StringName()); - static bool has_constants(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type = StringName()); + static T get_theme_item_in_types(Control *p_theme_owner, Window *p_theme_owner_window, Theme::DataType p_data_type, const StringName &p_name, List<StringName> p_theme_types); + static bool has_theme_item_in_types(Control *p_theme_owner, Window *p_theme_owner_window, Theme::DataType p_data_type, const StringName &p_name, List<StringName> p_theme_types); + _FORCE_INLINE_ void _get_theme_type_dependencies(const StringName &p_theme_type, List<StringName> *p_list) const; protected: virtual void add_child_notify(Node *p_child) override; @@ -282,7 +270,7 @@ protected: //virtual void _window_gui_input(InputEvent p_event); - virtual Vector<Vector2i> structured_text_parser(StructuredTextParser p_node_type, const Array &p_args, const String p_text) const; + virtual Vector<Vector2i> structured_text_parser(StructuredTextParser p_theme_type, const Array &p_args, const String p_text) const; bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; @@ -325,8 +313,8 @@ public: virtual Rect2 _edit_get_rect() const override; virtual bool _edit_use_rect() const override; - virtual void _edit_set_rotation(float p_rotation) override; - virtual float _edit_get_rotation() const override; + virtual void _edit_set_rotation(real_t p_rotation) override; + virtual real_t _edit_get_rotation() const override; virtual bool _edit_use_rotation() const override; virtual void _edit_set_pivot(const Point2 &p_pivot) override; @@ -364,13 +352,13 @@ public: void set_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode = PRESET_MODE_MINSIZE, int p_margin = 0); void set_anchors_and_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode = PRESET_MODE_MINSIZE, int p_margin = 0); - void set_anchor(Side p_side, float p_anchor, bool p_keep_offset = true, bool p_push_opposite_anchor = true); - float get_anchor(Side p_side) const; + void set_anchor(Side p_side, real_t p_anchor, bool p_keep_offset = true, bool p_push_opposite_anchor = true); + real_t get_anchor(Side p_side) const; - void set_offset(Side p_side, float p_value); - float get_offset(Side p_side) const; + void set_offset(Side p_side, real_t p_value); + real_t get_offset(Side p_side) const; - void set_anchor_and_offset(Side p_side, float p_anchor, float p_pos, bool p_push_opposite_anchor = true); + void set_anchor_and_offset(Side p_side, real_t p_anchor, real_t p_pos, bool p_push_opposite_anchor = true); void set_begin(const Point2 &p_point); // helper void set_end(const Point2 &p_point); // helper @@ -395,10 +383,10 @@ public: void set_rect(const Rect2 &p_rect); // Reset anchors to begin and set rect, for faster container children sorting. - void set_rotation(float p_radians); - void set_rotation_degrees(float p_degrees); - float get_rotation() const; - float get_rotation_degrees() const; + void set_rotation(real_t p_radians); + void set_rotation_degrees(real_t p_degrees); + real_t get_rotation() const; + real_t get_rotation_degrees() const; void set_h_grow_direction(GrowDirection p_direction); GrowDirection get_h_grow_direction() const; @@ -415,14 +403,17 @@ public: void set_theme(const Ref<Theme> &p_theme); Ref<Theme> get_theme() const; + void set_theme_custom_type(const StringName &p_theme_type); + StringName get_theme_custom_type() const; + void set_h_size_flags(int p_flags); int get_h_size_flags() const; void set_v_size_flags(int p_flags); int get_v_size_flags() const; - void set_stretch_ratio(float p_ratio); - float get_stretch_ratio() const; + void set_stretch_ratio(real_t p_ratio); + real_t get_stretch_ratio() const; void minimum_size_changed(); @@ -459,12 +450,19 @@ public: void add_theme_color_override(const StringName &p_name, const Color &p_color); void add_theme_constant_override(const StringName &p_name, int p_constant); - Ref<Texture2D> get_theme_icon(const StringName &p_name, const StringName &p_node_type = StringName()) const; - Ref<StyleBox> get_theme_stylebox(const StringName &p_name, const StringName &p_node_type = StringName()) const; - Ref<Font> get_theme_font(const StringName &p_name, const StringName &p_node_type = StringName()) const; - int get_theme_font_size(const StringName &p_name, const StringName &p_node_type = StringName()) const; - Color get_theme_color(const StringName &p_name, const StringName &p_node_type = StringName()) const; - int get_theme_constant(const StringName &p_name, const StringName &p_node_type = StringName()) const; + void remove_theme_icon_override(const StringName &p_name); + void remove_theme_style_override(const StringName &p_name); + void remove_theme_font_override(const StringName &p_name); + void remove_theme_font_size_override(const StringName &p_name); + void remove_theme_color_override(const StringName &p_name); + void remove_theme_constant_override(const StringName &p_name); + + Ref<Texture2D> get_theme_icon(const StringName &p_name, const StringName &p_theme_type = StringName()) const; + Ref<StyleBox> get_theme_stylebox(const StringName &p_name, const StringName &p_theme_type = StringName()) const; + Ref<Font> get_theme_font(const StringName &p_name, const StringName &p_theme_type = StringName()) const; + int get_theme_font_size(const StringName &p_name, const StringName &p_theme_type = StringName()) const; + Color get_theme_color(const StringName &p_name, const StringName &p_theme_type = StringName()) const; + int get_theme_constant(const StringName &p_name, const StringName &p_theme_type = StringName()) const; bool has_theme_icon_override(const StringName &p_name) const; bool has_theme_stylebox_override(const StringName &p_name) const; @@ -473,12 +471,12 @@ public: bool has_theme_color_override(const StringName &p_name) const; bool has_theme_constant_override(const StringName &p_name) const; - bool has_theme_icon(const StringName &p_name, const StringName &p_node_type = StringName()) const; - bool has_theme_stylebox(const StringName &p_name, const StringName &p_node_type = StringName()) const; - bool has_theme_font(const StringName &p_name, const StringName &p_node_type = StringName()) const; - bool has_theme_font_size(const StringName &p_name, const StringName &p_node_type = StringName()) const; - bool has_theme_color(const StringName &p_name, const StringName &p_node_type = StringName()) const; - bool has_theme_constant(const StringName &p_name, const StringName &p_node_type = StringName()) const; + bool has_theme_icon(const StringName &p_name, const StringName &p_theme_type = StringName()) const; + bool has_theme_stylebox(const StringName &p_name, const StringName &p_theme_type = StringName()) const; + bool has_theme_font(const StringName &p_name, const StringName &p_theme_type = StringName()) const; + bool has_theme_font_size(const StringName &p_name, const StringName &p_theme_type = StringName()) const; + bool has_theme_color(const StringName &p_name, const StringName &p_theme_type = StringName()) const; + bool has_theme_constant(const StringName &p_name, const StringName &p_theme_type = StringName()) const; /* TOOLTIP */ @@ -517,7 +515,7 @@ public: bool is_visibility_clip_disabled() const; virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; - virtual String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; Control() {} }; diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp index fdfbf9eafc..b6884bd37d 100644 --- a/scene/gui/dialogs.cpp +++ b/scene/gui/dialogs.cpp @@ -256,7 +256,7 @@ Button *AcceptDialog::add_button(const String &p_text, bool p_right, const Strin Button *AcceptDialog::add_cancel_button(const String &p_cancel) { String c = p_cancel; if (p_cancel == "") { - c = RTR("Cancel"); + c = TTRC("Cancel"); } Button *b = swap_cancel_ok ? add_button(c, true) : add_button(c); b->connect("pressed", callable_mp(this, &AcceptDialog::_cancel_pressed)); @@ -317,13 +317,13 @@ AcceptDialog::AcceptDialog() { hbc->add_spacer(); ok = memnew(Button); - ok->set_text(RTR("OK")); + ok->set_text(TTRC("OK")); hbc->add_child(ok); hbc->add_spacer(); ok->connect("pressed", callable_mp(this, &AcceptDialog::_ok_pressed)); - set_title(RTR("Alert!")); + set_title(TTRC("Alert!")); connect("window_input", callable_mp(this, &AcceptDialog::_input_from_window)); } @@ -342,7 +342,7 @@ Button *ConfirmationDialog::get_cancel_button() { } ConfirmationDialog::ConfirmationDialog() { - set_title(RTR("Please Confirm...")); + set_title(TTRC("Please Confirm...")); #ifdef TOOLS_ENABLED set_min_size(Size2(200, 70) * EDSCALE); #endif diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index 7453324505..806039d7ac 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -57,6 +57,14 @@ void FileDialog::_theme_changed() { dir_up->add_theme_color_override("icon_hover_color", font_hover_color); dir_up->add_theme_color_override("icon_pressed_color", font_pressed_color); + dir_prev->add_theme_color_override("icon_color_normal", font_color); + dir_prev->add_theme_color_override("icon_color_hover", font_hover_color); + dir_prev->add_theme_color_override("icon_color_pressed", font_pressed_color); + + dir_next->add_theme_color_override("icon_color_normal", font_color); + dir_next->add_theme_color_override("icon_color_hover", font_hover_color); + dir_next->add_theme_color_override("icon_color_pressed", font_pressed_color); + refresh->add_theme_color_override("icon_normal_color", font_color); refresh->add_theme_color_override("icon_hover_color", font_hover_color); refresh->add_theme_color_override("icon_pressed_color", font_pressed_color); @@ -74,6 +82,13 @@ void FileDialog::_notification(int p_what) { } if (p_what == NOTIFICATION_ENTER_TREE) { dir_up->set_icon(vbox->get_theme_icon("parent_folder", "FileDialog")); + if (vbox->is_layout_rtl()) { + dir_prev->set_icon(vbox->get_theme_icon("forward_folder", "FileDialog")); + dir_next->set_icon(vbox->get_theme_icon("back_folder", "FileDialog")); + } else { + dir_prev->set_icon(vbox->get_theme_icon("back_folder", "FileDialog")); + dir_next->set_icon(vbox->get_theme_icon("forward_folder", "FileDialog")); + } refresh->set_icon(vbox->get_theme_icon("reload", "FileDialog")); show_hidden->set_icon(vbox->get_theme_icon("toggle_hidden", "FileDialog")); _theme_changed(); @@ -81,6 +96,8 @@ void FileDialog::_notification(int p_what) { } void FileDialog::_unhandled_input(const Ref<InputEvent> &p_event) { + ERR_FAIL_COND(p_event.is_null()); + Ref<InputEventKey> k = p_event; if (k.is_valid() && has_focus()) { if (k->is_pressed()) { @@ -88,7 +105,7 @@ void FileDialog::_unhandled_input(const Ref<InputEvent> &p_event) { switch (k->get_keycode()) { case KEY_H: { - if (k->get_command()) { + if (k->is_command_pressed()) { set_show_hidden_files(!show_hidden_files); } else { handled = false; @@ -144,6 +161,7 @@ void FileDialog::_dir_entered(String p_dir) { file->set_text(""); invalidate(); update_dir(); + _push_history(); } void FileDialog::_file_entered(const String &p_file) { @@ -177,6 +195,21 @@ void FileDialog::_post_popup() { } else { file_box->set_visible(true); } + + local_history.clear(); + local_history_pos = -1; + _push_history(); +} + +void FileDialog::_push_history() { + local_history.resize(local_history_pos + 1); + String new_path = dir_access->get_current_dir(); + if (local_history.size() == 0 || new_path != local_history[local_history_pos]) { + local_history.push_back(new_path); + local_history_pos++; + dir_prev->set_disabled(local_history_pos == 0); + dir_next->set_disabled(true); + } } void FileDialog::_action_pressed() { @@ -272,7 +305,7 @@ void FileDialog::_action_pressed() { } if (dir_access->file_exists(f)) { - confirm_save->set_text(RTR("File exists, overwrite?")); + confirm_save->set_text(TTRC("File exists, overwrite?")); confirm_save->popup_centered(Size2(200, 80)); } else { emit_signal("file_selected", f); @@ -316,6 +349,35 @@ void FileDialog::_go_up() { dir_access->change_dir(".."); update_file_list(); update_dir(); + _push_history(); +} + +void FileDialog::_go_back() { + if (local_history_pos <= 0) { + return; + } + + local_history_pos--; + dir_access->change_dir(local_history[local_history_pos]); + update_file_list(); + update_dir(); + + dir_prev->set_disabled(local_history_pos == 0); + dir_next->set_disabled(local_history_pos == local_history.size() - 1); +} + +void FileDialog::_go_forward() { + if (local_history_pos == local_history.size() - 1) { + return; + } + + local_history_pos++; + dir_access->change_dir(local_history[local_history_pos]); + update_file_list(); + update_dir(); + + dir_prev->set_disabled(local_history_pos == 0); + dir_next->set_disabled(local_history_pos == local_history.size() - 1); } void FileDialog::deselect_all() { @@ -329,10 +391,10 @@ void FileDialog::deselect_all() { switch (mode) { case FILE_MODE_OPEN_FILE: case FILE_MODE_OPEN_FILES: - get_ok_button()->set_text(RTR("Open")); + get_ok_button()->set_text(TTRC("Open")); break; case FILE_MODE_OPEN_DIR: - get_ok_button()->set_text(RTR("Select Current Folder")); + get_ok_button()->set_text(TTRC("Select Current Folder")); break; case FILE_MODE_OPEN_ANY: case FILE_MODE_SAVE_FILE: @@ -356,7 +418,7 @@ void FileDialog::_tree_selected() { if (!d["dir"]) { file->set_text(d["name"]); } else if (mode == FILE_MODE_OPEN_DIR) { - get_ok_button()->set_text(RTR("Select This Folder")); + get_ok_button()->set_text(TTRC("Select This Folder")); } get_ok_button()->set_disabled(_is_open_should_be_disabled()); @@ -377,6 +439,7 @@ void FileDialog::_tree_item_activated() { } call_deferred("_update_file_list"); call_deferred("_update_dir"); + _push_history(); } else { _action_pressed(); } @@ -415,6 +478,13 @@ void FileDialog::update_file_list() { bool is_hidden; String item; + if (dir_access->is_readable(dir_access->get_current_dir().utf8().get_data())) { + message->hide(); + } else { + message->set_text(TTRC("You don't have permission to access contents of this folder.")); + message->show(); + } + while ((item = dir_access->get_next()) != "") { if (item == "." || item == "..") { continue; @@ -519,8 +589,8 @@ void FileDialog::update_file_list() { files.pop_front(); } - if (tree->get_root() && tree->get_root()->get_children() && tree->get_selected() == nullptr) { - tree->get_root()->get_children()->select(0); + if (tree->get_root() && tree->get_root()->get_first_child() && tree->get_selected() == nullptr) { + tree->get_root()->get_first_child()->select(0); } } @@ -549,7 +619,7 @@ void FileDialog::update_filters() { all_filters += ", ..."; } - filter->add_item(RTR("All Recognized") + " (" + all_filters + ")"); + filter->add_item(String(TTRC("All Recognized")) + " (" + all_filters + ")"); } for (int i = 0; i < filters.size(); i++) { String flt = filters[i].get_slice(";", 0).strip_edges(); @@ -561,7 +631,7 @@ void FileDialog::update_filters() { } } - filter->add_item(RTR("All Files (*)")); + filter->add_item(TTRC("All Files (*)")); } void FileDialog::clear_filters() { @@ -602,6 +672,7 @@ void FileDialog::set_current_dir(const String &p_dir) { dir_access->change_dir(p_dir); update_dir(); invalidate(); + _push_history(); } void FileDialog::set_current_file(const String &p_file) { @@ -646,37 +717,37 @@ void FileDialog::set_file_mode(FileMode p_mode) { mode = p_mode; switch (mode) { case FILE_MODE_OPEN_FILE: - get_ok_button()->set_text(RTR("Open")); + get_ok_button()->set_text(TTRC("Open")); if (mode_overrides_title) { - set_title(RTR("Open a File")); + set_title(TTRC("Open a File")); } makedir->hide(); break; case FILE_MODE_OPEN_FILES: - get_ok_button()->set_text(RTR("Open")); + get_ok_button()->set_text(TTRC("Open")); if (mode_overrides_title) { - set_title(RTR("Open File(s)")); + set_title(TTRC("Open File(s)")); } makedir->hide(); break; case FILE_MODE_OPEN_DIR: - get_ok_button()->set_text(RTR("Select Current Folder")); + get_ok_button()->set_text(TTRC("Select Current Folder")); if (mode_overrides_title) { - set_title(RTR("Open a Directory")); + set_title(TTRC("Open a Directory")); } makedir->show(); break; case FILE_MODE_OPEN_ANY: - get_ok_button()->set_text(RTR("Open")); + get_ok_button()->set_text(TTRC("Open")); if (mode_overrides_title) { - set_title(RTR("Open a File or Directory")); + set_title(TTRC("Open a File or Directory")); } makedir->show(); break; case FILE_MODE_SAVE_FILE: - get_ok_button()->set_text(RTR("Save")); + get_ok_button()->set_text(TTRC("Save")); if (mode_overrides_title) { - set_title(RTR("Save a File")); + set_title(TTRC("Save a File")); } makedir->show(); break; @@ -731,12 +802,13 @@ FileDialog::Access FileDialog::get_access() const { } void FileDialog::_make_dir_confirm() { - Error err = dir_access->make_dir(makedirname->get_text()); + Error err = dir_access->make_dir(makedirname->get_text().strip_edges()); if (err == OK) { - dir_access->change_dir(makedirname->get_text()); + dir_access->change_dir(makedirname->get_text().strip_edges()); invalidate(); update_filters(); update_dir(); + _push_history(); } else { mkdirerr->popup_centered(Size2(250, 50)); } @@ -754,6 +826,7 @@ void FileDialog::_select_drive(int p_idx) { file->set_text(""); invalidate(); update_dir(); + _push_history(); } void FileDialog::_update_drives() { @@ -857,17 +930,27 @@ FileDialog::FileDialog() { vbox->connect("theme_changed", callable_mp(this, &FileDialog::_theme_changed)); mode = FILE_MODE_SAVE_FILE; - set_title(RTR("Save a File")); + set_title(TTRC("Save a File")); HBoxContainer *hbc = memnew(HBoxContainer); + dir_prev = memnew(Button); + dir_prev->set_flat(true); + dir_prev->set_tooltip(TTRC("Go to previous folder.")); + dir_next = memnew(Button); + dir_next->set_flat(true); + dir_next->set_tooltip(TTRC("Go to next folder.")); dir_up = memnew(Button); dir_up->set_flat(true); - dir_up->set_tooltip(RTR("Go to parent folder.")); + dir_up->set_tooltip(TTRC("Go to parent folder.")); + hbc->add_child(dir_prev); + hbc->add_child(dir_next); hbc->add_child(dir_up); + dir_prev->connect("pressed", callable_mp(this, &FileDialog::_go_back)); + dir_next->connect("pressed", callable_mp(this, &FileDialog::_go_forward)); dir_up->connect("pressed", callable_mp(this, &FileDialog::_go_up)); - hbc->add_child(memnew(Label(RTR("Path:")))); + hbc->add_child(memnew(Label(TTRC("Path:")))); drives_container = memnew(HBoxContainer); hbc->add_child(drives_container); @@ -883,7 +966,7 @@ FileDialog::FileDialog() { refresh = memnew(Button); refresh->set_flat(true); - refresh->set_tooltip(RTR("Refresh files.")); + refresh->set_tooltip(TTRC("Refresh files.")); refresh->connect("pressed", callable_mp(this, &FileDialog::update_file_list)); hbc->add_child(refresh); @@ -891,7 +974,7 @@ FileDialog::FileDialog() { show_hidden->set_flat(true); show_hidden->set_toggle_mode(true); show_hidden->set_pressed(is_showing_hidden_files()); - show_hidden->set_tooltip(RTR("Toggle the visibility of hidden files.")); + show_hidden->set_tooltip(TTRC("Toggle the visibility of hidden files.")); show_hidden->connect("toggled", callable_mp(this, &FileDialog::set_show_hidden_files)); hbc->add_child(show_hidden); @@ -899,17 +982,24 @@ FileDialog::FileDialog() { hbc->add_child(shortcuts_container); makedir = memnew(Button); - makedir->set_text(RTR("Create Folder")); + makedir->set_text(TTRC("Create Folder")); makedir->connect("pressed", callable_mp(this, &FileDialog::_make_dir)); hbc->add_child(makedir); vbox->add_child(hbc); tree = memnew(Tree); tree->set_hide_root(true); - vbox->add_margin_child(RTR("Directories & Files:"), tree, true); + vbox->add_margin_child(TTRC("Directories & Files:"), tree, true); + + message = memnew(Label); + message->hide(); + message->set_anchors_and_offsets_preset(Control::PRESET_WIDE); + message->set_align(Label::ALIGN_CENTER); + message->set_valign(Label::VALIGN_CENTER); + tree->add_child(message); file_box = memnew(HBoxContainer); - file_box->add_child(memnew(Label(RTR("File:")))); + file_box->add_child(memnew(Label(TTRC("File:")))); file = memnew(LineEdit); file->set_structured_text_bidi_override(Control::STRUCTURED_TEXT_FILE); file->set_stretch_ratio(4); @@ -941,22 +1031,22 @@ FileDialog::FileDialog() { confirm_save->connect("confirmed", callable_mp(this, &FileDialog::_save_confirm_pressed)); makedialog = memnew(ConfirmationDialog); - makedialog->set_title(RTR("Create Folder")); + makedialog->set_title(TTRC("Create Folder")); VBoxContainer *makevb = memnew(VBoxContainer); makedialog->add_child(makevb); makedirname = memnew(LineEdit); makedirname->set_structured_text_bidi_override(Control::STRUCTURED_TEXT_FILE); - makevb->add_margin_child(RTR("Name:"), makedirname); + makevb->add_margin_child(TTRC("Name:"), makedirname); add_child(makedialog); makedialog->register_text_enter(makedirname); makedialog->connect("confirmed", callable_mp(this, &FileDialog::_make_dir_confirm)); mkdirerr = memnew(AcceptDialog); - mkdirerr->set_text(RTR("Could not create folder.")); + mkdirerr->set_text(TTRC("Could not create folder.")); add_child(mkdirerr); exterr = memnew(AcceptDialog); - exterr->set_text(RTR("Must use a valid extension.")); + exterr->set_text(TTRC("Must use a valid extension.")); add_child(exterr); update_filters(); diff --git a/scene/gui/file_dialog.h b/scene/gui/file_dialog.h index 25b742c234..4996f00cb3 100644 --- a/scene/gui/file_dialog.h +++ b/scene/gui/file_dialog.h @@ -86,6 +86,10 @@ private: DirAccess *dir_access; ConfirmationDialog *confirm_save; + Label *message; + + Button *dir_prev; + Button *dir_next; Button *dir_up; Button *refresh; @@ -93,6 +97,10 @@ private: Vector<String> filters; + Vector<String> local_history; + int local_history_pos = 0; + void _push_history(); + bool mode_overrides_title = true; static bool default_show_hidden_files; @@ -119,6 +127,8 @@ private: void _make_dir(); void _make_dir_confirm(); void _go_up(); + void _go_back(); + void _go_forward(); void _update_drives(); diff --git a/scene/gui/gradient_edit.cpp b/scene/gui/gradient_edit.cpp index 36b383f16c..7278ca6e94 100644 --- a/scene/gui/gradient_edit.cpp +++ b/scene/gui/gradient_edit.cpp @@ -92,6 +92,8 @@ GradientEdit::~GradientEdit() { } void GradientEdit::_gui_input(const Ref<InputEvent> &p_event) { + ERR_FAIL_COND(p_event.is_null()); + Ref<InputEventKey> k = p_event; if (k.is_valid() && k->is_pressed() && k->get_keycode() == KEY_DELETE && grabbed != -1) { @@ -105,7 +107,7 @@ void GradientEdit::_gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseButton> mb = p_event; //Show color picker on double click. - if (mb.is_valid() && mb->get_button_index() == 1 && mb->is_doubleclick() && mb->is_pressed()) { + if (mb.is_valid() && mb->get_button_index() == 1 && mb->is_double_click() && mb->is_pressed()) { grabbed = _get_point_from_pos(mb->get_position().x); _show_color_picker(); accept_event(); @@ -125,7 +127,7 @@ void GradientEdit::_gui_input(const Ref<InputEvent> &p_event) { } //Hold alt key to duplicate selected color - if (mb.is_valid() && mb->get_button_index() == 1 && mb->is_pressed() && mb->get_alt()) { + if (mb.is_valid() && mb->get_button_index() == 1 && mb->is_pressed() && mb->is_alt_pressed()) { int x = mb->get_position().x; grabbed = _get_point_from_pos(x); @@ -234,9 +236,9 @@ void GradientEdit::_gui_input(const Ref<InputEvent> &p_event) { // Snap to "round" coordinates if holding Ctrl. // Be more precise if holding Shift as well - if (mm->get_control()) { - newofs = Math::snapped(newofs, mm->get_shift() ? 0.025 : 0.1); - } else if (mm->get_shift()) { + if (mm->is_ctrl_pressed()) { + newofs = Math::snapped(newofs, mm->is_shift_pressed() ? 0.025 : 0.1); + } else if (mm->is_shift_pressed()) { // Snap to nearest point if holding just Shift const float snap_threshold = 0.03; float smallest_ofs = snap_threshold; diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index 331f0380c5..5a4dacd897 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -154,6 +154,8 @@ Vector2 GraphEditMinimap::_convert_to_graph_position(const Vector2 &p_position) } void GraphEditMinimap::_gui_input(const Ref<InputEvent> &p_ev) { + ERR_FAIL_COND(p_ev.is_null()); + if (!ge->is_minimap_enabled()) { return; } @@ -161,7 +163,7 @@ void GraphEditMinimap::_gui_input(const Ref<InputEvent> &p_ev) { Ref<InputEventMouseButton> mb = p_ev; Ref<InputEventMouseMotion> mm = p_ev; - if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT) { + if (mb.is_valid() && mb->get_button_index() == MOUSE_BUTTON_LEFT) { if (mb->is_pressed()) { is_pressing = true; @@ -180,7 +182,12 @@ void GraphEditMinimap::_gui_input(const Ref<InputEvent> &p_ev) { accept_event(); } else if (mm.is_valid() && is_pressing) { if (is_resizing) { - ge->set_minimap_size(ge->get_minimap_size() - mm->get_relative()); + // Prevent setting minimap wider than GraphEdit + Vector2 new_minimap_size; + new_minimap_size.x = MIN(get_size().x - mm->get_relative().x, ge->get_size().x - 2.0 * minimap_padding.x); + new_minimap_size.y = MIN(get_size().y - mm->get_relative().y, ge->get_size().y - 2.0 * minimap_padding.y); + ge->set_minimap_size(new_minimap_size); + update(); } else { Vector2 click_position = _convert_to_graph_position(mm->get_position() - minimap_padding) - graph_padding; @@ -548,7 +555,7 @@ bool GraphEdit::_filter_input(const Point2 &p_point) { void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { Ref<InputEventMouseButton> mb = p_ev; - if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT && mb->is_pressed()) { + if (mb.is_valid() && mb->get_button_index() == MOUSE_BUTTON_LEFT && mb->is_pressed()) { connecting_valid = false; Ref<Texture2D> port = get_theme_icon("port", "GraphNode"); click_pos = mb->get_position() / zoom; @@ -691,7 +698,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { } } - if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT && !mb->is_pressed()) { + if (mb.is_valid() && mb->get_button_index() == MOUSE_BUTTON_LEFT && !mb->is_pressed()) { if (connecting_valid) { if (connecting && connecting_target) { String from = connecting_from; @@ -1061,8 +1068,10 @@ void GraphEdit::set_selected(Node *p_child) { } void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) { + ERR_FAIL_COND(p_ev.is_null()); + Ref<InputEventMouseMotion> mm = p_ev; - if (mm.is_valid() && (mm->get_button_mask() & BUTTON_MASK_MIDDLE || (mm->get_button_mask() & BUTTON_MASK_LEFT && Input::get_singleton()->is_key_pressed(KEY_SPACE)))) { + if (mm.is_valid() && (mm->get_button_mask() & MOUSE_BUTTON_MASK_MIDDLE || (mm->get_button_mask() & MOUSE_BUTTON_MASK_LEFT && Input::get_singleton()->is_key_pressed(KEY_SPACE)))) { h_scroll->set_value(h_scroll->get_value() - mm->get_relative().x); v_scroll->set_value(v_scroll->get_value() - mm->get_relative().y); } @@ -1082,7 +1091,7 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) { // Snapping can be toggled temporarily by holding down Ctrl. // This is done here as to not toggle the grid when holding down Ctrl. - if (is_using_snap() ^ Input::get_singleton()->is_key_pressed(KEY_CONTROL)) { + if (is_using_snap() ^ Input::get_singleton()->is_key_pressed(KEY_CTRL)) { const int snap = get_snap(); pos = pos.snapped(Vector2(snap, snap)); } @@ -1118,7 +1127,7 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) { } gn->set_selected(box_selection_mode_additive); } else { - bool select = (previus_selected.find(gn) != nullptr); + bool select = (previous_selected.find(gn) != nullptr); if (gn->is_selected() && !select) { emit_signal("node_deselected", gn); } else if (!gn->is_selected() && select) { @@ -1134,7 +1143,7 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) { Ref<InputEventMouseButton> b = p_ev; if (b.is_valid()) { - if (b->get_button_index() == BUTTON_RIGHT && b->is_pressed()) { + if (b->get_button_index() == MOUSE_BUTTON_RIGHT && b->is_pressed()) { if (box_selecting) { box_selecting = false; for (int i = get_child_count() - 1; i >= 0; i--) { @@ -1143,7 +1152,7 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) { continue; } - bool select = (previus_selected.find(gn) != nullptr); + bool select = (previous_selected.find(gn) != nullptr); if (gn->is_selected() && !select) { emit_signal("node_deselected", gn); } else if (!gn->is_selected() && select) { @@ -1164,8 +1173,8 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) { } } - if (b->get_button_index() == BUTTON_LEFT && !b->is_pressed() && dragging) { - if (!just_selected && drag_accum == Vector2() && Input::get_singleton()->is_key_pressed(KEY_CONTROL)) { + if (b->get_button_index() == MOUSE_BUTTON_LEFT && !b->is_pressed() && dragging) { + if (!just_selected && drag_accum == Vector2() && Input::get_singleton()->is_key_pressed(KEY_CTRL)) { //deselect current node for (int i = get_child_count() - 1; i >= 0; i--) { GraphNode *gn = Object::cast_to<GraphNode>(get_child(i)); @@ -1203,7 +1212,7 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) { connections_layer->update(); } - if (b->get_button_index() == BUTTON_LEFT && b->is_pressed()) { + if (b->get_button_index() == MOUSE_BUTTON_LEFT && b->is_pressed()) { GraphNode *gn = nullptr; for (int i = get_child_count() - 1; i >= 0; i--) { @@ -1229,7 +1238,7 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) { dragging = true; drag_accum = Vector2(); just_selected = !gn->is_selected(); - if (!gn->is_selected() && !Input::get_singleton()->is_key_pressed(KEY_CONTROL)) { + if (!gn->is_selected() && !Input::get_singleton()->is_key_pressed(KEY_CTRL)) { for (int i = 0; i < get_child_count(); i++) { GraphNode *o_gn = Object::cast_to<GraphNode>(get_child(i)); if (o_gn) { @@ -1266,31 +1275,31 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) { box_selecting = true; box_selecting_from = b->get_position(); - if (b->get_control()) { + if (b->is_ctrl_pressed()) { box_selection_mode_additive = true; - previus_selected.clear(); + previous_selected.clear(); for (int i = get_child_count() - 1; i >= 0; i--) { GraphNode *gn2 = Object::cast_to<GraphNode>(get_child(i)); if (!gn2 || !gn2->is_selected()) { continue; } - previus_selected.push_back(gn2); + previous_selected.push_back(gn2); } - } else if (b->get_shift()) { + } else if (b->is_shift_pressed()) { box_selection_mode_additive = false; - previus_selected.clear(); + previous_selected.clear(); for (int i = get_child_count() - 1; i >= 0; i--) { GraphNode *gn2 = Object::cast_to<GraphNode>(get_child(i)); if (!gn2 || !gn2->is_selected()) { continue; } - previus_selected.push_back(gn2); + previous_selected.push_back(gn2); } } else { box_selection_mode_additive = true; - previus_selected.clear(); + previous_selected.clear(); for (int i = get_child_count() - 1; i >= 0; i--) { GraphNode *gn2 = Object::cast_to<GraphNode>(get_child(i)); if (!gn2) { @@ -1305,32 +1314,25 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) { } } - if (b->get_button_index() == BUTTON_LEFT && !b->is_pressed() && box_selecting) { + if (b->get_button_index() == MOUSE_BUTTON_LEFT && !b->is_pressed() && box_selecting) { box_selecting = false; - previus_selected.clear(); + box_selecting_rect = Rect2(); + previous_selected.clear(); top_layer->update(); minimap->update(); } - if (b->get_button_index() == BUTTON_WHEEL_UP && b->is_pressed()) { - //too difficult to get right - //set_zoom(zoom*ZOOM_SCALE); - } - - if (b->get_button_index() == BUTTON_WHEEL_DOWN && b->is_pressed()) { - //too difficult to get right - //set_zoom(zoom/ZOOM_SCALE); - } - if (b->get_button_index() == BUTTON_WHEEL_UP && !Input::get_singleton()->is_key_pressed(KEY_SHIFT)) { + if (b->get_button_index() == MOUSE_BUTTON_WHEEL_UP && Input::get_singleton()->is_key_pressed(KEY_CTRL)) { + set_zoom_custom(zoom * ZOOM_SCALE, b->get_position()); + } else if (b->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && Input::get_singleton()->is_key_pressed(KEY_CTRL)) { + set_zoom_custom(zoom / ZOOM_SCALE, b->get_position()); + } else if (b->get_button_index() == MOUSE_BUTTON_WHEEL_UP && !Input::get_singleton()->is_key_pressed(KEY_SHIFT)) { v_scroll->set_value(v_scroll->get_value() - v_scroll->get_page() * b->get_factor() / 8); - } - if (b->get_button_index() == BUTTON_WHEEL_DOWN && !Input::get_singleton()->is_key_pressed(KEY_SHIFT)) { + } else if (b->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && !Input::get_singleton()->is_key_pressed(KEY_SHIFT)) { v_scroll->set_value(v_scroll->get_value() + v_scroll->get_page() * b->get_factor() / 8); - } - if (b->get_button_index() == BUTTON_WHEEL_RIGHT || (b->get_button_index() == BUTTON_WHEEL_DOWN && Input::get_singleton()->is_key_pressed(KEY_SHIFT))) { + } else if (b->get_button_index() == MOUSE_BUTTON_WHEEL_RIGHT || (b->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && Input::get_singleton()->is_key_pressed(KEY_SHIFT))) { h_scroll->set_value(h_scroll->get_value() + h_scroll->get_page() * b->get_factor() / 8); - } - if (b->get_button_index() == BUTTON_WHEEL_LEFT || (b->get_button_index() == BUTTON_WHEEL_UP && Input::get_singleton()->is_key_pressed(KEY_SHIFT))) { + } else if (b->get_button_index() == MOUSE_BUTTON_WHEEL_LEFT || (b->get_button_index() == MOUSE_BUTTON_WHEEL_UP && Input::get_singleton()->is_key_pressed(KEY_SHIFT))) { h_scroll->set_value(h_scroll->get_value() - h_scroll->get_page() * b->get_factor() / 8); } } diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h index 8fdf975319..fa3b113705 100644 --- a/scene/gui/graph_edit.h +++ b/scene/gui/graph_edit.h @@ -150,7 +150,7 @@ private: Point2 box_selecting_from; Point2 box_selecting_to; Rect2 box_selecting_rect; - List<GraphNode *> previus_selected; + List<GraphNode *> previous_selected; bool setting_scroll_ofs = false; bool right_disconnects = false; diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp index b615cdb266..7771970a22 100644 --- a/scene/gui/graph_node.cpp +++ b/scene/gui/graph_node.cpp @@ -32,6 +32,12 @@ #include "core/string/translation.h" +struct _MinSizeCache { + int min_size; + bool will_stretch; + int final_size; +}; + bool GraphNode::_set(const StringName &p_name, const Variant &p_value) { String str = p_name; if (str.begins_with("opentype_features/")) { @@ -171,15 +177,23 @@ void GraphNode::_get_property_list(List<PropertyInfo> *p_list) const { } void GraphNode::_resort() { - int sep = get_theme_constant("separation"); + /** First pass, determine minimum size AND amount of stretchable elements */ + + Size2i new_size = get_size(); Ref<StyleBox> sb = get_theme_stylebox("frame"); - bool first = true; - Size2 minsize; + int sep = get_theme_constant("separation"); + + bool first = true; + int children_count = 0; + int stretch_min = 0; + int stretch_avail = 0; + float stretch_ratio_total = 0; + Map<Control *, _MinSizeCache> min_size_cache; for (int i = 0; i < get_child_count(); i++) { Control *c = Object::cast_to<Control>(get_child(i)); - if (!c) { + if (!c || !c->is_visible_in_tree()) { continue; } if (c->is_set_as_top_level()) { @@ -187,38 +201,120 @@ void GraphNode::_resort() { } Size2i size = c->get_combined_minimum_size(); + _MinSizeCache msc; - minsize.y += size.y; - minsize.x = MAX(minsize.x, size.x); + stretch_min += size.height; + msc.min_size = size.height; + msc.will_stretch = c->get_v_size_flags() & SIZE_EXPAND; - if (first) { - first = false; - } else { - minsize.y += sep; + if (msc.will_stretch) { + stretch_avail += msc.min_size; + stretch_ratio_total += c->get_stretch_ratio(); } + msc.final_size = msc.min_size; + min_size_cache[c] = msc; + children_count++; } - int vofs = 0; - int w = get_size().x - sb->get_minimum_size().x; + if (children_count == 0) { + return; + } + + int stretch_max = new_size.height - (children_count - 1) * sep; + int stretch_diff = stretch_max - stretch_min; + if (stretch_diff < 0) { + //avoid negative stretch space + stretch_diff = 0; + } + + stretch_avail += stretch_diff - sb->get_margin(SIDE_BOTTOM) - sb->get_margin(SIDE_TOP); //available stretch space. + /** Second, pass sucessively to discard elements that can't be stretched, this will run while stretchable + elements exist */ + + while (stretch_ratio_total > 0) { // first of all, don't even be here if no stretchable objects exist + bool refit_successful = true; //assume refit-test will go well + + for (int i = 0; i < get_child_count(); i++) { + Control *c = Object::cast_to<Control>(get_child(i)); + if (!c || !c->is_visible_in_tree()) { + continue; + } + if (c->is_set_as_top_level()) { + continue; + } + ERR_FAIL_COND(!min_size_cache.has(c)); + _MinSizeCache &msc = min_size_cache[c]; + + if (msc.will_stretch) { //wants to stretch + //let's see if it can really stretch + + int final_pixel_size = stretch_avail * c->get_stretch_ratio() / stretch_ratio_total; + if (final_pixel_size < msc.min_size) { + //if available stretching area is too small for widget, + //then remove it from stretching area + msc.will_stretch = false; + stretch_ratio_total -= c->get_stretch_ratio(); + refit_successful = false; + stretch_avail -= msc.min_size; + msc.final_size = msc.min_size; + break; + } else { + msc.final_size = final_pixel_size; + } + } + } + + if (refit_successful) { //uf refit went well, break + break; + } + } + + /** Final pass, draw and stretch elements **/ + + int ofs = sb->get_margin(SIDE_TOP); + + first = true; + int idx = 0; cache_y.clear(); + int w = new_size.width - sb->get_minimum_size().x; + for (int i = 0; i < get_child_count(); i++) { Control *c = Object::cast_to<Control>(get_child(i)); - if (!c) { + if (!c || !c->is_visible_in_tree()) { continue; } if (c->is_set_as_top_level()) { continue; } - Size2i size = c->get_combined_minimum_size(); + _MinSizeCache &msc = min_size_cache[c]; + + if (first) { + first = false; + } else { + ofs += sep; + } - Rect2 r(sb->get_margin(SIDE_LEFT), sb->get_margin(SIDE_TOP) + vofs, w, size.y); + int from = ofs; + int to = ofs + msc.final_size; - fit_child_in_rect(c, r); - cache_y.push_back(vofs + size.y * 0.5); + if (msc.will_stretch && idx == children_count - 1) { + //adjust so the last one always fits perfect + //compensating for numerical imprecision - vofs += size.y + sep; + to = new_size.height - sb->get_margin(SIDE_BOTTOM); + } + + int size = to - from; + + Rect2 rect(sb->get_margin(SIDE_LEFT), from, w, size); + + fit_child_in_rect(c, rect); + cache_y.push_back(from - sb->get_margin(SIDE_TOP) + size * 0.5); + + ofs = to; + idx++; } update(); @@ -363,7 +459,7 @@ void GraphNode::_shape() { } void GraphNode::set_slot(int p_idx, bool p_enable_left, int p_type_left, const Color &p_color_left, bool p_enable_right, int p_type_right, const Color &p_color_right, const Ref<Texture2D> &p_custom_left, const Ref<Texture2D> &p_custom_right) { - ERR_FAIL_COND(p_idx < 0); + ERR_FAIL_COND_MSG(p_idx < 0, vformat("Cannot set slot with p_idx (%d) lesser than zero.", p_idx)); if (!p_enable_left && p_type_left == 0 && p_color_left == Color(1, 1, 1, 1) && !p_enable_right && p_type_right == 0 && p_color_right == Color(1, 1, 1, 1) && @@ -407,6 +503,26 @@ bool GraphNode::is_slot_enabled_left(int p_idx) const { return slot_info[p_idx].enable_left; } +void GraphNode::set_slot_enabled_left(int p_idx, bool p_enable_left) { + ERR_FAIL_COND_MSG(p_idx < 0, vformat("Cannot set enable_left for the slot with p_idx (%d) lesser than zero.", p_idx)); + + slot_info[p_idx].enable_left = p_enable_left; + update(); + connpos_dirty = true; + + emit_signal("slot_updated", p_idx); +} + +void GraphNode::set_slot_type_left(int p_idx, int p_type_left) { + ERR_FAIL_COND_MSG(!slot_info.has(p_idx), vformat("Cannot set type_left for the slot '%d' because it hasn't been enabled.", p_idx)); + + slot_info[p_idx].type_left = p_type_left; + update(); + connpos_dirty = true; + + emit_signal("slot_updated", p_idx); +} + int GraphNode::get_slot_type_left(int p_idx) const { if (!slot_info.has(p_idx)) { return 0; @@ -414,6 +530,16 @@ int GraphNode::get_slot_type_left(int p_idx) const { return slot_info[p_idx].type_left; } +void GraphNode::set_slot_color_left(int p_idx, const Color &p_color_left) { + ERR_FAIL_COND_MSG(!slot_info.has(p_idx), vformat("Cannot set color_left for the slot '%d' because it hasn't been enabled.", p_idx)); + + slot_info[p_idx].color_left = p_color_left; + update(); + connpos_dirty = true; + + emit_signal("slot_updated", p_idx); +} + Color GraphNode::get_slot_color_left(int p_idx) const { if (!slot_info.has(p_idx)) { return Color(1, 1, 1, 1); @@ -428,6 +554,26 @@ bool GraphNode::is_slot_enabled_right(int p_idx) const { return slot_info[p_idx].enable_right; } +void GraphNode::set_slot_enabled_right(int p_idx, bool p_enable_right) { + ERR_FAIL_COND_MSG(p_idx < 0, vformat("Cannot set enable_right for the slot with p_idx (%d) lesser than zero.", p_idx)); + + slot_info[p_idx].enable_right = p_enable_right; + update(); + connpos_dirty = true; + + emit_signal("slot_updated", p_idx); +} + +void GraphNode::set_slot_type_right(int p_idx, int p_type_right) { + ERR_FAIL_COND_MSG(!slot_info.has(p_idx), vformat("Cannot set type_right for the slot '%d' because it hasn't been enabled.", p_idx)); + + slot_info[p_idx].type_right = p_type_right; + update(); + connpos_dirty = true; + + emit_signal("slot_updated", p_idx); +} + int GraphNode::get_slot_type_right(int p_idx) const { if (!slot_info.has(p_idx)) { return 0; @@ -435,6 +581,16 @@ int GraphNode::get_slot_type_right(int p_idx) const { return slot_info[p_idx].type_right; } +void GraphNode::set_slot_color_right(int p_idx, const Color &p_color_right) { + ERR_FAIL_COND_MSG(!slot_info.has(p_idx), vformat("Cannot set color_right for the slot '%d' because it hasn't been enabled.", p_idx)); + + slot_info[p_idx].color_right = p_color_right; + update(); + connpos_dirty = true; + + emit_signal("slot_updated", p_idx); +} + Color GraphNode::get_slot_color_right(int p_idx) const { if (!slot_info.has(p_idx)) { return Color(1, 1, 1, 1); @@ -708,11 +864,13 @@ Color GraphNode::get_connection_output_color(int p_idx) { } void GraphNode::_gui_input(const Ref<InputEvent> &p_ev) { + ERR_FAIL_COND(p_ev.is_null()); + Ref<InputEventMouseButton> mb = p_ev; if (mb.is_valid()) { ERR_FAIL_COND_MSG(get_parent_control() == nullptr, "GraphNode must be the child of a GraphEdit node."); - if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { + if (mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) { Vector2 mpos = Vector2(mb->get_position().x, mb->get_position().y); if (close_rect.size != Size2() && close_rect.has_point(mpos)) { //send focus to parent @@ -735,7 +893,7 @@ void GraphNode::_gui_input(const Ref<InputEvent> &p_ev) { emit_signal("raise_request"); } - if (!mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { + if (!mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) { resizing = false; } } @@ -793,11 +951,23 @@ void GraphNode::_bind_methods() { ClassDB::bind_method(D_METHOD("set_slot", "idx", "enable_left", "type_left", "color_left", "enable_right", "type_right", "color_right", "custom_left", "custom_right"), &GraphNode::set_slot, DEFVAL(Ref<Texture2D>()), DEFVAL(Ref<Texture2D>())); ClassDB::bind_method(D_METHOD("clear_slot", "idx"), &GraphNode::clear_slot); ClassDB::bind_method(D_METHOD("clear_all_slots"), &GraphNode::clear_all_slots); + ClassDB::bind_method(D_METHOD("is_slot_enabled_left", "idx"), &GraphNode::is_slot_enabled_left); + ClassDB::bind_method(D_METHOD("set_slot_enabled_left", "idx", "enable_left"), &GraphNode::set_slot_enabled_left); + + ClassDB::bind_method(D_METHOD("set_slot_type_left", "idx", "type_left"), &GraphNode::set_slot_type_left); ClassDB::bind_method(D_METHOD("get_slot_type_left", "idx"), &GraphNode::get_slot_type_left); + + ClassDB::bind_method(D_METHOD("set_slot_color_left", "idx", "color_left"), &GraphNode::set_slot_color_left); ClassDB::bind_method(D_METHOD("get_slot_color_left", "idx"), &GraphNode::get_slot_color_left); + ClassDB::bind_method(D_METHOD("is_slot_enabled_right", "idx"), &GraphNode::is_slot_enabled_right); + ClassDB::bind_method(D_METHOD("set_slot_enabled_right", "idx", "enable_right"), &GraphNode::set_slot_enabled_right); + + ClassDB::bind_method(D_METHOD("set_slot_type_right", "idx", "type_right"), &GraphNode::set_slot_type_right); ClassDB::bind_method(D_METHOD("get_slot_type_right", "idx"), &GraphNode::get_slot_type_right); + + ClassDB::bind_method(D_METHOD("set_slot_color_right", "idx", "color_right"), &GraphNode::set_slot_color_right); ClassDB::bind_method(D_METHOD("get_slot_color_right", "idx"), &GraphNode::get_slot_color_right); ClassDB::bind_method(D_METHOD("set_position_offset", "offset"), &GraphNode::set_position_offset); diff --git a/scene/gui/graph_node.h b/scene/gui/graph_node.h index 1bc54dddb7..c70f616b47 100644 --- a/scene/gui/graph_node.h +++ b/scene/gui/graph_node.h @@ -113,11 +113,23 @@ public: void set_slot(int p_idx, bool p_enable_left, int p_type_left, const Color &p_color_left, bool p_enable_right, int p_type_right, const Color &p_color_right, const Ref<Texture2D> &p_custom_left = Ref<Texture2D>(), const Ref<Texture2D> &p_custom_right = Ref<Texture2D>()); void clear_slot(int p_idx); void clear_all_slots(); + bool is_slot_enabled_left(int p_idx) const; + void set_slot_enabled_left(int p_idx, bool p_enable_left); + + void set_slot_type_left(int p_idx, int p_type_left); int get_slot_type_left(int p_idx) const; + + void set_slot_color_left(int p_idx, const Color &p_color_left); Color get_slot_color_left(int p_idx) const; + bool is_slot_enabled_right(int p_idx) const; + void set_slot_enabled_right(int p_idx, bool p_enable_right); + + void set_slot_type_right(int p_idx, int p_type_right); int get_slot_type_right(int p_idx) const; + + void set_slot_color_right(int p_idx, const Color &p_color_right); Color get_slot_color_right(int p_idx) const; void set_title(const String &p_title); diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index 7afc04c51c..150980b2e9 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -406,6 +406,9 @@ void ItemList::remove_item(int p_idx) { ERR_FAIL_INDEX(p_idx, items.size()); items.remove(p_idx); + if (current == p_idx) { + current = -1; + } update(); shape_changed = true; defer_select_single = -1; @@ -530,6 +533,8 @@ Size2 ItemList::Item::get_icon_size() const { } void ItemList::_gui_input(const Ref<InputEvent> &p_event) { + ERR_FAIL_COND(p_event.is_null()); + double prev_scroll = scroll_bar->get_value(); Ref<InputEventMouseMotion> mm = p_event; @@ -540,7 +545,7 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseButton> mb = p_event; - if (defer_select_single >= 0 && mb.is_valid() && mb->get_button_index() == BUTTON_LEFT && !mb->is_pressed()) { + if (defer_select_single >= 0 && mb.is_valid() && mb->get_button_index() == MOUSE_BUTTON_LEFT && !mb->is_pressed()) { select(defer_select_single, true); emit_signal("multi_selected", defer_select_single, true); @@ -548,7 +553,7 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) { return; } - if (mb.is_valid() && (mb->get_button_index() == BUTTON_LEFT || (allow_rmb_select && mb->get_button_index() == BUTTON_RIGHT)) && mb->is_pressed()) { + if (mb.is_valid() && (mb->get_button_index() == MOUSE_BUTTON_LEFT || (allow_rmb_select && mb->get_button_index() == MOUSE_BUTTON_RIGHT)) && mb->is_pressed()) { search_string = ""; //any mousepress cancels Vector2 pos = mb->get_position(); Ref<StyleBox> bg = get_theme_stylebox("bg"); @@ -576,11 +581,11 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) { if (closest != -1) { int i = closest; - if (select_mode == SELECT_MULTI && items[i].selected && mb->get_command()) { + if (select_mode == SELECT_MULTI && items[i].selected && mb->is_command_pressed()) { deselect(i); emit_signal("multi_selected", i, false); - } else if (select_mode == SELECT_MULTI && mb->get_shift() && current >= 0 && current < items.size() && current != i) { + } else if (select_mode == SELECT_MULTI && mb->is_shift_pressed() && current >= 0 && current < items.size() && current != i) { int from = current; int to = i; if (i < current) { @@ -594,21 +599,21 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) { } } - if (mb->get_button_index() == BUTTON_RIGHT) { + if (mb->get_button_index() == MOUSE_BUTTON_RIGHT) { emit_signal("item_rmb_selected", i, get_local_mouse_position()); } } else { - if (!mb->is_doubleclick() && !mb->get_command() && select_mode == SELECT_MULTI && items[i].selectable && !items[i].disabled && items[i].selected && mb->get_button_index() == BUTTON_LEFT) { + if (!mb->is_double_click() && !mb->is_command_pressed() && select_mode == SELECT_MULTI && items[i].selectable && !items[i].disabled && items[i].selected && mb->get_button_index() == MOUSE_BUTTON_LEFT) { defer_select_single = i; return; } - if (items[i].selected && mb->get_button_index() == BUTTON_RIGHT) { + if (items[i].selected && mb->get_button_index() == MOUSE_BUTTON_RIGHT) { emit_signal("item_rmb_selected", i, get_local_mouse_position()); } else { bool selected = items[i].selected; - select(i, select_mode == SELECT_SINGLE || !mb->get_command()); + select(i, select_mode == SELECT_SINGLE || !mb->is_command_pressed()); if (!selected || allow_reselect) { if (select_mode == SELECT_SINGLE) { @@ -618,9 +623,9 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) { } } - if (mb->get_button_index() == BUTTON_RIGHT) { + if (mb->get_button_index() == MOUSE_BUTTON_RIGHT) { emit_signal("item_rmb_selected", i, get_local_mouse_position()); - } else if (/*select_mode==SELECT_SINGLE &&*/ mb->is_doubleclick()) { + } else if (/*select_mode==SELECT_SINGLE &&*/ mb->is_double_click()) { emit_signal("item_activated", i); } } @@ -628,7 +633,7 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) { return; } - if (mb->get_button_index() == BUTTON_RIGHT) { + if (mb->get_button_index() == MOUSE_BUTTON_RIGHT) { emit_signal("rmb_clicked", mb->get_position()); return; @@ -637,10 +642,10 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) { // Since closest is null, more likely we clicked on empty space, so send signal to interested controls. Allows, for example, implement items deselecting. emit_signal("nothing_selected"); } - if (mb.is_valid() && mb->get_button_index() == BUTTON_WHEEL_UP && mb->is_pressed()) { + if (mb.is_valid() && mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP && mb->is_pressed()) { scroll_bar->set_value(scroll_bar->get_value() - scroll_bar->get_page() * mb->get_factor() / 8); } - if (mb.is_valid() && mb->get_button_index() == BUTTON_WHEEL_DOWN && mb->is_pressed()) { + if (mb.is_valid() && mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && mb->is_pressed()) { scroll_bar->set_value(scroll_bar->get_value() + scroll_bar->get_page() * mb->get_factor() / 8); } @@ -768,7 +773,7 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) { } } } else if (p_event->is_action("ui_accept")) { - search_string = ""; //any mousepress cance + search_string = ""; //any mousepress cancels if (current >= 0 && current < items.size()) { emit_signal("item_activated", current); diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index b98caf3562..be73fd8f51 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -260,7 +260,8 @@ void Label::_notification(int p_what) { } } } - visible_glyphs = total_glyphs * percent_visible; + + visible_glyphs = MIN(total_glyphs, visible_chars); } Vector2 ofs; @@ -541,6 +542,8 @@ void Label::set_visible_characters(int p_amount) { visible_chars = p_amount; if (get_total_character_count() > 0) { percent_visible = (float)p_amount / (float)get_total_character_count(); + } else { + percent_visible = 1.0; } update(); } diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 3e8ebd2429..98dd9f624b 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -51,13 +51,13 @@ void LineEdit::_swap_current_input_direction() { } else { input_direction = TEXT_DIRECTION_LTR; } - set_cursor_position(get_cursor_position()); + set_caret_column(get_caret_column()); update(); } -void LineEdit::_move_cursor_left(bool p_select, bool p_move_by_word) { +void LineEdit::_move_caret_left(bool p_select, bool p_move_by_word) { if (selection.enabled && !p_select) { - set_cursor_position(selection.begin); + set_caret_column(selection.begin); deselect(); return; } @@ -65,7 +65,7 @@ void LineEdit::_move_cursor_left(bool p_select, bool p_move_by_word) { shift_selection_check_pre(p_select); if (p_move_by_word) { - int cc = cursor_pos; + int cc = caret_column; Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid); for (int i = words.size() - 1; i >= 0; i--) { @@ -75,21 +75,21 @@ void LineEdit::_move_cursor_left(bool p_select, bool p_move_by_word) { } } - set_cursor_position(cc); + set_caret_column(cc); } else { - if (mid_grapheme_caret_enabled) { - set_cursor_position(get_cursor_position() - 1); + if (caret_mid_grapheme_enabled) { + set_caret_column(get_caret_column() - 1); } else { - set_cursor_position(TS->shaped_text_prev_grapheme_pos(text_rid, get_cursor_position())); + set_caret_column(TS->shaped_text_prev_grapheme_pos(text_rid, get_caret_column())); } } shift_selection_check_post(p_select); } -void LineEdit::_move_cursor_right(bool p_select, bool p_move_by_word) { +void LineEdit::_move_caret_right(bool p_select, bool p_move_by_word) { if (selection.enabled && !p_select) { - set_cursor_position(selection.end); + set_caret_column(selection.end); deselect(); return; } @@ -97,7 +97,7 @@ void LineEdit::_move_cursor_right(bool p_select, bool p_move_by_word) { shift_selection_check_pre(p_select); if (p_move_by_word) { - int cc = cursor_pos; + int cc = caret_column; Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid); for (int i = 0; i < words.size(); i++) { @@ -107,27 +107,27 @@ void LineEdit::_move_cursor_right(bool p_select, bool p_move_by_word) { } } - set_cursor_position(cc); + set_caret_column(cc); } else { - if (mid_grapheme_caret_enabled) { - set_cursor_position(get_cursor_position() + 1); + if (caret_mid_grapheme_enabled) { + set_caret_column(get_caret_column() + 1); } else { - set_cursor_position(TS->shaped_text_next_grapheme_pos(text_rid, get_cursor_position())); + set_caret_column(TS->shaped_text_next_grapheme_pos(text_rid, get_caret_column())); } } shift_selection_check_post(p_select); } -void LineEdit::_move_cursor_start(bool p_select) { +void LineEdit::_move_caret_start(bool p_select) { shift_selection_check_pre(p_select); - set_cursor_position(0); + set_caret_column(0); shift_selection_check_post(p_select); } -void LineEdit::_move_cursor_end(bool p_select) { +void LineEdit::_move_caret_end(bool p_select) { shift_selection_check_pre(p_select); - set_cursor_position(text.length()); + set_caret_column(text.length()); shift_selection_check_post(p_select); } @@ -138,7 +138,7 @@ void LineEdit::_backspace(bool p_word, bool p_all_to_left) { if (p_all_to_left) { deselect(); - text = text.substr(0, cursor_pos); + text = text.substr(0, caret_column); _text_changed(); return; } @@ -149,18 +149,19 @@ void LineEdit::_backspace(bool p_word, bool p_all_to_left) { } if (p_word) { - int cc = cursor_pos; + int cc = caret_column; Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid); for (int i = words.size() - 1; i >= 0; i--) { if (words[i].x < cc) { cc = words[i].x; + break; } } - delete_text(cc, cursor_pos); + delete_text(cc, caret_column); - set_cursor_position(cc); + set_caret_column(cc); } else { delete_char(); } @@ -173,9 +174,9 @@ void LineEdit::_delete(bool p_word, bool p_all_to_right) { if (p_all_to_right) { deselect(); - text = text.substr(cursor_pos, text.length() - cursor_pos); + text = text.substr(caret_column, text.length() - caret_column); _shape(); - set_cursor_position(0); + set_caret_column(0); _text_changed(); return; } @@ -187,12 +188,12 @@ void LineEdit::_delete(bool p_word, bool p_all_to_right) { int text_len = text.length(); - if (cursor_pos == text_len) { + if (caret_column == text_len) { return; // Nothing to do. } if (p_word) { - int cc = cursor_pos; + int cc = caret_column; Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid); for (int i = 0; i < words.size(); i++) { if (words[i].y > cc) { @@ -201,20 +202,23 @@ void LineEdit::_delete(bool p_word, bool p_all_to_right) { } } - delete_text(cursor_pos, cc); + delete_text(caret_column, cc); + set_caret_column(caret_column); } else { - if (mid_grapheme_caret_enabled) { - set_cursor_position(cursor_pos + 1); + if (caret_mid_grapheme_enabled) { + set_caret_column(caret_column + 1); delete_char(); } else { - int cc = cursor_pos; - set_cursor_position(TS->shaped_text_next_grapheme_pos(text_rid, cursor_pos)); - delete_text(cc, cursor_pos); + int cc = caret_column; + set_caret_column(TS->shaped_text_next_grapheme_pos(text_rid, caret_column)); + delete_text(cc, caret_column); } } } void LineEdit::_gui_input(Ref<InputEvent> p_event) { + ERR_FAIL_COND(p_event.is_null()); + Ref<InputEventMouseButton> b = p_event; if (b.is_valid()) { @@ -222,7 +226,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { // Ignore mouse clicks in IME input mode. return; } - if (b->is_pressed() && b->get_button_index() == BUTTON_RIGHT && context_menu_enabled) { + if (b->is_pressed() && b->get_button_index() == MOUSE_BUTTON_RIGHT && context_menu_enabled) { menu->set_position(get_screen_transform().xform(get_local_mouse_position())); menu->set_size(Vector2(1, 1)); _generate_context_menu(); @@ -232,7 +236,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { return; } - if (b->get_button_index() != BUTTON_LEFT) { + if (b->get_button_index() != MOUSE_BUTTON_LEFT) { return; } @@ -246,35 +250,35 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { return; } - shift_selection_check_pre(b->get_shift()); + shift_selection_check_pre(b->is_shift_pressed()); - set_cursor_at_pixel_pos(b->get_position().x); + set_caret_at_pixel_pos(b->get_position().x); - if (b->get_shift()) { - selection_fill_at_cursor(); + if (b->is_shift_pressed()) { + selection_fill_at_caret(); selection.creating = true; } else { if (selecting_enabled) { - if (!b->is_doubleclick() && (OS::get_singleton()->get_ticks_msec() - selection.last_dblclk) < 600) { + if (!b->is_double_click() && (OS::get_singleton()->get_ticks_msec() - selection.last_dblclk) < 600) { // Triple-click select all. selection.enabled = true; selection.begin = 0; selection.end = text.length(); - selection.doubleclick = true; + selection.double_click = true; selection.last_dblclk = 0; - cursor_pos = selection.begin; - } else if (b->is_doubleclick()) { + caret_column = selection.begin; + } else if (b->is_double_click()) { // Double-click select word. Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid); for (int i = 0; i < words.size(); i++) { - if (words[i].x < cursor_pos && words[i].y > cursor_pos) { + if (words[i].x < caret_column && words[i].y > caret_column) { selection.enabled = true; selection.begin = words[i].x; selection.end = words[i].y; - selection.doubleclick = true; + selection.double_click = true; selection.last_dblclk = OS::get_singleton()->get_ticks_msec(); - cursor_pos = selection.end; + caret_column = selection.end; break; } } @@ -283,9 +287,9 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { selection.drag_attempt = false; - if ((cursor_pos < selection.begin) || (cursor_pos > selection.end) || !selection.enabled) { + if ((caret_column < selection.begin) || (caret_column > selection.end) || !selection.enabled) { deselect(); - selection.cursor_start = cursor_pos; + selection.start_column = caret_column; selection.creating = true; } else if (selection.enabled) { selection.drag_attempt = true; @@ -304,11 +308,11 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { } } - if ((!selection.creating) && (!selection.doubleclick)) { + if ((!selection.creating) && (!selection.double_click)) { deselect(); } selection.creating = false; - selection.doubleclick = false; + selection.double_click = false; show_virtual_keyboard(); } @@ -327,10 +331,10 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { } } - if (m->get_button_mask() & BUTTON_LEFT) { + if (m->get_button_mask() & MOUSE_BUTTON_LEFT) { if (selection.creating) { - set_cursor_at_pixel_pos(m->get_position().x); - selection_fill_at_cursor(); + set_caret_at_pixel_pos(m->get_position().x); + selection_fill_at_caret(); } } } @@ -344,7 +348,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { if (context_menu_enabled) { if (k->is_action("ui_menu", true)) { - Point2 pos = Point2(get_cursor_pixel_pos().x, (get_size().y + get_theme_font("font")->get_height(get_theme_font_size("font_size"))) / 2); + Point2 pos = Point2(get_caret_pixel_pos().x, (get_size().y + get_theme_font("font")->get_height(get_theme_font_size("font_size"))) / 2); menu->set_position(get_global_transform().xform(pos)); menu->set_size(Vector2(1, 1)); _generate_context_menu(); @@ -438,39 +442,39 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { // Cursor Movement k = k->duplicate(); - bool shift_pressed = k->get_shift(); + bool shift_pressed = k->is_shift_pressed(); // Remove shift or else actions will not match. Use above variable for selection. - k->set_shift(false); + k->set_shift_pressed(false); if (k->is_action("ui_text_caret_word_left", true)) { - _move_cursor_left(shift_pressed, true); + _move_caret_left(shift_pressed, true); accept_event(); return; } if (k->is_action("ui_text_caret_left", true)) { - _move_cursor_left(shift_pressed); + _move_caret_left(shift_pressed); accept_event(); return; } if (k->is_action("ui_text_caret_word_right", true)) { - _move_cursor_right(shift_pressed, true); + _move_caret_right(shift_pressed, true); accept_event(); return; } if (k->is_action("ui_text_caret_right", true)) { - _move_cursor_right(shift_pressed, false); + _move_caret_right(shift_pressed, false); accept_event(); return; } // Up = Home, Down = End if (k->is_action("ui_text_caret_up", true) || k->is_action("ui_text_caret_line_start", true) || k->is_action("ui_text_caret_page_up", true)) { - _move_cursor_start(shift_pressed); + _move_caret_start(shift_pressed); accept_event(); return; } if (k->is_action("ui_text_caret_down", true) || k->is_action("ui_text_caret_line_end", true) || k->is_action("ui_text_caret_page_down", true)) { - _move_cursor_end(shift_pressed); + _move_caret_end(shift_pressed); accept_event(); return; } @@ -486,14 +490,14 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { // Allow unicode handling if: // * No Modifiers are pressed (except shift) - bool allow_unicode_handling = !(k->get_command() || k->get_control() || k->get_alt() || k->get_metakey()); + bool allow_unicode_handling = !(k->is_command_pressed() || k->is_ctrl_pressed() || k->is_alt_pressed() || k->is_meta_pressed()); if (allow_unicode_handling && editable && k->get_unicode() >= 32) { // Handle Unicode (if no modifiers active) selection_delete(); char32_t ucodestr[2] = { (char32_t)k->get_unicode(), 0 }; int prev_len = text.length(); - append_at_cursor(ucodestr); + insert_text_at_caret(ucodestr); if (text.length() != prev_len) { _text_changed(); } @@ -540,20 +544,20 @@ void LineEdit::drop_data(const Point2 &p_point, const Variant &p_data) { Control::drop_data(p_point, p_data); if (p_data.get_type() == Variant::STRING) { - set_cursor_at_pixel_pos(p_point.x); + set_caret_at_pixel_pos(p_point.x); int selected = selection.end - selection.begin; text.erase(selection.begin, selected); _shape(); - append_at_cursor(p_data); - selection.begin = cursor_pos - selected; - selection.end = cursor_pos; + insert_text_at_caret(p_data); + selection.begin = caret_column - selected; + selection.end = caret_column; } } Control::CursorShape LineEdit::get_cursor_shape(const Point2 &p_pos) const { - if (!text.is_empty() && is_editable() && _is_over_clear_button(p_pos)) { + if ((!text.is_empty() && is_editable() && _is_over_clear_button(p_pos)) || (!is_editable() && (!is_selecting_enabled() || text.is_empty()))) { return CURSOR_ARROW; } return Control::get_cursor_shape(p_pos); @@ -573,8 +577,8 @@ void LineEdit::_notification(int p_what) { #ifdef TOOLS_ENABLED case NOTIFICATION_ENTER_TREE: { if (Engine::get_singleton()->is_editor_hint() && !get_tree()->is_node_being_edited(this)) { - cursor_set_blink_enabled(EDITOR_DEF("text_editor/cursor/caret_blink", false)); - cursor_set_blink_speed(EDITOR_DEF("text_editor/cursor/caret_blink_speed", 0.65)); + set_caret_blink_enabled(EDITOR_DEF("text_editor/cursor/caret_blink", false)); + set_caret_blink_speed(EDITOR_DEF("text_editor/cursor/caret_blink_speed", 0.65)); if (!EditorSettings::get_singleton()->is_connected("settings_changed", callable_mp(this, &LineEdit::_editor_settings_changed))) { EditorSettings::get_singleton()->connect("settings_changed", callable_mp(this, &LineEdit::_editor_settings_changed)); @@ -585,7 +589,7 @@ void LineEdit::_notification(int p_what) { case NOTIFICATION_RESIZED: { _fit_to_width(); scroll_offset = 0; - set_cursor_position(get_cursor_position()); + set_caret_column(get_caret_column()); } break; case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: case NOTIFICATION_THEME_CHANGED: { @@ -672,7 +676,7 @@ void LineEdit::_notification(int p_what) { Color selection_color = get_theme_color("selection_color"); Color font_color = is_editable() ? get_theme_color("font_color") : get_theme_color("font_uneditable_color"); Color font_selected_color = get_theme_color("font_selected_color"); - Color cursor_color = get_theme_color("cursor_color"); + Color caret_color = get_theme_color("caret_color"); // Draw placeholder color. if (using_placeholder) { @@ -776,7 +780,7 @@ void LineEdit::_notification(int p_what) { // Normal caret. Rect2 l_caret, t_caret; TextServer::Direction l_dir, t_dir; - TS->shaped_text_get_carets(text_rid, cursor_pos, l_caret, l_dir, t_caret, t_dir); + TS->shaped_text_get_carets(text_rid, caret_column, l_caret, l_dir, t_caret, t_dir); if (l_caret == Rect2() && t_caret == Rect2()) { // No carets, add one at the start. @@ -789,28 +793,28 @@ void LineEdit::_notification(int p_what) { l_dir = TextServer::DIRECTION_LTR; l_caret = Rect2(Vector2(x_ofs, y), Size2(caret_width, h)); } - RenderingServer::get_singleton()->canvas_item_add_rect(ci, l_caret, cursor_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, l_caret, caret_color); } else { if (l_caret != Rect2() && l_dir == TextServer::DIRECTION_AUTO) { // Draw extra marker on top of mid caret. Rect2 trect = Rect2(l_caret.position.x - 3 * caret_width, l_caret.position.y, 6 * caret_width, caret_width); trect.position += ofs; - RenderingServer::get_singleton()->canvas_item_add_rect(ci, trect, cursor_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, trect, caret_color); } l_caret.position += ofs; l_caret.size.x = caret_width; - RenderingServer::get_singleton()->canvas_item_add_rect(ci, l_caret, cursor_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, l_caret, caret_color); t_caret.position += ofs; t_caret.size.x = caret_width; - RenderingServer::get_singleton()->canvas_item_add_rect(ci, t_caret, cursor_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, t_caret, caret_color); } } else { { - // IME intermidiet text range. - Vector<Vector2> sel = TS->shaped_text_get_selection(text_rid, cursor_pos, cursor_pos + ime_text.length()); + // IME intermediate text range. + Vector<Vector2> sel = TS->shaped_text_get_selection(text_rid, caret_column, caret_column + ime_text.length()); for (int i = 0; i < sel.size(); i++) { Rect2 rect = Rect2(sel[i].x + ofs.x, ofs.y, sel[i].y - sel[i].x, text_height); if (rect.position.x + rect.size.x <= x_ofs || rect.position.x > ofs_max) { @@ -823,12 +827,12 @@ void LineEdit::_notification(int p_what) { rect.size.x = ofs_max - rect.position.x; } rect.size.y = caret_width; - RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, cursor_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, caret_color); } } { // IME caret. - Vector<Vector2> sel = TS->shaped_text_get_selection(text_rid, cursor_pos + ime_selection.x, cursor_pos + ime_selection.x + ime_selection.y); + Vector<Vector2> sel = TS->shaped_text_get_selection(text_rid, caret_column + ime_selection.x, caret_column + ime_selection.x + ime_selection.y); for (int i = 0; i < sel.size(); i++) { Rect2 rect = Rect2(sel[i].x + ofs.x, ofs.y, sel[i].y - sel[i].x, text_height); if (rect.position.x + rect.size.x <= x_ofs || rect.position.x > ofs_max) { @@ -841,14 +845,14 @@ void LineEdit::_notification(int p_what) { rect.size.x = ofs_max - rect.position.x; } rect.size.y = caret_width * 3; - RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, cursor_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, caret_color); } } } } if (has_focus()) { - if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID) { + if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) { DisplayServer::get_singleton()->window_set_ime_active(true, get_viewport()->get_window_id()); DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + Point2(using_placeholder ? 0 : x_ofs, y_ofs + TS->shaped_text_get_size(text_rid).y), get_viewport()->get_window_id()); } @@ -865,10 +869,10 @@ void LineEdit::_notification(int p_what) { } } - if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID) { + if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) { DisplayServer::get_singleton()->window_set_ime_active(true, get_viewport()->get_window_id()); - Point2 cursor_pos = Point2(get_cursor_position(), 1) * get_minimum_size().height; - DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + cursor_pos, get_viewport()->get_window_id()); + Point2 caret_column = Point2(get_caret_column(), 1) * get_minimum_size().height; + DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + caret_column, get_viewport()->get_window_id()); } show_virtual_keyboard(); @@ -878,14 +882,14 @@ void LineEdit::_notification(int p_what) { caret_blink_timer->stop(); } - if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID) { + if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) { DisplayServer::get_singleton()->window_set_ime_position(Point2(), get_viewport()->get_window_id()); DisplayServer::get_singleton()->window_set_ime_active(false, get_viewport()->get_window_id()); } ime_text = ""; ime_selection = Point2(); _shape(); - set_cursor_position(cursor_pos); // Update scroll_offset + set_caret_column(caret_column); // Update scroll_offset if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) { DisplayServer::get_singleton()->virtual_keyboard_hide(); @@ -897,7 +901,7 @@ void LineEdit::_notification(int p_what) { ime_text = DisplayServer::get_singleton()->ime_get_text(); ime_selection = DisplayServer::get_singleton()->ime_get_selection(); _shape(); - set_cursor_position(cursor_pos); // Update scroll_offset + set_caret_column(caret_column); // Update scroll_offset update(); } @@ -931,7 +935,7 @@ void LineEdit::paste_text() { if (selection.enabled) { selection_delete(); } - append_at_cursor(paste_buffer); + insert_text_at_caret(paste_buffer); if (!text_changed_dirty) { if (is_inside_tree() && text.length() != prev_len) { @@ -959,7 +963,7 @@ void LineEdit::undo() { TextOperation op = undo_stack_pos->get(); text = op.text; scroll_offset = op.scroll_offset; - set_cursor_position(op.cursor_pos); + set_caret_column(op.caret_column); _shape(); _emit_text_change(); @@ -980,7 +984,7 @@ void LineEdit::redo() { TextOperation op = undo_stack_pos->get(); text = op.text; scroll_offset = op.scroll_offset; - set_cursor_position(op.cursor_pos); + set_caret_column(op.caret_column); _shape(); _emit_text_change(); @@ -988,7 +992,7 @@ void LineEdit::redo() { void LineEdit::shift_selection_check_pre(bool p_shift) { if (!selection.enabled && p_shift) { - selection.cursor_start = cursor_pos; + selection.start_column = caret_column; } if (!p_shift) { deselect(); @@ -997,11 +1001,11 @@ void LineEdit::shift_selection_check_pre(bool p_shift) { void LineEdit::shift_selection_check_post(bool p_shift) { if (p_shift) { - selection_fill_at_cursor(); + selection_fill_at_caret(); } } -void LineEdit::set_cursor_at_pixel_pos(int p_x) { +void LineEdit::set_caret_at_pixel_pos(int p_x) { Ref<StyleBox> style = get_theme_stylebox("normal"); bool rtl = is_layout_rtl(); @@ -1046,10 +1050,10 @@ void LineEdit::set_cursor_at_pixel_pos(int p_x) { } int ofs = TS->shaped_text_hit_test_position(text_rid, p_x - x_ofs - scroll_offset); - set_cursor_position(ofs); + set_caret_column(ofs); } -Vector2i LineEdit::get_cursor_pixel_pos() { +Vector2i LineEdit::get_caret_pixel_pos() { Ref<StyleBox> style = get_theme_stylebox("normal"); bool rtl = is_layout_rtl(); @@ -1098,9 +1102,9 @@ Vector2i LineEdit::get_cursor_pixel_pos() { TextServer::Direction l_dir, t_dir; // Get position of the start of caret. if (ime_text.length() != 0 && ime_selection.x != 0) { - TS->shaped_text_get_carets(text_rid, cursor_pos + ime_selection.x, l_caret, l_dir, t_caret, t_dir); + TS->shaped_text_get_carets(text_rid, caret_column + ime_selection.x, l_caret, l_dir, t_caret, t_dir); } else { - TS->shaped_text_get_carets(text_rid, cursor_pos, l_caret, l_dir, t_caret, t_dir); + TS->shaped_text_get_carets(text_rid, caret_column, l_caret, l_dir, t_caret, t_dir); } if ((l_caret != Rect2() && (l_dir == TextServer::DIRECTION_AUTO || l_dir == (TextServer::Direction)input_direction)) || (t_caret == Rect2())) { @@ -1112,9 +1116,9 @@ Vector2i LineEdit::get_cursor_pixel_pos() { // Get position of the end of caret. if (ime_text.length() != 0) { if (ime_selection.y != 0) { - TS->shaped_text_get_carets(text_rid, cursor_pos + ime_selection.x + ime_selection.y, l_caret, l_dir, t_caret, t_dir); + TS->shaped_text_get_carets(text_rid, caret_column + ime_selection.x + ime_selection.y, l_caret, l_dir, t_caret, t_dir); } else { - TS->shaped_text_get_carets(text_rid, cursor_pos + ime_text.size(), l_caret, l_dir, t_caret, t_dir); + TS->shaped_text_get_carets(text_rid, caret_column + ime_text.size(), l_caret, l_dir, t_caret, t_dir); } if ((l_caret != Rect2() && (l_dir == TextServer::DIRECTION_AUTO || l_dir == (TextServer::Direction)input_direction)) || (t_caret == Rect2())) { ret.y = x_ofs + l_caret.position.x + scroll_offset; @@ -1128,19 +1132,19 @@ Vector2i LineEdit::get_cursor_pixel_pos() { return ret; } -void LineEdit::set_mid_grapheme_caret_enabled(const bool p_enabled) { - mid_grapheme_caret_enabled = p_enabled; +void LineEdit::set_caret_mid_grapheme_enabled(const bool p_enabled) { + caret_mid_grapheme_enabled = p_enabled; } -bool LineEdit::get_mid_grapheme_caret_enabled() const { - return mid_grapheme_caret_enabled; +bool LineEdit::is_caret_mid_grapheme_enabled() const { + return caret_mid_grapheme_enabled; } -bool LineEdit::cursor_get_blink_enabled() const { +bool LineEdit::is_caret_blink_enabled() const { return caret_blink_enabled; } -void LineEdit::cursor_set_blink_enabled(const bool p_enabled) { +void LineEdit::set_caret_blink_enabled(const bool p_enabled) { caret_blink_enabled = p_enabled; if (has_focus() || caret_force_displayed) { @@ -1154,23 +1158,25 @@ void LineEdit::cursor_set_blink_enabled(const bool p_enabled) { } draw_caret = true; + + notify_property_list_changed(); } -bool LineEdit::cursor_get_force_displayed() const { +bool LineEdit::is_caret_force_displayed() const { return caret_force_displayed; } -void LineEdit::cursor_set_force_displayed(const bool p_enabled) { +void LineEdit::set_caret_force_displayed(const bool p_enabled) { caret_force_displayed = p_enabled; - cursor_set_blink_enabled(caret_blink_enabled); + set_caret_blink_enabled(caret_blink_enabled); update(); } -float LineEdit::cursor_get_blink_speed() const { +float LineEdit::get_caret_blink_speed() const { return caret_blink_timer->get_wait_time(); } -void LineEdit::cursor_set_blink_speed(const float p_speed) { +void LineEdit::set_caret_blink_speed(const float p_speed) { ERR_FAIL_COND(p_speed <= 0); caret_blink_timer->set_wait_time(p_speed); } @@ -1194,14 +1200,14 @@ void LineEdit::_toggle_draw_caret() { } void LineEdit::delete_char() { - if ((text.length() <= 0) || (cursor_pos == 0)) { + if ((text.length() <= 0) || (caret_column == 0)) { return; } - text.erase(cursor_pos - 1, 1); + text.erase(caret_column - 1, 1); _shape(); - set_cursor_position(get_cursor_position() - 1); + set_caret_column(get_caret_column() - 1); _text_changed(); } @@ -1213,10 +1219,10 @@ void LineEdit::delete_text(int p_from_column, int p_to_column) { text.erase(p_from_column, p_to_column - p_from_column); _shape(); - cursor_pos -= CLAMP(cursor_pos - p_from_column, 0, p_to_column - p_from_column); + caret_column -= CLAMP(caret_column - p_from_column, 0, p_to_column - p_from_column); - if (cursor_pos >= text.length()) { - cursor_pos = text.length(); + if (caret_column >= text.length()) { + caret_column = text.length(); } if (!text_changed_dirty) { @@ -1229,10 +1235,11 @@ void LineEdit::delete_text(int p_from_column, int p_to_column) { void LineEdit::set_text(String p_text) { clear_internal(); - append_at_cursor(p_text); + insert_text_at_caret(p_text); + _create_undo_state(); update(); - cursor_pos = 0; + caret_column = 0; scroll_offset = 0; } @@ -1342,7 +1349,7 @@ void LineEdit::show_virtual_keyboard() { if (selection.enabled) { DisplayServer::get_singleton()->virtual_keyboard_show(text, get_global_rect(), false, max_length, selection.begin, selection.end); } else { - DisplayServer::get_singleton()->virtual_keyboard_show(text, get_global_rect(), false, max_length, cursor_pos); + DisplayServer::get_singleton()->virtual_keyboard_show(text, get_global_rect(), false, max_length, caret_column); } } } @@ -1371,16 +1378,16 @@ float LineEdit::get_placeholder_alpha() const { return placeholder_alpha; } -void LineEdit::set_cursor_position(int p_pos) { - if (p_pos > (int)text.length()) { - p_pos = text.length(); +void LineEdit::set_caret_column(int p_column) { + if (p_column > (int)text.length()) { + p_column = text.length(); } - if (p_pos < 0) { - p_pos = 0; + if (p_column < 0) { + p_column = 0; } - cursor_pos = p_pos; + caret_column = p_column; // Fit to window. @@ -1434,8 +1441,8 @@ void LineEdit::set_cursor_position(int p_pos) { ofs_max -= r_icon->get_width(); } - // Note: Use too coordinates to fit IME input range. - Vector2i primary_catret_offset = get_cursor_pixel_pos(); + // Note: Use two coordinates to fit IME input range. + Vector2i primary_catret_offset = get_caret_pixel_pos(); if (MIN(primary_catret_offset.x, primary_catret_offset.y) <= x_ofs) { scroll_offset += (x_ofs - MIN(primary_catret_offset.x, primary_catret_offset.y)); @@ -1447,8 +1454,8 @@ void LineEdit::set_cursor_position(int p_pos) { update(); } -int LineEdit::get_cursor_position() const { - return cursor_pos; +int LineEdit::get_caret_column() const { + return caret_column; } void LineEdit::set_scroll_offset(int p_pos) { @@ -1462,17 +1469,17 @@ int LineEdit::get_scroll_offset() const { return scroll_offset; } -void LineEdit::append_at_cursor(String p_text) { +void LineEdit::insert_text_at_caret(String p_text) { if ((max_length <= 0) || (text.length() + p_text.length() <= max_length)) { - String pre = text.substr(0, cursor_pos); - String post = text.substr(cursor_pos, text.length() - cursor_pos); + String pre = text.substr(0, caret_column); + String post = text.substr(caret_column, text.length() - caret_column); text = pre + p_text + post; _shape(); - TextServer::Direction dir = TS->shaped_text_get_dominant_direciton_in_range(text_rid, cursor_pos, cursor_pos + p_text.length()); + TextServer::Direction dir = TS->shaped_text_get_dominant_direciton_in_range(text_rid, caret_column, caret_column + p_text.length()); if (dir != TextServer::DIRECTION_AUTO) { input_direction = (TextDirection)dir; } - set_cursor_position(cursor_pos + p_text.length()); + set_caret_column(caret_column + p_text.length()); } else { emit_signal("text_change_rejected"); } @@ -1481,7 +1488,7 @@ void LineEdit::append_at_cursor(String p_text) { void LineEdit::clear_internal() { deselect(); _clear_undo_stack(); - cursor_pos = 0; + caret_column = 0; scroll_offset = 0; undo_text = ""; text = ""; @@ -1501,7 +1508,7 @@ Size2 LineEdit::get_minimum_size() const { min_size.width = get_theme_constant("minimum_character_width") * em_space_size; if (expand_to_text_length) { - // Add a space because some fonts are too exact, and because cursor needs a bit more when at the end. + // Add a space because some fonts are too exact, and because caret needs a bit more when at the end. min_size.width = MAX(min_size.width, full_width + em_space_size); } @@ -1522,10 +1529,10 @@ Size2 LineEdit::get_minimum_size() const { void LineEdit::deselect() { selection.begin = 0; selection.end = 0; - selection.cursor_start = 0; + selection.start_column = 0; selection.enabled = false; selection.creating = false; - selection.doubleclick = false; + selection.double_click = false; update(); } @@ -1547,13 +1554,13 @@ int LineEdit::get_max_length() const { return max_length; } -void LineEdit::selection_fill_at_cursor() { +void LineEdit::selection_fill_at_caret() { if (!selecting_enabled) { return; } - selection.begin = cursor_pos; - selection.end = selection.cursor_start; + selection.begin = caret_column; + selection.end = selection.start_column; if (selection.end < selection.begin) { int aux = selection.end; @@ -1652,7 +1659,7 @@ void LineEdit::select(int p_from, int p_to) { selection.begin = p_from; selection.end = p_to; selection.creating = false; - selection.doubleclick = false; + selection.double_click = false; update(); } @@ -1710,82 +1717,82 @@ void LineEdit::menu_option(int p_option) { } break; case MENU_INSERT_LRM: { if (editable) { - append_at_cursor(String::chr(0x200E)); + insert_text_at_caret(String::chr(0x200E)); } } break; case MENU_INSERT_RLM: { if (editable) { - append_at_cursor(String::chr(0x200F)); + insert_text_at_caret(String::chr(0x200F)); } } break; case MENU_INSERT_LRE: { if (editable) { - append_at_cursor(String::chr(0x202A)); + insert_text_at_caret(String::chr(0x202A)); } } break; case MENU_INSERT_RLE: { if (editable) { - append_at_cursor(String::chr(0x202B)); + insert_text_at_caret(String::chr(0x202B)); } } break; case MENU_INSERT_LRO: { if (editable) { - append_at_cursor(String::chr(0x202D)); + insert_text_at_caret(String::chr(0x202D)); } } break; case MENU_INSERT_RLO: { if (editable) { - append_at_cursor(String::chr(0x202E)); + insert_text_at_caret(String::chr(0x202E)); } } break; case MENU_INSERT_PDF: { if (editable) { - append_at_cursor(String::chr(0x202C)); + insert_text_at_caret(String::chr(0x202C)); } } break; case MENU_INSERT_ALM: { if (editable) { - append_at_cursor(String::chr(0x061C)); + insert_text_at_caret(String::chr(0x061C)); } } break; case MENU_INSERT_LRI: { if (editable) { - append_at_cursor(String::chr(0x2066)); + insert_text_at_caret(String::chr(0x2066)); } } break; case MENU_INSERT_RLI: { if (editable) { - append_at_cursor(String::chr(0x2067)); + insert_text_at_caret(String::chr(0x2067)); } } break; case MENU_INSERT_FSI: { if (editable) { - append_at_cursor(String::chr(0x2068)); + insert_text_at_caret(String::chr(0x2068)); } } break; case MENU_INSERT_PDI: { if (editable) { - append_at_cursor(String::chr(0x2069)); + insert_text_at_caret(String::chr(0x2069)); } } break; case MENU_INSERT_ZWJ: { if (editable) { - append_at_cursor(String::chr(0x200D)); + insert_text_at_caret(String::chr(0x200D)); } } break; case MENU_INSERT_ZWNJ: { if (editable) { - append_at_cursor(String::chr(0x200C)); + insert_text_at_caret(String::chr(0x200C)); } } break; case MENU_INSERT_WJ: { if (editable) { - append_at_cursor(String::chr(0x2060)); + insert_text_at_caret(String::chr(0x2060)); } } break; case MENU_INSERT_SHY: { if (editable) { - append_at_cursor(String::chr(0x00AD)); + insert_text_at_caret(String::chr(0x00AD)); } } } @@ -1805,18 +1812,18 @@ PopupMenu *LineEdit::get_menu() const { void LineEdit::_editor_settings_changed() { #ifdef TOOLS_ENABLED - cursor_set_blink_enabled(EDITOR_DEF("text_editor/cursor/caret_blink", false)); - cursor_set_blink_speed(EDITOR_DEF("text_editor/cursor/caret_blink_speed", 0.65)); + set_caret_blink_enabled(EDITOR_DEF("text_editor/cursor/caret_blink", false)); + set_caret_blink_speed(EDITOR_DEF("text_editor/cursor/caret_blink_speed", 0.65)); #endif } -void LineEdit::set_expand_to_text_length(bool p_enabled) { +void LineEdit::set_expand_to_text_length_enabled(bool p_enabled) { expand_to_text_length = p_enabled; minimum_size_changed(); - set_cursor_position(cursor_pos); + set_caret_column(caret_column); } -bool LineEdit::get_expand_to_text_length() const { +bool LineEdit::is_expand_to_text_length_enabled() const { return expand_to_text_length; } @@ -1901,7 +1908,7 @@ void LineEdit::_shape() { t = secret_character.repeat(text.length() + ime_text.length()); } else { if (ime_text.length() > 0) { - t = text.substr(0, cursor_pos) + ime_text + text.substr(cursor_pos, text.length()); + t = text.substr(0, caret_column) + ime_text + text.substr(caret_column, text.length()); } else { t = text; } @@ -1966,7 +1973,7 @@ void LineEdit::_clear_undo_stack() { void LineEdit::_create_undo_state() { TextOperation op; op.text = text; - op.cursor_pos = cursor_pos; + op.caret_column = caret_column; op.scroll_offset = scroll_offset; undo_stack.push_back(op); } @@ -2075,6 +2082,12 @@ void LineEdit::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); } +void LineEdit::_validate_property(PropertyInfo &property) const { + if (!caret_blink_enabled && property.name == "caret_blink_speed") { + property.usage = PROPERTY_USAGE_NOEDITOR; + } +} + void LineEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("_text_changed"), &LineEdit::_text_changed); @@ -2105,23 +2118,23 @@ void LineEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("get_placeholder"), &LineEdit::get_placeholder); ClassDB::bind_method(D_METHOD("set_placeholder_alpha", "alpha"), &LineEdit::set_placeholder_alpha); ClassDB::bind_method(D_METHOD("get_placeholder_alpha"), &LineEdit::get_placeholder_alpha); - ClassDB::bind_method(D_METHOD("set_cursor_position", "position"), &LineEdit::set_cursor_position); - ClassDB::bind_method(D_METHOD("get_cursor_position"), &LineEdit::get_cursor_position); + ClassDB::bind_method(D_METHOD("set_caret_column", "position"), &LineEdit::set_caret_column); + ClassDB::bind_method(D_METHOD("get_caret_column"), &LineEdit::get_caret_column); ClassDB::bind_method(D_METHOD("get_scroll_offset"), &LineEdit::get_scroll_offset); - ClassDB::bind_method(D_METHOD("set_expand_to_text_length", "enabled"), &LineEdit::set_expand_to_text_length); - ClassDB::bind_method(D_METHOD("get_expand_to_text_length"), &LineEdit::get_expand_to_text_length); - ClassDB::bind_method(D_METHOD("cursor_set_blink_enabled", "enabled"), &LineEdit::cursor_set_blink_enabled); - ClassDB::bind_method(D_METHOD("cursor_get_blink_enabled"), &LineEdit::cursor_get_blink_enabled); - ClassDB::bind_method(D_METHOD("set_mid_grapheme_caret_enabled", "enabled"), &LineEdit::set_mid_grapheme_caret_enabled); - ClassDB::bind_method(D_METHOD("get_mid_grapheme_caret_enabled"), &LineEdit::get_mid_grapheme_caret_enabled); - ClassDB::bind_method(D_METHOD("cursor_set_force_displayed", "enabled"), &LineEdit::cursor_set_force_displayed); - ClassDB::bind_method(D_METHOD("cursor_get_force_displayed"), &LineEdit::cursor_get_force_displayed); - ClassDB::bind_method(D_METHOD("cursor_set_blink_speed", "blink_speed"), &LineEdit::cursor_set_blink_speed); - ClassDB::bind_method(D_METHOD("cursor_get_blink_speed"), &LineEdit::cursor_get_blink_speed); + ClassDB::bind_method(D_METHOD("set_expand_to_text_length_enabled", "enabled"), &LineEdit::set_expand_to_text_length_enabled); + ClassDB::bind_method(D_METHOD("is_expand_to_text_length_enabled"), &LineEdit::is_expand_to_text_length_enabled); + ClassDB::bind_method(D_METHOD("set_caret_blink_enabled", "enabled"), &LineEdit::set_caret_blink_enabled); + ClassDB::bind_method(D_METHOD("is_caret_blink_enabled"), &LineEdit::is_caret_blink_enabled); + ClassDB::bind_method(D_METHOD("set_caret_mid_grapheme_enabled", "enabled"), &LineEdit::set_caret_mid_grapheme_enabled); + ClassDB::bind_method(D_METHOD("is_caret_mid_grapheme_enabled"), &LineEdit::is_caret_mid_grapheme_enabled); + ClassDB::bind_method(D_METHOD("set_caret_force_displayed", "enabled"), &LineEdit::set_caret_force_displayed); + ClassDB::bind_method(D_METHOD("is_caret_force_displayed"), &LineEdit::is_caret_force_displayed); + ClassDB::bind_method(D_METHOD("set_caret_blink_speed", "blink_speed"), &LineEdit::set_caret_blink_speed); + ClassDB::bind_method(D_METHOD("get_caret_blink_speed"), &LineEdit::get_caret_blink_speed); ClassDB::bind_method(D_METHOD("set_max_length", "chars"), &LineEdit::set_max_length); ClassDB::bind_method(D_METHOD("get_max_length"), &LineEdit::get_max_length); - ClassDB::bind_method(D_METHOD("append_at_cursor", "text"), &LineEdit::append_at_cursor); - ClassDB::bind_method(D_METHOD("delete_char_at_cursor"), &LineEdit::delete_char); + ClassDB::bind_method(D_METHOD("insert_text_at_caret", "text"), &LineEdit::insert_text_at_caret); + ClassDB::bind_method(D_METHOD("delete_char_at_caret"), &LineEdit::delete_char); ClassDB::bind_method(D_METHOD("delete_text", "from_column", "to_column"), &LineEdit::delete_text); ClassDB::bind_method(D_METHOD("set_editable", "enabled"), &LineEdit::set_editable); ClassDB::bind_method(D_METHOD("is_editable"), &LineEdit::is_editable); @@ -2189,7 +2202,7 @@ void LineEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "secret"), "set_secret", "is_secret"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "secret_character"), "set_secret_character", "get_secret_character"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand_to_text_length"), "set_expand_to_text_length", "get_expand_to_text_length"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand_to_text_length"), "set_expand_to_text_length_enabled", "is_expand_to_text_length_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "virtual_keyboard_enabled"), "set_virtual_keyboard_enabled", "is_virtual_keyboard_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clear_button_enabled"), "set_clear_button_enabled", "is_clear_button_enabled"); @@ -2206,11 +2219,11 @@ void LineEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::STRING, "placeholder_text"), "set_placeholder", "get_placeholder"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "placeholder_alpha", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_placeholder_alpha", "get_placeholder_alpha"); ADD_GROUP("Caret", "caret_"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_blink"), "cursor_set_blink_enabled", "cursor_get_blink_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.01"), "cursor_set_blink_speed", "cursor_get_blink_speed"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "caret_position"), "set_cursor_position", "get_cursor_position"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_force_displayed"), "cursor_set_force_displayed", "cursor_get_force_displayed"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_mid_grapheme"), "set_mid_grapheme_caret_enabled", "get_mid_grapheme_caret_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_blink"), "set_caret_blink_enabled", "is_caret_blink_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.01"), "set_caret_blink_speed", "get_caret_blink_speed"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "caret_column"), "set_caret_column", "get_caret_column"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_force_displayed"), "set_caret_force_displayed", "is_caret_force_displayed"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_mid_grapheme"), "set_caret_mid_grapheme_enabled", "is_caret_mid_grapheme_enabled"); } LineEdit::LineEdit() { @@ -2226,7 +2239,7 @@ LineEdit::LineEdit() { add_child(caret_blink_timer); caret_blink_timer->set_wait_time(0.65); caret_blink_timer->connect("timeout", callable_mp(this, &LineEdit::_toggle_draw_caret)); - cursor_set_blink_enabled(false); + set_caret_blink_enabled(false); menu = memnew(PopupMenu); add_child(menu); diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index f1d9de255a..12fec2f98b 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -103,9 +103,9 @@ private: PopupMenu *menu_dir = nullptr; PopupMenu *menu_ctl = nullptr; - bool mid_grapheme_caret_enabled = false; + bool caret_mid_grapheme_enabled = false; - int cursor_pos = 0; + int caret_column = 0; int scroll_offset = 0; int max_length = 0; // 0 for no maximum. @@ -131,16 +131,16 @@ private: struct Selection { int begin = 0; int end = 0; - int cursor_start = 0; + int start_column = 0; bool enabled = false; bool creating = false; - bool doubleclick = false; + bool double_click = false; bool drag_attempt = false; uint64_t last_dblclk = 0; } selection; struct TextOperation { - int cursor_pos = 0; + int caret_column = 0; int scroll_offset = 0; int cached_width = 0; String text; @@ -175,12 +175,12 @@ private: void shift_selection_check_pre(bool); void shift_selection_check_post(bool); - void selection_fill_at_cursor(); + void selection_fill_at_caret(); void set_scroll_offset(int p_pos); int get_scroll_offset() const; - void set_cursor_at_pixel_pos(int p_x); - Vector2i get_cursor_pixel_pos(); + void set_caret_at_pixel_pos(int p_x); + Vector2i get_caret_pixel_pos(); void _reset_caret_blink_timer(); void _toggle_draw_caret(); @@ -191,22 +191,22 @@ private: void _editor_settings_changed(); void _swap_current_input_direction(); - void _move_cursor_left(bool p_select, bool p_move_by_word = false); - void _move_cursor_right(bool p_select, bool p_move_by_word = false); - void _move_cursor_start(bool p_select); - void _move_cursor_end(bool p_select); + void _move_caret_left(bool p_select, bool p_move_by_word = false); + void _move_caret_right(bool p_select, bool p_move_by_word = false); + void _move_caret_start(bool p_select); + void _move_caret_end(bool p_select); void _backspace(bool p_word = false, bool p_all_to_left = false); void _delete(bool p_word = false, bool p_all_to_right = false); - void _gui_input(Ref<InputEvent> p_event); - void _notification(int p_what); - protected: + void _notification(int p_what); static void _bind_methods(); + void _gui_input(Ref<InputEvent> p_event); bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; + void _validate_property(PropertyInfo &property) const override; public: void set_align(Align p_align); @@ -259,26 +259,26 @@ public: void set_placeholder_alpha(float p_alpha); float get_placeholder_alpha() const; - void set_cursor_position(int p_pos); - int get_cursor_position() const; + void set_caret_column(int p_column); + int get_caret_column() const; void set_max_length(int p_max_length); int get_max_length() const; - void append_at_cursor(String p_text); + void insert_text_at_caret(String p_text); void clear(); - void set_mid_grapheme_caret_enabled(const bool p_enabled); - bool get_mid_grapheme_caret_enabled() const; + void set_caret_mid_grapheme_enabled(const bool p_enabled); + bool is_caret_mid_grapheme_enabled() const; - bool cursor_get_blink_enabled() const; - void cursor_set_blink_enabled(const bool p_enabled); + bool is_caret_blink_enabled() const; + void set_caret_blink_enabled(const bool p_enabled); - float cursor_get_blink_speed() const; - void cursor_set_blink_speed(const float p_speed); + float get_caret_blink_speed() const; + void set_caret_blink_speed(const float p_speed); - bool cursor_get_force_displayed() const; - void cursor_set_force_displayed(const bool p_enabled); + void set_caret_force_displayed(const bool p_enabled); + bool is_caret_force_displayed() const; void copy_text(); void cut_text(); @@ -297,8 +297,8 @@ public: virtual Size2 get_minimum_size() const override; - void set_expand_to_text_length(bool p_enabled); - bool get_expand_to_text_length() const; + void set_expand_to_text_length_enabled(bool p_enabled); + bool is_expand_to_text_length_enabled() const; void set_clear_button_enabled(bool p_enabled); bool is_clear_button_enabled() const; diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp index 5acc7e808a..1e9baa77fc 100644 --- a/scene/gui/menu_button.cpp +++ b/scene/gui/menu_button.cpp @@ -34,6 +34,8 @@ #include "scene/main/window.h" void MenuButton::_unhandled_key_input(Ref<InputEvent> p_event) { + ERR_FAIL_COND(p_event.is_null()); + if (!_is_focus_owner_in_shorcut_context()) { return; } diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index f237f79be1..2100707d2d 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -222,7 +222,7 @@ void PopupMenu::_activate_submenu(int p_over) { submenu_popup->set_close_on_parent_focus(false); submenu_popup->set_position(submenu_pos); - submenu_popup->set_as_minsize(); // Shrink the popup size to it's contents. + submenu_popup->set_as_minsize(); // Shrink the popup size to its contents. submenu_popup->popup(); // Set autohide areas @@ -252,6 +252,8 @@ void PopupMenu::_submenu_timeout() { } void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) { + ERR_FAIL_COND(p_event.is_null()); + if (p_event->is_action("ui_down") && p_event->is_pressed()) { int search_from = mouse_over + 1; if (search_from >= items.size()) { @@ -359,7 +361,7 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) { if (b->is_pressed() || (!b->is_pressed() && during_grabbed_click)) { // Allow activating item by releasing the LMB or any that was down when the popup appeared. // However, if button was not held when opening menu, do not allow release to activate item. - if (button_idx == BUTTON_LEFT || (initial_button_mask & (1 << (button_idx - 1)))) { + if (button_idx == MOUSE_BUTTON_LEFT || (initial_button_mask & (1 << (button_idx - 1)))) { bool was_during_grabbed_click = during_grabbed_click; during_grabbed_click = false; initial_button_mask = 0; @@ -722,6 +724,7 @@ void PopupMenu::_notification(int p_what) { for (int i = 0; i < items.size(); i++) { items.write[i].xl_text = tr(items[i].text); items.write[i].dirty = true; + _shape_item(i); } child_controls_changed(); @@ -1279,16 +1282,16 @@ bool PopupMenu::activate_item_by_event(const Ref<InputEvent> &p_event, bool p_fo if (code == 0) { code = k->get_unicode(); } - if (k->get_control()) { + if (k->is_ctrl_pressed()) { code |= KEY_MASK_CTRL; } - if (k->get_alt()) { + if (k->is_alt_pressed()) { code |= KEY_MASK_ALT; } - if (k->get_metakey()) { + if (k->is_meta_pressed()) { code |= KEY_MASK_META; } - if (k->get_shift()) { + if (k->is_shift_pressed()) { code |= KEY_MASK_SHIFT; } } diff --git a/scene/gui/range.cpp b/scene/gui/range.cpp index 86b775e795..adc1ed67ca 100644 --- a/scene/gui/range.cpp +++ b/scene/gui/range.cpp @@ -30,17 +30,14 @@ #include "range.h" -String Range::get_configuration_warning() const { - String warning = Control::get_configuration_warning(); +TypedArray<String> Range::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); if (shared->exp_ratio && shared->min <= 0) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("If \"Exp Edit\" is enabled, \"Min Value\" must be greater than 0."); + warnings.push_back(TTR("If \"Exp Edit\" is enabled, \"Min Value\" must be greater than 0.")); } - return warning; + return warnings; } void Range::_value_changed_notify() { @@ -106,7 +103,7 @@ void Range::set_min(double p_min) { shared->emit_changed("min"); - update_configuration_warning(); + update_configuration_warnings(); } void Range::set_max(double p_max) { @@ -181,7 +178,6 @@ double Range::get_as_ratio() const { double v = Math::log(value) / Math::log((double)2); return CLAMP((v - exp_min) / (exp_max - exp_min), 0, 1); - } else { float value = CLAMP(get_value(), shared->min, shared->max); return CLAMP((value - get_min()) / (get_max() - get_min()), 0, 1); @@ -287,7 +283,7 @@ bool Range::is_using_rounded_values() const { void Range::set_exp_ratio(bool p_enable) { shared->exp_ratio = p_enable; - update_configuration_warning(); + update_configuration_warnings(); } bool Range::is_ratio_exp() const { diff --git a/scene/gui/range.h b/scene/gui/range.h index 1072a109c6..7a129e88d6 100644 --- a/scene/gui/range.h +++ b/scene/gui/range.h @@ -97,7 +97,7 @@ public: void share(Range *p_range); void unshare(); - virtual String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; Range(); ~Range(); diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 682584d73f..2800ab0442 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -147,7 +147,7 @@ RichTextLabel::Item *RichTextLabel::_get_item_at_pos(RichTextLabel::Item *p_item case ITEM_TEXT: { ItemText *t = (ItemText *)it; offset += t->text.length(); - if (offset >= p_position) { + if (offset > p_position) { return it; } } break; @@ -588,7 +588,8 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> offset.x += table->columns[column].width + hseparation + frame->padding.size.x; row_height = MAX(yofs, row_height); - if (column == col_count - 1) { + // Add row height after last column of the row or last cell of the table. + if (column == col_count - 1 || E->next() == nullptr) { offset.x = 0; row_height += vseparation; table->total_height += row_height; @@ -759,7 +760,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o //draw_rect(Rect2(p_ofs + off, TS->shaped_text_get_size(rid)), Color(1,0,0), false, 2); //DEBUG_RECTS - off.y += TS->shaped_text_get_ascent(rid); + off.y += TS->shaped_text_get_ascent(rid) + l.text_buf->get_spacing_top(); // Draw inlined objects. Array objects = TS->shaped_text_get_objects(rid); for (int i = 0; i < objects.size(); i++) { @@ -1078,7 +1079,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o off.x += glyphs[i].advance; } } - off.y += TS->shaped_text_get_descent(rid); + off.y += TS->shaped_text_get_descent(rid) + l.text_buf->get_spacing_bottom(); } return line_count; @@ -1117,7 +1118,8 @@ void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item Point2 ofs = text_rect.get_position() + Vector2(0, main->lines[from_line].offset.y - vofs); while (ofs.y < size.height && from_line < main->lines.size()) { - ofs.y += _find_click_in_line(p_frame, from_line, ofs, text_rect.size.x, p_click, r_click_frame, r_click_line, r_click_item, r_click_char); + _find_click_in_line(p_frame, from_line, ofs, text_rect.size.x, p_click, r_click_frame, r_click_line, r_click_item, r_click_char); + ofs.y += main->lines[from_line].text_buf->get_size().y; if (((r_click_item != nullptr) && ((*r_click_item) != nullptr)) || ((r_click_frame != nullptr) && ((*r_click_frame) != nullptr))) { if (r_outside != nullptr) { *r_outside = false; @@ -1172,7 +1174,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V } break; } - off.y += TS->shaped_text_get_ascent(rid); + off.y += TS->shaped_text_get_ascent(rid) + l.text_buf->get_spacing_top(); Array objects = TS->shaped_text_get_objects(rid); for (int i = 0; i < objects.size(); i++) { @@ -1236,7 +1238,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V if (rect.has_point(p_click) && !table_hit) { char_pos = TS->shaped_text_hit_test_position(rid, p_click.x - rect.position.x); } - off.y += TS->shaped_text_get_descent(rid); + off.y += TS->shaped_text_get_descent(rid) + l.text_buf->get_spacing_bottom(); } if (char_pos >= 0) { @@ -1244,8 +1246,19 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V if (r_click_item != nullptr) { Item *it = p_frame->lines[p_line].from; Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr; - it = _get_item_at_pos(it, it_to, char_pos); - *r_click_item = it; + if (char_pos == p_frame->lines[p_line].char_count) { + // Selection after the end of line, select last item. + if (it_to != nullptr) { + *r_click_item = _get_prev_item(it_to); + } else { + for (Item *i = it; i && i != it_to; i = _get_next_item(i)) { + *r_click_item = i; + } + } + } else { + // Selection in the line. + *r_click_item = _get_item_at_pos(it, it_to, char_pos); + } } if (r_click_frame != nullptr) { @@ -1419,10 +1432,11 @@ void RichTextLabel::_notification(int p_what) { } } break; case NOTIFICATION_INTERNAL_PROCESS: { - float dt = get_process_delta_time(); - - _update_fx(main, dt); - update(); + if (is_visible_in_tree()) { + float dt = get_process_delta_time(); + _update_fx(main, dt); + update(); + } } } } @@ -1456,6 +1470,8 @@ Control::CursorShape RichTextLabel::get_cursor_shape(const Point2 &p_pos) const } void RichTextLabel::_gui_input(Ref<InputEvent> p_event) { + ERR_FAIL_COND(p_event.is_null()); + Ref<InputEventMouseButton> b = p_event; if (b.is_valid()) { @@ -1466,8 +1482,8 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) { return; } - if (b->get_button_index() == BUTTON_LEFT) { - if (b->is_pressed() && !b->is_doubleclick()) { + if (b->get_button_index() == MOUSE_BUTTON_LEFT) { + if (b->is_pressed() && !b->is_double_click()) { scroll_updated = false; ItemFrame *c_frame = nullptr; int c_line = 0; @@ -1499,8 +1515,8 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) { } } } - } else if (b->is_pressed() && b->is_doubleclick() && selection.enabled) { - //doubleclick: select word + } else if (b->is_pressed() && b->is_double_click() && selection.enabled) { + //double_click: select word ItemFrame *c_frame = nullptr; int c_line = 0; @@ -1534,7 +1550,7 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) { } else if (!b->is_pressed()) { selection.click_item = nullptr; - if (!b->is_doubleclick() && !scroll_updated) { + if (!b->is_double_click() && !scroll_updated) { Item *c_item = nullptr; bool outside = true; @@ -1551,12 +1567,12 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) { } } - if (b->get_button_index() == BUTTON_WHEEL_UP) { + if (b->get_button_index() == MOUSE_BUTTON_WHEEL_UP) { if (scroll_active) { vscroll->set_value(vscroll->get_value() - vscroll->get_page() * b->get_factor() * 0.5 / 8); } } - if (b->get_button_index() == BUTTON_WHEEL_DOWN) { + if (b->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN) { if (scroll_active) { vscroll->set_value(vscroll->get_value() + vscroll->get_page() * b->get_factor() * 0.5 / 8); } @@ -2597,6 +2613,14 @@ void RichTextLabel::pop() { current = current->parent; } +// Creates a new line without adding an ItemNewline to the previous line. +// Useful when wanting to calling remove_line and add a new line immediately after. +void RichTextLabel::increment_line_count() { + _validate_line_caches(main); + current_frame->lines.resize(current_frame->lines.size() + 1); + _invalidate_current_line(current_frame); +} + void RichTextLabel::clear() { main->_clear_children(); current = main; @@ -3634,6 +3658,7 @@ void RichTextLabel::set_use_bbcode(bool p_enable) { } use_bbcode = p_enable; set_bbcode(bbcode); + notify_property_list_changed(); } bool RichTextLabel::is_using_bbcode() const { @@ -3742,7 +3767,9 @@ void RichTextLabel::set_effects(const Vector<Variant> &effects) { custom_effects.push_back(effect); } - parse_bbcode(bbcode); + if ((bbcode != "") && use_bbcode) { + parse_bbcode(bbcode); + } } Vector<Variant> RichTextLabel::get_effects() { @@ -3759,7 +3786,9 @@ void RichTextLabel::install_effect(const Variant effect) { if (rteffect.is_valid()) { custom_effects.push_back(effect); - parse_bbcode(bbcode); + if ((bbcode != "") && use_bbcode) { + parse_bbcode(bbcode); + } } } @@ -3771,6 +3800,12 @@ int RichTextLabel::get_content_height() const { return total_height; } +void RichTextLabel::_validate_property(PropertyInfo &property) const { + if (!use_bbcode && property.name == "bbcode_text") { + property.usage = PROPERTY_USAGE_NOEDITOR; + } +} + void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("_gui_input"), &RichTextLabel::_gui_input); ClassDB::bind_method(D_METHOD("get_text"), &RichTextLabel::get_text); @@ -3979,14 +4014,16 @@ void RichTextLabel::set_fixed_size_to_width(int p_width) { } Size2 RichTextLabel::get_minimum_size() const { - Size2 size(0, 0); + Ref<StyleBox> style = get_theme_stylebox("normal"); + Size2 size = style->get_minimum_size(); + if (fixed_width != -1) { - size.x = fixed_width; + size.x += fixed_width; } if (fixed_width != -1 || fit_content_height) { const_cast<RichTextLabel *>(this)->_validate_line_caches(main); - size.y = get_content_height(); + size.y += get_content_height(); } return size; diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 2351aff0a4..afc88e070a 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -81,7 +81,9 @@ public: }; protected: + void _notification(int p_what); static void _bind_methods(); + void _validate_property(PropertyInfo &property) const override; private: struct Item; @@ -441,9 +443,6 @@ private: bool fit_content_height = false; -protected: - void _notification(int p_what); - public: String get_text(); void add_text(const String &p_text); @@ -484,6 +483,8 @@ public: void push_cell(); void pop(); + void increment_line_count(); + void clear(); void set_offset(int p_pixel); diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp index f2516e76a5..62276e3af0 100644 --- a/scene/gui/scroll_bar.cpp +++ b/scene/gui/scroll_bar.cpp @@ -42,6 +42,8 @@ void ScrollBar::set_can_focus_by_default(bool p_can_focus) { } void ScrollBar::_gui_input(Ref<InputEvent> p_event) { + ERR_FAIL_COND(p_event.is_null()); + Ref<InputEventMouseMotion> m = p_event; if (!m.is_valid() || drag.active) { emit_signal("scrolling"); @@ -52,17 +54,17 @@ void ScrollBar::_gui_input(Ref<InputEvent> p_event) { if (b.is_valid()) { accept_event(); - if (b->get_button_index() == BUTTON_WHEEL_DOWN && b->is_pressed()) { + if (b->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && b->is_pressed()) { set_value(get_value() + get_page() / 4.0); accept_event(); } - if (b->get_button_index() == BUTTON_WHEEL_UP && b->is_pressed()) { + if (b->get_button_index() == MOUSE_BUTTON_WHEEL_UP && b->is_pressed()) { set_value(get_value() - get_page() / 4.0); accept_event(); } - if (b->get_button_index() != BUTTON_LEFT) { + if (b->get_button_index() != MOUSE_BUTTON_LEFT) { return; } diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp index 411891ece8..46db4a3c2f 100644 --- a/scene/gui/scroll_container.cpp +++ b/scene/gui/scroll_container.cpp @@ -88,37 +88,39 @@ void ScrollContainer::_cancel_drag() { } void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) { + ERR_FAIL_COND(p_gui_input.is_null()); + 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()) { + if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP && mb->is_pressed()) { // only horizontal is enabled, scroll horizontally - if (h_scroll->is_visible() && (!v_scroll->is_visible() || mb->get_shift())) { + if (h_scroll->is_visible() && (!v_scroll->is_visible() || mb->is_shift_pressed())) { 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()); } } - if (mb->get_button_index() == BUTTON_WHEEL_DOWN && mb->is_pressed()) { + if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && mb->is_pressed()) { // only horizontal is enabled, scroll horizontally - if (h_scroll->is_visible() && (!v_scroll->is_visible() || mb->get_shift())) { + if (h_scroll->is_visible() && (!v_scroll->is_visible() || mb->is_shift_pressed())) { 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()); } } - if (mb->get_button_index() == BUTTON_WHEEL_LEFT && mb->is_pressed()) { + if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_LEFT && mb->is_pressed()) { if (h_scroll->is_visible_in_tree()) { h_scroll->set_value(h_scroll->get_value() - h_scroll->get_page() * mb->get_factor() / 8); } } - if (mb->get_button_index() == BUTTON_WHEEL_RIGHT && mb->is_pressed()) { + if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_RIGHT && mb->is_pressed()) { if (h_scroll->is_visible_in_tree()) { h_scroll->set_value(h_scroll->get_value() + h_scroll->get_page() * mb->get_factor() / 8); } @@ -132,7 +134,7 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) { return; } - if (mb->get_button_index() != BUTTON_LEFT) { + if (mb->get_button_index() != MOUSE_BUTTON_LEFT) { return; } @@ -297,7 +299,7 @@ void ScrollContainer::_update_dimensions() { child_max_size.x = MAX(child_max_size.x, minsize.x); child_max_size.y = MAX(child_max_size.y, minsize.y); - Rect2 r = Rect2(-scroll, minsize); + Rect2 r = Rect2(-Size2(get_h_scroll(), get_v_scroll()), minsize); if (!scroll_h || (!h_scroll->is_visible_in_tree() && c->get_h_size_flags() & SIZE_EXPAND)) { r.position.x = 0; if (c->get_h_size_flags() & SIZE_EXPAND) { @@ -432,40 +434,16 @@ void ScrollContainer::update_scrollbars() { Size2 min = child_max_size; - bool hide_scroll_v = !scroll_v || min.height <= size.height; - bool hide_scroll_h = !scroll_h || min.width <= size.width; - - v_scroll->set_max(min.height); - if (hide_scroll_v) { - v_scroll->set_page(size.height); - v_scroll->hide(); - scroll.y = 0; - } else { - v_scroll->show(); - if (hide_scroll_h) { - v_scroll->set_page(size.height); - } else { - v_scroll->set_page(size.height - hmin.height); - } - - scroll.y = v_scroll->get_value(); - } + bool hide_scroll_h = !scroll_h || min.width <= size.width || !h_scroll_visible; + bool hide_scroll_v = !scroll_v || min.height <= size.height || !v_scroll_visible; h_scroll->set_max(min.width); - if (hide_scroll_h) { - h_scroll->set_page(size.width); - h_scroll->hide(); - scroll.x = 0; - } else { - h_scroll->show(); - if (hide_scroll_v) { - h_scroll->set_page(size.width); - } else { - h_scroll->set_page(size.width - vmin.width); - } + h_scroll->set_page(size.width - (hide_scroll_v ? 0 : vmin.width)); + h_scroll->set_visible(!hide_scroll_h); - scroll.x = h_scroll->get_value(); - } + v_scroll->set_max(min.height); + v_scroll->set_page(size.height - (hide_scroll_h ? 0 : hmin.height)); + v_scroll->set_visible(!hide_scroll_v); // Avoid scrollbar overlapping. h_scroll->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, hide_scroll_v ? 0 : -vmin.width); @@ -473,13 +451,28 @@ void ScrollContainer::update_scrollbars() { } void ScrollContainer::_scroll_moved(float) { - scroll.x = h_scroll->get_value(); - scroll.y = v_scroll->get_value(); queue_sort(); - update(); }; +void ScrollContainer::set_h_scroll(int p_pos) { + h_scroll->set_value(p_pos); + _cancel_drag(); +} + +int ScrollContainer::get_h_scroll() const { + return h_scroll->get_value(); +} + +void ScrollContainer::set_v_scroll(int p_pos) { + v_scroll->set_value(p_pos); + _cancel_drag(); +} + +int ScrollContainer::get_v_scroll() const { + return v_scroll->get_value(); +} + void ScrollContainer::set_enable_h_scroll(bool p_enable) { if (scroll_h == p_enable) { return; @@ -508,22 +501,30 @@ bool ScrollContainer::is_v_scroll_enabled() const { return scroll_v; } -int ScrollContainer::get_v_scroll() const { - return v_scroll->get_value(); +void ScrollContainer::set_h_scroll_visible(bool p_visible) { + if (h_scroll_visible == p_visible) { + return; + } + + h_scroll_visible = p_visible; + update_scrollbars(); } -void ScrollContainer::set_v_scroll(int p_pos) { - v_scroll->set_value(p_pos); - _cancel_drag(); +bool ScrollContainer::is_h_scroll_visible() const { + return h_scroll_visible; } -int ScrollContainer::get_h_scroll() const { - return h_scroll->get_value(); +void ScrollContainer::set_v_scroll_visible(bool p_visible) { + if (v_scroll_visible == p_visible) { + return; + } + + v_scroll_visible = p_visible; + update_scrollbars(); } -void ScrollContainer::set_h_scroll(int p_pos) { - h_scroll->set_value(p_pos); - _cancel_drag(); +bool ScrollContainer::is_v_scroll_visible() const { + return v_scroll_visible; } int ScrollContainer::get_deadzone() const { @@ -542,8 +543,8 @@ void ScrollContainer::set_follow_focus(bool p_follow) { follow_focus = p_follow; } -String ScrollContainer::get_configuration_warning() const { - String warning = Container::get_configuration_warning(); +TypedArray<String> ScrollContainer::get_configuration_warnings() const { + TypedArray<String> warnings = Container::get_configuration_warnings(); int found = 0; @@ -563,12 +564,10 @@ String ScrollContainer::get_configuration_warning() const { } if (found != 1) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("ScrollContainer is intended to work with a single child control.\nUse a container as child (VBox, HBox, etc.), or a Control and set the custom minimum size manually."); + warnings.push_back(TTR("ScrollContainer is intended to work with a single child control.\nUse a container as child (VBox, HBox, etc.), or a Control and set the custom minimum size manually.")); } - return warning; + + return warnings; } HScrollBar *ScrollContainer::get_h_scrollbar() { @@ -581,17 +580,29 @@ VScrollBar *ScrollContainer::get_v_scrollbar() { void ScrollContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("_gui_input"), &ScrollContainer::_gui_input); - ClassDB::bind_method(D_METHOD("set_enable_h_scroll", "enable"), &ScrollContainer::set_enable_h_scroll); - ClassDB::bind_method(D_METHOD("is_h_scroll_enabled"), &ScrollContainer::is_h_scroll_enabled); - ClassDB::bind_method(D_METHOD("set_enable_v_scroll", "enable"), &ScrollContainer::set_enable_v_scroll); - ClassDB::bind_method(D_METHOD("is_v_scroll_enabled"), &ScrollContainer::is_v_scroll_enabled); ClassDB::bind_method(D_METHOD("_update_scrollbar_position"), &ScrollContainer::_update_scrollbar_position); + ClassDB::bind_method(D_METHOD("set_h_scroll", "value"), &ScrollContainer::set_h_scroll); ClassDB::bind_method(D_METHOD("get_h_scroll"), &ScrollContainer::get_h_scroll); + ClassDB::bind_method(D_METHOD("set_v_scroll", "value"), &ScrollContainer::set_v_scroll); ClassDB::bind_method(D_METHOD("get_v_scroll"), &ScrollContainer::get_v_scroll); + + ClassDB::bind_method(D_METHOD("set_enable_h_scroll", "enable"), &ScrollContainer::set_enable_h_scroll); + ClassDB::bind_method(D_METHOD("is_h_scroll_enabled"), &ScrollContainer::is_h_scroll_enabled); + + ClassDB::bind_method(D_METHOD("set_enable_v_scroll", "enable"), &ScrollContainer::set_enable_v_scroll); + ClassDB::bind_method(D_METHOD("is_v_scroll_enabled"), &ScrollContainer::is_v_scroll_enabled); + + ClassDB::bind_method(D_METHOD("set_h_scroll_visible", "visible"), &ScrollContainer::set_h_scroll_visible); + ClassDB::bind_method(D_METHOD("is_h_scroll_visible"), &ScrollContainer::is_h_scroll_visible); + + ClassDB::bind_method(D_METHOD("set_v_scroll_visible", "visible"), &ScrollContainer::set_v_scroll_visible); + ClassDB::bind_method(D_METHOD("is_v_scroll_visible"), &ScrollContainer::is_v_scroll_visible); + ClassDB::bind_method(D_METHOD("set_deadzone", "deadzone"), &ScrollContainer::set_deadzone); ClassDB::bind_method(D_METHOD("get_deadzone"), &ScrollContainer::get_deadzone); + ClassDB::bind_method(D_METHOD("set_follow_focus", "enabled"), &ScrollContainer::set_follow_focus); ClassDB::bind_method(D_METHOD("is_following_focus"), &ScrollContainer::is_following_focus); @@ -604,10 +615,12 @@ void ScrollContainer::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "follow_focus"), "set_follow_focus", "is_following_focus"); ADD_GROUP("Scroll", "scroll_"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_horizontal_enabled"), "set_enable_h_scroll", "is_h_scroll_enabled"); ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_horizontal"), "set_h_scroll", "get_h_scroll"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_vertical_enabled"), "set_enable_v_scroll", "is_v_scroll_enabled"); ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_vertical"), "set_v_scroll", "get_v_scroll"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_horizontal_enabled"), "set_enable_h_scroll", "is_h_scroll_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_vertical_enabled"), "set_enable_v_scroll", "is_v_scroll_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_horizontal_visible"), "set_h_scroll_visible", "is_h_scroll_visible"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_vertical_visible"), "set_v_scroll_visible", "is_v_scroll_visible"); ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_deadzone"), "set_deadzone", "get_deadzone"); GLOBAL_DEF("gui/common/default_scroll_deadzone", 0); diff --git a/scene/gui/scroll_container.h b/scene/gui/scroll_container.h index 9d3ce39345..f61df70b85 100644 --- a/scene/gui/scroll_container.h +++ b/scene/gui/scroll_container.h @@ -42,7 +42,6 @@ class ScrollContainer : public Container { VScrollBar *v_scroll; Size2 child_max_size; - Size2 scroll; void update_scrollbars(); @@ -50,16 +49,17 @@ class ScrollContainer : public Container { Vector2 drag_accum; Vector2 drag_from; Vector2 last_drag_accum; - float last_drag_time = 0.0; - float time_since_motion = 0.0; + float time_since_motion = 0.0f; bool drag_touching = false; bool drag_touching_deaccel = false; - bool click_handled = false; bool beyond_deadzone = false; bool scroll_h = true; bool scroll_v = true; + bool h_scroll_visible = true; + bool v_scroll_visible = true; + int deadzone = 0; bool follow_focus = false; @@ -80,11 +80,11 @@ protected: void _ensure_focused_visible(Control *p_node); public: - int get_v_scroll() const; - void set_v_scroll(int p_pos); - - int get_h_scroll() const; void set_h_scroll(int p_pos); + int get_h_scroll() const; + + void set_v_scroll(int p_pos); + int get_v_scroll() const; void set_enable_h_scroll(bool p_enable); bool is_h_scroll_enabled() const; @@ -92,6 +92,12 @@ public: void set_enable_v_scroll(bool p_enable); bool is_v_scroll_enabled() const; + void set_h_scroll_visible(bool p_visible); + bool is_h_scroll_visible() const; + + void set_v_scroll_visible(bool p_visible); + bool is_v_scroll_visible() const; + int get_deadzone() const; void set_deadzone(int p_deadzone); @@ -103,7 +109,7 @@ public: virtual bool clips_input() const override; - virtual String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; ScrollContainer(); }; diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp index 2239226c78..a407ef21cb 100644 --- a/scene/gui/slider.cpp +++ b/scene/gui/slider.cpp @@ -46,6 +46,8 @@ Size2 Slider::get_minimum_size() const { } void Slider::_gui_input(Ref<InputEvent> p_event) { + ERR_FAIL_COND(p_event.is_null()); + if (!editable) { return; } @@ -53,7 +55,7 @@ void Slider::_gui_input(Ref<InputEvent> p_event) { Ref<InputEventMouseButton> mb = p_event; if (mb.is_valid()) { - if (mb->get_button_index() == BUTTON_LEFT) { + if (mb->get_button_index() == MOUSE_BUTTON_LEFT) { if (mb->is_pressed()) { Ref<Texture2D> grabber = get_theme_icon(mouse_inside || has_focus() ? "grabber_highlight" : "grabber"); grab.pos = orientation == VERTICAL ? mb->get_position().y : mb->get_position().x; @@ -72,10 +74,10 @@ void Slider::_gui_input(Ref<InputEvent> p_event) { grab.active = false; } } else if (scrollable) { - if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_UP) { + if (mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP) { grab_focus(); set_value(get_value() + get_step()); - } else if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_DOWN) { + } else if (mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN) { grab_focus(); set_value(get_value() - get_step()); } diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp index 2c9720e4b6..9dc2afdb2d 100644 --- a/scene/gui/spin_box.cpp +++ b/scene/gui/spin_box.cpp @@ -76,7 +76,7 @@ void SpinBox::_line_edit_input(const Ref<InputEvent> &p_event) { } void SpinBox::_range_click_timeout() { - if (!drag.enabled && Input::get_singleton()->is_mouse_button_pressed(BUTTON_LEFT)) { + if (!drag.enabled && Input::get_singleton()->is_mouse_button_pressed(MOUSE_BUTTON_LEFT)) { bool up = get_local_mouse_position().y < (get_size().height / 2); set_value(get_value() + (up ? get_step() : -get_step())); @@ -91,7 +91,17 @@ void SpinBox::_range_click_timeout() { } } +void SpinBox::_release_mouse() { + if (drag.enabled) { + drag.enabled = false; + Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE); + warp_mouse(drag.capture_pos); + } +} + void SpinBox::_gui_input(const Ref<InputEvent> &p_event) { + ERR_FAIL_COND(p_event.is_null()); + if (!is_editable()) { return; } @@ -102,7 +112,7 @@ void SpinBox::_gui_input(const Ref<InputEvent> &p_event) { bool up = mb->get_position().y < (get_size().height / 2); switch (mb->get_button_index()) { - case BUTTON_LEFT: { + case MOUSE_BUTTON_LEFT: { line_edit->grab_focus(); set_value(get_value() + (up ? get_step() : -get_step())); @@ -114,17 +124,17 @@ void SpinBox::_gui_input(const Ref<InputEvent> &p_event) { drag.allowed = true; drag.capture_pos = mb->get_position(); } break; - case BUTTON_RIGHT: { + case MOUSE_BUTTON_RIGHT: { line_edit->grab_focus(); set_value((up ? get_max() : get_min())); } break; - case BUTTON_WHEEL_UP: { + case MOUSE_BUTTON_WHEEL_UP: { if (line_edit->has_focus()) { set_value(get_value() + get_step() * mb->get_factor()); accept_event(); } } break; - case BUTTON_WHEEL_DOWN: { + case MOUSE_BUTTON_WHEEL_DOWN: { if (line_edit->has_focus()) { set_value(get_value() - get_step() * mb->get_factor()); accept_event(); @@ -133,21 +143,16 @@ void SpinBox::_gui_input(const Ref<InputEvent> &p_event) { } } - if (mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { + if (mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) { //set_default_cursor_shape(CURSOR_ARROW); range_click_timer->stop(); - - if (drag.enabled) { - drag.enabled = false; - Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE); - warp_mouse(drag.capture_pos); - } + _release_mouse(); drag.allowed = false; } Ref<InputEventMouseMotion> mm = p_event; - if (mm.is_valid() && mm->get_button_mask() & BUTTON_MASK_LEFT) { + if (mm.is_valid() && mm->get_button_mask() & MOUSE_BUTTON_MASK_LEFT) { if (drag.enabled) { drag.diff_y += mm->get_relative().y; float diff_y = -0.01 * Math::pow(ABS(drag.diff_y), 1.8f) * SGN(drag.diff_y); @@ -199,6 +204,8 @@ void SpinBox::_notification(int p_what) { } else if (p_what == NOTIFICATION_ENTER_TREE) { _adjust_width_for_icon(get_theme_icon("updown")); _value_changed(0); + } else if (p_what == NOTIFICATION_EXIT_TREE) { + _release_mouse(); } else if (p_what == NOTIFICATION_TRANSLATION_CHANGED) { _value_changed(0); } else if (p_what == NOTIFICATION_THEME_CHANGED) { diff --git a/scene/gui/spin_box.h b/scene/gui/spin_box.h index 4c3adf30e8..e116adb64c 100644 --- a/scene/gui/spin_box.h +++ b/scene/gui/spin_box.h @@ -43,6 +43,7 @@ class SpinBox : public Range { Timer *range_click_timer; void _range_click_timeout(); + void _release_mouse(); void _text_entered(const String &p_string); virtual void _value_changed(double) override; diff --git a/scene/gui/split_container.cpp b/scene/gui/split_container.cpp index d43e195df1..13ff2c5b86 100644 --- a/scene/gui/split_container.cpp +++ b/scene/gui/split_container.cpp @@ -207,6 +207,8 @@ void SplitContainer::_notification(int p_what) { } void SplitContainer::_gui_input(const Ref<InputEvent> &p_event) { + ERR_FAIL_COND(p_event.is_null()); + if (collapsed || !_getch(0) || !_getch(1) || dragger_visibility != DRAGGER_VISIBLE) { return; } @@ -214,7 +216,7 @@ void SplitContainer::_gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseButton> mb = p_event; if (mb.is_valid()) { - if (mb->get_button_index() == BUTTON_LEFT) { + if (mb->get_button_index() == MOUSE_BUTTON_LEFT) { if (mb->is_pressed()) { int sep = get_theme_constant("separation"); diff --git a/scene/gui/subviewport_container.cpp b/scene/gui/subviewport_container.cpp index 8ffdd269a4..bfc7e29f9c 100644 --- a/scene/gui/subviewport_container.cpp +++ b/scene/gui/subviewport_container.cpp @@ -140,6 +140,8 @@ void SubViewportContainer::_notification(int p_what) { } void SubViewportContainer::_input(const Ref<InputEvent> &p_event) { + ERR_FAIL_COND(p_event.is_null()); + if (Engine::get_singleton()->is_editor_hint()) { return; } @@ -165,6 +167,8 @@ void SubViewportContainer::_input(const Ref<InputEvent> &p_event) { } void SubViewportContainer::_unhandled_input(const Ref<InputEvent> &p_event) { + ERR_FAIL_COND(p_event.is_null()); + if (Engine::get_singleton()->is_editor_hint()) { return; } diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index e3e3f549de..acf0641005 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -72,11 +72,13 @@ int TabContainer::_get_top_margin() const { } void TabContainer::_gui_input(const Ref<InputEvent> &p_event) { + ERR_FAIL_COND(p_event.is_null()); + Ref<InputEventMouseButton> mb = p_event; Popup *popup = get_popup(); - if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { + if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) { Point2 pos(mb->get_position().x, mb->get_position().y); Size2 size = get_size(); @@ -392,6 +394,7 @@ void TabContainer::_notification(int p_what) { Vector<int> tab_widths; for (int i = first_tab_cache; i < tabs.size(); i++) { if (get_tab_hidden(i)) { + tab_widths.push_back(0); continue; } int tab_width = _get_tab_width(i); @@ -645,7 +648,7 @@ int TabContainer::_get_tab_width(int p_index) const { // Get the width of the text displayed on the tab. Ref<Font> font = get_theme_font("font"); int font_size = get_theme_font_size("font_size"); - String text = control->has_meta("_tab_name") ? String(tr(String(control->get_meta("_tab_name")))) : String(control->get_name()); + String text = control->has_meta("_tab_name") ? String(tr(String(control->get_meta("_tab_name")))) : String(tr(control->get_name())); int width = font->get_string_size(text, font_size).width; // Add space for a tab icon. @@ -1023,6 +1026,7 @@ void TabContainer::set_tab_title(int p_tab, const String &p_title) { Control *child = _get_tab(p_tab); ERR_FAIL_COND(!child); child->set_meta("_tab_name", p_title); + _refresh_texts(); update(); } diff --git a/scene/gui/tabs.cpp b/scene/gui/tabs.cpp index da1a9698d0..471b26be75 100644 --- a/scene/gui/tabs.cpp +++ b/scene/gui/tabs.cpp @@ -83,11 +83,16 @@ Size2 Tabs::get_minimum_size() const { } } - ms.width = 0; //TODO: should make this optional + if (clip_tabs) { + ms.width = 0; + } + return ms; } void Tabs::_gui_input(const Ref<InputEvent> &p_event) { + ERR_FAIL_COND(p_event.is_null()); + Ref<InputEventMouseMotion> mm = p_event; if (mm.is_valid()) { @@ -105,10 +110,10 @@ void Tabs::_gui_input(const Ref<InputEvent> &p_event) { highlight_arrow = 0; } } else { - int limit = get_size().width - incr->get_width() - decr->get_width(); - if (pos.x > limit + decr->get_width()) { + int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width(); + if (pos.x > limit_minus_buttons + decr->get_width()) { highlight_arrow = 1; - } else if (pos.x > limit) { + } else if (pos.x > limit_minus_buttons) { highlight_arrow = 0; } } @@ -122,7 +127,7 @@ void Tabs::_gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseButton> mb = p_event; if (mb.is_valid()) { - if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_UP && !mb->get_command()) { + if (mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP && !mb->is_command_pressed()) { if (scrolling_enabled && buttons_visible) { if (offset > 0) { offset--; @@ -131,7 +136,7 @@ void Tabs::_gui_input(const Ref<InputEvent> &p_event) { } } - if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_DOWN && !mb->get_command()) { + if (mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && !mb->is_command_pressed()) { if (scrolling_enabled && buttons_visible) { if (missing_right) { offset++; @@ -140,7 +145,7 @@ void Tabs::_gui_input(const Ref<InputEvent> &p_event) { } } - if (rb_pressing && !mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { + if (rb_pressing && !mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) { if (rb_hover != -1) { //pressed emit_signal("right_button_pressed", rb_hover); @@ -150,7 +155,7 @@ void Tabs::_gui_input(const Ref<InputEvent> &p_event) { update(); } - if (cb_pressing && !mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { + if (cb_pressing && !mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) { if (cb_hover != -1) { //pressed emit_signal("tab_closed", cb_hover); @@ -160,7 +165,7 @@ void Tabs::_gui_input(const Ref<InputEvent> &p_event) { update(); } - if (mb->is_pressed() && (mb->get_button_index() == BUTTON_LEFT || (select_with_rmb && mb->get_button_index() == BUTTON_RIGHT))) { + if (mb->is_pressed() && (mb->get_button_index() == MOUSE_BUTTON_LEFT || (select_with_rmb && mb->get_button_index() == MOUSE_BUTTON_RIGHT))) { // clicks Point2 pos(mb->get_position().x, mb->get_position().y); @@ -305,7 +310,8 @@ void Tabs::_notification(int p_what) { Ref<Texture2D> incr_hl = get_theme_icon("increment_highlight"); Ref<Texture2D> decr_hl = get_theme_icon("decrement_highlight"); - int limit = get_size().width - incr->get_size().width - decr->get_size().width; + int limit = get_size().width; + int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width(); missing_right = false; @@ -328,7 +334,8 @@ void Tabs::_notification(int p_what) { col = font_unselected_color; } - if (w + lsize > limit) { + int new_width = w + lsize; + if (new_width > limit || (i < tabs.size() - 1 && new_width > limit_minus_buttons)) { // For the last tab, we accept if the tab covers the buttons. max_drawn_tab = i - 1; missing_right = true; break; @@ -459,15 +466,15 @@ void Tabs::_notification(int p_what) { } } else { if (offset > 0) { - draw_texture(highlight_arrow == 0 ? decr_hl : decr, Point2(limit, vofs)); + draw_texture(highlight_arrow == 0 ? decr_hl : decr, Point2(limit_minus_buttons, vofs)); } else { - draw_texture(decr, Point2(limit, vofs), Color(1, 1, 1, 0.5)); + draw_texture(decr, Point2(limit_minus_buttons, vofs), Color(1, 1, 1, 0.5)); } if (missing_right) { - draw_texture(highlight_arrow == 1 ? incr_hl : incr, Point2(limit + decr->get_size().width, vofs)); + draw_texture(highlight_arrow == 1 ? incr_hl : incr, Point2(limit_minus_buttons + decr->get_size().width, vofs)); } else { - draw_texture(incr, Point2(limit + decr->get_size().width, vofs), Color(1, 1, 1, 0.5)); + draw_texture(incr, Point2(limit_minus_buttons + decr->get_size().width, vofs), Color(1, 1, 1, 0.5)); } } @@ -666,7 +673,7 @@ void Tabs::_update_cache() { Ref<StyleBox> tab_selected = get_theme_stylebox("tab_selected"); Ref<Texture2D> incr = get_theme_icon("increment"); Ref<Texture2D> decr = get_theme_icon("decrement"); - int limit = get_size().width - incr->get_width() - decr->get_width(); + int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width(); int w = 0; int mw = 0; @@ -686,7 +693,7 @@ void Tabs::_update_cache() { } int m_width = min_width; if (count_resize > 0) { - m_width = MAX((limit - size_fixed) / count_resize, min_width); + m_width = MAX((limit_minus_buttons - size_fixed) / count_resize, min_width); } for (int i = offset; i < tabs.size(); i++) { Ref<StyleBox> sb; @@ -699,7 +706,7 @@ void Tabs::_update_cache() { } int lsize = tabs[i].size_cache; int slen = tabs[i].size_text; - if (min_width > 0 && mw > limit && i != current) { + if (min_width > 0 && mw > limit_minus_buttons && i != current) { if (lsize > m_width) { slen = m_width - (sb->get_margin(SIDE_LEFT) + sb->get_margin(SIDE_RIGHT)); if (tabs[i].icon.is_valid()) { @@ -909,6 +916,19 @@ Tabs::TabAlign Tabs::get_tab_align() const { return tab_align; } +void Tabs::set_clip_tabs(bool p_clip_tabs) { + if (clip_tabs == p_clip_tabs) { + return; + } + clip_tabs = p_clip_tabs; + update(); + minimum_size_changed(); +} + +bool Tabs::get_clip_tabs() const { + return clip_tabs; +} + void Tabs::move_tab(int from, int to) { if (from == to) { return; @@ -975,7 +995,8 @@ void Tabs::_ensure_no_over_offset() { Ref<Texture2D> incr = get_theme_icon("increment"); Ref<Texture2D> decr = get_theme_icon("decrement"); - int limit = get_size().width - incr->get_width() - decr->get_width(); + int limit = get_size().width; + int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width(); while (offset > 0) { int total_w = 0; @@ -983,7 +1004,7 @@ void Tabs::_ensure_no_over_offset() { total_w += tabs[i].size_cache; } - if (total_w < limit) { + if ((buttons_visible && total_w < limit_minus_buttons) || total_w < limit) { // For the last tab, we accept if the tab covers the buttons. offset--; update(); } else { @@ -1014,9 +1035,12 @@ void Tabs::ensure_tab_visible(int p_idx) { int prev_offset = offset; Ref<Texture2D> incr = get_theme_icon("increment"); Ref<Texture2D> decr = get_theme_icon("decrement"); - int limit = get_size().width - incr->get_width() - decr->get_width(); + int limit = get_size().width; + int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width(); + for (int i = offset; i <= p_idx; i++) { - if (tabs[i].ofs_cache + tabs[i].size_cache > limit) { + int total_w = tabs[i].ofs_cache + tabs[i].size_cache; + if (total_w > limit || (buttons_visible && total_w > limit_minus_buttons)) { offset++; } } @@ -1105,6 +1129,8 @@ void Tabs::_bind_methods() { ClassDB::bind_method(D_METHOD("add_tab", "title", "icon"), &Tabs::add_tab, DEFVAL(""), DEFVAL(Ref<Texture2D>())); ClassDB::bind_method(D_METHOD("set_tab_align", "align"), &Tabs::set_tab_align); ClassDB::bind_method(D_METHOD("get_tab_align"), &Tabs::get_tab_align); + ClassDB::bind_method(D_METHOD("set_clip_tabs", "clip_tabs"), &Tabs::set_clip_tabs); + ClassDB::bind_method(D_METHOD("get_clip_tabs"), &Tabs::get_clip_tabs); ClassDB::bind_method(D_METHOD("get_tab_offset"), &Tabs::get_tab_offset); ClassDB::bind_method(D_METHOD("get_offset_buttons_visible"), &Tabs::get_offset_buttons_visible); ClassDB::bind_method(D_METHOD("ensure_tab_visible", "idx"), &Tabs::ensure_tab_visible); @@ -1131,6 +1157,7 @@ void Tabs::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "current_tab", PROPERTY_HINT_RANGE, "-1,4096,1", PROPERTY_USAGE_EDITOR), "set_current_tab", "get_current_tab"); ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_tab_align", "get_tab_align"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_tabs"), "set_clip_tabs", "get_clip_tabs"); ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_close_display_policy", PROPERTY_HINT_ENUM, "Show Never,Show Active Only,Show Always"), "set_tab_close_display_policy", "get_tab_close_display_policy"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scrolling_enabled"), "set_scrolling_enabled", "get_scrolling_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_to_rearrange_enabled"), "set_drag_to_rearrange_enabled", "get_drag_to_rearrange_enabled"); diff --git a/scene/gui/tabs.h b/scene/gui/tabs.h index 86877f4d80..61c9a5d96a 100644 --- a/scene/gui/tabs.h +++ b/scene/gui/tabs.h @@ -85,6 +85,7 @@ private: int previous = 0; int _get_top_margin() const; TabAlign tab_align = ALIGN_CENTER; + bool clip_tabs = true; int rb_hover = -1; bool rb_pressing = false; @@ -148,6 +149,9 @@ public: void set_tab_align(TabAlign p_align); TabAlign get_tab_align() const; + void set_clip_tabs(bool p_clip_tabs); + bool get_clip_tabs() const; + void move_tab(int from, int to); void set_tab_close_display_policy(CloseButtonDisplayPolicy p_policy); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 5a7901c11b..e305c6210d 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -357,10 +357,10 @@ void TextEdit::_update_scrollbars() { } void TextEdit::_click_selection_held() { - // Warning: is_mouse_button_pressed(BUTTON_LEFT) returns false for double+ clicks, so this doesn't work for MODE_WORD + // Warning: is_mouse_button_pressed(MOUSE_BUTTON_LEFT) returns false for double+ clicks, so this doesn't work for MODE_WORD // and MODE_LINE. However, moving the mouse triggers _gui_input, which calls these functions too, so that's not a huge problem. // I'm unsure if there's an actual fix that doesn't have a ton of side effects. - if (Input::get_singleton()->is_mouse_button_pressed(BUTTON_LEFT) && selection.selecting_mode != SelectionMode::SELECTION_MODE_NONE) { + if (Input::get_singleton()->is_mouse_button_pressed(MOUSE_BUTTON_LEFT) && selection.selecting_mode != SelectionMode::SELECTION_MODE_NONE) { switch (selection.selecting_mode) { case SelectionMode::SELECTION_MODE_POINTER: { _update_selection_mode_pointer(); @@ -808,7 +808,7 @@ void TextEdit::_notification(int p_what) { // Get the highlighted words. String highlighted_text = get_selection_text(); - // Check if highlighted words contains only whitespaces (tabs or spaces). + // Check if highlighted words contain only whitespaces (tabs or spaces). bool only_whitespaces_highlighted = highlighted_text.strip_edges() == String(); int cursor_wrap_index = get_cursor_wrap_index(); @@ -1057,7 +1057,7 @@ void TextEdit::_notification(int p_what) { } if (str.length() == 0) { - // Draw line background if empty as we won't loop at at all. + // Draw line background if empty as we won't loop at all. if (line == cursor.line && cursor_wrap_index == line_wrap_index && highlight_current_line) { if (rtl) { RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - ofs_x - xmargin_end, ofs_y, xmargin_end, row_height), cache.current_line_color); @@ -1442,7 +1442,7 @@ void TextEdit::_notification(int p_what) { } } else { { - // IME intermidiet text range. + // IME Intermediate text range. Vector<Vector2> sel = TS->shaped_text_get_selection(rid, cursor.column, cursor.column + ime_text.length()); for (int j = 0; j < sel.size(); j++) { Rect2 rect = Rect2(sel[j].x + char_margin + ofs_x, ofs_y, sel[j].y - sel[j].x, text_height); @@ -1557,7 +1557,7 @@ void TextEdit::_notification(int p_what) { completion_rect.position.x = rect_left_border_x; } - if (cursor_pos.y + row_height + total_height > get_size().height) { + if (cursor_pos.y + row_height + total_height > get_size().height && cursor_pos.y > total_height) { // Completion panel above the cursor line completion_rect.position.y = cursor_pos.y - total_height; } else { @@ -1702,7 +1702,7 @@ void TextEdit::_notification(int p_what) { } if (has_focus()) { - if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID) { + if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) { DisplayServer::get_singleton()->window_set_ime_active(true, get_viewport()->get_window_id()); DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + cursor_pos, get_viewport()->get_window_id()); } @@ -1715,7 +1715,7 @@ void TextEdit::_notification(int p_what) { draw_caret = true; } - if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID) { + if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) { DisplayServer::get_singleton()->window_set_ime_active(true, get_viewport()->get_window_id()); DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + _get_cursor_pixel_pos(false), get_viewport()->get_window_id()); } @@ -1744,7 +1744,7 @@ void TextEdit::_notification(int p_what) { caret_blink_timer->stop(); } - if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID) { + if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) { DisplayServer::get_singleton()->window_set_ime_position(Point2(), get_viewport()->get_window_id()); DisplayServer::get_singleton()->window_set_ime_active(false, get_viewport()->get_window_id()); } @@ -1973,7 +1973,7 @@ void TextEdit::backspace_at_cursor() { } } - cursor_set_line(prev_line, true, true); + cursor_set_line(prev_line, false, true); cursor_set_column(prev_column); } @@ -2009,7 +2009,7 @@ void TextEdit::indent_selected_lines_right() { // 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. + // Since we will add these many spaces, we want to move the 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; @@ -2034,7 +2034,7 @@ void TextEdit::indent_selected_lines_left() { 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), + // changing content of line can move cursor and selection on its 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; @@ -2054,6 +2054,7 @@ void TextEdit::indent_selected_lines_left() { if (is_selection_active() && get_selection_to_column() == 0) { end_line--; } + String first_line_text = get_line(start_line); String last_line_text = get_line(end_line); for (int i = start_line; i <= end_line; i++) { @@ -2078,10 +2079,17 @@ void TextEdit::indent_selected_lines_left() { } } - // 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 - removed_characters, - selection.to_line, initial_selection_end_column - removed_characters); + if (is_selection_active()) { + // Fix selection being off by one on the first line. + if (first_line_text != get_line(start_line)) { + select(selection.from_line, selection.from_column - removed_characters, + selection.to_line, initial_selection_end_column); + } + // Fix selection being off by one on the last line. + if (last_line_text != get_line(end_line)) { + select(selection.from_line, selection.from_column, + selection.to_line, initial_selection_end_column - removed_characters); + } } cursor_set_column(initial_cursor_column - removed_characters, false); end_complex_operation(); @@ -2190,9 +2198,14 @@ void TextEdit::_new_line(bool p_split_current_line, bool p_above) { // No need to move the brace below if we are not taking the text with us. char32_t closing_char = _get_right_pair_symbol(indent_char); - if ((closing_char != 0) && (closing_char == text[cursor.line][cursor.column]) && !p_split_current_line) { - brace_indent = true; - ins += "\n" + ins.substr(1, ins.length() - 2); + if ((closing_char != 0) && (closing_char == text[cursor.line][cursor.column])) { + if (p_split_current_line) { + brace_indent = true; + ins += "\n" + ins.substr(1, ins.length() - 2); + } else { + brace_indent = false; + ins = "\n" + ins.substr(1, ins.length() - 2); + } } } } @@ -2202,7 +2215,7 @@ void TextEdit::_new_line(bool p_split_current_line, bool p_above) { if (!p_split_current_line) { if (p_above) { if (cursor.line > 0) { - cursor_set_line(cursor.line - 1); + cursor_set_line(cursor.line - 1, false); cursor_set_column(text[cursor.line].length()); } else { cursor_set_column(0); @@ -2218,7 +2231,7 @@ void TextEdit::_new_line(bool p_split_current_line, bool p_above) { if (first_line) { cursor_set_line(0); } else if (brace_indent) { - cursor_set_line(cursor.line - 1); + cursor_set_line(cursor.line - 1, false); cursor_set_column(text[cursor.line].length()); } end_complex_operation(); @@ -2449,7 +2462,7 @@ void TextEdit::_move_cursor_to_line_start(bool p_select) { row_start_col += rows[i].length(); } if (cursor.column == row_start_col || wi == 0) { - // Compute whitespace symbols seq length. + // Compute whitespace symbols sequence length. int current_line_whitespace_len = 0; while (current_line_whitespace_len < text[cursor.line].length()) { char32_t c = text[cursor.line][current_line_whitespace_len]; @@ -2568,7 +2581,7 @@ void TextEdit::_backspace(bool p_word, bool p_all_to_left) { _remove_text(line, column, cursor.line, cursor.column); - cursor_set_line(line); + cursor_set_line(line, false); cursor_set_column(column); } else { // One character. @@ -2635,7 +2648,7 @@ void TextEdit::_delete_selection() { selection.active = false; update(); _remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column); - cursor_set_line(selection.from_line, true, false); + cursor_set_line(selection.from_line, false, false); cursor_set_column(selection.from_column); update(); } @@ -2849,6 +2862,8 @@ void TextEdit::_get_minimap_mouse_row(const Point2i &p_mouse, int &r_row) const } void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { + ERR_FAIL_COND(p_gui_input.is_null()); + double prev_v_scroll = v_scroll->get_value(); double prev_h_scroll = h_scroll->get_value(); @@ -2868,14 +2883,14 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { return; } - if (mb->get_button_index() == BUTTON_WHEEL_UP) { + if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP) { if (completion_index > 0) { completion_index--; completion_current = completion_options[completion_index]; update(); } } - if (mb->get_button_index() == BUTTON_WHEEL_DOWN) { + if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN) { if (completion_index < completion_options.size() - 1) { completion_index++; completion_current = completion_options[completion_index]; @@ -2883,12 +2898,12 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { } } - if (mb->get_button_index() == BUTTON_LEFT) { + if (mb->get_button_index() == MOUSE_BUTTON_LEFT) { completion_index = CLAMP(completion_line_ofs + (mpos.y - completion_rect.position.y) / get_row_height(), 0, completion_options.size() - 1); completion_current = completion_options[completion_index]; update(); - if (mb->is_doubleclick()) { + if (mb->is_double_click()) { _confirm_completion(); } } @@ -2899,27 +2914,35 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { } if (mb->is_pressed()) { - if (mb->get_button_index() == BUTTON_WHEEL_UP && !mb->get_command()) { - if (mb->get_shift()) { + if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP && !mb->is_command_pressed()) { + if (mb->is_shift_pressed()) { h_scroll->set_value(h_scroll->get_value() - (100 * mb->get_factor())); + } else if (mb->is_alt_pressed()) { + // Scroll 5 times as fast as normal (like in Visual Studio Code). + _scroll_up(15 * mb->get_factor()); } else if (v_scroll->is_visible()) { + // Scroll 3 lines. _scroll_up(3 * mb->get_factor()); } } - if (mb->get_button_index() == BUTTON_WHEEL_DOWN && !mb->get_command()) { - if (mb->get_shift()) { + if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && !mb->is_command_pressed()) { + if (mb->is_shift_pressed()) { h_scroll->set_value(h_scroll->get_value() + (100 * mb->get_factor())); + } else if (mb->is_alt_pressed()) { + // Scroll 5 times as fast as normal (like in Visual Studio Code). + _scroll_down(15 * mb->get_factor()); } else if (v_scroll->is_visible()) { + // Scroll 3 lines. _scroll_down(3 * mb->get_factor()); } } - if (mb->get_button_index() == BUTTON_WHEEL_LEFT) { + if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_LEFT) { h_scroll->set_value(h_scroll->get_value() - (100 * mb->get_factor())); } - if (mb->get_button_index() == BUTTON_WHEEL_RIGHT) { + if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_RIGHT) { h_scroll->set_value(h_scroll->get_value() + (100 * mb->get_factor())); } - if (mb->get_button_index() == BUTTON_LEFT) { + if (mb->get_button_index() == MOUSE_BUTTON_LEFT) { _reset_caret_blink_timer(); int row, col; @@ -2962,7 +2985,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { cursor_set_line(row, false, false); cursor_set_column(col); - if (mb->get_shift() && (cursor.column != prev_col || cursor.line != prev_line)) { + if (mb->is_shift_pressed() && (cursor.column != prev_col || cursor.line != prev_line)) { if (!selection.active) { selection.active = true; selection.selecting_mode = SelectionMode::SELECTION_MODE_POINTER; @@ -2984,8 +3007,6 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { } else { if (cursor.line < selection.selecting_line || (cursor.line == selection.selecting_line && cursor.column < selection.selecting_column)) { if (selection.shiftclick_left) { - SWAP(selection.from_column, selection.to_column); - SWAP(selection.from_line, selection.to_line); selection.shiftclick_left = !selection.shiftclick_left; } selection.from_column = cursor.column; @@ -3013,12 +3034,12 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { selection.selecting_column = col; } - if (!mb->is_doubleclick() && (OS::get_singleton()->get_ticks_msec() - last_dblclk) < 600 && cursor.line == prev_line) { + if (!mb->is_double_click() && (OS::get_singleton()->get_ticks_msec() - last_dblclk) < 600 && cursor.line == prev_line) { // Triple-click select line. selection.selecting_mode = SelectionMode::SELECTION_MODE_LINE; _update_selection_mode_line(); last_dblclk = 0; - } else if (mb->is_doubleclick() && text[cursor.line].length()) { + } else if (mb->is_double_click() && text[cursor.line].length()) { // Double-click select word. selection.selecting_mode = SelectionMode::SELECTION_MODE_WORD; _update_selection_mode_word(); @@ -3028,7 +3049,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { update(); } - if (mb->get_button_index() == BUTTON_RIGHT && context_menu_enabled) { + if (mb->get_button_index() == MOUSE_BUTTON_RIGHT && context_menu_enabled) { _reset_caret_blink_timer(); int row, col; @@ -3059,8 +3080,8 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { grab_focus(); } } else { - if (mb->get_button_index() == BUTTON_LEFT) { - if (mb->get_command() && highlighted_word != String()) { + if (mb->get_button_index() == MOUSE_BUTTON_LEFT) { + if (mb->is_command_pressed() && highlighted_word != String()) { int row, col; _get_mouse_pos(Point2i(mpos.x, mpos.y), row, col); @@ -3103,7 +3124,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { mpos.x = get_size().x - mpos.x; } if (select_identifiers_enabled) { - if (!dragging_minimap && !dragging_selection && mm->get_command() && mm->get_button_mask() == 0) { + if (!dragging_minimap && !dragging_selection && mm->is_command_pressed() && mm->get_button_mask() == 0) { String new_word = get_word_at_pos(mpos); if (new_word != highlighted_word) { emit_signal("symbol_validate", new_word); @@ -3115,7 +3136,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { } } - if (mm->get_button_mask() & BUTTON_MASK_LEFT && get_viewport()->gui_get_drag_data() == Variant()) { // Ignore if dragging. + if (mm->get_button_mask() & MOUSE_BUTTON_MASK_LEFT && get_viewport()->gui_get_drag_data() == Variant()) { // Ignore if dragging. _reset_caret_blink_timer(); if (draw_minimap && !dragging_selection) { @@ -3152,7 +3173,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { #ifdef OSX_ENABLED if (k->get_keycode() == KEY_META) { #else - if (k->get_keycode() == KEY_CONTROL) { + if (k->get_keycode() == KEY_CTRL) { #endif if (select_identifiers_enabled) { if (k->is_pressed() && !dragging_minimap && !dragging_selection) { @@ -3170,7 +3191,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { } // If a modifier has been pressed, and nothing else, return. - if (k->get_keycode() == KEY_CONTROL || k->get_keycode() == KEY_ALT || k->get_keycode() == KEY_SHIFT || k->get_keycode() == KEY_META) { + if (k->get_keycode() == KEY_CTRL || k->get_keycode() == KEY_ALT || k->get_keycode() == KEY_SHIFT || k->get_keycode() == KEY_META) { return; } @@ -3178,7 +3199,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { // Allow unicode handling if: // * No Modifiers are pressed (except shift) - bool allow_unicode_handling = !(k->get_command() || k->get_control() || k->get_alt() || k->get_metakey()); + bool allow_unicode_handling = !(k->is_command_pressed() || k->is_ctrl_pressed() || k->is_alt_pressed() || k->is_meta_pressed()); // Save here for insert mode, just in case it is cleared in the following section. bool had_selection = selection.active; @@ -3256,7 +3277,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { accept_event(); return; } - if (k->is_action("ui_accept", true) || k->is_action("ui_text_completion_accept", true)) { + if (k->is_action("ui_text_completion_accept", true)) { _confirm_completion(); accept_event(); return; @@ -3423,9 +3444,9 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { // CURSOR MOVEMENT k = k->duplicate(); - bool shift_pressed = k->get_shift(); + bool shift_pressed = k->is_shift_pressed(); // Remove shift or else actions will not match. Use above variable for selection. - k->set_shift(false); + k->set_shift_pressed(false); // CURSOR MOVEMENT - LEFT, RIGHT. if (k->is_action("ui_text_caret_word_left", true)) { @@ -3846,7 +3867,7 @@ void TextEdit::_insert_text_at_cursor(const String &p_text) { int new_column, new_line; _insert_text(cursor.line, cursor.column, p_text, &new_line, &new_column); _update_scrollbars(); - cursor_set_line(new_line); + cursor_set_line(new_line, false); cursor_set_column(new_column); update(); @@ -4420,7 +4441,7 @@ int TextEdit::get_column_x_offset_for_line(int p_char, int p_line) const { void TextEdit::insert_text_at_cursor(const String &p_text) { if (selection.active) { - cursor_set_line(selection.from_line); + cursor_set_line(selection.from_line, false); cursor_set_column(selection.from_column); _remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column); @@ -4437,7 +4458,7 @@ Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const { return CURSOR_POINTING_HAND; } - if ((completion_active && completion_rect.has_point(p_pos))) { + if ((completion_active && completion_rect.has_point(p_pos)) || (is_readonly() && (!is_selecting_enabled() || text.size() == 0))) { return CURSOR_ARROW; } @@ -5037,7 +5058,7 @@ void TextEdit::cut() { DisplayServer::get_singleton()->clipboard_set(clipboard); _remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column); - cursor_set_line(selection.from_line); // Set afterwards else it causes the view to be offset. + cursor_set_line(selection.from_line, false); // Set afterwards else it causes the view to be offset. cursor_set_column(selection.from_column); selection.active = false; @@ -5073,7 +5094,7 @@ void TextEdit::paste() { selection.active = false; selection.selecting_mode = SelectionMode::SELECTION_MODE_NONE; _remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column); - cursor_set_line(selection.from_line); + cursor_set_line(selection.from_line, false); cursor_set_column(selection.from_column); } else if (!cut_copy_line.is_empty() && cut_copy_line == clipboard) { @@ -5812,11 +5833,11 @@ void TextEdit::undo() { _update_scrollbars(); if (undo_stack_pos->get().type == TextOperation::TYPE_REMOVE) { - cursor_set_line(undo_stack_pos->get().to_line); + cursor_set_line(undo_stack_pos->get().to_line, false); cursor_set_column(undo_stack_pos->get().to_column); _cancel_code_hint(); } else { - cursor_set_line(undo_stack_pos->get().from_line); + cursor_set_line(undo_stack_pos->get().from_line, false); cursor_set_column(undo_stack_pos->get().from_column); } update(); @@ -5851,7 +5872,7 @@ void TextEdit::redo() { } _update_scrollbars(); - cursor_set_line(undo_stack_pos->get().to_line); + cursor_set_line(undo_stack_pos->get().to_line, false); cursor_set_column(undo_stack_pos->get().to_column); undo_stack_pos = undo_stack_pos->next(); update(); @@ -6832,6 +6853,7 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("get_line_count"), &TextEdit::get_line_count); ClassDB::bind_method(D_METHOD("get_text"), &TextEdit::get_text); ClassDB::bind_method(D_METHOD("get_line", "line"), &TextEdit::get_line); + ClassDB::bind_method(D_METHOD("get_visible_line_count"), &TextEdit::get_total_visible_rows); ClassDB::bind_method(D_METHOD("set_line", "line", "new_text"), &TextEdit::set_line); ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override", "parser"), &TextEdit::set_structured_text_bidi_override); diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 6ac4d7fd2f..2f78736a12 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -47,36 +47,6 @@ #include <limits.h> -void TreeItem::move_to_top() { - if (!parent || parent->children == this) { - return; //already on top - } - TreeItem *prev = get_prev(); - prev->next = next; - next = parent->children; - parent->children = this; -} - -void TreeItem::move_to_bottom() { - if (!parent || !next) { - return; - } - - TreeItem *prev = get_prev(); - TreeItem *last = next; - while (last->next) { - last = last->next; - } - - if (prev) { - prev->next = next; - } else { - parent->children = next; - } - last->next = this; - next = nullptr; -} - Size2 TreeItem::Cell::get_icon_size() const { if (icon.is_null()) { return Size2(); @@ -118,6 +88,54 @@ void TreeItem::_cell_deselected(int p_cell) { tree->item_deselected(p_cell, this); } +void TreeItem::_change_tree(Tree *p_tree) { + if (p_tree == tree) { + return; + } + + TreeItem *c = first_child; + while (c) { + c->_change_tree(p_tree); + c = c->next; + } + + if (tree && tree->root == this) { + tree->root = nullptr; + } + + if (tree && tree->popup_edited_item == this) { + tree->popup_edited_item = nullptr; + tree->pressing_for_editor = false; + } + + if (tree && tree->cache.hover_item == this) { + tree->cache.hover_item = nullptr; + } + + if (tree && tree->selected_item == this) { + tree->selected_item = nullptr; + } + + if (tree && tree->drop_mode_over == this) { + tree->drop_mode_over = nullptr; + } + + if (tree && tree->single_select_defer == this) { + tree->single_select_defer = nullptr; + } + + if (tree && tree->edited_item == this) { + tree->edited_item = nullptr; + tree->pressing_for_editor = false; + } + + tree = p_tree; + + if (tree) { + cells.resize(tree->columns.size()); + } +} + /* cell mode */ void TreeItem::set_cell_mode(int p_column, TreeCellMode p_mode) { ERR_FAIL_INDEX(p_column, cells.size()); @@ -410,6 +428,14 @@ bool TreeItem::is_collapsed() { return collapsed; } +void TreeItem::uncollapse_tree() { + TreeItem *t = this; + while (t) { + t->set_collapsed(false); + t = t->parent; + } +} + void TreeItem::set_custom_minimum_height(int p_height) { custom_min_height = p_height; _changed_notify(); @@ -419,19 +445,73 @@ int TreeItem::get_custom_minimum_height() const { return custom_min_height; } +/* Item manipulation */ + +TreeItem *TreeItem::create_child(int p_idx) { + TreeItem *ti = memnew(TreeItem(tree)); + if (tree) { + ti->cells.resize(tree->columns.size()); + } + + TreeItem *l_prev = nullptr; + TreeItem *c = first_child; + int idx = 0; + + while (c) { + if (idx++ == p_idx) { + c->prev = ti; + ti->next = c; + break; + } + l_prev = c; + c = c->next; + } + + if (l_prev) { + l_prev->next = ti; + ti->prev = l_prev; + if (!children_cache.is_empty()) { + if (ti->next) { + children_cache.insert(p_idx, ti); + } else { + children_cache.append(ti); + } + } + } else { + first_child = ti; + if (!children_cache.is_empty()) { + children_cache.insert(0, ti); + } + } + + ti->parent = this; + + return ti; +} + +Tree *TreeItem::get_tree() { + return tree; +} + TreeItem *TreeItem::get_next() { return next; } TreeItem *TreeItem::get_prev() { - if (!parent || parent->children == this) { - return nullptr; + if (prev) { + return prev; } - TreeItem *prev = parent->children; - while (prev && prev->next != this) { - prev = prev->next; + if (!parent || parent->first_child == this) { + return nullptr; } + // This is an edge case + TreeItem *l_prev = parent->first_child; + while (l_prev && l_prev->next != this) { + l_prev = l_prev->next; + } + + prev = l_prev; return prev; } @@ -440,8 +520,8 @@ TreeItem *TreeItem::get_parent() { return parent; } -TreeItem *TreeItem::get_children() { - return children; +TreeItem *TreeItem::get_first_child() { + return first_child; } TreeItem *TreeItem::get_prev_visible(bool p_wrap) { @@ -467,10 +547,10 @@ TreeItem *TreeItem::get_prev_visible(bool p_wrap) { } } else { current = prev; - while (!current->collapsed && current->children) { + while (!current->collapsed && current->first_child) { //go to the very end - current = current->children; + current = current->first_child; while (current->next) { current = current->next; } @@ -483,8 +563,8 @@ TreeItem *TreeItem::get_prev_visible(bool p_wrap) { TreeItem *TreeItem::get_next_visible(bool p_wrap) { TreeItem *current = this; - if (!current->collapsed && current->children) { - current = current->children; + if (!current->collapsed && current->first_child) { + current = current->first_child; } else if (current->next) { current = current->next; @@ -507,24 +587,136 @@ TreeItem *TreeItem::get_next_visible(bool p_wrap) { return current; } -void TreeItem::remove_child(TreeItem *p_item) { +TreeItem *TreeItem::get_child(int p_idx) { + _create_children_cache(); + ERR_FAIL_INDEX_V(p_idx, children_cache.size(), nullptr); + return children_cache.get(p_idx); +} + +int TreeItem::get_child_count() { + _create_children_cache(); + return children_cache.size(); +} + +Array TreeItem::get_children() { + int size = get_child_count(); + Array arr; + arr.resize(size); + for (int i = 0; i < size; i++) { + arr[i] = children_cache[i]; + } + + return arr; +} + +int TreeItem::get_index() { + int idx = 0; + TreeItem *c = this; + + while (c) { + c = c->get_prev(); + idx++; + } + return idx - 1; +} + +void TreeItem::move_before(TreeItem *p_item) { ERR_FAIL_NULL(p_item); - TreeItem **c = &children; + ERR_FAIL_COND(is_root); + ERR_FAIL_COND(!p_item->parent); + + if (p_item == this) { + return; + } - while (*c) { - if ((*c) == p_item) { - TreeItem *aux = *c; + TreeItem *p = p_item->parent; + while (p) { + ERR_FAIL_COND_MSG(p == this, "Can't move to a descendant"); + p = p->parent; + } - *c = (*c)->next; + Tree *old_tree = tree; + _unlink_from_tree(); + _change_tree(p_item->tree); - aux->parent = nullptr; - return; - } + parent = p_item->parent; + + TreeItem *item_prev = p_item->get_prev(); + if (item_prev) { + item_prev->next = this; + parent->children_cache.clear(); + } else { + parent->first_child = this; + parent->children_cache.insert(0, this); + } + + prev = item_prev; + next = p_item; + p_item->prev = this; - c = &(*c)->next; + if (old_tree && old_tree != tree) { + old_tree->update(); } - ERR_FAIL(); + if (tree) { + tree->update(); + } +} + +void TreeItem::move_after(TreeItem *p_item) { + ERR_FAIL_NULL(p_item); + ERR_FAIL_COND(is_root); + ERR_FAIL_COND(!p_item->parent); + + if (p_item == this) { + return; + } + + TreeItem *p = p_item->parent; + while (p) { + ERR_FAIL_COND_MSG(p == this, "Can't move to a descendant"); + p = p->parent; + } + + Tree *old_tree = tree; + _unlink_from_tree(); + _change_tree(p_item->tree); + + if (p_item->next) { + p_item->next->prev = this; + } + parent = p_item->parent; + prev = p_item; + next = p_item->next; + p_item->next = this; + + if (next) { + parent->children_cache.clear(); + } else { + parent->children_cache.append(this); + } + + if (old_tree && old_tree != tree) { + old_tree->update(); + } + + if (tree) { + tree->update(); + } +} + +void TreeItem::remove_child(TreeItem *p_item) { + ERR_FAIL_NULL(p_item); + ERR_FAIL_COND(p_item->parent != this); + + p_item->_unlink_from_tree(); + p_item->prev = nullptr; + p_item->next = nullptr; + p_item->parent = nullptr; + + if (tree) { + tree->update(); + } } void TreeItem::set_selectable(int p_column, bool p_selectable) { @@ -777,7 +969,7 @@ void recursive_call_aux(TreeItem *p_item, const StringName &p_method, const Vari return; } p_item->call(p_method, p_args, p_argcount, r_error); - TreeItem *c = p_item->get_children(); + TreeItem *c = p_item->get_first_child(); while (c) { recursive_call_aux(c, p_method, p_args, p_argcount, r_error); c = c->get_next(); @@ -842,19 +1034,11 @@ void TreeItem::_bind_methods() { ClassDB::bind_method(D_METHOD("set_collapsed", "enable"), &TreeItem::set_collapsed); ClassDB::bind_method(D_METHOD("is_collapsed"), &TreeItem::is_collapsed); + ClassDB::bind_method(D_METHOD("uncollapse_tree"), &TreeItem::uncollapse_tree); + ClassDB::bind_method(D_METHOD("set_custom_minimum_height", "height"), &TreeItem::set_custom_minimum_height); ClassDB::bind_method(D_METHOD("get_custom_minimum_height"), &TreeItem::get_custom_minimum_height); - ClassDB::bind_method(D_METHOD("get_next"), &TreeItem::get_next); - ClassDB::bind_method(D_METHOD("get_prev"), &TreeItem::get_prev); - 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", "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); - ClassDB::bind_method(D_METHOD("set_selectable", "column", "selectable"), &TreeItem::set_selectable); ClassDB::bind_method(D_METHOD("is_selectable", "column"), &TreeItem::is_selectable); @@ -885,19 +1069,38 @@ void TreeItem::_bind_methods() { ClassDB::bind_method(D_METHOD("set_button_disabled", "column", "button_idx", "disabled"), &TreeItem::set_button_disabled); ClassDB::bind_method(D_METHOD("is_button_disabled", "column", "button_idx"), &TreeItem::is_button_disabled); - ClassDB::bind_method(D_METHOD("set_expand_right", "column", "enable"), &TreeItem::set_expand_right); - ClassDB::bind_method(D_METHOD("get_expand_right", "column"), &TreeItem::get_expand_right); - ClassDB::bind_method(D_METHOD("set_tooltip", "column", "tooltip"), &TreeItem::set_tooltip); ClassDB::bind_method(D_METHOD("get_tooltip", "column"), &TreeItem::get_tooltip); ClassDB::bind_method(D_METHOD("set_text_align", "column", "text_align"), &TreeItem::set_text_align); ClassDB::bind_method(D_METHOD("get_text_align", "column"), &TreeItem::get_text_align); - ClassDB::bind_method(D_METHOD("move_to_top"), &TreeItem::move_to_top); - ClassDB::bind_method(D_METHOD("move_to_bottom"), &TreeItem::move_to_bottom); + + ClassDB::bind_method(D_METHOD("set_expand_right", "column", "enable"), &TreeItem::set_expand_right); + ClassDB::bind_method(D_METHOD("get_expand_right", "column"), &TreeItem::get_expand_right); ClassDB::bind_method(D_METHOD("set_disable_folding", "disable"), &TreeItem::set_disable_folding); ClassDB::bind_method(D_METHOD("is_folding_disabled"), &TreeItem::is_folding_disabled); + ClassDB::bind_method(D_METHOD("create_child", "idx"), &TreeItem::create_child, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("get_tree"), &TreeItem::get_tree); + + ClassDB::bind_method(D_METHOD("get_next"), &TreeItem::get_next); + ClassDB::bind_method(D_METHOD("get_prev"), &TreeItem::get_prev); + ClassDB::bind_method(D_METHOD("get_parent"), &TreeItem::get_parent); + ClassDB::bind_method(D_METHOD("get_first_child"), &TreeItem::get_first_child); + + 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("get_child", "idx"), &TreeItem::get_child); + ClassDB::bind_method(D_METHOD("get_child_count"), &TreeItem::get_child_count); + ClassDB::bind_method(D_METHOD("get_children"), &TreeItem::get_children); + ClassDB::bind_method(D_METHOD("get_index"), &TreeItem::get_index); + + ClassDB::bind_method(D_METHOD("move_before", "item"), &TreeItem::_move_before); + ClassDB::bind_method(D_METHOD("move_after", "item"), &TreeItem::_move_after); + + ClassDB::bind_method(D_METHOD("remove_child", "child"), &TreeItem::_remove_child); + { MethodInfo mi; mi.name = "call_recursive"; @@ -922,7 +1125,7 @@ void TreeItem::_bind_methods() { } void TreeItem::clear_children() { - TreeItem *c = children; + TreeItem *c = first_child; while (c) { TreeItem *aux = c; c = c->get_next(); @@ -930,56 +1133,18 @@ void TreeItem::clear_children() { memdelete(aux); } - children = nullptr; + first_child = nullptr; }; TreeItem::TreeItem(Tree *p_tree) { tree = p_tree; - collapsed = false; - disable_folding = false; - custom_min_height = 0; - - parent = nullptr; // parent item - next = nullptr; // next in list - children = nullptr; //child items } TreeItem::~TreeItem() { + _unlink_from_tree(); + prev = nullptr; clear_children(); - - if (parent) { - parent->remove_child(this); - } - - if (tree && tree->root == this) { - tree->root = nullptr; - } - - if (tree && tree->popup_edited_item == this) { - tree->popup_edited_item = nullptr; - tree->pressing_for_editor = false; - } - - if (tree && tree->cache.hover_item == this) { - tree->cache.hover_item = nullptr; - } - - if (tree && tree->selected_item == this) { - tree->selected_item = nullptr; - } - - if (tree && tree->drop_mode_over == this) { - tree->drop_mode_over = nullptr; - } - - if (tree && tree->single_select_defer == this) { - tree->single_select_defer = nullptr; - } - - if (tree && tree->edited_item == this) { - tree->edited_item = nullptr; - tree->pressing_for_editor = false; - } + _change_tree(nullptr); } /**********************************************/ @@ -1106,7 +1271,7 @@ int Tree::get_item_height(TreeItem *p_item) const { if (!p_item->collapsed) { /* if not collapsed, check the children */ - TreeItem *c = p_item->children; + TreeItem *c = p_item->first_child; while (c) { height += get_item_height(c); @@ -1253,7 +1418,7 @@ void Tree::update_item_cache(TreeItem *p_item) { update_item_cell(p_item, i); } - TreeItem *c = p_item->children; + TreeItem *c = p_item->first_child; while (c) { update_item_cache(c); c = c->next; @@ -1378,7 +1543,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 } } - if ((select_mode == SELECT_ROW && selected_item == p_item) || p_item->cells[i].selected) { + if ((select_mode == SELECT_ROW && selected_item == p_item) || p_item->cells[i].selected || !p_item->has_meta("__focus_rect")) { Rect2i r(cell_rect.position, cell_rect.size); p_item->set_meta("__focus_rect", Rect2(r.position, r.size)); @@ -1420,7 +1585,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 if (drop_mode_flags && drop_mode_over == p_item) { Rect2 r = cell_rect; - bool has_parent = p_item->get_children() != nullptr; + bool has_parent = p_item->get_first_child() != nullptr; if (rtl) { r.position.x = get_size().width - r.position.x - r.size.x; } @@ -1578,7 +1743,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 if (p_item->cells[i].custom_button) { if (cache.hover_item == p_item && cache.hover_cell == i) { - if (Input::get_singleton()->is_mouse_button_pressed(BUTTON_LEFT)) { + if (Input::get_singleton()->is_mouse_button_pressed(MOUSE_BUTTON_LEFT)) { draw_style_box(cache.custom_button_pressed, ir); } else { draw_style_box(cache.custom_button_hover, ir); @@ -1616,7 +1781,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 } } - if (!p_item->disable_folding && !hide_folding && p_item->children) { //has children, draw the guide box + if (!p_item->disable_folding && !hide_folding && p_item->first_child) { //has children, draw the guide box Ref<Texture2D> arrow; @@ -1646,7 +1811,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 if (!p_item->collapsed) { /* if not collapsed, check the children */ - TreeItem *c = p_item->children; + TreeItem *c = p_item->first_child; int prev_ofs = children_pos.y - cache.offset.y + p_draw_ofs.y; @@ -1656,13 +1821,13 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 int parent_ofs = p_pos.x + ((p_item->disable_folding || hide_folding) ? cache.hseparation : cache.item_margin); Point2i root_pos = Point2i(root_ofs, children_pos.y + label_h / 2) - cache.offset + p_draw_ofs; - if (c->get_children() != nullptr) { + if (c->get_first_child() != nullptr) { root_pos -= Point2i(cache.arrow->get_width(), 0); } float line_width = 1.0; #ifdef TOOLS_ENABLED - line_width *= EDSCALE; + line_width *= Math::round(EDSCALE); #endif Point2i parent_pos = Point2i(parent_ofs - cache.arrow->get_width() / 2, p_pos.y + label_h / 2 + cache.arrow->get_height() / 2) - cache.offset + p_draw_ofs; @@ -1713,8 +1878,8 @@ int Tree::_count_selected_items(TreeItem *p_from) const { } } - if (p_from->get_children()) { - count += _count_selected_items(p_from->get_children()); + if (p_from->get_first_child()) { + count += _count_selected_items(p_from->get_first_child()); } if (p_from->get_next()) { @@ -1802,7 +1967,7 @@ void Tree::select_single_item(TreeItem *p_selected, TreeItem *p_current, int p_c *r_in_range = false; } - TreeItem *c = p_current->children; + TreeItem *c = p_current->first_child; while (c) { select_single_item(p_selected, c, p_col, p_prev, r_in_range, p_current->is_collapsed() || p_force_deselect); @@ -1815,7 +1980,7 @@ Rect2 Tree::search_item_rect(TreeItem *p_from, TreeItem *p_item) { } void Tree::_range_click_timeout() { - if (range_item_last && !range_drag_enabled && Input::get_singleton()->is_mouse_button_pressed(BUTTON_LEFT)) { + if (range_item_last && !range_drag_enabled && Input::get_singleton()->is_mouse_button_pressed(MOUSE_BUTTON_LEFT)) { Point2 pos = get_local_mouse_position() - cache.bg->get_offset(); if (show_column_titles) { pos.y -= _get_title_button_height(); @@ -1829,11 +1994,10 @@ void Tree::_range_click_timeout() { click_handled = false; Ref<InputEventMouseButton> mb; mb.instance(); - ; propagate_mouse_activated = false; // done from outside, so signal handler can't clear the tree in the middle of emit (which is a common case) blocked++; - propagate_mouse_event(pos + cache.offset, 0, 0, false, root, BUTTON_LEFT, mb); + propagate_mouse_event(pos + cache.offset, 0, 0, false, root, MOUSE_BUTTON_LEFT, mb); blocked--; if (range_click_timer->is_one_shot()) { @@ -1856,7 +2020,7 @@ void Tree::_range_click_timeout() { } } -int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool p_doubleclick, TreeItem *p_item, int p_button, const Ref<InputEventWithModifiers> &p_mod) { +int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool p_double_click, TreeItem *p_item, int p_button, const Ref<InputEventWithModifiers> &p_mod) { int item_h = compute_item_height(p_item) + cache.vseparation; bool skip = (p_item == root && hide_root); @@ -1869,7 +2033,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool } if (!p_item->disable_folding && !hide_folding && (p_pos.x >= x_ofs && p_pos.x < (x_ofs + cache.item_margin))) { - if (p_item->children) { + if (p_item->first_child) { p_item->set_collapsed(!p_item->is_collapsed()); } @@ -1916,7 +2080,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool x -= cache.hseparation; } - if (!p_item->disable_folding && !hide_folding && !p_item->cells[col].editable && !p_item->cells[col].selectable && p_item->get_children()) { + if (!p_item->disable_folding && !hide_folding && !p_item->cells[col].editable && !p_item->cells[col].selectable && p_item->get_first_child()) { p_item->set_collapsed(!p_item->is_collapsed()); return -1; //collapse/uncollapse because nothing can be done with item } @@ -1950,10 +2114,10 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool col_width -= w + cache.button_margin; } - if (p_button == BUTTON_LEFT || (p_button == BUTTON_RIGHT && allow_rmb_select)) { + if (p_button == MOUSE_BUTTON_LEFT || (p_button == MOUSE_BUTTON_RIGHT && allow_rmb_select)) { /* process selection */ - if (p_doubleclick && (!c.editable || c.mode == TreeItem::CELL_MODE_CUSTOM || c.mode == TreeItem::CELL_MODE_ICON /*|| c.mode==TreeItem::CELL_MODE_CHECK*/)) { //it's confusing for check + if (p_double_click && (!c.editable || c.mode == TreeItem::CELL_MODE_CUSTOM || c.mode == TreeItem::CELL_MODE_ICON /*|| c.mode==TreeItem::CELL_MODE_CHECK*/)) { //it's confusing for check propagate_mouse_activated = true; @@ -1961,11 +2125,11 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool return -1; } - if (select_mode == SELECT_MULTI && p_mod->get_command() && c.selectable) { - if (!c.selected || p_button == BUTTON_RIGHT) { + if (select_mode == SELECT_MULTI && p_mod->is_command_pressed() && c.selectable) { + if (!c.selected || p_button == MOUSE_BUTTON_RIGHT) { p_item->select(col); emit_signal("multi_selected", p_item, col, true); - if (p_button == BUTTON_RIGHT) { + if (p_button == MOUSE_BUTTON_RIGHT) { emit_signal("item_rmb_selected", get_local_mouse_position()); } @@ -1978,25 +2142,25 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool } else { if (c.selectable) { - if (select_mode == SELECT_MULTI && p_mod->get_shift() && selected_item && selected_item != p_item) { + if (select_mode == SELECT_MULTI && p_mod->is_shift_pressed() && selected_item && selected_item != p_item) { bool inrange = false; select_single_item(p_item, root, col, selected_item, &inrange); - if (p_button == BUTTON_RIGHT) { + if (p_button == MOUSE_BUTTON_RIGHT) { emit_signal("item_rmb_selected", get_local_mouse_position()); } } else { int icount = _count_selected_items(root); - if (select_mode == SELECT_MULTI && icount > 1 && p_button != BUTTON_RIGHT) { + if (select_mode == SELECT_MULTI && icount > 1 && p_button != MOUSE_BUTTON_RIGHT) { single_select_defer = p_item; single_select_defer_column = col; } else { - if (p_button != BUTTON_RIGHT || !c.selected) { + if (p_button != MOUSE_BUTTON_RIGHT || !c.selected) { select_single_item(p_item, root, col); } - if (p_button == BUTTON_RIGHT) { + if (p_button == MOUSE_BUTTON_RIGHT) { emit_signal("item_rmb_selected", get_local_mouse_position()); } } @@ -2066,7 +2230,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool /* touching the combo */ bool up = p_pos.y < (item_h / 2); - if (p_button == BUTTON_LEFT) { + if (p_button == MOUSE_BUTTON_LEFT) { if (range_click_timer->get_time_left() == 0) { range_item_last = p_item; range_up_last = up; @@ -2083,13 +2247,13 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool item_edited(col, p_item); - } else if (p_button == BUTTON_RIGHT) { + } else if (p_button == MOUSE_BUTTON_RIGHT) { p_item->set_range(col, (up ? c.max : c.min)); item_edited(col, p_item); - } else if (p_button == BUTTON_WHEEL_UP) { + } else if (p_button == MOUSE_BUTTON_WHEEL_UP) { p_item->set_range(col, c.val + c.step); item_edited(col, p_item); - } else if (p_button == BUTTON_WHEEL_DOWN) { + } else if (p_button == MOUSE_BUTTON_WHEEL_DOWN) { p_item->set_range(col, c.val - c.step); item_edited(col, p_item); } @@ -2122,14 +2286,14 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool } if (!p_item->cells[col].custom_button || !on_arrow) { - item_edited(col, p_item, p_button == BUTTON_LEFT); + item_edited(col, p_item, p_button == MOUSE_BUTTON_LEFT); } click_handled = true; return -1; } break; }; - if (!bring_up_editor || p_button != BUTTON_LEFT) { + if (!bring_up_editor || p_button != MOUSE_BUTTON_LEFT) { return -1; } @@ -2154,10 +2318,10 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool if (!p_item->collapsed) { /* if not collapsed, check the children */ - TreeItem *c = p_item->children; + TreeItem *c = p_item->first_child; while (c) { - int child_h = propagate_mouse_event(new_pos, x_ofs, y_ofs, p_doubleclick, c, p_button, p_mod); + int child_h = propagate_mouse_event(new_pos, x_ofs, y_ofs, p_double_click, c, p_button, p_mod); if (child_h < 0) { return -1; // break, stop propagating, no need to anymore @@ -2169,7 +2333,7 @@ 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) { + if (p_item == root && p_button == MOUSE_BUTTON_RIGHT) { emit_signal("empty_rmb", get_local_mouse_position()); } } @@ -2260,7 +2424,7 @@ void Tree::popup_select(int p_option) { void Tree::_go_left() { if (selected_col == 0) { - if (selected_item->get_children() != nullptr && !selected_item->is_collapsed()) { + if (selected_item->get_first_child() != nullptr && !selected_item->is_collapsed()) { selected_item->set_collapsed(true); } else { if (columns.size() == 1) { // goto parent with one column @@ -2288,7 +2452,7 @@ void Tree::_go_left() { void Tree::_go_right() { if (selected_col == (columns.size() - 1)) { - if (selected_item->get_children() != nullptr && selected_item->is_collapsed()) { + if (selected_item->get_first_child() != nullptr && selected_item->is_collapsed()) { selected_item->set_collapsed(false); } else if (selected_item->get_next_visible()) { selected_col = 0; @@ -2392,9 +2556,11 @@ void Tree::_go_down() { } void Tree::_gui_input(Ref<InputEvent> p_event) { + ERR_FAIL_COND(p_event.is_null()); + Ref<InputEventKey> k = p_event; - bool is_command = k.is_valid() && k->get_command(); + bool is_command = k.is_valid() && k->is_command_pressed(); if (p_event->is_action("ui_right") && p_event->is_pressed()) { if (!cursor_can_exit_tree) { accept_event(); @@ -2403,9 +2569,9 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { if (!selected_item || select_mode == SELECT_ROW || selected_col > (columns.size() - 1)) { return; } - if (k.is_valid() && k->get_alt()) { + if (k.is_valid() && k->is_alt_pressed()) { selected_item->set_collapsed(false); - TreeItem *next = selected_item->get_children(); + TreeItem *next = selected_item->get_first_child(); while (next && next != selected_item->next) { next->set_collapsed(false); next = next->get_next_visible(); @@ -2422,9 +2588,9 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { return; } - if (k.is_valid() && k->get_alt()) { + if (k.is_valid() && k->is_alt_pressed()) { selected_item->set_collapsed(true); - TreeItem *next = selected_item->get_children(); + TreeItem *next = selected_item->get_first_child(); while (next && next != selected_item->next) { next->set_collapsed(true); next = next->get_next_visible(); @@ -2552,7 +2718,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { if (!k->is_pressed()) { return; } - if (k->get_command() || (k->get_shift() && k->get_unicode() == 0) || k->get_metakey()) { + if (k->is_command_pressed() || (k->is_shift_pressed() && k->get_unicode() == 0) || k->is_meta_pressed()) { return; } if (!root) { @@ -2710,7 +2876,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { bool rtl = is_layout_rtl(); if (!b->is_pressed()) { - if (b->get_button_index() == BUTTON_LEFT) { + if (b->get_button_index() == MOUSE_BUTTON_LEFT) { Point2 pos = b->get_position(); if (rtl) { pos.x = get_size().width - pos.x; @@ -2791,8 +2957,8 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { } switch (b->get_button_index()) { - case BUTTON_RIGHT: - case BUTTON_LEFT: { + case MOUSE_BUTTON_RIGHT: + case MOUSE_BUTTON_LEFT: { Ref<StyleBox> bg = cache.bg; Point2 pos = b->get_position(); @@ -2805,7 +2971,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { pos.y -= _get_title_button_height(); if (pos.y < 0) { - if (b->get_button_index() == BUTTON_LEFT) { + if (b->get_button_index() == MOUSE_BUTTON_LEFT) { pos.x += cache.offset.x; int len = 0; for (int i = 0; i < columns.size(); i++) { @@ -2822,8 +2988,8 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { break; } } - if (!root || (!root->get_children() && hide_root)) { - if (b->get_button_index() == BUTTON_RIGHT && allow_rmb_select) { + if (!root || (!root->get_first_child() && hide_root)) { + if (b->get_button_index() == MOUSE_BUTTON_RIGHT && allow_rmb_select) { emit_signal("empty_tree_rmb_selected", get_local_mouse_position()); } break; @@ -2834,7 +3000,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { propagate_mouse_activated = false; blocked++; - propagate_mouse_event(pos + cache.offset, 0, 0, b->is_doubleclick(), root, b->get_button_index(), b); + propagate_mouse_event(pos + cache.offset, 0, 0, b->is_double_click(), root, b->get_button_index(), b); blocked--; if (pressing_for_editor) { @@ -2844,7 +3010,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { } } - if (b->get_button_index() == BUTTON_RIGHT) { + if (b->get_button_index() == MOUSE_BUTTON_RIGHT) { break; } @@ -2867,8 +3033,8 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { set_physics_process_internal(true); } - if (b->get_button_index() == BUTTON_LEFT) { - if (get_item_at_position(b->get_position()) == nullptr && !b->get_shift() && !b->get_control() && !b->get_command()) { + if (b->get_button_index() == MOUSE_BUTTON_LEFT) { + if (get_item_at_position(b->get_position()) == nullptr && !b->is_shift_pressed() && !b->is_ctrl_pressed() && !b->is_command_pressed()) { emit_signal("nothing_selected"); } } @@ -2880,7 +3046,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { } } break; - case BUTTON_WHEEL_UP: { + case MOUSE_BUTTON_WHEEL_UP: { double prev_value = v_scroll->get_value(); v_scroll->set_value(v_scroll->get_value() - v_scroll->get_page() * b->get_factor() / 8); if (v_scroll->get_value() != prev_value) { @@ -2888,7 +3054,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { } } break; - case BUTTON_WHEEL_DOWN: { + case MOUSE_BUTTON_WHEEL_DOWN: { double prev_value = v_scroll->get_value(); v_scroll->set_value(v_scroll->get_value() + v_scroll->get_page() * b->get_factor() / 8); if (v_scroll->get_value() != prev_value) { @@ -3001,6 +3167,10 @@ bool Tree::edit_selected() { return false; } +bool Tree::is_editing() { + return popup_editor->is_visible(); +} + Size2 Tree::get_internal_min_size() const { Size2i size = cache.bg->get_offset(); if (root) { @@ -3162,7 +3332,6 @@ void Tree::_notification(int p_what) { RID ci = get_canvas_item(); Ref<StyleBox> bg = cache.bg; - Ref<StyleBox> bg_focus = get_theme_stylebox("bg_focus"); Color font_outline_color = get_theme_color("font_outline_color"); int outline_size = get_theme_constant("outline_size"); @@ -3171,11 +3340,6 @@ void Tree::_notification(int p_what) { Size2 draw_size = get_size() - bg->get_minimum_size(); bg->draw(ci, Rect2(Point2(), get_size())); - if (has_focus()) { - RenderingServer::get_singleton()->canvas_item_add_clip_ignore(ci, true); - bg_focus->draw(ci, Rect2(Point2(), get_size())); - RenderingServer::get_singleton()->canvas_item_add_clip_ignore(ci, false); - } int tbh = _get_title_button_height(); @@ -3209,6 +3373,15 @@ void Tree::_notification(int p_what) { columns[i].text_buf->draw(ci, text_pos, cache.title_button_color); } } + + // Draw the background focus outline last, so that it is drawn in front of the section headings. + // Otherwise, section heading backgrounds can appear to be in front of the focus outline when scrolling. + if (has_focus()) { + RenderingServer::get_singleton()->canvas_item_add_clip_ignore(ci, true); + const Ref<StyleBox> bg_focus = get_theme_stylebox("bg_focus"); + bg_focus->draw(ci, Rect2(Point2(), get_size())); + RenderingServer::get_singleton()->canvas_item_add_clip_ignore(ci, false); + } } if (p_what == NOTIFICATION_THEME_CHANGED || p_what == NOTIFICATION_LAYOUT_DIRECTION_CHANGED || p_what == NOTIFICATION_TRANSLATION_CHANGED) { @@ -3250,38 +3423,15 @@ TreeItem *Tree::create_item(TreeItem *p_parent, int p_idx) { TreeItem *ti = nullptr; if (p_parent) { - // Append or insert a new item to the given parent. - ti = memnew(TreeItem(this)); - ERR_FAIL_COND_V(!ti, nullptr); - ti->cells.resize(columns.size()); - - TreeItem *prev = nullptr; - TreeItem *c = p_parent->children; - int idx = 0; - - while (c) { - if (idx++ == p_idx) { - ti->next = c; - break; - } - prev = c; - c = c->next; - } - - if (prev) { - prev->next = ti; - } else { - p_parent->children = ti; - } - ti->parent = p_parent; - + ERR_FAIL_COND_V_MSG(p_parent->tree != this, nullptr, "A different tree owns the given parent"); + ti = p_parent->create_child(p_idx); } else { if (!root) { // No root exists, make the given item the new root. ti = memnew(TreeItem(this)); ERR_FAIL_COND_V(!ti, nullptr); ti->cells.resize(columns.size()); - + ti->is_root = true; root = ti; } else { // Root exists, append or insert to root. @@ -3302,8 +3452,8 @@ TreeItem *Tree::get_last_item() { while (last) { if (last->next) { last = last->next; - } else if (last->children) { - last = last->children; + } else if (last->first_child) { + last = last->first_child; } else { break; } @@ -3472,8 +3622,8 @@ TreeItem *Tree::get_next_selected(TreeItem *p_item) { if (!p_item) { p_item = root; } else { - if (p_item->children) { - p_item = p_item->children; + if (p_item->first_child) { + p_item = p_item->first_child; } else if (p_item->next) { p_item = p_item->next; @@ -3506,9 +3656,13 @@ int Tree::get_column_width(int p_column) const { return columns[p_column].min_width; } + int expand_area = get_size().width; + Ref<StyleBox> bg = cache.bg; - int expand_area = get_size().width - (bg->get_margin(SIDE_LEFT) + bg->get_margin(SIDE_RIGHT)); + if (bg.is_valid()) { + expand_area -= bg->get_margin(SIDE_LEFT) + bg->get_margin(SIDE_RIGHT); + } if (v_scroll->is_visible_in_tree()) { expand_area -= v_scroll->get_combined_minimum_size().width; @@ -3538,7 +3692,7 @@ int Tree::get_column_width(int p_column) const { void Tree::propagate_set_columns(TreeItem *p_item) { p_item->cells.resize(columns.size()); - TreeItem *c = p_item->get_children(); + TreeItem *c = p_item->get_first_child(); while (c) { propagate_set_columns(c); c = c->next; @@ -3588,8 +3742,8 @@ int Tree::get_item_offset(TreeItem *p_item) const { ofs += cache.vseparation; } - if (it->children && !it->collapsed) { - it = it->children; + if (it->first_child && !it->collapsed) { + it = it->first_child; } else if (it->next) { it = it->next; @@ -3912,7 +4066,7 @@ TreeItem *Tree::_find_item_at_pos(TreeItem *p_item, const Point2 &p_pos, int &r_ return nullptr; // do not try children, it's collapsed } - TreeItem *n = p_item->get_children(); + TreeItem *n = p_item->get_first_child(); while (n) { int ch; TreeItem *r = _find_item_at_pos(n, pos, r_column, ch, section); @@ -4246,7 +4400,7 @@ void Tree::_bind_methods() { ADD_SIGNAL(MethodInfo("item_custom_button_pressed")); ADD_SIGNAL(MethodInfo("item_double_clicked")); ADD_SIGNAL(MethodInfo("item_collapsed", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "TreeItem"))); - //ADD_SIGNAL( MethodInfo("item_doubleclicked" ) ); + //ADD_SIGNAL( MethodInfo("item_double_clicked" ) ); ADD_SIGNAL(MethodInfo("button_pressed", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "TreeItem"), PropertyInfo(Variant::INT, "column"), PropertyInfo(Variant::INT, "id"))); ADD_SIGNAL(MethodInfo("custom_popup_edited", PropertyInfo(Variant::BOOL, "arrow_clicked"))); ADD_SIGNAL(MethodInfo("item_activated")); @@ -4311,6 +4465,8 @@ Tree::Tree() { set_mouse_filter(MOUSE_FILTER_STOP); set_clip_contents(true); + + update_cache(); } Tree::~Tree() { diff --git a/scene/gui/tree.h b/scene/gui/tree.h index 1be21cb4a4..9dbfd93082 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -122,14 +122,18 @@ private: Vector<Cell> cells; - bool collapsed; // won't show children - bool disable_folding; - int custom_min_height; + bool collapsed = false; // won't show children + bool disable_folding = false; + int custom_min_height = 0; - TreeItem *parent; // parent item - TreeItem *next; // next in list - TreeItem *children; //child items - Tree *tree; //tree (for reference) + TreeItem *parent = nullptr; // parent item + TreeItem *prev = nullptr; // previous in list + TreeItem *next = nullptr; // next in list + TreeItem *first_child = nullptr; + + Vector<TreeItem *> children_cache; + bool is_root = false; // for tree root + Tree *tree; // tree (for reference) TreeItem(Tree *p_tree); @@ -138,9 +142,40 @@ private: void _cell_selected(int p_cell); void _cell_deselected(int p_cell); + void _change_tree(Tree *p_tree); + + _FORCE_INLINE_ void _create_children_cache() { + if (children_cache.is_empty()) { + TreeItem *c = first_child; + while (c) { + children_cache.append(c); + c = c->next; + } + } + } + + _FORCE_INLINE_ void _unlink_from_tree() { + TreeItem *p = get_prev(); + if (p) { + p->next = next; + } + if (next) { + next->prev = p; + } + if (parent) { + if (!parent->children_cache.is_empty()) { + parent->children_cache.remove(get_index()); + } + if (parent->first_child == this) { + parent->first_child = next; + } + } + } + protected: static void _bind_methods(); - //bind helpers + + // Bind helpers Dictionary _get_range_config(int p_column) { Dictionary d; double min = 0.0, max = 0.0, step = 0.0; @@ -156,6 +191,13 @@ protected: remove_child(Object::cast_to<TreeItem>(p_child)); } + void _move_before(Object *p_item) { + move_before(Object::cast_to<TreeItem>(p_item)); + } + void _move_after(Object *p_item) { + move_after(Object::cast_to<TreeItem>(p_item)); + } + Variant _call_recursive_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error); public: @@ -229,19 +271,11 @@ public: void set_collapsed(bool p_collapsed); bool is_collapsed(); + void uncollapse_tree(); + void set_custom_minimum_height(int p_height); int get_custom_minimum_height() const; - TreeItem *get_prev(); - TreeItem *get_next(); - TreeItem *get_parent(); - TreeItem *get_children(); - - TreeItem *get_prev_visible(bool p_wrap = false); - TreeItem *get_next_visible(bool p_wrap = false); - - void remove_child(TreeItem *p_item); - void set_selectable(int p_column, bool p_selectable); bool is_selectable(int p_column) const; @@ -267,22 +301,43 @@ public: void set_tooltip(int p_column, const String &p_tooltip); String get_tooltip(int p_column) const; - void clear_children(); - void set_text_align(int p_column, TextAlign p_align); TextAlign get_text_align(int p_column) const; void set_expand_right(int p_column, bool p_enable); bool get_expand_right(int p_column) const; - void move_to_top(); - void move_to_bottom(); - void set_disable_folding(bool p_disable); bool is_folding_disabled() const; + /* Item manipulation */ + + TreeItem *create_child(int p_idx = -1); + + Tree *get_tree(); + + TreeItem *get_prev(); + TreeItem *get_next(); + TreeItem *get_parent(); + TreeItem *get_first_child(); + + TreeItem *get_prev_visible(bool p_wrap = false); + TreeItem *get_next_visible(bool p_wrap = false); + + TreeItem *get_child(int p_idx); + int get_child_count(); + Array get_children(); + int get_index(); + + void move_before(TreeItem *p_item); + void move_after(TreeItem *p_item); + + void remove_child(TreeItem *p_item); + void call_recursive(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error); + void clear_children(); + ~TreeItem(); }; @@ -388,7 +443,7 @@ private: void draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Color &p_color, const Color &p_icon_color, int p_ol_size, const Color &p_ol_color); int draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 &p_draw_size, TreeItem *p_item); void select_single_item(TreeItem *p_selected, TreeItem *p_current, int p_col, TreeItem *p_prev = nullptr, bool *r_in_range = nullptr, bool p_force_deselect = false); - int propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool p_doubleclick, TreeItem *p_item, int p_button, const Ref<InputEventWithModifiers> &p_mod); + int propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool p_double_click, TreeItem *p_item, int p_button, const Ref<InputEventWithModifiers> &p_mod); void _text_editor_enter(String p_text); void _text_editor_modal_close(); void value_editor_changed(double p_value); @@ -602,6 +657,7 @@ public: int get_item_offset(TreeItem *p_item) const; Rect2 get_item_rect(TreeItem *p_item, int p_column = -1) const; bool edit_selected(); + bool is_editing(); // First item that starts with the text, from the current focused item down and wraps around. TreeItem *search_item_text(const String &p_find, int *r_col = nullptr, bool p_selectable = false); diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp index 90bc99a941..fa98a10a26 100644 --- a/scene/main/canvas_item.cpp +++ b/scene/main/canvas_item.cpp @@ -729,13 +729,13 @@ void CanvasItem::item_rect_changed(bool p_size_changed) { emit_signal(SceneStringNames::get_singleton()->item_rect_changed); } -void CanvasItem::draw_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, float p_width) { +void CanvasItem::draw_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, real_t p_width) { ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); RenderingServer::get_singleton()->canvas_item_add_line(canvas_item, p_from, p_to, p_color, p_width); } -void CanvasItem::draw_polyline(const Vector<Point2> &p_points, const Color &p_color, float p_width, bool p_antialiased) { +void CanvasItem::draw_polyline(const Vector<Point2> &p_points, const Color &p_color, real_t p_width, bool p_antialiased) { ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); Vector<Color> colors; @@ -743,25 +743,25 @@ void CanvasItem::draw_polyline(const Vector<Point2> &p_points, const Color &p_co RenderingServer::get_singleton()->canvas_item_add_polyline(canvas_item, p_points, colors, p_width, p_antialiased); } -void CanvasItem::draw_polyline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width, bool p_antialiased) { +void CanvasItem::draw_polyline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, real_t p_width, bool p_antialiased) { ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); RenderingServer::get_singleton()->canvas_item_add_polyline(canvas_item, p_points, p_colors, p_width, p_antialiased); } -void CanvasItem::draw_arc(const Vector2 &p_center, float p_radius, float p_start_angle, float p_end_angle, int p_point_count, const Color &p_color, float p_width, bool p_antialiased) { +void CanvasItem::draw_arc(const Vector2 &p_center, real_t p_radius, real_t p_start_angle, real_t p_end_angle, int p_point_count, const Color &p_color, real_t p_width, bool p_antialiased) { Vector<Point2> points; points.resize(p_point_count); - const float delta_angle = p_end_angle - p_start_angle; + const real_t delta_angle = p_end_angle - p_start_angle; for (int i = 0; i < p_point_count; i++) { - float theta = (i / (p_point_count - 1.0f)) * delta_angle + p_start_angle; + real_t theta = (i / (p_point_count - 1.0f)) * delta_angle + p_start_angle; points.set(i, p_center + Vector2(Math::cos(theta), Math::sin(theta)) * p_radius); } draw_polyline(points, p_color, p_width, p_antialiased); } -void CanvasItem::draw_multiline(const Vector<Point2> &p_points, const Color &p_color, float p_width) { +void CanvasItem::draw_multiline(const Vector<Point2> &p_points, const Color &p_color, real_t p_width) { ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); Vector<Color> colors; @@ -769,13 +769,13 @@ void CanvasItem::draw_multiline(const Vector<Point2> &p_points, const Color &p_c RenderingServer::get_singleton()->canvas_item_add_multiline(canvas_item, p_points, colors, p_width); } -void CanvasItem::draw_multiline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width) { +void CanvasItem::draw_multiline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, real_t p_width) { ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); RenderingServer::get_singleton()->canvas_item_add_multiline(canvas_item, p_points, p_colors, p_width); } -void CanvasItem::draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled, float p_width) { +void CanvasItem::draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled, real_t p_width) { ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); if (p_filled) { @@ -787,7 +787,7 @@ void CanvasItem::draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_fil } else { // Thick lines are offset depending on their width to avoid partial overlapping. // Thin lines don't require an offset, so don't apply one in this case - float offset; + real_t offset; if (p_width >= 2) { offset = p_width / 2.0; } else { @@ -821,7 +821,7 @@ void CanvasItem::draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_fil } } -void CanvasItem::draw_circle(const Point2 &p_pos, float p_radius, const Color &p_color) { +void CanvasItem::draw_circle(const Point2 &p_pos, real_t p_radius, const Color &p_color) { ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); RenderingServer::get_singleton()->canvas_item_add_circle(canvas_item, p_pos, p_radius, p_color); @@ -856,14 +856,14 @@ void CanvasItem::draw_style_box(const Ref<StyleBox> &p_style_box, const Rect2 &p p_style_box->draw(canvas_item, p_rect); } -void CanvasItem::draw_primitive(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, Ref<Texture2D> p_texture, float p_width) { +void CanvasItem::draw_primitive(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, Ref<Texture2D> p_texture, real_t p_width) { ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); RID rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); RenderingServer::get_singleton()->canvas_item_add_primitive(canvas_item, p_points, p_colors, p_uvs, rid, p_width); } -void CanvasItem::draw_set_transform(const Point2 &p_offset, float p_rot, const Size2 &p_scale) { +void CanvasItem::draw_set_transform(const Point2 &p_offset, real_t p_rot, const Size2 &p_scale) { ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); Transform2D xform(p_rot, p_offset); @@ -907,19 +907,19 @@ void CanvasItem::draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Tex RenderingServer::get_singleton()->canvas_item_add_multimesh(canvas_item, p_multimesh->get_rid(), texture_rid); } -void CanvasItem::draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align, float p_width, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint8_t p_flags) const { +void CanvasItem::draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align, real_t p_width, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint8_t p_flags) const { ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); ERR_FAIL_COND(p_font.is_null()); p_font->draw_string(canvas_item, p_pos, p_text, p_align, p_width, p_size, p_modulate, p_outline_size, p_outline_modulate, p_flags); } -void CanvasItem::draw_multiline_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align, float p_width, int p_max_lines, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint8_t p_flags) const { +void CanvasItem::draw_multiline_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align, real_t p_width, int p_max_lines, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint8_t p_flags) const { ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); ERR_FAIL_COND(p_font.is_null()); p_font->draw_multiline_string(canvas_item, p_pos, p_text, p_align, p_width, p_max_lines, p_size, p_modulate, p_outline_size, p_outline_modulate, p_flags); } -float CanvasItem::draw_char(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, const String &p_next, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate) const { +real_t CanvasItem::draw_char(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, const String &p_next, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate) const { ERR_FAIL_COND_V_MSG(!drawing, 0.f, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); ERR_FAIL_COND_V(p_font.is_null(), 0.f); ERR_FAIL_COND_V(p_char.length() != 1, 0.f); @@ -1210,7 +1210,7 @@ void CanvasItem::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "light_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_light_mask", "get_light_mask"); ADD_GROUP("Texture", "texture_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Inherit,Nearest,Linear,MipmapNearest,MipmapLinear,MipmapNearestAniso,MipmapLinearAniso"), "set_texture_filter", "get_texture_filter"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Inherit,Nearest,Linear,Nearest Mipmap,Linear Mipmap,Nearest Mipmap Aniso.,Linear Mipmap Aniso."), "set_texture_filter", "get_texture_filter"); ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_repeat", PROPERTY_HINT_ENUM, "Inherit,Disabled,Enabled,Mirror"), "set_texture_repeat", "get_texture_repeat"); ADD_GROUP("Material", ""); @@ -1441,6 +1441,7 @@ void CanvasTexture::set_specular_texture(const Ref<Texture2D> &p_specular) { RID tex_rid = specular_texture.is_valid() ? specular_texture->get_rid() : RID(); RS::get_singleton()->canvas_texture_set_channel(canvas_texture, RS::CANVAS_TEXTURE_CHANNEL_SPECULAR, tex_rid); } + Ref<Texture2D> CanvasTexture::get_specular_texture() const { return specular_texture; } @@ -1449,15 +1450,17 @@ void CanvasTexture::set_specular_color(const Color &p_color) { specular = p_color; RS::get_singleton()->canvas_texture_set_shading_parameters(canvas_texture, specular, shininess); } + Color CanvasTexture::get_specular_color() const { return specular; } -void CanvasTexture::set_specular_shininess(float p_shininess) { +void CanvasTexture::set_specular_shininess(real_t p_shininess) { shininess = p_shininess; RS::get_singleton()->canvas_texture_set_shading_parameters(canvas_texture, specular, shininess); } -float CanvasTexture::get_specular_shininess() const { + +real_t CanvasTexture::get_specular_shininess() const { return shininess; } @@ -1508,9 +1511,9 @@ bool CanvasTexture::has_alpha() const { } } -Ref<Image> CanvasTexture::get_data() const { +Ref<Image> CanvasTexture::get_image() const { if (diffuse_texture.is_valid()) { - return diffuse_texture->get_data(); + return diffuse_texture->get_image(); } else { return Ref<Image>(); } diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h index e22f93a7ea..1c64cafab8 100644 --- a/scene/main/canvas_item.h +++ b/scene/main/canvas_item.h @@ -290,8 +290,8 @@ public: // Used to rotate the node virtual bool _edit_use_rotation() const { return false; }; - virtual void _edit_set_rotation(float p_rotation) {} - virtual float _edit_get_rotation() const { return 0.0; }; + virtual void _edit_set_rotation(real_t p_rotation) {} + virtual real_t _edit_get_rotation() const { return 0.0; }; // Used to resize/move the node virtual bool _edit_use_rect() const { return false; }; // MAYBE REPLACE BY A _edit_get_editmode() @@ -331,30 +331,30 @@ public: /* DRAWING API */ - void draw_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, float p_width = 1.0); - void draw_polyline(const Vector<Point2> &p_points, const Color &p_color, float p_width = 1.0, bool p_antialiased = false); - void draw_polyline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width = 1.0, bool p_antialiased = false); - void draw_arc(const Vector2 &p_center, float p_radius, float p_start_angle, float p_end_angle, int p_point_count, const Color &p_color, float p_width = 1.0, bool p_antialiased = false); - void draw_multiline(const Vector<Point2> &p_points, const Color &p_color, float p_width = 1.0); - void draw_multiline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width = 1.0); - void draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled = true, float p_width = 1.0); - void draw_circle(const Point2 &p_pos, float p_radius, const Color &p_color); + void draw_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, real_t p_width = 1.0); + void draw_polyline(const Vector<Point2> &p_points, const Color &p_color, real_t p_width = 1.0, bool p_antialiased = false); + void draw_polyline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, real_t p_width = 1.0, bool p_antialiased = false); + void draw_arc(const Vector2 &p_center, real_t p_radius, real_t p_start_angle, real_t p_end_angle, int p_point_count, const Color &p_color, real_t p_width = 1.0, bool p_antialiased = false); + void draw_multiline(const Vector<Point2> &p_points, const Color &p_color, real_t p_width = 1.0); + void draw_multiline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, real_t p_width = 1.0); + void draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled = true, real_t p_width = 1.0); + void draw_circle(const Point2 &p_pos, real_t p_radius, const Color &p_color); void draw_texture(const Ref<Texture2D> &p_texture, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1, 1)); void draw_texture_rect(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false); void draw_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = false); void draw_style_box(const Ref<StyleBox> &p_style_box, const Rect2 &p_rect); - void draw_primitive(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, Ref<Texture2D> p_texture = Ref<Texture2D>(), float p_width = 1); + void draw_primitive(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, Ref<Texture2D> p_texture = Ref<Texture2D>(), real_t p_width = 1); void draw_polygon(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), Ref<Texture2D> p_texture = Ref<Texture2D>()); void draw_colored_polygon(const Vector<Point2> &p_points, const Color &p_color, const Vector<Point2> &p_uvs = Vector<Point2>(), Ref<Texture2D> p_texture = Ref<Texture2D>()); void draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture2D> &p_texture, const Transform2D &p_transform = Transform2D(), const Color &p_modulate = Color(1, 1, 1)); void draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Texture2D> &p_texture); - void draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, float p_width = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint8_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; - void draw_multiline_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, float p_width = -1, int p_max_lines = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint8_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; - float draw_char(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, const String &p_next = "", int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0)) const; + void draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint8_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; + void draw_multiline_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_max_lines = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint8_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; + real_t draw_char(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, const String &p_next = "", int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0)) const; - void draw_set_transform(const Point2 &p_offset, float p_rot = 0.0, const Size2 &p_scale = Size2(1.0, 1.0)); + void draw_set_transform(const Point2 &p_offset, real_t p_rot = 0.0, const Size2 &p_scale = Size2(1.0, 1.0)); void draw_set_transform_matrix(const Transform2D &p_matrix); static CanvasItem *get_current_item_drawn(); @@ -437,7 +437,7 @@ class CanvasTexture : public Texture2D { Ref<Texture2D> normal_texture; Ref<Texture2D> specular_texture; Color specular = Color(1, 1, 1, 1); - float shininess = 1.0; + real_t shininess = 1.0; RID canvas_texture; @@ -460,8 +460,8 @@ public: void set_specular_color(const Color &p_color); Color get_specular_color() const; - void set_specular_shininess(float p_shininess); - float get_specular_shininess() const; + void set_specular_shininess(real_t p_shininess); + real_t get_specular_shininess() const; void set_texture_filter(CanvasItem::TextureFilter p_filter); CanvasItem::TextureFilter get_texture_filter() const; @@ -475,7 +475,7 @@ public: virtual bool is_pixel_opaque(int p_x, int p_y) const override; virtual bool has_alpha() const override; - virtual Ref<Image> get_data() const override; + virtual Ref<Image> get_image() const override; virtual RID get_rid() const override; diff --git a/scene/main/canvas_layer.cpp b/scene/main/canvas_layer.cpp index d9b29daf26..85d7edd64b 100644 --- a/scene/main/canvas_layer.cpp +++ b/scene/main/canvas_layer.cpp @@ -258,13 +258,11 @@ void CanvasLayer::_update_follow_viewport(bool p_force_exit) { } } -#ifdef TOOLS_ENABLED void CanvasLayer::_validate_property(PropertyInfo &property) const { if (!follow_viewport && property.name == "follow_viewport_scale") { property.usage = PROPERTY_USAGE_NOEDITOR; } } -#endif void CanvasLayer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_layer", "layer"), &CanvasLayer::set_layer); diff --git a/scene/main/canvas_layer.h b/scene/main/canvas_layer.h index b20b291367..899039340a 100644 --- a/scene/main/canvas_layer.h +++ b/scene/main/canvas_layer.h @@ -61,13 +61,10 @@ class CanvasLayer : public Node { void _update_locrotscale(); void _update_follow_viewport(bool p_force_exit = false); -#ifdef TOOLS_ENABLED - void _validate_property(PropertyInfo &property) const override; -#endif - protected: void _notification(int p_what); static void _bind_methods(); + void _validate_property(PropertyInfo &property) const override; public: void set_layer(int p_xform); diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp index 71c372aec2..884696d58d 100644 --- a/scene/main/http_request.cpp +++ b/scene/main/http_request.cpp @@ -40,9 +40,7 @@ Error HTTPRequest::_request() { } Error HTTPRequest::_parse_url(const String &p_url) { - url = p_url; use_ssl = false; - request_string = ""; port = 80; request_sent = false; @@ -52,35 +50,20 @@ Error HTTPRequest::_parse_url(const String &p_url) { downloaded.set(0); redirections = 0; - String url_lower = url.to_lower(); - if (url_lower.begins_with("http://")) { - url = url.substr(7, url.length() - 7); - } else if (url_lower.begins_with("https://")) { - url = url.substr(8, url.length() - 8); + String scheme; + Error err = p_url.parse_url(scheme, url, port, request_string); + ERR_FAIL_COND_V_MSG(err != OK, err, "Error parsing URL: " + p_url + "."); + if (scheme == "https://") { use_ssl = true; - port = 443; - } else { - ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Malformed URL: " + url + "."); + } else if (scheme != "http://") { + ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Invalid URL scheme: " + scheme + "."); } - - ERR_FAIL_COND_V_MSG(url.length() < 1, ERR_INVALID_PARAMETER, "URL too short: " + url + "."); - - int slash_pos = url.find("/"); - - if (slash_pos != -1) { - request_string = url.substr(slash_pos, url.length()); - url = url.substr(0, slash_pos); - } else { - request_string = "/"; + if (port == 0) { + port = use_ssl ? 443 : 80; } - - int colon_pos = url.find(":"); - if (colon_pos != -1) { - port = url.substr(colon_pos + 1, url.length()).to_int(); - url = url.substr(0, colon_pos); - ERR_FAIL_COND_V(port < 1 || port > 65535, ERR_INVALID_PARAMETER); + if (request_string.is_empty()) { + request_string = "/"; } - return OK; } @@ -123,7 +106,7 @@ Error HTTPRequest::request(const String &p_url, const Vector<String> &p_custom_h size_t len = charstr.length(); raw_data.resize(len); uint8_t *w = raw_data.ptrw(); - copymem(w, charstr.ptr(), len); + memcpy(w, charstr.ptr(), len); return request_raw(p_url, p_custom_headers, p_ssl_validate_domain, p_method, raw_data); } @@ -335,7 +318,7 @@ bool HTTPRequest::_update_connection() { call_deferred("_request_done", RESULT_CHUNKED_BODY_SIZE_MISMATCH, response_code, response_headers, PackedByteArray()); return true; - // Request migh have been done + // Request might have been done } else { // Did not request yet, do request @@ -454,7 +437,7 @@ void HTTPRequest::_request_done(int p_status, int p_code, const PackedStringArra is_compressed = false; } - const PackedByteArray *data = NULL; + const PackedByteArray *data = nullptr; if (accept_gzip && is_compressed && p_data.size() > 0) { // Decompress request body diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 9d8c7981e6..c90d3e4a32 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -428,7 +428,7 @@ void Node::set_process_mode(ProcessMode p_mode) { _propagate_process_owner(data.process_owner, pause_notification); #ifdef TOOLS_ENABLED // This is required for the editor to update the visibility of disabled nodes - // Its very expensive during runtime to change, so editor-only + // It's very expensive during runtime to change, so editor-only if (Engine::get_singleton()->is_editor_hint()) { get_tree()->emit_signal("tree_process_mode_changed"); } @@ -591,7 +591,7 @@ Variant Node::_rpc_bind(const Variant **p_args, int p_argcount, Callable::CallEr if (p_args[0]->get_type() != Variant::STRING_NAME) { r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; r_error.argument = 0; - r_error.expected = Variant::STRING; + r_error.expected = Variant::STRING_NAME; return Variant(); } @@ -620,7 +620,7 @@ Variant Node::_rpc_id_bind(const Variant **p_args, int p_argcount, Callable::Cal if (p_args[1]->get_type() != Variant::STRING_NAME) { r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; r_error.argument = 1; - r_error.expected = Variant::STRING; + r_error.expected = Variant::STRING_NAME; return Variant(); } @@ -643,7 +643,7 @@ Variant Node::_rpc_unreliable_bind(const Variant **p_args, int p_argcount, Calla if (p_args[0]->get_type() != Variant::STRING_NAME) { r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; r_error.argument = 0; - r_error.expected = Variant::STRING; + r_error.expected = Variant::STRING_NAME; return Variant(); } @@ -672,7 +672,7 @@ Variant Node::_rpc_unreliable_id_bind(const Variant **p_args, int p_argcount, Ca if (p_args[1]->get_type() != Variant::STRING_NAME) { r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; r_error.argument = 1; - r_error.expected = Variant::STRING; + r_error.expected = Variant::STRING_NAME; return Variant(); } @@ -1021,22 +1021,8 @@ void Node::_set_name_nocheck(const StringName &p_name) { data.name = p_name; } -String Node::invalid_character = ". : @ / \""; - -bool Node::_validate_node_name(String &p_name) { - String name = p_name; - Vector<String> chars = Node::invalid_character.split(" "); - for (int i = 0; i < chars.size(); i++) { - name = name.replace(chars[i], ""); - } - bool is_valid = name == p_name; - p_name = name; - return is_valid; -} - void Node::set_name(const String &p_name) { - String name = p_name; - _validate_node_name(name); + String name = p_name.validate_node_name(); ERR_FAIL_COND(name == ""); data.name = name; @@ -1144,7 +1130,7 @@ String increase_numeric_string(const String &s) { void Node::_generate_serial_child_name(const Node *p_child, StringName &name) const { if (name == StringName()) { - //no name and a new nade is needed, create one. + //no name and a new name is needed, create one. name = p_child->get_class(); // Adjust casing according to project setting. The current type name is expected to be in PascalCase. @@ -1170,7 +1156,7 @@ void Node::_generate_serial_child_name(const Node *p_child, StringName &name) co bool exists = false; for (int i = 0; i < cc; i++) { - if (children_ptr[i] == p_child) { //exclude self in renaming if its already a child + if (children_ptr[i] == p_child) { //exclude self in renaming if it's already a child continue; } if (children_ptr[i]->data.name == name) { @@ -1704,7 +1690,7 @@ NodePath Node::get_path_to(const Node *p_node) const { n = n->data.parent; } - path.invert(); + path.reverse(); return NodePath(path, false); } @@ -1725,7 +1711,7 @@ NodePath Node::get_path() const { n = n->data.parent; } - path.invert(); + path.reverse(); data.path_cache = memnew(NodePath(path, true)); @@ -1959,7 +1945,7 @@ void Node::set_editable_instance(Node *p_node, bool p_editable) { if (!p_editable) { p_node->data.editable_instance = false; // Avoid this flag being needlessly saved; - // also give more visual feedback if editable children is re-enabled + // also give more visual feedback if editable children are re-enabled set_display_folded(false); } else { p_node->data.editable_instance = true; @@ -1982,8 +1968,9 @@ Node *Node::get_deepest_editable_node(Node *p_start_node) const { Node *node = p_start_node; while (iterated_item->get_owner() && iterated_item->get_owner() != this) { - if (!is_editable_instance(iterated_item->get_owner())) + if (!is_editable_instance(iterated_item->get_owner())) { node = iterated_item->get_owner(); + } iterated_item = iterated_item->get_owner(); } @@ -2065,19 +2052,26 @@ Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const // Since nodes in the instanced hierarchy won't be duplicated explicitly, we need to make an inventory // of all the nodes in the tree of the instanced scene in order to transfer the values of the properties + Vector<const Node *> instance_roots; + instance_roots.push_back(this); + for (List<const Node *>::Element *N = node_tree.front(); N; N = N->next()) { for (int i = 0; i < N->get()->get_child_count(); ++i) { Node *descendant = N->get()->get_child(i); // Skip nodes not really belonging to the instanced hierarchy; they'll be processed normally later // but remember non-instanced nodes that are hidden below instanced ones - if (descendant->data.owner != this) { - if (descendant->get_parent() && descendant->get_parent() != this && descendant->get_parent()->data.owner == this && descendant->data.owner != descendant->get_parent()) { + if (!instance_roots.has(descendant->get_owner())) { + if (descendant->get_parent() && descendant->get_parent() != this && descendant->data.owner != descendant->get_parent()) { hidden_roots.push_back(descendant); } continue; } node_tree.push_back(descendant); + + if (descendant->get_filename() != "" && instance_roots.has(descendant->get_owner())) { + instance_roots.push_back(descendant); + } } } } @@ -2640,15 +2634,27 @@ void Node::clear_internal_tree_resource_paths() { } } -String Node::get_configuration_warning() const { +TypedArray<String> Node::get_configuration_warnings() const { if (get_script_instance() && get_script_instance()->get_script().is_valid() && - get_script_instance()->get_script()->is_tool() && get_script_instance()->has_method("_get_configuration_warning")) { - return get_script_instance()->call("_get_configuration_warning"); + get_script_instance()->get_script()->is_tool() && get_script_instance()->has_method("_get_configuration_warnings")) { + return get_script_instance()->call("_get_configuration_warnings"); + } + return Array(); +} + +String Node::get_configuration_warnings_as_string() const { + TypedArray<String> warnings = get_configuration_warnings(); + String all_warnings = String(); + for (int i = 0; i < warnings.size(); i++) { + if (i > 0) { + all_warnings += "\n\n"; + } + all_warnings += String(warnings[i]); } - return String(); + return all_warnings; } -void Node::update_configuration_warning() { +void Node::update_configuration_warnings() { #ifdef TOOLS_ENABLED if (!is_inside_tree()) { return; @@ -2804,7 +2810,7 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("rset_unreliable", "property", "value"), &Node::rset_unreliable); ClassDB::bind_method(D_METHOD("rset_unreliable_id", "peer_id", "property", "value"), &Node::rset_unreliable_id); - ClassDB::bind_method(D_METHOD("update_configuration_warning"), &Node::update_configuration_warning); + ClassDB::bind_method(D_METHOD("update_configuration_warnings"), &Node::update_configuration_warnings); BIND_CONSTANT(NOTIFICATION_ENTER_TREE); BIND_CONSTANT(NOTIFICATION_EXIT_TREE); @@ -2880,7 +2886,7 @@ void Node::_bind_methods() { BIND_VMETHOD(MethodInfo("_input", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"))); BIND_VMETHOD(MethodInfo("_unhandled_input", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"))); BIND_VMETHOD(MethodInfo("_unhandled_key_input", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEventKey"))); - BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_configuration_warning")); + BIND_VMETHOD(MethodInfo(PropertyInfo(Variant::ARRAY, "", PROPERTY_HINT_ARRAY_TYPE, "String"), "_get_configuration_warnings")); } String Node::_get_name_num_separator() { diff --git a/scene/main/node.h b/scene/main/node.h index d47d271a10..6ca2317d9e 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -190,12 +190,6 @@ private: _FORCE_INLINE_ bool _can_process(bool p_paused) const; -#ifdef TOOLS_ENABLED - friend class SceneTreeEditor; -#endif - static String invalid_character; - static bool _validate_node_name(String &p_name); - protected: void _block() { data.blocked++; } void _unblock() { data.blocked--; } @@ -418,9 +412,10 @@ public: _FORCE_INLINE_ Viewport *get_viewport() const { return data.viewport; } - virtual String get_configuration_warning() const; + virtual TypedArray<String> get_configuration_warnings() const; + String get_configuration_warnings_as_string() const; - void update_configuration_warning(); + void update_configuration_warnings(); void set_display_folded(bool p_folded); bool is_displayed_folded() const; diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 9aaddfd373..387af3703b 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -389,6 +389,7 @@ void SceneTree::set_group(const StringName &p_group, const String &p_name, const } void SceneTree::initialize() { + ERR_FAIL_COND(!root); initialized = true; root->_set_tree(this); MainLoop::initialize(); @@ -1348,6 +1349,8 @@ SceneTree::SceneTree() { GLOBAL_DEF("debug/shapes/collision/draw_2d_outlines", true); + Math::randomize(); + // Create with mainloop. root = memnew(Window); @@ -1375,6 +1378,9 @@ SceneTree::SceneTree() { const bool use_debanding = GLOBAL_DEF("rendering/anti_aliasing/quality/use_debanding", false); root->set_use_debanding(use_debanding); + const bool use_occlusion_culling = GLOBAL_DEF("rendering/occlusion_culling/use_occlusion_culling", false); + root->set_use_occlusion_culling(use_occlusion_culling); + float lod_threshold = GLOBAL_DEF("rendering/mesh_lod/lod_change/threshold_pixels", 1.0); ProjectSettings::get_singleton()->set_custom_property_info("rendering/mesh_lod/lod_change/threshold_pixels", PropertyInfo(Variant::FLOAT, "rendering/mesh_lod/lod_change/threshold_pixels", PROPERTY_HINT_RANGE, "0,1024,0.1")); root->set_lod_threshold(lod_threshold); diff --git a/scene/main/shader_globals_override.cpp b/scene/main/shader_globals_override.cpp index b6b2982155..cb3b2cb392 100644 --- a/scene/main/shader_globals_override.cpp +++ b/scene/main/shader_globals_override.cpp @@ -232,7 +232,7 @@ void ShaderGlobalsOverride::_activate() { } } - update_configuration_warning(); //may have activated + update_configuration_warnings(); //may have activated } } @@ -260,17 +260,14 @@ void ShaderGlobalsOverride::_notification(int p_what) { } } -String ShaderGlobalsOverride::get_configuration_warning() const { - String warning = Node::get_configuration_warning(); +TypedArray<String> ShaderGlobalsOverride::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); if (!active) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("ShaderGlobalsOverride is not active because another node of the same type is in the scene."); + warnings.push_back(TTR("ShaderGlobalsOverride is not active because another node of the same type is in the scene.")); } - return warning; + return warnings; } void ShaderGlobalsOverride::_bind_methods() { diff --git a/scene/main/shader_globals_override.h b/scene/main/shader_globals_override.h index 8d8794d465..2d9c3c76bd 100644 --- a/scene/main/shader_globals_override.h +++ b/scene/main/shader_globals_override.h @@ -58,7 +58,7 @@ protected: static void _bind_methods(); public: - String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; ShaderGlobalsOverride(); }; diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 40b85e6d7b..62e1223fb0 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -131,7 +131,7 @@ bool ViewportTexture::has_alpha() const { return false; } -Ref<Image> ViewportTexture::get_data() const { +Ref<Image> ViewportTexture::get_image() const { ERR_FAIL_COND_V_MSG(!vp, Ref<Image>(), "Viewport Texture must be set to use it."); return RS::get_singleton()->texture_2d_get(vp->texture_rid); } @@ -617,10 +617,10 @@ void Viewport::_process_picking() { mm->set_device(InputEvent::DEVICE_ID_INTERNAL); mm->set_global_position(physics_last_mousepos); mm->set_position(physics_last_mousepos); - mm->set_alt(physics_last_mouse_state.alt); - mm->set_shift(physics_last_mouse_state.shift); - mm->set_control(physics_last_mouse_state.control); - mm->set_metakey(physics_last_mouse_state.meta); + mm->set_alt_pressed(physics_last_mouse_state.alt); + mm->set_shift_pressed(physics_last_mouse_state.shift); + mm->set_ctrl_pressed(physics_last_mouse_state.control); + mm->set_meta_pressed(physics_last_mouse_state.meta); mm->set_button_mask(physics_last_mouse_state.mouse_mask); physics_picking_events.push_back(mm); } @@ -641,10 +641,10 @@ void Viewport::_process_picking() { physics_has_last_mousepos = true; physics_last_mousepos = pos; - physics_last_mouse_state.alt = mm->get_alt(); - physics_last_mouse_state.shift = mm->get_shift(); - physics_last_mouse_state.control = mm->get_control(); - physics_last_mouse_state.meta = mm->get_metakey(); + physics_last_mouse_state.alt = mm->is_alt_pressed(); + physics_last_mouse_state.shift = mm->is_shift_pressed(); + physics_last_mouse_state.control = mm->is_ctrl_pressed(); + physics_last_mouse_state.meta = mm->is_meta_pressed(); physics_last_mouse_state.mouse_mask = mm->get_button_mask(); } @@ -656,10 +656,10 @@ void Viewport::_process_picking() { physics_has_last_mousepos = true; physics_last_mousepos = pos; - physics_last_mouse_state.alt = mb->get_alt(); - physics_last_mouse_state.shift = mb->get_shift(); - physics_last_mouse_state.control = mb->get_control(); - physics_last_mouse_state.meta = mb->get_metakey(); + physics_last_mouse_state.alt = mb->is_alt_pressed(); + physics_last_mouse_state.shift = mb->is_shift_pressed(); + physics_last_mouse_state.control = mb->is_ctrl_pressed(); + physics_last_mouse_state.meta = mb->is_meta_pressed(); if (mb->is_pressed()) { physics_last_mouse_state.mouse_mask |= (1 << (mb->get_button_index() - 1)); @@ -676,10 +676,10 @@ void Viewport::_process_picking() { Ref<InputEventKey> k = ev; if (k.is_valid()) { //only for mask - physics_last_mouse_state.alt = k->get_alt(); - physics_last_mouse_state.shift = k->get_shift(); - physics_last_mouse_state.control = k->get_control(); - physics_last_mouse_state.meta = k->get_metakey(); + physics_last_mouse_state.alt = k->is_alt_pressed(); + physics_last_mouse_state.shift = k->is_shift_pressed(); + physics_last_mouse_state.control = k->is_ctrl_pressed(); + physics_last_mouse_state.meta = k->is_meta_pressed(); continue; } @@ -1163,7 +1163,7 @@ void Viewport::set_world_2d(const Ref<World2D> &p_world_2d) { if (p_world_2d.is_valid()) { world_2d = p_world_2d; } else { - WARN_PRINT("Invalid world_3d"); + WARN_PRINT("Invalid world_2d"); world_2d = Ref<World2D>(memnew(World2D)); } @@ -1412,6 +1412,16 @@ void Viewport::_update_canvas_items(Node *p_node) { } } +void Viewport::set_use_xr(bool p_use_xr) { + use_xr = p_use_xr; + + RS::get_singleton()->viewport_set_use_xr(viewport, use_xr); +} + +bool Viewport::is_using_xr() { + return use_xr; +} + Ref<ViewportTexture> Viewport::get_texture() const { return default_texture; } @@ -1621,10 +1631,10 @@ void Viewport::_gui_call_input(Control *p_control, const Ref<InputEvent> &p_inpu Ref<InputEventMouseButton> mb = p_input; bool cant_stop_me_now = (mb.is_valid() && - (mb->get_button_index() == BUTTON_WHEEL_DOWN || - mb->get_button_index() == BUTTON_WHEEL_UP || - mb->get_button_index() == BUTTON_WHEEL_LEFT || - mb->get_button_index() == BUTTON_WHEEL_RIGHT)); + (mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN || + mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP || + mb->get_button_index() == MOUSE_BUTTON_WHEEL_LEFT || + mb->get_button_index() == MOUSE_BUTTON_WHEEL_RIGHT)); Ref<InputEventPanGesture> pn = p_input; cant_stop_me_now = pn.is_valid() || cant_stop_me_now; @@ -1830,6 +1840,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { Control *over = nullptr; Point2 mpos = mb->get_position(); + gui.last_mouse_pos = mpos; if (mb->is_pressed()) { Size2 pos = mpos; if (gui.mouse_focus_mask) { @@ -1860,7 +1871,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { gui.mouse_focus_mask = 1 << (mb->get_button_index() - 1); - if (mb->get_button_index() == BUTTON_LEFT) { + if (mb->get_button_index() == MOUSE_BUTTON_LEFT) { gui.drag_accum = Vector2(); gui.drag_attempted = false; } @@ -1883,7 +1894,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { } #endif - if (mb->get_button_index() == BUTTON_LEFT) { //assign focus + if (mb->get_button_index() == MOUSE_BUTTON_LEFT) { //assign focus CanvasItem *ci = gui.mouse_focus; while (ci) { Control *control = Object::cast_to<Control>(ci); @@ -1914,7 +1925,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { set_input_as_handled(); - if (gui.drag_data.get_type() != Variant::NIL && mb->get_button_index() == BUTTON_LEFT) { + if (gui.drag_data.get_type() != Variant::NIL && mb->get_button_index() == MOUSE_BUTTON_LEFT) { //alternate drop use (when using force_drag(), as proposed by #5342 if (gui.mouse_focus) { _gui_drop(gui.mouse_focus, pos, false); @@ -1934,7 +1945,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { _gui_cancel_tooltip(); } else { - if (gui.drag_data.get_type() != Variant::NIL && mb->get_button_index() == BUTTON_LEFT) { + if (gui.drag_data.get_type() != Variant::NIL && mb->get_button_index() == MOUSE_BUTTON_LEFT) { if (gui.drag_mouse_over) { _gui_drop(gui.drag_mouse_over, gui.drag_mouse_over_pos, false); } @@ -1978,7 +1989,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { _gui_call_input(mouse_focus, mb); } - /*if (gui.drag_data.get_type()!=Variant::NIL && mb->get_button_index()==BUTTON_LEFT) { + /*if (gui.drag_data.get_type()!=Variant::NIL && mb->get_button_index()==MOUSE_BUTTON_LEFT) { _propagate_viewport_notification(this,NOTIFICATION_DRAG_END); gui.drag_data=Variant(); //always clear }*/ @@ -2022,7 +2033,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { Control *over = nullptr; // D&D - if (!gui.drag_attempted && gui.mouse_focus && mm->get_button_mask() & BUTTON_MASK_LEFT) { + if (!gui.drag_attempted && gui.mouse_focus && mm->get_button_mask() & MOUSE_BUTTON_MASK_LEFT) { gui.drag_accum += mm->get_relative(); float len = gui.drag_accum.length(); if (len > 10) { @@ -2201,7 +2212,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { embedder = this; viewport_pos = mpos; } else { - //not an embeder, but may be a subwindow of an embedder + //not an embedder, but may be a subwindow of an embedder Window *w = Object::cast_to<Window>(this); if (w) { if (w->is_embedded()) { @@ -2266,7 +2277,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { Transform2D localizer = gui.drag_mouse_over->get_global_transform_with_canvas().affine_inverse(); gui.drag_mouse_over_pos = localizer.xform(viewport_pos); - if (mm->get_button_mask() & BUTTON_MASK_LEFT) { + if (mm->get_button_mask() & MOUSE_BUTTON_MASK_LEFT) { bool can_drop = _gui_drop(gui.drag_mouse_over, gui.drag_mouse_over_pos, true); if (!can_drop) { @@ -2391,37 +2402,35 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { Control *from = gui.key_focus ? gui.key_focus : nullptr; //hmm //keyboard focus - //if (from && p_event->is_pressed() && !p_event->get_alt() && !p_event->get_metakey() && !p_event->key->get_command()) { + //if (from && p_event->is_pressed() && !p_event->is_alt_pressed() && !p_event->is_meta_pressed() && !p_event->key->is_command_pressed()) { Ref<InputEventKey> k = p_event; - //need to check for mods, otherwise any combination of alt/ctrl/shift+<up/down/left/righ/etc> is handled here when it shouldn't be. - bool mods = k.is_valid() && (k->get_control() || k->get_alt() || k->get_shift() || k->get_metakey()); + //need to check for mods, otherwise any combination of alt/ctrl/shift+<up/down/left/right/etc> is handled here when it shouldn't be. + bool mods = k.is_valid() && (k->is_ctrl_pressed() || k->is_alt_pressed() || k->is_shift_pressed() || k->is_meta_pressed()); if (from && p_event->is_pressed()) { Control *next = nullptr; - Input *input = Input::get_singleton(); - - if (p_event->is_action_pressed("ui_focus_next") && input->is_action_just_pressed("ui_focus_next")) { + if (p_event->is_action_pressed("ui_focus_next", true)) { next = from->find_next_valid_focus(); } - if (p_event->is_action_pressed("ui_focus_prev") && input->is_action_just_pressed("ui_focus_prev")) { + if (p_event->is_action_pressed("ui_focus_prev", true)) { next = from->find_prev_valid_focus(); } - if (!mods && p_event->is_action_pressed("ui_up") && input->is_action_just_pressed("ui_up")) { + if (!mods && p_event->is_action_pressed("ui_up", true)) { next = from->_get_focus_neighbor(SIDE_TOP); } - if (!mods && p_event->is_action_pressed("ui_left") && input->is_action_just_pressed("ui_left")) { + if (!mods && p_event->is_action_pressed("ui_left", true)) { next = from->_get_focus_neighbor(SIDE_LEFT); } - if (!mods && p_event->is_action_pressed("ui_right") && input->is_action_just_pressed("ui_right")) { + if (!mods && p_event->is_action_pressed("ui_right", true)) { next = from->_get_focus_neighbor(SIDE_RIGHT); } - if (!mods && p_event->is_action_pressed("ui_down") && input->is_action_just_pressed("ui_down")) { + if (!mods && p_event->is_action_pressed("ui_down", true)) { next = from->_get_focus_neighbor(SIDE_BOTTOM); } @@ -2672,7 +2681,7 @@ void Viewport::_post_gui_grab_click_focus() { Ref<InputEventMouseButton> mb; mb.instance(); - //send unclic + //send unclick mb->set_position(click); mb->set_button_index(i + 1); @@ -2690,7 +2699,7 @@ void Viewport::_post_gui_grab_click_focus() { Ref<InputEventMouseButton> mb; mb.instance(); - //send clic + //send click mb->set_position(click); mb->set_button_index(i + 1); @@ -2783,7 +2792,7 @@ bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) { ERR_FAIL_COND_V(gui.subwindow_focused == nullptr, false); Ref<InputEventMouseButton> mb = p_event; - if (mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { + if (mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) { if (gui.subwindow_drag == SUB_WINDOW_DRAG_CLOSE) { if (gui.subwindow_drag_close_rect.has_point(mb->get_position())) { //close window @@ -2908,7 +2917,7 @@ bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseButton> mb = p_event; //if the event is a mouse button, we need to check whether another window was clicked - if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { + if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) { bool click_on_window = false; for (int i = gui.sub_windows.size() - 1; i >= 0; i--) { SubWindow &sw = gui.sub_windows.write[i]; @@ -3067,6 +3076,7 @@ void Viewport::input(const Ref<InputEvent> &p_event, bool p_local_coords) { } void Viewport::unhandled_input(const Ref<InputEvent> &p_event, bool p_local_coords) { + ERR_FAIL_COND(p_event.is_null()); ERR_FAIL_COND(!is_inside_tree()); if (disable_input || !_can_consume_input_events()) { @@ -3176,20 +3186,17 @@ Variant Viewport::gui_get_drag_data() const { return gui.drag_data; } -String Viewport::get_configuration_warning() const { +TypedArray<String> Viewport::get_configuration_warnings() const { /*if (get_parent() && !Object::cast_to<Control>(get_parent()) && !render_target) { return TTR("This viewport is not set as render target. If you intend for it to display its contents directly to the screen, make it a child of a Control so it can obtain a size. Otherwise, make it a RenderTarget and assign its internal texture to some node for display."); }*/ - String warning = Node::get_configuration_warning(); + TypedArray<String> warnings = Node::get_configuration_warnings(); if (size.x == 0 || size.y == 0) { - if (!warning.is_empty()) { - warning += "\n\n"; - } - warning += TTR("Viewport size must be greater than 0 to render anything."); + warnings.push_back(TTR("Viewport size must be greater than 0 to render anything.")); } - return warning; + return warnings; } void Viewport::gui_reset_canvas_sort_index() { @@ -3227,8 +3234,9 @@ Viewport::ScreenSpaceAA Viewport::get_screen_space_aa() const { } void Viewport::set_use_debanding(bool p_use_debanding) { - if (use_debanding == p_use_debanding) + if (use_debanding == p_use_debanding) { return; + } use_debanding = p_use_debanding; RS::get_singleton()->viewport_set_use_debanding(viewport, p_use_debanding); } @@ -3245,6 +3253,21 @@ float Viewport::get_lod_threshold() const { return lod_threshold; } +void Viewport::set_use_occlusion_culling(bool p_use_occlusion_culling) { + if (use_occlusion_culling == p_use_occlusion_culling) { + return; + } + + use_occlusion_culling = p_use_occlusion_culling; + RS::get_singleton()->viewport_set_use_occlusion_culling(viewport, p_use_occlusion_culling); + + notify_property_list_changed(); +} + +bool Viewport::is_using_occlusion_culling() const { + return use_occlusion_culling; +} + void Viewport::set_debug_draw(DebugDraw p_debug_draw) { debug_draw = p_debug_draw; RS::get_singleton()->viewport_set_debug_draw(viewport, RS::ViewportDebugDraw(p_debug_draw)); @@ -3334,9 +3357,6 @@ bool Viewport::is_handling_input_locally() const { return handle_input_locally; } -void Viewport::_validate_property(PropertyInfo &property) const { -} - void Viewport::set_default_canvas_item_texture_filter(DefaultCanvasItemTextureFilter p_filter) { ERR_FAIL_INDEX(p_filter, DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_MAX); @@ -3481,11 +3501,17 @@ void Viewport::_bind_methods() { ClassDB::bind_method(D_METHOD("set_use_debanding", "enable"), &Viewport::set_use_debanding); ClassDB::bind_method(D_METHOD("is_using_debanding"), &Viewport::is_using_debanding); + ClassDB::bind_method(D_METHOD("set_use_occlusion_culling", "enable"), &Viewport::set_use_occlusion_culling); + ClassDB::bind_method(D_METHOD("is_using_occlusion_culling"), &Viewport::is_using_occlusion_culling); + ClassDB::bind_method(D_METHOD("set_debug_draw", "debug_draw"), &Viewport::set_debug_draw); ClassDB::bind_method(D_METHOD("get_debug_draw"), &Viewport::get_debug_draw); ClassDB::bind_method(D_METHOD("get_render_info", "info"), &Viewport::get_render_info); + ClassDB::bind_method(D_METHOD("set_use_xr", "use"), &Viewport::set_use_xr); + ClassDB::bind_method(D_METHOD("is_using_xr"), &Viewport::is_using_xr); + ClassDB::bind_method(D_METHOD("get_texture"), &Viewport::get_texture); ClassDB::bind_method(D_METHOD("set_physics_object_picking", "enable"), &Viewport::set_physics_object_picking); @@ -3566,6 +3592,7 @@ void Viewport::_bind_methods() { ClassDB::bind_method(D_METHOD("_process_picking"), &Viewport::_process_picking); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_xr"), "set_use_xr", "is_using_xr"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "own_world_3d"), "set_use_own_world_3d", "is_using_own_world_3d"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "world_3d", PROPERTY_HINT_RESOURCE_TYPE, "World3D"), "set_world_3d", "get_world_3d"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "world_2d", PROPERTY_HINT_RESOURCE_TYPE, "World2D", 0), "set_world_2d", "get_world_2d"); @@ -3577,10 +3604,11 @@ void Viewport::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "msaa", PROPERTY_HINT_ENUM, "Disabled,2x,4x,8x,16x,AndroidVR 2x,AndroidVR 4x"), "set_msaa", "get_msaa"); ADD_PROPERTY(PropertyInfo(Variant::INT, "screen_space_aa", PROPERTY_HINT_ENUM, "Disabled,FXAA"), "set_screen_space_aa", "get_screen_space_aa"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_debanding"), "set_use_debanding", "is_using_debanding"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_occlusion_culling"), "set_use_occlusion_culling", "is_using_occlusion_culling"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lod_threshold", PROPERTY_HINT_RANGE, "0,1024,0.1"), "set_lod_threshold", "get_lod_threshold"); ADD_PROPERTY(PropertyInfo(Variant::INT, "debug_draw", PROPERTY_HINT_ENUM, "Disabled,Unshaded,Overdraw,Wireframe"), "set_debug_draw", "get_debug_draw"); ADD_GROUP("Canvas Items", "canvas_item_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "canvas_item_default_texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,MipmapLinear,MipmapNearest"), "set_default_canvas_item_texture_filter", "get_default_canvas_item_texture_filter"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "canvas_item_default_texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,Linear Mipmap,Nearest Mipmap"), "set_default_canvas_item_texture_filter", "get_default_canvas_item_texture_filter"); ADD_PROPERTY(PropertyInfo(Variant::INT, "canvas_item_default_texture_repeat", PROPERTY_HINT_ENUM, "Disabled,Enabled,Mirror"), "set_default_canvas_item_texture_repeat", "get_default_canvas_item_texture_repeat"); ADD_GROUP("Audio Listener", "audio_listener_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "audio_listener_enable_2d"), "set_as_audio_listener_2d", "is_audio_listener_2d"); @@ -3658,6 +3686,7 @@ void Viewport::_bind_methods() { BIND_ENUM_CONSTANT(DEBUG_DRAW_CLUSTER_SPOT_LIGHTS); BIND_ENUM_CONSTANT(DEBUG_DRAW_CLUSTER_DECALS); BIND_ENUM_CONSTANT(DEBUG_DRAW_CLUSTER_REFLECTION_PROBES); + BIND_ENUM_CONSTANT(DEBUG_DRAW_OCCLUDERS) BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST); BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_LINEAR); @@ -3732,16 +3761,6 @@ Viewport::~Viewport() { ///////////////////////////////// -void SubViewport::set_use_xr(bool p_use_xr) { - xr = p_use_xr; - - RS::get_singleton()->viewport_set_use_xr(get_viewport_rid(), xr); -} - -bool SubViewport::is_using_xr() { - return xr; -} - void SubViewport::set_size(const Size2i &p_size) { _set_size(p_size, _get_size_2d_override(), Rect2i(), _stretch_transform(), true); } @@ -3814,9 +3833,6 @@ void SubViewport::_notification(int p_what) { } void SubViewport::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_use_xr", "use"), &SubViewport::set_use_xr); - ClassDB::bind_method(D_METHOD("is_using_xr"), &SubViewport::is_using_xr); - ClassDB::bind_method(D_METHOD("set_size", "size"), &SubViewport::set_size); ClassDB::bind_method(D_METHOD("get_size"), &SubViewport::get_size); @@ -3832,7 +3848,6 @@ void SubViewport::_bind_methods() { ClassDB::bind_method(D_METHOD("set_clear_mode", "mode"), &SubViewport::set_clear_mode); ClassDB::bind_method(D_METHOD("get_clear_mode"), &SubViewport::get_clear_mode); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "xr"), "set_use_xr", "is_using_xr"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size_2d_override"), "set_size_2d_override", "get_size_2d_override"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "size_2d_override_stretch"), "set_size_2d_override_stretch", "is_size_2d_override_stretch_enabled"); diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 0f11e6fb19..2e88e1251d 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -77,7 +77,7 @@ public: virtual bool has_alpha() const override; - virtual Ref<Image> get_data() const override; + virtual Ref<Image> get_image() const override; ViewportTexture(); ~ViewportTexture(); @@ -147,6 +147,7 @@ public: DEBUG_DRAW_CLUSTER_SPOT_LIGHTS, DEBUG_DRAW_CLUSTER_DECALS, DEBUG_DRAW_CLUSTER_REFLECTION_PROBES, + DEBUG_DRAW_OCCLUDERS, }; enum DefaultCanvasItemTextureFilter { @@ -233,6 +234,7 @@ private: Size2i size; Size2i size_2d_override; bool size_allocated = false; + bool use_xr = false; RID contact_2d_debug; RID contact_3d_debug_multimesh; @@ -304,6 +306,7 @@ private: ScreenSpaceAA screen_space_aa = SCREEN_SPACE_AA_DISABLED; bool use_debanding = false; float lod_threshold = 1.0; + bool use_occlusion_culling = false; Ref<ViewportTexture> default_texture; Set<ViewportTexture *> viewport_textures; @@ -480,7 +483,6 @@ protected: void _notification(int p_what); void _process_picking(); static void _bind_methods(); - virtual void _validate_property(PropertyInfo &property) const override; public: uint64_t get_processed_events_count() const { return event_count; } @@ -533,6 +535,9 @@ public: void set_transparent_background(bool p_enable); bool has_transparent_background() const; + void set_use_xr(bool p_use_xr); + bool is_using_xr(); + Ref<ViewportTexture> get_texture() const; void set_shadow_atlas_size(int p_size); @@ -556,6 +561,9 @@ public: void set_lod_threshold(float p_pixels); float get_lod_threshold() const; + void set_use_occlusion_culling(bool p_us_occlusion_culling); + bool is_using_occlusion_culling() const; + Vector2 get_camera_coords(const Vector2 &p_viewport_coords) const; Vector2 get_camera_rect_size() const; @@ -580,7 +588,7 @@ public: void gui_reset_canvas_sort_index(); int gui_get_canvas_sort_index(); - virtual String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; void set_debug_draw(DebugDraw p_debug_draw); DebugDraw get_debug_draw() const; @@ -652,7 +660,6 @@ public: private: UpdateMode update_mode = UPDATE_WHEN_VISIBLE; ClearMode clear_mode = CLEAR_MODE_ALWAYS; - bool xr = false; bool size_2d_override_stretch = false; protected: @@ -668,9 +675,6 @@ public: void set_size_2d_override(const Size2i &p_size); Size2i get_size_2d_override() const; - void set_use_xr(bool p_use_xr); - bool is_using_xr(); - void set_size_2d_override_stretch(bool p_enable); bool is_size_2d_override_stretch_enabled() const; diff --git a/scene/main/window.cpp b/scene/main/window.cpp index d697a1a5dd..b7bc2a83c5 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -232,7 +232,7 @@ void Window::_make_window() { DisplayServer::get_singleton()->window_set_current_screen(current_screen, window_id); DisplayServer::get_singleton()->window_set_max_size(max_size, window_id); DisplayServer::get_singleton()->window_set_min_size(min_size, window_id); - DisplayServer::get_singleton()->window_set_title(title, window_id); + DisplayServer::get_singleton()->window_set_title(tr(title), window_id); DisplayServer::get_singleton()->window_attach_instance_id(get_instance_id(), window_id); _update_window_size(); @@ -759,6 +759,10 @@ void Window::_notification(int p_what) { } } + if (p_what == NOTIFICATION_TRANSLATION_CHANGED) { + child_controls_changed(); + } + if (p_what == NOTIFICATION_EXIT_TREE) { if (transient) { _clear_transient(); @@ -893,12 +897,13 @@ void Window::_window_input(const Ref<InputEvent> &p_ev) { } if (exclusive_child != nullptr) { + /* Window *focus_target = exclusive_child; focus_target->grab_focus(); while (focus_target->exclusive_child != nullptr) { focus_target = focus_target->exclusive_child; focus_target->grab_focus(); - } + }*/ if (!is_embedding_subwindows()) { //not embedding, no need for event return; @@ -1164,64 +1169,96 @@ Ref<Theme> Window::get_theme() const { return theme; } -Ref<Texture2D> Window::get_theme_icon(const StringName &p_name, const StringName &p_type) const { - StringName type = p_type ? p_type : get_class_name(); - return Control::get_icons(theme_owner, theme_owner_window, p_name, type); +void Window::set_theme_custom_type(const StringName &p_theme_type) { + theme_custom_type = p_theme_type; + Control::_propagate_theme_changed(this, theme_owner, theme_owner_window); +} + +StringName Window::get_theme_custom_type() const { + return theme_custom_type; +} + +void Window::_get_theme_type_dependencies(const StringName &p_theme_type, List<StringName> *p_list) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == theme_custom_type) { + if (theme_custom_type != StringName()) { + p_list->push_back(theme_custom_type); + } + Theme::get_type_dependencies(get_class_name(), p_list); + } else { + Theme::get_type_dependencies(p_theme_type, p_list); + } } -Ref<StyleBox> Window::get_theme_stylebox(const StringName &p_name, const StringName &p_type) const { - StringName type = p_type ? p_type : get_class_name(); - return Control::get_styleboxs(theme_owner, theme_owner_window, p_name, type); +Ref<Texture2D> Window::get_theme_icon(const StringName &p_name, const StringName &p_theme_type) const { + List<StringName> theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return Control::get_theme_item_in_types<Ref<Texture2D>>(theme_owner, theme_owner_window, Theme::DATA_TYPE_ICON, p_name, theme_types); } -Ref<Font> Window::get_theme_font(const StringName &p_name, const StringName &p_type) const { - StringName type = p_type ? p_type : get_class_name(); - return Control::get_fonts(theme_owner, theme_owner_window, p_name, type); +Ref<StyleBox> Window::get_theme_stylebox(const StringName &p_name, const StringName &p_theme_type) const { + List<StringName> theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return Control::get_theme_item_in_types<Ref<StyleBox>>(theme_owner, theme_owner_window, Theme::DATA_TYPE_STYLEBOX, p_name, theme_types); } -int Window::get_theme_font_size(const StringName &p_name, const StringName &p_type) const { - StringName type = p_type ? p_type : get_class_name(); - return Control::get_font_sizes(theme_owner, theme_owner_window, p_name, type); +Ref<Font> Window::get_theme_font(const StringName &p_name, const StringName &p_theme_type) const { + List<StringName> theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return Control::get_theme_item_in_types<Ref<Font>>(theme_owner, theme_owner_window, Theme::DATA_TYPE_FONT, p_name, theme_types); } -Color Window::get_theme_color(const StringName &p_name, const StringName &p_type) const { - StringName type = p_type ? p_type : get_class_name(); - return Control::get_colors(theme_owner, theme_owner_window, p_name, type); +int Window::get_theme_font_size(const StringName &p_name, const StringName &p_theme_type) const { + List<StringName> theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return Control::get_theme_item_in_types<int>(theme_owner, theme_owner_window, Theme::DATA_TYPE_FONT_SIZE, p_name, theme_types); } -int Window::get_theme_constant(const StringName &p_name, const StringName &p_type) const { - StringName type = p_type ? p_type : get_class_name(); - return Control::get_constants(theme_owner, theme_owner_window, p_name, type); +Color Window::get_theme_color(const StringName &p_name, const StringName &p_theme_type) const { + List<StringName> theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return Control::get_theme_item_in_types<Color>(theme_owner, theme_owner_window, Theme::DATA_TYPE_COLOR, p_name, theme_types); } -bool Window::has_theme_icon(const StringName &p_name, const StringName &p_type) const { - StringName type = p_type ? p_type : get_class_name(); - return Control::has_icons(theme_owner, theme_owner_window, p_name, type); +int Window::get_theme_constant(const StringName &p_name, const StringName &p_theme_type) const { + List<StringName> theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return Control::get_theme_item_in_types<int>(theme_owner, theme_owner_window, Theme::DATA_TYPE_CONSTANT, p_name, theme_types); } -bool Window::has_theme_stylebox(const StringName &p_name, const StringName &p_type) const { - StringName type = p_type ? p_type : get_class_name(); - return Control::has_styleboxs(theme_owner, theme_owner_window, p_name, type); +bool Window::has_theme_icon(const StringName &p_name, const StringName &p_theme_type) const { + List<StringName> theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return Control::has_theme_item_in_types(theme_owner, theme_owner_window, Theme::DATA_TYPE_ICON, p_name, theme_types); } -bool Window::has_theme_font(const StringName &p_name, const StringName &p_type) const { - StringName type = p_type ? p_type : get_class_name(); - return Control::has_fonts(theme_owner, theme_owner_window, p_name, type); +bool Window::has_theme_stylebox(const StringName &p_name, const StringName &p_theme_type) const { + List<StringName> theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return Control::has_theme_item_in_types(theme_owner, theme_owner_window, Theme::DATA_TYPE_STYLEBOX, p_name, theme_types); } -bool Window::has_theme_font_size(const StringName &p_name, const StringName &p_type) const { - StringName type = p_type ? p_type : get_class_name(); - return Control::has_font_sizes(theme_owner, theme_owner_window, p_name, type); +bool Window::has_theme_font(const StringName &p_name, const StringName &p_theme_type) const { + List<StringName> theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return Control::has_theme_item_in_types(theme_owner, theme_owner_window, Theme::DATA_TYPE_FONT, p_name, theme_types); } -bool Window::has_theme_color(const StringName &p_name, const StringName &p_type) const { - StringName type = p_type ? p_type : get_class_name(); - return Control::has_colors(theme_owner, theme_owner_window, p_name, type); +bool Window::has_theme_font_size(const StringName &p_name, const StringName &p_theme_type) const { + List<StringName> theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return Control::has_theme_item_in_types(theme_owner, theme_owner_window, Theme::DATA_TYPE_FONT_SIZE, p_name, theme_types); } -bool Window::has_theme_constant(const StringName &p_name, const StringName &p_type) const { - StringName type = p_type ? p_type : get_class_name(); - return Control::has_constants(theme_owner, theme_owner_window, p_name, type); +bool Window::has_theme_color(const StringName &p_name, const StringName &p_theme_type) const { + List<StringName> theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return Control::has_theme_item_in_types(theme_owner, theme_owner_window, Theme::DATA_TYPE_COLOR, p_name, theme_types); +} + +bool Window::has_theme_constant(const StringName &p_name, const StringName &p_theme_type) const { + List<StringName> theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return Control::has_theme_item_in_types(theme_owner, theme_owner_window, Theme::DATA_TYPE_CONSTANT, p_name, theme_types); } Rect2i Window::get_parent_rect() const { @@ -1377,19 +1414,22 @@ void Window::_bind_methods() { ClassDB::bind_method(D_METHOD("set_theme", "theme"), &Window::set_theme); ClassDB::bind_method(D_METHOD("get_theme"), &Window::get_theme); - ClassDB::bind_method(D_METHOD("get_theme_icon", "name", "type"), &Window::get_theme_icon, DEFVAL("")); - ClassDB::bind_method(D_METHOD("get_theme_stylebox", "name", "type"), &Window::get_theme_stylebox, DEFVAL("")); - ClassDB::bind_method(D_METHOD("get_theme_font", "name", "type"), &Window::get_theme_font, DEFVAL("")); - ClassDB::bind_method(D_METHOD("get_theme_font_size", "name", "type"), &Window::get_theme_font_size, DEFVAL("")); - ClassDB::bind_method(D_METHOD("get_theme_color", "name", "type"), &Window::get_theme_color, DEFVAL("")); - ClassDB::bind_method(D_METHOD("get_theme_constant", "name", "type"), &Window::get_theme_constant, DEFVAL("")); + ClassDB::bind_method(D_METHOD("set_theme_custom_type", "theme_type"), &Window::set_theme_custom_type); + ClassDB::bind_method(D_METHOD("get_theme_custom_type"), &Window::get_theme_custom_type); + + ClassDB::bind_method(D_METHOD("get_theme_icon", "name", "theme_type"), &Window::get_theme_icon, DEFVAL("")); + ClassDB::bind_method(D_METHOD("get_theme_stylebox", "name", "theme_type"), &Window::get_theme_stylebox, DEFVAL("")); + ClassDB::bind_method(D_METHOD("get_theme_font", "name", "theme_type"), &Window::get_theme_font, DEFVAL("")); + ClassDB::bind_method(D_METHOD("get_theme_font_size", "name", "theme_type"), &Window::get_theme_font_size, DEFVAL("")); + ClassDB::bind_method(D_METHOD("get_theme_color", "name", "theme_type"), &Window::get_theme_color, DEFVAL("")); + ClassDB::bind_method(D_METHOD("get_theme_constant", "name", "theme_type"), &Window::get_theme_constant, DEFVAL("")); - ClassDB::bind_method(D_METHOD("has_theme_icon", "name", "type"), &Window::has_theme_icon, DEFVAL("")); - ClassDB::bind_method(D_METHOD("has_theme_stylebox", "name", "type"), &Window::has_theme_stylebox, DEFVAL("")); - ClassDB::bind_method(D_METHOD("has_theme_font", "name", "type"), &Window::has_theme_font, DEFVAL("")); - ClassDB::bind_method(D_METHOD("has_theme_font_size", "name", "type"), &Window::has_theme_font_size, DEFVAL("")); - ClassDB::bind_method(D_METHOD("has_theme_color", "name", "type"), &Window::has_theme_color, DEFVAL("")); - ClassDB::bind_method(D_METHOD("has_theme_constant", "name", "type"), &Window::has_theme_constant, DEFVAL("")); + ClassDB::bind_method(D_METHOD("has_theme_icon", "name", "theme_type"), &Window::has_theme_icon, DEFVAL("")); + ClassDB::bind_method(D_METHOD("has_theme_stylebox", "name", "theme_type"), &Window::has_theme_stylebox, DEFVAL("")); + ClassDB::bind_method(D_METHOD("has_theme_font", "name", "theme_type"), &Window::has_theme_font, DEFVAL("")); + ClassDB::bind_method(D_METHOD("has_theme_font_size", "name", "theme_type"), &Window::has_theme_font_size, DEFVAL("")); + ClassDB::bind_method(D_METHOD("has_theme_color", "name", "theme_type"), &Window::has_theme_color, DEFVAL("")); + ClassDB::bind_method(D_METHOD("has_theme_constant", "name", "theme_type"), &Window::has_theme_constant, DEFVAL("")); ClassDB::bind_method(D_METHOD("set_layout_direction", "direction"), &Window::set_layout_direction); ClassDB::bind_method(D_METHOD("get_layout_direction"), &Window::get_layout_direction); @@ -1423,8 +1463,9 @@ void Window::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "content_scale_size"), "set_content_scale_size", "get_content_scale_size"); ADD_PROPERTY(PropertyInfo(Variant::INT, "content_scale_mode", PROPERTY_HINT_ENUM, "Disabled,CanvasItems,Viewport"), "set_content_scale_mode", "get_content_scale_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "content_scale_aspect", PROPERTY_HINT_ENUM, "Ignore,Keep,KeepWidth,KeepHeight,Expand"), "set_content_scale_aspect", "get_content_scale_aspect"); - ADD_GROUP("Theme", ""); + ADD_GROUP("Theme", "theme_"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "theme", PROPERTY_HINT_RESOURCE_TYPE, "Theme"), "set_theme", "get_theme"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "theme_custom_type"), "set_theme_custom_type", "get_theme_custom_type"); ADD_SIGNAL(MethodInfo("window_input", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"))); ADD_SIGNAL(MethodInfo("files_dropped", PropertyInfo(Variant::PACKED_STRING_ARRAY, "files"))); diff --git a/scene/main/window.h b/scene/main/window.h index 38846ed00e..494c386606 100644 --- a/scene/main/window.h +++ b/scene/main/window.h @@ -130,6 +130,7 @@ private: Ref<Theme> theme; Control *theme_owner = nullptr; Window *theme_owner_window = nullptr; + StringName theme_custom_type; Viewport *embedder = nullptr; @@ -241,6 +242,10 @@ public: void set_theme(const Ref<Theme> &p_theme); Ref<Theme> get_theme() const; + void set_theme_custom_type(const StringName &p_theme_type); + StringName get_theme_custom_type() const; + _FORCE_INLINE_ void _get_theme_type_dependencies(const StringName &p_theme_type, List<StringName> *p_list) const; + Size2 get_contents_minimum_size() const; void grab_focus(); @@ -252,19 +257,19 @@ public: Rect2i get_usable_parent_rect() const; - Ref<Texture2D> get_theme_icon(const StringName &p_name, const StringName &p_type = StringName()) const; - Ref<StyleBox> get_theme_stylebox(const StringName &p_name, const StringName &p_type = StringName()) const; - Ref<Font> get_theme_font(const StringName &p_name, const StringName &p_type = StringName()) const; - int get_theme_font_size(const StringName &p_name, const StringName &p_type = StringName()) const; - Color get_theme_color(const StringName &p_name, const StringName &p_type = StringName()) const; - int get_theme_constant(const StringName &p_name, const StringName &p_type = StringName()) const; - - bool has_theme_icon(const StringName &p_name, const StringName &p_type = StringName()) const; - bool has_theme_stylebox(const StringName &p_name, const StringName &p_type = StringName()) const; - bool has_theme_font(const StringName &p_name, const StringName &p_type = StringName()) const; - bool has_theme_font_size(const StringName &p_name, const StringName &p_type = StringName()) const; - bool has_theme_color(const StringName &p_name, const StringName &p_type = StringName()) const; - bool has_theme_constant(const StringName &p_name, const StringName &p_type = StringName()) const; + Ref<Texture2D> get_theme_icon(const StringName &p_name, const StringName &p_theme_type = StringName()) const; + Ref<StyleBox> get_theme_stylebox(const StringName &p_name, const StringName &p_theme_type = StringName()) const; + Ref<Font> get_theme_font(const StringName &p_name, const StringName &p_theme_type = StringName()) const; + int get_theme_font_size(const StringName &p_name, const StringName &p_theme_type = StringName()) const; + Color get_theme_color(const StringName &p_name, const StringName &p_theme_type = StringName()) const; + int get_theme_constant(const StringName &p_name, const StringName &p_theme_type = StringName()) const; + + bool has_theme_icon(const StringName &p_name, const StringName &p_theme_type = StringName()) const; + bool has_theme_stylebox(const StringName &p_name, const StringName &p_theme_type = StringName()) const; + bool has_theme_font(const StringName &p_name, const StringName &p_theme_type = StringName()) const; + bool has_theme_font_size(const StringName &p_name, const StringName &p_theme_type = StringName()) const; + bool has_theme_color(const StringName &p_name, const StringName &p_theme_type = StringName()) const; + bool has_theme_constant(const StringName &p_name, const StringName &p_theme_type = StringName()) const; Rect2i get_parent_rect() const; virtual DisplayServer::WindowID get_window_id() const override; diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 51d4643883..f24ea7e39b 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -50,7 +50,6 @@ #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/navigation_agent_2d.h" #include "scene/2d/navigation_obstacle_2d.h" #include "scene/2d/parallax_background.h" @@ -206,10 +205,10 @@ #include "scene/3d/listener_3d.h" #include "scene/3d/mesh_instance_3d.h" #include "scene/3d/multimesh_instance_3d.h" -#include "scene/3d/navigation_3d.h" #include "scene/3d/navigation_agent_3d.h" #include "scene/3d/navigation_obstacle_3d.h" #include "scene/3d/navigation_region_3d.h" +#include "scene/3d/occluder_instance_3d.h" #include "scene/3d/path_3d.h" #include "scene/3d/physics_body_3d.h" #include "scene/3d/physics_joint_3d.h" @@ -444,6 +443,8 @@ void register_scene_types() { ClassDB::register_class<XRAnchor3D>(); ClassDB::register_class<XROrigin3D>(); ClassDB::register_class<MeshInstance3D>(); + ClassDB::register_class<OccluderInstance3D>(); + ClassDB::register_class<Occluder3D>(); ClassDB::register_class<ImmediateGeometry3D>(); ClassDB::register_virtual_class<SpriteBase3D>(); ClassDB::register_class<Sprite3D>(); @@ -516,7 +517,6 @@ void register_scene_types() { ClassDB::register_class<ConeTwistJoint3D>(); ClassDB::register_class<Generic6DOFJoint3D>(); - ClassDB::register_class<Navigation3D>(); ClassDB::register_class<NavigationRegion3D>(); ClassDB::register_class<NavigationAgent3D>(); ClassDB::register_class<NavigationObstacle3D>(); @@ -535,6 +535,7 @@ void register_scene_types() { ClassDB::register_virtual_class<VisualShaderNodeResizableBase>(); ClassDB::register_virtual_class<VisualShaderNodeGroupBase>(); ClassDB::register_virtual_class<VisualShaderNodeConstant>(); + ClassDB::register_class<VisualShaderNodeComment>(); ClassDB::register_class<VisualShaderNodeFloatConstant>(); ClassDB::register_class<VisualShaderNodeIntConstant>(); ClassDB::register_class<VisualShaderNodeBooleanConstant>(); @@ -654,6 +655,9 @@ void register_scene_types() { ClassDB::register_class<GrooveJoint2D>(); ClassDB::register_class<DampedSpringJoint2D>(); ClassDB::register_class<TileSet>(); + ClassDB::register_virtual_class<TileSetSource>(); + ClassDB::register_class<TileSetAtlasSource>(); + ClassDB::register_class<TileData>(); ClassDB::register_class<TileMap>(); ClassDB::register_class<ParallaxBackground>(); ClassDB::register_class<ParallaxLayer>(); @@ -688,6 +692,8 @@ void register_scene_types() { ClassDB::register_class<PrismMesh>(); ClassDB::register_class<QuadMesh>(); ClassDB::register_class<SphereMesh>(); + ClassDB::register_class<TubeTrailMesh>(); + ClassDB::register_class<RibbonTrailMesh>(); ClassDB::register_class<PointMesh>(); ClassDB::register_virtual_class<Material>(); ClassDB::register_virtual_class<BaseMaterial3D>(); @@ -728,7 +734,6 @@ void register_scene_types() { ClassDB::register_class<ImageTexture>(); ClassDB::register_class<AtlasTexture>(); ClassDB::register_class<MeshTexture>(); - ClassDB::register_class<LargeTexture>(); ClassDB::register_class<CurveTexture>(); ClassDB::register_class<GradientTexture>(); ClassDB::register_class<ProxyTexture>(); @@ -793,7 +798,6 @@ void register_scene_types() { ClassDB::register_class<PathFollow2D>(); ClassDB::register_class<NavigationMesh>(); - ClassDB::register_class<Navigation2D>(); ClassDB::register_class<NavigationPolygon>(); ClassDB::register_class<NavigationRegion2D>(); ClassDB::register_class<NavigationAgent2D>(); @@ -814,6 +818,8 @@ void register_scene_types() { ClassDB::add_compatibility_class("DynamicFont", "Font"); ClassDB::add_compatibility_class("DynamicFontData", "FontData"); ClassDB::add_compatibility_class("ToolButton", "Button"); + ClassDB::add_compatibility_class("Navigation3D", "Node3D"); + ClassDB::add_compatibility_class("Navigation2D", "Node2D"); // Renamed in 4.0. // Keep alphabetical ordering to easily locate classes and avoid duplicates. @@ -865,7 +871,6 @@ void register_scene_types() { ClassDB::add_compatibility_class("Listener", "Listener3D"); ClassDB::add_compatibility_class("MeshInstance", "MeshInstance3D"); ClassDB::add_compatibility_class("MultiMeshInstance", "MultiMeshInstance3D"); - ClassDB::add_compatibility_class("Navigation", "Navigation3D"); ClassDB::add_compatibility_class("NavigationAgent", "NavigationAgent3D"); ClassDB::add_compatibility_class("NavigationMeshInstance", "NavigationRegion3D"); ClassDB::add_compatibility_class("NavigationObstacle", "NavigationObstacle3D"); @@ -950,15 +955,17 @@ void register_scene_types() { for (int i = 0; i < 20; i++) { GLOBAL_DEF_BASIC(vformat("layer_names/2d_render/layer_%d", i), ""); GLOBAL_DEF_BASIC(vformat("layer_names/2d_physics/layer_%d", i), ""); + GLOBAL_DEF_BASIC(vformat("layer_names/2d_navigation/layer_%d", i), ""); GLOBAL_DEF_BASIC(vformat("layer_names/3d_render/layer_%d", i), ""); GLOBAL_DEF_BASIC(vformat("layer_names/3d_physics/layer_%d", i), ""); + GLOBAL_DEF_BASIC(vformat("layer_names/3d_navigation/layer_%d", i), ""); } bool default_theme_hidpi = GLOBAL_DEF("gui/theme/use_hidpi", false); ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/use_hidpi", PropertyInfo(Variant::BOOL, "gui/theme/use_hidpi", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)); - String theme_path = GLOBAL_DEF("gui/theme/custom", ""); + String theme_path = GLOBAL_DEF_RST("gui/theme/custom", ""); ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/custom", PropertyInfo(Variant::STRING, "gui/theme/custom", PROPERTY_HINT_FILE, "*.tres,*.res,*.theme", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)); - String font_path = GLOBAL_DEF("gui/theme/custom_font", ""); + String font_path = GLOBAL_DEF_RST("gui/theme/custom_font", ""); ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/custom_font", PropertyInfo(Variant::STRING, "gui/theme/custom_font", PROPERTY_HINT_FILE, "*.tres,*.res,*.font", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)); Ref<Font> font; @@ -972,6 +979,7 @@ void register_scene_types() { // Always make the default theme to avoid invalid default font/icon/style in the given theme. if (RenderingServer::get_singleton()) { make_default_theme(default_theme_hidpi, font); + ColorPicker::init_shaders(); // RenderingServer needs to exist for this to succeed. } if (theme_path != String()) { @@ -1028,5 +1036,6 @@ void unregister_scene_types() { ParticlesMaterial::finish_shaders(); CanvasItemMaterial::finish_shaders(); + ColorPicker::finish_shaders(); SceneStringNames::free(); } diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index cc1dafd0db..6f64ac6d04 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -2738,7 +2738,7 @@ bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, cons real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1); if (d > pd.length() * p_alowed_linear_err) { - return false; //beyond allowed error for colinearity + return false; //beyond allowed error for collinearity } if (p_norm != Vector3() && Math::acos(pd.normalized().dot(p_norm)) > p_alowed_angular_err) { @@ -2828,7 +2828,7 @@ bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, cons real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1); if (d > pd.length() * p_alowed_linear_err) { - return false; //beyond allowed error for colinearity + return false; //beyond allowed error for collinearity } t[2] = (d1 - d0) / (d2 - d0); diff --git a/scene/resources/audio_stream_sample.cpp b/scene/resources/audio_stream_sample.cpp index 06a91fb2f8..9a9f019dda 100644 --- a/scene/resources/audio_stream_sample.cpp +++ b/scene/resources/audio_stream_sample.cpp @@ -490,9 +490,9 @@ void AudioStreamSample::set_data(const Vector<uint8_t> &p_data) { const uint8_t *r = p_data.ptr(); int alloc_len = datalen + DATA_PAD * 2; data = memalloc(alloc_len); //alloc with some padding for interpolation - zeromem(data, alloc_len); + memset(data, 0, alloc_len); uint8_t *dataptr = (uint8_t *)data; - copymem(dataptr + DATA_PAD, r, datalen); + memcpy(dataptr + DATA_PAD, r, datalen); data_bytes = datalen; } @@ -507,7 +507,7 @@ Vector<uint8_t> AudioStreamSample::get_data() const { { uint8_t *w = pv.ptrw(); uint8_t *dataptr = (uint8_t *)data; - copymem(w, dataptr + DATA_PAD, data_bytes); + memcpy(w, dataptr + DATA_PAD, data_bytes); } } diff --git a/scene/resources/bit_map.cpp b/scene/resources/bit_map.cpp index 8ffc7b4b4c..0ffeb8a5bf 100644 --- a/scene/resources/bit_map.cpp +++ b/scene/resources/bit_map.cpp @@ -38,8 +38,8 @@ void BitMap::create(const Size2 &p_size) { width = p_size.width; height = p_size.height; - bitmask.resize(((width * height) / 8) + 1); - zeromem(bitmask.ptrw(), bitmask.size()); + bitmask.resize((((width * height) - 1) / 8) + 1); + memset(bitmask.ptrw(), 0, bitmask.size()); } void BitMap::create_from_image_alpha(const Ref<Image> &p_image, float p_threshold) { @@ -285,7 +285,7 @@ Vector<Vector2> BitMap::_march_square(const Rect2i &rect, const Point2i &start) +---+---+ | 4 | | +---+---+ - this normally go RIGHT, but if its coming from RIGHT, it should go LEFT + this normally go RIGHT, but if it's coming from RIGHT, it should go LEFT */ if (case6s.has(Point2i(curx, cury))) { //found, so we go left, and delete from case6s; diff --git a/scene/resources/camera_effects.cpp b/scene/resources/camera_effects.cpp index 00312fc7b2..34c6bc05bc 100644 --- a/scene/resources/camera_effects.cpp +++ b/scene/resources/camera_effects.cpp @@ -145,7 +145,6 @@ void CameraEffects::_update_override_exposure() { // Private methods, constructor and destructor -#ifdef TOOLS_ENABLED void CameraEffects::_validate_property(PropertyInfo &property) const { if ((!dof_blur_far_enabled && (property.name == "dof_blur_far_distance" || property.name == "dof_blur_far_transition")) || (!dof_blur_near_enabled && (property.name == "dof_blur_near_distance" || property.name == "dof_blur_near_transition")) || @@ -153,7 +152,6 @@ void CameraEffects::_validate_property(PropertyInfo &property) const { property.usage = PROPERTY_USAGE_NOEDITOR; } } -#endif void CameraEffects::_bind_methods() { // DOF blur diff --git a/scene/resources/camera_effects.h b/scene/resources/camera_effects.h index 51fb2b6cf7..b9338f4806 100644 --- a/scene/resources/camera_effects.h +++ b/scene/resources/camera_effects.h @@ -57,12 +57,9 @@ private: float override_exposure = 1.0; void _update_override_exposure(); -#ifdef TOOLS_ENABLED - void _validate_property(PropertyInfo &property) const override; -#endif - protected: static void _bind_methods(); + void _validate_property(PropertyInfo &property) const override; public: virtual RID get_rid() const override; diff --git a/scene/resources/concave_polygon_shape_3d.cpp b/scene/resources/concave_polygon_shape_3d.cpp index f067695d7d..3fed700383 100644 --- a/scene/resources/concave_polygon_shape_3d.cpp +++ b/scene/resources/concave_polygon_shape_3d.cpp @@ -35,13 +35,12 @@ Vector<Vector3> ConcavePolygonShape3D::get_debug_mesh_lines() const { Set<DrawEdge> edges; - Vector<Vector3> data = get_faces(); - int datalen = data.size(); - ERR_FAIL_COND_V((datalen % 3) != 0, Vector<Vector3>()); + int index_count = faces.size(); + ERR_FAIL_COND_V((index_count % 3) != 0, Vector<Vector3>()); - const Vector3 *r = data.ptr(); + const Vector3 *r = faces.ptr(); - for (int i = 0; i < datalen; i += 3) { + for (int i = 0; i < index_count; i += 3) { for (int j = 0; j < 3; j++) { DrawEdge de(r[i + j], r[i + ((j + 1) % 3)]); edges.insert(de); @@ -71,22 +70,46 @@ real_t ConcavePolygonShape3D::get_enclosing_radius() const { } void ConcavePolygonShape3D::_update_shape() { + Dictionary d; + d["faces"] = faces; + d["backface_collision"] = backface_collision; + PhysicsServer3D::get_singleton()->shape_set_data(get_shape(), d); + Shape3D::_update_shape(); } void ConcavePolygonShape3D::set_faces(const Vector<Vector3> &p_faces) { - PhysicsServer3D::get_singleton()->shape_set_data(get_shape(), p_faces); + faces = p_faces; + _update_shape(); notify_change_to_owners(); } Vector<Vector3> ConcavePolygonShape3D::get_faces() const { - return PhysicsServer3D::get_singleton()->shape_get_data(get_shape()); + return faces; +} + +void ConcavePolygonShape3D::set_backface_collision_enabled(bool p_enabled) { + backface_collision = p_enabled; + + if (!faces.is_empty()) { + _update_shape(); + notify_change_to_owners(); + } +} + +bool ConcavePolygonShape3D::is_backface_collision_enabled() const { + return backface_collision; } void ConcavePolygonShape3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_faces", "faces"), &ConcavePolygonShape3D::set_faces); ClassDB::bind_method(D_METHOD("get_faces"), &ConcavePolygonShape3D::get_faces); + + ClassDB::bind_method(D_METHOD("set_backface_collision_enabled", "enabled"), &ConcavePolygonShape3D::set_backface_collision_enabled); + ClassDB::bind_method(D_METHOD("is_backface_collision_enabled"), &ConcavePolygonShape3D::is_backface_collision_enabled); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_faces", "get_faces"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "backface_collision"), "set_backface_collision_enabled", "is_backface_collision_enabled"); } ConcavePolygonShape3D::ConcavePolygonShape3D() : diff --git a/scene/resources/concave_polygon_shape_3d.h b/scene/resources/concave_polygon_shape_3d.h index 391459a3d7..bb28359dcc 100644 --- a/scene/resources/concave_polygon_shape_3d.h +++ b/scene/resources/concave_polygon_shape_3d.h @@ -36,6 +36,9 @@ class ConcavePolygonShape3D : public Shape3D { GDCLASS(ConcavePolygonShape3D, Shape3D); + Vector<Vector3> faces; + bool backface_collision = false; + struct DrawEdge { Vector3 a; Vector3 b; @@ -65,6 +68,9 @@ public: void set_faces(const Vector<Vector3> &p_faces); Vector<Vector3> get_faces() const; + void set_backface_collision_enabled(bool p_enabled); + bool is_backface_collision_enabled() const; + virtual Vector<Vector3> get_debug_mesh_lines() const override; virtual real_t get_enclosing_radius() const override; diff --git a/scene/resources/convex_polygon_shape_2d.cpp b/scene/resources/convex_polygon_shape_2d.cpp index 6e56f6e7fc..ac31315858 100644 --- a/scene/resources/convex_polygon_shape_2d.cpp +++ b/scene/resources/convex_polygon_shape_2d.cpp @@ -41,7 +41,7 @@ bool ConvexPolygonShape2D::_edit_is_selected_on_click(const Point2 &p_point, dou void ConvexPolygonShape2D::_update_shape() { Vector<Vector2> final_points = points; if (Geometry2D::is_polygon_clockwise(final_points)) { //needs to be counter clockwise - final_points.invert(); + final_points.reverse(); } PhysicsServer2D::get_singleton()->shape_set_data(get_rid(), final_points); emit_changed(); diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp index bc479e557a..846da39221 100644 --- a/scene/resources/curve.cpp +++ b/scene/resources/curve.cpp @@ -445,10 +445,10 @@ void Curve::set_bake_resolution(int p_resolution) { _baked_cache_dirty = true; } -real_t Curve::interpolate_baked(real_t offset) { +real_t Curve::interpolate_baked(real_t offset) const { if (_baked_cache_dirty) { // Last-second bake if not done already - bake(); + const_cast<Curve *>(this)->bake(); } // Special cases if the cache is too small diff --git a/scene/resources/curve.h b/scene/resources/curve.h index 402c893cd8..746c6fa597 100644 --- a/scene/resources/curve.h +++ b/scene/resources/curve.h @@ -122,7 +122,7 @@ public: void bake(); int get_bake_resolution() const { return _bake_resolution; } void set_bake_resolution(int p_resolution); - real_t interpolate_baked(real_t offset); + real_t interpolate_baked(real_t offset) const; void ensure_default_setup(float p_min, float p_max); diff --git a/scene/resources/default_theme/SCsub b/scene/resources/default_theme/SCsub index fc61250247..0fb6bb2c62 100644 --- a/scene/resources/default_theme/SCsub +++ b/scene/resources/default_theme/SCsub @@ -2,4 +2,16 @@ Import("env") +import os +import os.path +from platform_methods import run_in_subprocess +import default_theme_builders + env.add_source_files(env.scene_sources, "*.cpp") + +env.Depends("#scene/resources/default_theme/default_font.gen.h", "#thirdparty/fonts/OpenSans_SemiBold.ttf") +env.CommandNoCache( + "#scene/resources/default_theme/default_font.gen.h", + "#thirdparty/fonts/OpenSans_SemiBold.ttf", + run_in_subprocess(default_theme_builders.make_fonts_header), +) diff --git a/scene/resources/default_theme/bar_arrow.png b/scene/resources/default_theme/bar_arrow.png Binary files differnew file mode 100644 index 0000000000..7cf146b8da --- /dev/null +++ b/scene/resources/default_theme/bar_arrow.png diff --git a/scene/resources/default_theme/checked_disabled.png b/scene/resources/default_theme/checked_disabled.png Binary files differnew file mode 100644 index 0000000000..70549e2edc --- /dev/null +++ b/scene/resources/default_theme/checked_disabled.png diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index a94209c75f..b671ee4644 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -30,15 +30,12 @@ #include "default_theme.h" -#include "scene/resources/theme.h" - #include "core/os/os.h" -#include "theme_data.h" - -#include "font_hidpi.inc" -#include "font_lodpi.inc" - +#include "default_font.gen.h" +#include "scene/resources/font.h" +#include "scene/resources/theme.h" #include "servers/text_server.h" +#include "theme_data.h" typedef Map<const void *, Ref<ImageTexture>> TexCacheMap; @@ -114,7 +111,7 @@ static Ref<Texture2D> flip_icon(Ref<Texture2D> p_texture, bool p_flip_y = false, } Ref<ImageTexture> texture(memnew(ImageTexture)); - Ref<Image> img = p_texture->get_data(); + Ref<Image> img = p_texture->get_image(); img = img->duplicate(); if (p_flip_y) { @@ -128,38 +125,6 @@ static Ref<Texture2D> flip_icon(Ref<Texture2D> p_texture, bool p_flip_y = false, return texture; } -static Ref<FontData> make_font(int p_height, int p_ascent, int p_charcount, const int *p_char_rects, int p_kerning_count, const int *p_kernings, int p_w, int p_h, const unsigned char *p_img) { - Ref<FontData> font(memnew(FontData)); - font->new_bitmap(p_height, p_ascent, p_height); - - Ref<Image> image = memnew(Image(p_img)); - Ref<ImageTexture> tex = memnew(ImageTexture); - tex->create_from_image(image); - - font->bitmap_add_texture(tex); - - for (int i = 0; i < p_charcount; i++) { - const int *c = &p_char_rects[i * 8]; - - int chr = c[0]; - Rect2 frect; - frect.position.x = c[1]; - frect.position.y = c[2]; - frect.size.x = c[3]; - frect.size.y = c[4]; - Point2 align(c[6], c[5]); - int advance = c[7]; - - font->bitmap_add_char(chr, 0, frect, align, advance); - } - - for (int i = 0; i < p_kerning_count; i++) { - font->bitmap_add_kerning_pair(p_kernings[i * 3 + 0], p_kernings[i * 3 + 1], p_kernings[i * 3 + 2]); - } - - return font; -} - static Ref<StyleBox> make_empty_stylebox(float p_margin_left = -1, float p_margin_top = -1, float p_margin_right = -1, float p_margin_bottom = -1) { Ref<StyleBox> style(memnew(StyleBoxEmpty)); @@ -348,9 +313,13 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_stylebox("focus", "CheckBox", cbx_focus); theme->set_icon("checked", "CheckBox", make_icon(checked_png)); + theme->set_icon("checked_disabled", "CheckBox", make_icon(checked_disabled_png)); theme->set_icon("unchecked", "CheckBox", make_icon(unchecked_png)); + theme->set_icon("unchecked_disabled", "CheckBox", make_icon(unchecked_disabled_png)); theme->set_icon("radio_checked", "CheckBox", make_icon(radio_checked_png)); + theme->set_icon("radio_checked_disabled", "CheckBox", make_icon(radio_checked_disabled_png)); theme->set_icon("radio_unchecked", "CheckBox", make_icon(radio_unchecked_png)); + theme->set_icon("radio_unchecked_disabled", "CheckBox", make_icon(radio_unchecked_disabled_png)); theme->set_font("font", "CheckBox", Ref<Font>()); theme->set_font_size("font_size", "CheckBox", -1); @@ -434,7 +403,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("font_selected_color", "LineEdit", Color(0, 0, 0)); theme->set_color("font_uneditable_color", "LineEdit", Color(control_font_color.r, control_font_color.g, control_font_color.b, 0.5f)); theme->set_color("font_outline_color", "LineEdit", Color(1, 1, 1)); - theme->set_color("cursor_color", "LineEdit", control_font_hover_color); + theme->set_color("caret_color", "LineEdit", control_font_hover_color); theme->set_color("selection_color", "LineEdit", control_selection_color); theme->set_color("clear_button_color", "LineEdit", control_font_color); theme->set_color("clear_button_color_pressed", "LineEdit", control_font_pressed_color); @@ -628,6 +597,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const // File Dialog theme->set_icon("parent_folder", "FileDialog", make_icon(icon_parent_folder_png)); + theme->set_icon("back_folder", "FileDialog", make_icon(arrow_left_png)); + theme->set_icon("forward_folder", "FileDialog", make_icon(arrow_right_png)); theme->set_icon("reload", "FileDialog", make_icon(icon_reload_png)); theme->set_icon("toggle_hidden", "FileDialog", make_icon(icon_visibility_png)); @@ -824,7 +795,6 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_stylebox("tab_selected", "Tabs", sb_expand(make_stylebox(tab_current_png, 4, 3, 4, 1, 16, 3, 16, 2), 2, 2, 2, 2)); theme->set_stylebox("tab_unselected", "Tabs", sb_expand(make_stylebox(tab_behind_png, 5, 4, 5, 1, 16, 5, 16, 2), 3, 3, 3, 3)); theme->set_stylebox("tab_disabled", "Tabs", sb_expand(make_stylebox(tab_disabled_png, 5, 5, 5, 1, 16, 6, 16, 4), 3, 0, 3, 3)); - theme->set_stylebox("panel", "Tabs", tc_sb); theme->set_stylebox("button_pressed", "Tabs", make_stylebox(button_pressed_png, 4, 4, 4, 4)); theme->set_stylebox("button", "Tabs", make_stylebox(button_normal_png, 4, 4, 4, 4)); @@ -884,6 +854,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("color_sample", "ColorPicker", make_icon(color_picker_sample_png)); theme->set_icon("preset_bg", "ColorPicker", make_icon(mini_checkerboard_png)); theme->set_icon("overbright_indicator", "ColorPicker", make_icon(overbright_indicator_png)); + theme->set_icon("bar_arrow", "ColorPicker", make_icon(bar_arrow_png)); + theme->set_icon("picker_cursor", "ColorPicker", make_icon(picker_cursor_png)); theme->set_icon("bg", "ColorPickerButton", make_icon(mini_checkerboard_png)); @@ -1019,16 +991,23 @@ void make_default_theme(bool p_hidpi, Ref<Font> p_font) { Ref<Font> default_font; int default_font_size = 16; if (p_font.is_valid()) { + // Use the custom font defined in the Project Settings. default_font = p_font; - } else if (p_hidpi) { - Ref<FontData> font_data = make_font(_hidpi_font_height, _hidpi_font_ascent, _hidpi_font_charcount, &_hidpi_font_charrects[0][0], _hidpi_font_kerning_pair_count, &_hidpi_font_kerning_pairs[0][0], _hidpi_font_img_width, _hidpi_font_img_height, _hidpi_font_img_data); - default_font.instance(); - default_font->add_data(font_data); } else { - Ref<FontData> font_data = make_font(_lodpi_font_height, _lodpi_font_ascent, _lodpi_font_charcount, &_lodpi_font_charrects[0][0], _lodpi_font_kerning_pair_count, &_lodpi_font_kerning_pairs[0][0], _lodpi_font_img_width, _lodpi_font_img_height, _lodpi_font_img_data); - default_font.instance(); - default_font->add_data(font_data); + // Use the default DynamicFont (separate from the editor font). + // The default DynamicFont is chosen to have a small file size since it's + // embedded in both editor and export template binaries. + Ref<Font> dynamic_font; + dynamic_font.instance(); + + Ref<FontData> dynamic_font_data; + dynamic_font_data.instance(); + dynamic_font_data->load_memory(_font_OpenSans_SemiBold, _font_OpenSans_SemiBold_size, "ttf", default_font_size); + dynamic_font->add_data(dynamic_font_data); + + default_font = dynamic_font; } + Ref<Font> large_font = default_font; fill_default_theme(t, default_font, large_font, default_icon, default_style, p_hidpi ? 2.0 : 1.0); diff --git a/scene/resources/default_theme/default_theme_builders.py b/scene/resources/default_theme/default_theme_builders.py new file mode 100644 index 0000000000..0455d6d246 --- /dev/null +++ b/scene/resources/default_theme/default_theme_builders.py @@ -0,0 +1,40 @@ +"""Functions used to generate source files during build time + +All such functions are invoked in a subprocess on Windows to prevent build flakiness. + +""" +import os +import os.path +from platform_methods import subprocess_main + + +def make_fonts_header(target, source, env): + dst = target[0] + + g = open(dst, "w", encoding="utf-8") + + g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") + g.write("#ifndef _DEFAULT_FONTS_H\n") + g.write("#define _DEFAULT_FONTS_H\n") + + # Saving uncompressed, since FreeType will reference from memory pointer. + for i in range(len(source)): + with open(source[i], "rb") as f: + buf = f.read() + + name = os.path.splitext(os.path.basename(source[i]))[0] + + g.write("static const int _font_" + name + "_size = " + str(len(buf)) + ";\n") + g.write("static const unsigned char _font_" + name + "[] = {\n") + for j in range(len(buf)): + g.write("\t" + str(buf[j]) + ",\n") + + g.write("};\n") + + g.write("#endif") + + g.close() + + +if __name__ == "__main__": + subprocess_main(globals()) diff --git a/scene/resources/default_theme/font_hidpi.inc b/scene/resources/default_theme/font_hidpi.inc deleted file mode 100644 index 4860149e6b..0000000000 --- a/scene/resources/default_theme/font_hidpi.inc +++ /dev/null @@ -1,25463 +0,0 @@ -/* clang-format off */ -static const int _hidpi_font_height=25; -static const int _hidpi_font_ascent=19; -static const int _hidpi_font_charcount=191; -static const int _hidpi_font_charrects[191][8]={ -/* charidx , ofs_x, ofs_y, size_x, size_y, valign, halign, advance */ -{192,63,23,16,23,-4,-1,15}, -{224,184,182,10,19,0,1,13}, -{64,98,2,18,19,2,1,21}, -{96,159,234,5,4,0,5,14}, -{160,0,0,0,0,19,0,6}, -{32,0,0,0,0,19,0,6}, -{33,2,249,3,17,2,2,6}, -{193,83,25,16,23,-4,-1,15}, -{225,92,160,10,19,0,1,13}, -{65,163,23,16,17,2,-1,15}, -{161,246,236,3,17,6,2,6}, -{97,142,190,10,13,6,1,13}, -{162,67,214,9,17,2,2,13}, -{98,242,136,11,18,1,2,14}, -{194,103,25,16,23,-4,-1,15}, -{226,106,160,10,19,0,1,13}, -{66,122,146,11,17,2,2,15}, -{34,226,203,8,6,2,1,10}, -{35,189,44,14,17,2,1,16}, -{163,62,166,11,17,2,1,13}, -{195,123,23,16,23,-4,-1,15}, -{227,134,167,10,19,0,1,13}, -{67,209,65,12,17,2,1,14}, -{99,225,186,9,13,6,1,11}, -{228,170,182,10,18,1,1,13}, -{100,227,143,11,18,1,1,14}, -{196,23,23,16,22,-3,-1,15}, -{36,2,179,10,20,1,2,13}, -{68,193,65,12,17,2,2,16}, -{164,176,65,13,12,5,0,13}, -{37,120,2,18,17,2,1,20}, -{69,41,222,9,17,2,2,13}, -{165,98,99,12,17,2,1,13}, -{197,43,23,16,21,-2,-1,15}, -{229,16,179,10,20,-1,1,13}, -{101,189,109,11,13,6,1,13}, -{38,183,23,16,17,2,1,17}, -{70,54,222,9,17,2,2,12}, -{198,75,2,19,17,2,-1,20}, -{102,202,212,8,18,1,1,8}, -{166,44,268,2,24,1,6,13}, -{230,142,2,18,13,6,1,20}, -{71,2,71,14,17,2,1,17}, -{167,176,160,10,18,1,0,12}, -{199,146,94,12,23,2,1,14}, -{103,159,67,13,19,6,0,13}, -{231,212,189,9,19,6,1,11}, -{39,30,271,3,6,2,1,5}, -{72,130,99,12,17,2,2,17}, -{104,162,160,10,18,1,2,14}, -{200,238,186,9,23,-4,2,13}, -{40,46,243,7,21,2,1,7}, -{232,2,156,11,19,0,1,13}, -{168,68,235,7,3,1,4,14}, -{73,106,209,8,17,2,0,9}, -{169,230,2,17,17,2,1,20}, -{105,222,232,4,18,1,1,6}, -{201,28,222,9,23,-4,2,13}, -{41,130,238,6,21,2,0,7}, -{233,197,126,11,19,0,1,13}, -{202,93,209,9,23,-4,2,13}, -{74,13,225,7,22,2,-2,7}, -{234,167,137,11,19,0,1,13}, -{42,82,120,12,11,1,0,13}, -{170,100,236,6,8,2,1,8}, -{106,110,237,6,24,1,-1,6}, -{171,144,121,11,10,8,0,12}, -{43,2,119,12,12,5,1,13}, -{203,80,209,9,22,-3,2,13}, -{107,32,177,11,18,1,2,12}, -{235,77,166,11,18,1,1,13}, -{75,74,77,13,17,2,2,14}, -{44,230,213,4,6,16,1,6}, -{172,204,109,11,7,10,1,13}, -{236,204,234,5,19,0,0,6}, -{204,130,211,8,23,-4,0,9}, -{108,23,271,3,18,1,2,6}, -{76,86,188,10,17,2,2,12}, -{173,140,238,6,2,12,1,8}, -{45,120,237,6,2,12,1,8}, -{109,208,2,18,13,6,2,22}, -{205,178,205,8,23,-4,0,9}, -{237,213,234,5,19,0,2,6}, -{77,143,19,16,17,2,2,21}, -{46,9,251,3,3,16,2,6}, -{110,100,183,10,13,6,2,14}, -{206,154,207,8,23,-4,0,9}, -{238,238,213,8,19,0,-1,6}, -{174,2,23,17,17,2,1,20}, -{78,91,78,13,17,2,2,18}, -{175,18,125,12,2,-1,0,12}, -{111,162,90,12,13,6,1,14}, -{207,118,211,8,22,-3,0,9}, -{239,24,249,7,18,1,0,6}, -{79,203,23,15,17,2,1,18}, -{47,120,167,10,17,2,-1,9}, -{176,166,207,8,8,2,1,10}, -{112,212,143,11,19,6,2,14}, -{240,18,103,12,18,1,1,14}, -{208,171,44,14,17,2,0,16}, -{80,128,190,10,17,2,2,14}, -{48,234,115,11,17,2,1,13}, -{177,66,127,12,14,5,1,13}, -{113,182,126,11,19,6,1,14}, -{241,30,199,10,19,0,2,14}, -{81,97,52,15,22,2,1,18}, -{209,142,67,13,23,-4,2,18}, -{49,57,243,7,17,2,2,13}, -{178,190,206,8,10,2,0,8}, -{114,142,207,8,13,6,2,10}, -{242,242,87,12,19,0,1,14}, -{210,59,50,15,23,-4,1,18}, -{82,82,99,12,17,2,2,14}, -{50,2,135,11,17,2,1,13}, -{179,35,249,7,10,2,0,8}, -{115,72,188,10,13,6,0,11}, -{243,226,92,12,19,0,1,14}, -{211,40,49,15,23,-4,1,18}, -{83,129,125,11,17,2,0,13}, -{51,17,135,11,17,2,1,13}, -{180,168,219,5,4,0,5,14}, -{116,214,212,8,16,3,0,8}, -{244,194,86,12,19,0,1,14}, -{212,21,49,15,23,-4,1,18}, -{84,66,106,12,17,2,0,13}, -{52,125,78,13,17,2,0,13}, -{53,32,156,11,17,2,1,13}, -{85,50,106,12,17,2,2,17}, -{181,204,166,10,19,6,2,14}, -{117,198,189,10,13,6,2,14}, -{245,178,86,12,19,0,1,14}, -{213,2,44,15,23,-4,1,18}, -{54,47,156,11,17,2,1,13}, -{86,222,23,15,17,2,-1,14}, -{246,241,65,12,18,1,1,14}, -{214,116,52,15,22,-3,1,18}, -{182,34,131,12,21,1,1,16}, -{118,135,50,14,13,6,-1,12}, -{55,62,145,11,17,2,1,13}, -{87,2,2,22,17,2,-1,21}, -{119,28,2,20,13,6,-1,18}, -{215,114,125,11,11,5,1,13}, -{247,210,92,12,10,6,0,13}, -{183,16,251,3,3,9,2,6}, -{56,77,145,11,17,2,1,13}, -{88,225,44,14,17,2,-1,13}, -{216,78,52,15,19,1,1,18}, -{248,98,120,12,15,5,1,14}, -{120,50,127,12,13,6,0,12}, -{184,150,234,5,6,19,0,5}, -{89,207,44,14,17,2,-1,13}, -{121,153,44,14,19,6,-1,12}, -{217,225,65,12,23,-4,2,17}, -{249,44,199,10,19,0,2,14}, -{57,92,139,11,17,2,1,13}, -{185,177,232,5,10,2,0,8}, -{218,2,92,12,23,-4,2,17}, -{250,156,184,10,19,0,2,14}, -{90,232,165,10,17,2,1,13}, -{122,148,167,10,13,6,1,11}, -{58,37,263,3,13,6,2,6}, -{186,90,236,6,8,2,1,8}, -{219,34,104,12,23,-4,2,17}, -{123,2,224,7,21,2,1,9}, -{91,186,232,5,21,2,2,7}, -{251,58,191,10,19,0,2,14}, -{59,238,236,4,16,6,1,6}, -{187,47,177,11,10,8,1,12}, -{188,164,2,18,17,2,0,18}, -{252,190,149,10,18,1,2,14}, -{124,50,268,2,24,1,6,13}, -{220,114,99,12,22,-3,2,17}, -{92,137,146,11,17,2,-1,9}, -{60,159,121,11,12,5,1,13}, -{189,186,2,18,17,2,0,18}, -{253,56,77,14,25,0,-1,12}, -{221,20,76,14,23,-4,-1,13}, -{125,79,235,7,21,2,1,9}, -{93,195,234,5,21,2,0,7}, -{61,152,137,11,6,8,1,13}, -{190,52,2,19,17,2,0,18}, -{222,114,188,10,17,2,2,14}, -{254,219,115,11,24,1,2,14}, -{62,174,109,11,12,5,1,13}, -{94,108,78,13,11,2,0,13}, -{126,107,140,11,5,8,1,13}, -{223,17,156,11,18,1,2,14}, -{191,15,203,9,18,6,0,10}, -{255,38,76,14,24,1,-1,12}, -{63,2,203,9,17,2,0,10}, -{95,218,166,10,2,21,0,10}, -}; -static const int _hidpi_font_kerning_pair_count=0; -static const int _hidpi_font_kerning_pairs[1][3]={ -{0,0,0} -}; -static const int _hidpi_font_img_width=256; -static const int _hidpi_font_img_height=512; -static const int _hidpi_font_img_data_size=25255; -static const unsigned char _hidpi_font_img_data[25255]={ -137, -80, -78, -71, -13, -10, -26, -10, -0, -0, -0, -13, -73, -72, -68, -82, -0, -0, -1, -0, -0, -0, -2, -0, -8, -6, -0, -0, -0, -109, -154, -178, -251, -0, -0, -32, -0, -73, -68, -65, -84, -120, -156, -236, -157, -119, -184, -38, -53, -245, -199, -51, -84, -165, -55, -1, -21, -16, -41, -34, -42, -130, -98, -23, -1, -21, -20, -84, -108, -40, -138, -138, -10, -34, -40, -86, -148, -159, -216, -11, -34, -138, -130, -162, -40, -32, -160, -128, -160, -2, -162, -32, -162, -34, -34, -29, -145, -142, -235, -210, -219, -210, -151, -14, -219, -251, -231, -247, -199, -57, -97, -114, -231, -205, -204, -100, -102, -50, -239, -189, -119, -111, -190, -207, -179, -207, -221, -55, -57, -57, -147, -153, -73, -206, -36, -39, -167, -24, -147, -144, -144, -144, -144, -144, -144, -48, -129, -1, -172, -75, -142, -45, -74, -104, -158, -231, -208, -188, -176, -132, -230, -85, -14, -205, -58, -158, -250, -245, -156, -250, -103, -121, -234, -95, -237, -212, -239, -225, -169, -95, -25, -88, -160, -245, -235, -122, -234, -55, -5, -78, -0, -238, -5, -230, -2, -83, -128, -95, -2, -107, -149, -244, -119, -105, -224, -22, -224, -65, -96, -37, -31, -77, -8, -128, -143, -211, -0, -129, -60, -215, -1, -190, -8, -156, -15, -220, -13, -204, -1, -30, -7, -38, -1, -63, -3, -182, -12, -228, -243, -33, -224, -102, -109, -255, -63, -224, -109, -21, -180, -235, -3, -179, -244, -249, -173, -88, -195, -119, -109, -224, -43, -192, -101, -192, -19, -250, -188, -239, -5, -126, -3, -108, -160, -52, -27, -0, -191, -7, -30, -86, -190, -215, -0, -159, -0, -150, -168, -224, -187, -42, -112, -172, -62, -170, -211, -42, -232, -26, -189, -187, -62, -248, -42, -175, -235, -244, -255, -239, -211, -103, -49, -67, -223, -211, -133, -246, -89, -3, -75, -0, -159, -5, -174, -211, -231, -240, -36, -240, -79, -224, -117, -117, -253, -214, -246, -43, -105, -251, -127, -1, -143, -0, -243, -245, -239, -185, -192, -103, -234, -222, -85, -16, -128, -27, -244, -134, -246, -45, -169, -255, -164, -51, -134, -247, -41, -161, -249, -138, -214, -79, -174, -184, -206, -205, -74, -243, -126, -79, -221, -33, -206, -53, -78, -247, -212, -239, -164, -117, -55, -150, -240, -254, -43, -48, -93, -7, -225, -87, -129, -95, -1, -139, -128, -127, -149, -208, -127, -65, -249, -125, -188, -172, -191, -33, -0, -174, -5, -22, -2, -151, -56, -253, -191, -86, -127, -15, -252, -171, -225, -245, -52, -224, -251, -192, -108, -229, -115, -43, -112, -6, -112, -52, -112, -18, -112, -149, -94, -11, -224, -52, -96, -149, -10, -94, -175, -215, -251, -183, -130, -240, -126, -29, -60, -94, -225, -1, -156, -165, -124, -119, -14, -184, -231, -43, -149, -118, -1, -112, -35, -112, -5, -50, -209, -1, -30, -211, -107, -63, -166, -191, -239, -214, -241, -101, -251, -125, -98, -9, -207, -247, -2, -83, -129, -187, -128, -153, -84, -79, -212, -224, -119, -215, -35, -95, -144, -9, -253, -85, -253, -255, -125, -250, -126, -158, -212, -223, -139, -128, -157, -245, -61, -161, -215, -191, -70, -251, -128, -190, -139, -87, -215, -92, -99, -39, -224, -81, -100, -194, -31, -13, -236, -14, -188, -93, -255, -30, -173, -229, -143, -80, -33, -216, -131, -0, -28, -166, -157, -250, -75, -73, -253, -105, -136, -148, -159, -11, -156, -82, -66, -115, -142, -242, -56, -164, -226, -58, -63, -87, -154, -35, -60, -117, -183, -235, -96, -189, -18, -145, -164, -79, -43, -212, -31, -170, -109, -127, -90, -194, -123, -85, -96, -181, -66, -217, -121, -192, -116, -15, -237, -26, -136, -164, -254, -47, -21, -95, -164, -58, -0, -175, -209, -62, -253, -85, -127, -91, -120, -87, -82, -53, -188, -214, -0, -254, -173, -237, -79, -1, -94, -80, -66, -183, -46, -240, -107, -165, -187, -17, -88, -181, -132, -238, -79, -74, -179, -137, -254, -126, -169, -254, -62, -201, -67, -251, -110, -247, -62, -2, -250, -250, -38, -224, -115, -238, -243, -70, -190, -158, -71, -58, -131, -127, -33, -176, -139, -83, -255, -34, -100, -34, -2, -188, -215, -195, -243, -64, -224, -183, -192, -234, -84, -76, -212, -166, -239, -174, -71, -190, -22, -243, -129, -15, -58, -229, -203, -33, -31, -35, -128, -121, -200, -234, -235, -237, -133, -235, -92, -163, -245, -103, -84, -240, -255, -178, -62, -195, -31, -0, -43, -148, -208, -172, -0, -28, -172, -116, -95, -174, -235, -115, -213, -205, -236, -168, -29, -122, -18, -88, -178, -80, -183, -4, -34, -133, -174, -212, -127, -15, -120, -218, -47, -67, -46, -217, -182, -171, -184, -206, -59, -148, -102, -114, -161, -124, -11, -45, -63, -94, -111, -24, -224, -45, -5, -154, -171, -181, -188, -86, -218, -33, -95, -210, -215, -35, -203, -211, -223, -122, -234, -127, -161, -188, -222, -80, -199, -171, -230, -58, -191, -85, -62, -239, -208, -223, -22, -141, -4, -0, -176, -20, -178, -66, -152, -15, -236, -22, -216, -102, -95, -189, -214, -159, -75, -234, -239, -5, -110, -45, -148, -221, -9, -220, -89, -40, -91, -1, -184, -7, -249, -154, -61, -183, -73, -191, -61, -215, -92, -203, -121, -6, -199, -121, -234, -247, -215, -186, -127, -212, -240, -129, -242, -137, -218, -250, -221, -197, -228, -235, -220, -231, -209, -158, -186, -45, -156, -250, -67, -61, -245, -239, -209, -186, -7, -75, -120, -239, -132, -76, -234, -93, -245, -247, -18, -192, -222, -200, -252, -155, -137, -204, -199, -75, -128, -79, -107, -253, -110, -74, -223, -110, -37, -128, -72, -173, -57, -218, -169, -87, -20, -234, -236, -151, -227, -112, -231, -33, -109, -82, -160, -217, -74, -203, -103, -0, -203, -86, -92, -103, -37, -29, -228, -139, -128, -213, -157, -242, -3, -180, -253, -135, -145, -175, -11, -192, -145, -78, -253, -202, -122, -131, -243, -40, -145, -134, -14, -173, -139, -127, -22, -251, -3, -188, -0, -89, -186, -122, -39, -78, -40, -128, -103, -32, -43, -162, -251, -80, -161, -233, -92, -183, -169, -0, -248, -134, -182, -251, -124, -195, -118, -118, -121, -249, -90, -79, -221, -60, -224, -226, -66, -217, -165, -192, -220, -66, -217, -143, -149, -199, -87, -155, -92, -187, -162, -79, -115, -149, -223, -174, -158, -186, -55, -106, -221, -35, -53, -60, -192, -51, -81, -187, -190, -187, -152, -124, -157, -119, -61, -48, -233, -128, -167, -59, -245, -59, -120, -234, -55, -211, -186, -5, -37, -109, -31, -6, -14, -114, -202, -78, -82, -250, -169, -250, -255, -211, -149, -230, -215, -14, -205, -193, -200, -118, -160, -157, -78, -0, -81, -42, -0, -236, -95, -40, -255, -63, -251, -66, -129, -15, -234, -255, -247, -46, -208, -216, -1, -124, -86, -192, -117, -46, -85, -218, -119, -56, -101, -215, -35, -66, -97, -45, -125, -0, -179, -129, -123, -157, -122, -187, -255, -191, -48, -128, -255, -14, -200, -222, -235, -8, -109, -115, -80, -161, -254, -108, -100, -114, -108, -92, -199, -171, -230, -58, -86, -231, -241, -93, -167, -204, -34, -88, -0, -32, -66, -241, -9, -100, -133, -147, -57, -229, -203, -34, -203, -192, -201, -136, -112, -158, -141, -124, -193, -207, -36, -95, -214, -219, -47, -141, -111, -75, -53, -157, -130, -206, -1, -217, -98, -76, -115, -126, -111, -142, -12, -252, -27, -128, -165, -27, -244, -121, -73, -68, -241, -117, -18, -162, -220, -186, -31, -152, -166, -207, -53, -4, -11, -107, -248, -131, -127, -162, -118, -122, -119, -49, -249, -58, -247, -178, -89, -77, -253, -139, -61, -117, -27, -217, -74, -79, -221, -199, -16, -37, -228, -242, -250, -251, -189, -74, -122, -5, -35, -63, -154, -203, -0, -43, -59, -191, -87, -64, -4, -192, -103, -66, -239, -161, -120, -97, -59, -209, -255, -81, -40, -63, -91, -203, -159, -173, -255, -0, -126, -87, -160, -57, -79, -203, -63, -29, -112, -157, -111, -43, -237, -161, -250, -219, -158, -48, -92, -234, -208, -252, -93, -203, -94, -170, -191, -237, -254, -255, -107, -13, -239, -233, -66, -224, -9, -231, -183, -221, -234, -252, -184, -9, -31, -15, -223, -37, -16, -125, -197, -66, -224, -57, -78, -185, -69, -19, -1, -176, -151, -182, -249, -136, -83, -182, -52, -114, -2, -0, -162, -80, -60, -4, -89, -129, -221, -173, -101, -175, -117, -104, -239, -3, -174, -247, -240, -189, -137, -193, -45, -192, -20, -84, -137, -10, -100, -228, -58, -135, -109, -27, -244, -119, -13, -70, -42, -1, -255, -131, -40, -94, -127, -10, -28, -228, -60, -131, -235, -40, -81, -132, -82, -175, -12, -133, -194, -68, -141, -241, -238, -98, -242, -117, -238, -115, -163, -166, -245, -84, -11, -128, -243, -25, -185, -250, -253, -135, -146, -110, -19, -208, -167, -163, -129, -115, -155, -220, -135, -219, -120, -115, -189, -208, -12, -244, -107, -64, -190, -183, -191, -201, -161, -187, -13, -184, -207, -249, -189, -44, -185, -214, -218, -251, -48, -10, -215, -121, -173, -210, -94, -166, -191, -191, -172, -191, -247, -115, -104, -62, -165, -101, -223, -208, -223, -118, -192, -189, -162, -130, -239, -42, -133, -223, -75, -34, -95, -182, -71, -244, -247, -82, -246, -183, -75, -11, -124, -196, -55, -48, -106, -238, -225, -237, -218, -230, -239, -133, -242, -90, -120, -120, -157, -170, -85, -207, -112, -202, -172, -48, -62, -13, -88, -202, -41, -63, -89, -203, -95, -228, -148, -93, -12, -60, -238, -225, -251, -59, -165, -45, -42, -1, -79, -214, -223, -123, -235, -239, -19, -244, -119, -208, -145, -33, -162, -167, -1, -81, -150, -61, -167, -80, -151, -57, -183, -218, -90, -191, -82, -124, -31, -177, -222, -93, -13, -223, -117, -17, -1, -118, -135, -62, -131, -59, -145, -15, -79, -153, -2, -206, -34, -182, -0, -120, -20, -248, -168, -243, -219, -30, -253, -45, -89, -164, -245, -180, -221, -157, -154, -237, -85, -85, -227, -12, -120, -64, -251, -181, -149, -150, -109, -173, -191, -143, -112, -232, -236, -121, -234, -70, -250, -123, -91, -253, -125, -91, -224, -117, -150, -66, -148, -141, -115, -144, -47, -157, -61, -62, -219, -208, -161, -89, -95, -203, -46, -66, -20, -122, -243, -245, -193, -84, -157, -33, -207, -65, -142, -204, -190, -11, -124, -29, -153, -24, -0, -7, -107, -253, -167, -245, -247, -167, -156, -54, -175, -64, -246, -85, -115, -66, -7, -145, -182, -179, -171, -162, -119, -21, -202, -45, -130, -143, -1, -145, -163, -190, -187, -10, -101, -147, -148, -207, -250, -37, -215, -93, -199, -41, -59, -135, -194, -190, -94, -203, -95, -137, -124, -161, -167, -144, -31, -3, -46, -68, -236, -45, -158, -129, -28, -211, -61, -166, -255, -15, -62, -50, -212, -247, -0, -126, -77, -254, -218, -206, -51, -104, -183, -20, -53, -222, -137, -26, -229, -221, -85, -241, -5, -254, -172, -207, -224, -12, -228, -163, -100, -79, -90, -6, -148, -200, -14, -47, -136, -47, -0, -22, -48, -242, -212, -96, -62, -30, -197, -123, -201, -53, -223, -14, -204, -15, -161, -45, -99, -240, -27, -237, -151, -253, -242, -126, -71, -127, -191, -199, -161, -249, -144, -150, -125, -76, -127, -219, -37, -253, -207, -27, -92, -231, -207, -218, -230, -213, -200, -222, -107, -146, -135, -102, -178, -214, -109, -163, -180, -127, -168, -225, -121, -16, -114, -44, -54, -83, -7, -197, -100, -196, -152, -102, -73, -228, -136, -240, -17, -68, -215, -176, -148, -210, -175, -141, -104, -202, -247, -68, -20, -87, -161, -131, -104, -35, -29, -40, -247, -227, -124, -157, -181, -206, -162, -201, -22, -224, -73, -224, -170, -66, -217, -108, -224, -81, -15, -237, -85, -202, -127, -69, -167, -236, -106, -28, -125, -73, -129, -254, -29, -200, -23, -110, -174, -254, -221, -89, -203, -237, -123, -222, -75, -127, -255, -81, -127, -23, -87, -11, -3, -103, -246, -228, -202, -226, -55, -122, -234, -236, -106, -14, -224, -114, -28, -157, -70, -19, -104, -251, -211, -244, -255, -49, -223, -93, -41, -95, -96, -53, -224, -85, -5, -250, -243, -128, -89, -21, -188, -160, -255, -21, -192, -195, -200, -60, -8, -57, -154, -108, -191, -2, -80, -6, -86, -201, -247, -47, -253, -125, -1, -131, -26, -251, -117, -148, -230, -4, -135, -6, -26, -28, -65, -144, -75, -222, -163, -245, -239, -119, -60, -52, -7, -219, -235, -232, -223, -214, -6, -59, -192, -79, -148, -199, -14, -250, -123, -25, -68, -25, -249, -91, -253, -253, -212, -192, -8, -224, -101, -245, -17, -85, -104, -34, -0, -230, -1, -255, -46, -148, -61, -12, -60, -89, -40, -179, -43, -39, -236, -96, -32, -63, -189, -105, -162, -189, -182, -43, -182, -127, -163, -19, -20, -255, -145, -225, -20, -10, -71, -134, -90, -110, -183, -99, -167, -0, -203, -104, -89, -134, -28, -69, -205, -70, -116, -2, -183, -43, -205, -177, -192, -154, -78, -219, -117, -16, -163, -178, -183, -54, -232, -111, -180, -119, -87, -197, -183, -132, -230, -122, -60, -31, -39, -231, -186, -16, -95, -0, -92, -200, -72, -29, -128, -213, -135, -189, -38, -224, -158, -142, -166, -196, -240, -45, -8, -192, -154, -200, -132, -159, -142, -44, -207, -103, -162, -230, -142, -5, -186, -219, -144, -189, -210, -82, -250, -210, -231, -160, -90, -203, -192, -235, -108, -162, -55, -53, -189, -108, -194, -0, -175, -211, -186, -25, -250, -119, -253, -150, -247, -180, -49, -50, -201, -254, -238, -148, -253, -18, -217, -195, -46, -167, -191, -131, -6, -17, -114, -66, -241, -168, -62, -35, -223, -18, -223, -162, -137, -0, -152, -138, -163, -99, -209, -178, -127, -41, -159, -173, -156, -178, -143, -146, -91, -211, -109, -168, -101, -86, -87, -50, -112, -228, -86, -114, -173, -101, -144, -85, -210, -124, -28, -237, -180, -62, -31, -223, -137, -129, -111, -107, -177, -141, -211, -143, -135, -145, -21, -136, -181, -250, -187, -9, -153, -228, -47, -39, -223, -42, -44, -210, -255, -91, -59, -17, -8, -80, -22, -235, -181, -162, -189, -187, -58, -190, -30, -154, -207, -107, -223, -119, -42, -169, -183, -136, -45, -0, -246, -98, -228, -41, -128, -181, -25, -248, -15, -176, -70, -129, -118, -25, -231, -255, -246, -20, -224, -115, -229, -119, -30, -0, -114, -43, -165, -119, -234, -95, -159, -33, -131, -213, -3, -216, -115, -221, -198, -154, -71, -114, -141, -246, -192, -87, -70, -235, -151, -116, -6, -214, -45, -109, -238, -69, -249, -156, -137, -12, -248, -23, -232, -239, -79, -40, -223, -13, -28, -154, -160, -65, -4, -236, -81, -117, -191, -206, -75, -111, -34, -0, -46, -213, -254, -173, -224, -148, -189, -81, -7, -223, -147, -136, -117, -221, -73, -58, -96, -63, -141, -28, -25, -94, -139, -216, -100, -204, -210, -246, -75, -85, -93, -195, -225, -251, -53, -223, -59, -37, -224, -200, -176, -80, -183, -53, -114, -108, -252, -48, -50, -177, -111, -68, -108, -57, -220, -99, -169, -231, -32, -71, -177, -119, -34, -203, -244, -25, -218, -239, -131, -128, -181, -3, -251, -27, -237, -221, -85, -241, -245, -212, -127, -82, -235, -247, -172, -224, -97, -17, -91, -0, -44, -135, -8, -204, -239, -57, -101, -118, -203, -246, -16, -162, -8, -62, -22, -177, -113, -113, -143, -116, -127, -160, -207, -102, -229, -34, -207, -70, -64, -108, -209, -33, -215, -56, -15, -44, -215, -200, -245, -0, -182, -99, -251, -249, -120, -141, -54, -200, -5, -212, -207, -157, -178, -186, -179, -234, -245, -43, -248, -217, -229, -239, -46, -37, -245, -22, -77, -4, -192, -129, -218, -230, -125, -133, -242, -183, -34, -75, -208, -185, -136, -118, -126, -119, -45, -223, -17, -89, -98, -63, -170, -207, -127, -117, -63, -231, -129, -235, -108, -128, -8, -140, -187, -41, -104, -182, -41, -63, -50, -108, -45, -120, -187, -34, -246, -187, -171, -226, -91, -168, -255, -54, -34, -120, -223, -226, -171, -31, -6, -128, -119, -225, -152, -82, -35, -91, -172, -61, -80, -161, -140, -163, -220, -213, -250, -15, -40, -253, -59, -99, -92, -252, -245, -250, -128, -158, -64, -164, -224, -128, -101, -17, -185, -30, -224, -113, -253, -251, -34, -31, -175, -209, -4, -178, -130, -248, -175, -246, -209, -213, -97, -124, -222, -243, -15, -228, -220, -250, -243, -148, -120, -129, -33, -90, -103, -144, -229, -153, -215, -104, -198, -25, -136, -77, -4, -192, -198, -250, -66, -175, -43, -227, -27, -3, -192, -223, -180, -111, -239, -242, -212, -149, -29, -25, -54, -222, -91, -199, -64, -236, -119, -87, -199, -215, -169, -251, -37, -34, -12, -159, -223, -215, -189, -133, -2, -57, -201, -90, -8, -124, -143, -146, -237, -53, -176, -60, -242, -193, -94, -8, -124, -61, -214, -133, -151, -33, -223, -119, -95, -86, -65, -119, -155, -210, -220, -19, -229, -194, -145, -65, -110, -96, -227, -245, -112, -44, -208, -214, -14, -118, -114, -101, -228, -193, -53, -124, -160, -161, -55, -32, -185, -213, -226, -81, -116, -112, -78, -170, -232, -151, -221, -71, -122, -45, -53, -41, -63, -50, -12, -114, -89, -141, -141, -216, -239, -46, -132, -47, -114, -252, -55, -19, -248, -166, -71, -200, -120, -173, -253, -250, -6, -178, -18, -120, -28, -249, -232, -28, -137, -232, -129, -222, -174, -127, -143, -212, -242, -199, -136, -241, -229, -47, -92, -248, -47, -250, -160, -190, -87, -65, -99, -245, -0, -199, -70, -189, -120, -4, -32, -230, -181, -15, -34, -190, -221, -181, -95, -213, -186, -65, -132, -120, -146, -205, -70, -246, -229, -165, -198, -78, -4, -160, -164, -221, -178, -228, -74, -196, -243, -241, -216, -246, -59, -180, -27, -0, -95, -168, -187, -39, -135, -126, -69, -68, -203, -63, -139, -234, -237, -205, -187, -144, -189, -252, -92, -253, -235, -221, -230, -244, -141, -216, -239, -46, -148, -111, -205, -107, -251, -68, -219, -251, -233, -10, -96, -21, -228, -56, -251, -2, -100, -219, -183, -64, -255, -94, -128, -184, -47, -119, -219, -243, -39, -140, -13, -168, -16, -248, -57, -178, -237, -2, -57, -29, -248, -27, -112, -34, -162, -4, -252, -7, -98, -246, -91, -42, -72, -134, -216, -87, -23, -63, -11, -160, -255, -180, -219, -96, -24, -125, -76, -72, -24, -151, -0, -158, -139, -24, -96, -93, -128, -44, -197, -231, -32, -202, -175, -135, -16, -37, -208, -143, -40, -24, -172, -140, -66, -31, -93, -220, -75, -141, -193, -15, -35, -143, -72, -147, -0, -72, -72, -24, -207, -208, -121, -188, -8, -177, -182, -132, -10, -35, -21, -68, -97, -188, -8, -81, -212, -37, -1, -48, -70, -16, -93, -209, -148, -48, -225, -144, -25, -99, -206, -214, -255, -191, -167, -130, -238, -125, -74, -219, -41, -254, -66, -194, -144, -64, -79, -65, -8, -17, -207, -182, -219, -172, -210, -2, -177, -197, -190, -3, -245, -78, -107, -201, -115, -51, -196, -230, -124, -134, -46, -153, -91, -251, -248, -147, -219, -217, -79, -199, -241, -204, -155, -104, -32, -204, -235, -204, -194, -6, -111, -185, -171, -130, -246, -74, -29, -67, -79, -5, -141, -109, -208, -151, -23, -58, -215, -218, -60, -180, -93, -129, -199, -43, -16, -95, -135, -169, -228, -78, -101, -127, -35, -192, -189, -182, -132, -223, -106, -72, -28, -192, -75, -17, -165, -226, -60, -253, -123, -9, -240, -37, -234, -143, -34, -215, -38, -183, -164, -172, -220, -202, -57, -207, -108, -33, -129, -198, -83, -157, -64, -143, -65, -8, -201, -157, -72, -94, -171, -191, -109, -20, -161, -217, -29, -250, -123, -57, -162, -28, -219, -6, -241, -138, -187, -168, -3, -175, -231, -234, -139, -4, -79, -128, -141, -197, -9, -58, -8, -207, -67, -142, -152, -78, -67, -237, -22, -16, -123, -254, -251, -2, -218, -91, -108, -130, -24, -43, -129, -199, -85, -27, -57, -177, -0, -241, -98, -124, -81, -11, -1, -240, -67, -231, -90, -3, -22, -169, -1, -237, -183, -39, -87, -170, -62, -138, -152, -45, -91, -19, -229, -133, -148, -152, -249, -86, -240, -123, -55, -185, -237, -11, -136, -149, -227, -21, -228, -71, -226, -232, -24, -26, -112, -148, -42, -240, -185, -64, -105, -75, -143, -148, -149, -206, -250, -195, -92, -208, -164, -159, -173, -64, -207, -65, -8, -25, -92, -1, -172, -130, -172, -0, -188, -65, -70, -3, -121, -206, -68, -221, -82, -129, -93, -240, -4, -0, -109, -200, -111, -75, -196, -0, -106, -33, -240, -242, -46, -188, -10, -124, -215, -36, -183, -173, -184, -162, -3, -31, -87, -155, -30, -100, -79, -95, -194, -231, -56, -228, -88, -112, -50, -178, -63, -183, -251, -249, -89, -192, -49, -1, -237, -45, -54, -35, -247, -24, -253, -145, -135, -206, -70, -204, -253, -8, -121, -188, -137, -208, -208, -232, -75, -34, -167, -30, -211, -244, -223, -3, -4, -172, -78, -10, -60, -236, -170, -238, -120, -52, -52, -28, -114, -218, -98, -45, -88, -7, -252, -92, -42, -120, -237, -72, -30, -150, -254, -116, -10, -199, -193, -136, -233, -243, -239, -181, -126, -14, -21, -95, -119, -114, -31, -142, -74, -75, -75, -228, -200, -18, -28, -55, -232, -94, -192, -96, -16, -66, -23, -179, -16, -5, -206, -103, -28, -250, -110, -65, -8, -35, -1, -89, -94, -158, -133, -216, -166, -159, -67, -64, -200, -176, -0, -158, -91, -234, -96, -187, -33, -70, -31, -149, -231, -81, -250, -44, -31, -160, -16, -68, -163, -1, -143, -23, -33, -66, -196, -78, -136, -25, -148, -216, -178, -7, -240, -154, -10, -124, -64, -255, -255, -98, -68, -56, -63, -137, -172, -10, -188, -121, -20, -10, -237, -45, -182, -32, -143, -111, -119, -135, -135, -110, -146, -78, -134, -149, -112, -2, -101, -6, -246, -241, -205, -74, -126, -166, -254, -3, -216, -177, -225, -125, -218, -85, -231, -75, -10, -229, -91, -218, -137, -26, -200, -231, -105, -200, -105, -7, -72, -0, -151, -210, -83, -15, -228, -216, -22, -196, -124, -219, -43, -176, -24, -185, -13, -240, -90, -209, -146, -111, -127, -250, -93, -254, -227, -15, -66, -8, -178, -23, -62, -10, -248, -3, -185, -27, -234, -199, -28, -154, -110, -65, -8, -35, -0, -177, -129, -182, -152, -6, -60, -111, -180, -250, -82, -6, -196, -51, -110, -33, -178, -116, -108, -28, -46, -92, -121, -172, -133, -44, -55, -167, -33, -49, -20, -94, -167, -2, -224, -142, -144, -9, -235, -225, -23, -228, -60, -84, -209, -254, -41, -1, -160, -191, -109, -190, -135, -45, -29, -154, -77, -181, -236, -116, -253, -221, -84, -0, -216, -136, -203, -123, -34, -113, -242, -0, -126, -223, -176, -159, -214, -225, -236, -205, -133, -114, -203, -47, -200, -215, -129, -220, -77, -126, -54, -142, -123, -115, -9, -173, -13, -63, -14, -78, -220, -75, -15, -221, -133, -74, -243, -141, -146, -250, -175, -107, -125, -231, -143, -90, -37, -40, -4, -33, -212, -50, -128, -41, -206, -239, -151, -105, -217, -121, -78, -89, -227, -32, -132, -68, -80, -234, -20, -248, -117, -218, -35, -14, -3, -136, -85, -221, -84, -2, -179, -249, -148, -240, -184, -10, -49, -211, -117, -93, -120, -95, -138, -36, -155, -184, -170, -170, -109, -31, -240, -8, -0, -235, -208, -244, -125, -135, -198, -110, -13, -172, -67, -75, -176, -0, -64, -172, -23, -103, -34, -75, -238, -103, -32, -138, -183, -121, -200, -106, -52, -56, -139, -19, -121, -20, -235, -243, -200, -99, -31, -108, -71, -190, -143, -31, -240, -139, -40, -225, -99, -151, -246, -65, -39, -25, -228, -1, -86, -142, -175, -160, -177, -219, -185, -107, -75, -234, -109, -24, -252, -214, -91, -189, -32, -80, -8, -66, -168, -101, -69, -1, -176, -162, -150, -253, -175, -64, -215, -40, -8, -97, -204, -9, -75, -132, -61, -162, -135, -103, -84, -1, -53, -214, -209, -246, -126, -61, -2, -192, -78, -238, -91, -28, -154, -155, -144, -85, -202, -114, -5, -154, -16, -1, -96, -221, -174, -255, -233, -148, -89, -103, -166, -143, -85, -181, -45, -240, -121, -22, -185, -75, -249, -49, -200, -170, -98, -17, -34, -76, -62, -217, -128, -143, -93, -225, -4, -133, -78, -39, -143, -140, -52, -16, -172, -213, -161, -113, -183, -1, -235, -23, -234, -108, -26, -189, -133, -192, -51, -67, -251, -217, -10, -20, -66, -16, -105, -217, -83, -2, -0, -241, -79, -182, -46, -194, -71, -20, -232, -130, -67, -16, -197, -158, -176, -68, -216, -35, -122, -120, -142, -249, -21, -69, -76, -180, -189, -223, -162, -0, -208, -178, -91, -181, -108, -115, -103, -178, -255, -214, -169, -111, -34, -0, -172, -150, -124, -79, -167, -236, -35, -90, -214, -104, -73, -76, -158, -245, -200, -226, -82, -52, -218, -116, -3, -30, -118, -11, -252, -161, -64, -250, -221, -148, -254, -177, -26, -58, -187, -13, -216, -183, -80, -254, -89, -45, -111, -125, -170, -21, -12, -10, -65, -8, -181, -12, -68, -82, -90, -205, -181, -125, -112, -69, -23, -202, -224, -32, -132, -68, -158, -176, -68, -216, -35, -22, -248, -141, -249, -21, -69, -76, -126, -93, -238, -215, -233, -131, -43, -0, -108, -70, -167, -3, -157, -255, -239, -228, -212, -7, -9, -0, -68, -155, -110, -191, -210, -110, -234, -177, -149, -17, -165, -222, -34, -2, -163, -67, -33, -43, -9, -155, -142, -204, -226, -7, -33, -109, -11, -124, -172, -246, -191, -54, -111, -162, -210, -239, -172, -244, -149, -115, -131, -124, -27, -80, -76, -224, -114, -190, -150, -183, -14, -172, -26, -12, -202, -87, -0, -51, -16, -39, -148, -187, -245, -1, -108, -224, -105, -219, -100, -5, -16, -109, -194, -18, -105, -143, -88, -224, -57, -230, -87, -20, -49, -249, -117, -185, -95, -167, -15, -174, -0, -176, -154, -245, -155, -17, -229, -228, -99, -140, -12, -89, -21, -42, -0, -108, -146, -153, -129, -112, -93, -136, -203, -46, -192, -55, -107, -120, -44, -137, -28, -117, -218, -113, -252, -85, -36, -145, -137, -205, -90, -244, -253, -2, -189, -53, -104, -242, -70, -182, -70, -142, -134, -33, -60, -109, -91, -232, -10, -224, -153, -200, -50, -127, -33, -170, -92, -68, -242, -46, -44, -64, -4, -221, -64, -22, -237, -232, -160, -16, -132, -80, -203, -32, -223, -2, -216, -24, -248, -190, -140, -42, -65, -65, -8, -137, -60, -97, -137, -180, -71, -44, -240, -28, -211, -43, -138, -30, -248, -181, -190, -95, -114, -108, -81, -40, -191, -195, -169, -59, -182, -80, -23, -42, -0, -110, -161, -30, -183, -214, -240, -176, -123, -240, -135, -113, -210, -217, -35, -81, -150, -236, -209, -160, -171, -176, -252, -165, -150, -121, -147, -131, -32, -138, -92, -8, -215, -1, -212, -102, -202, -118, -104, -47, -82, -218, -143, -235, -239, -143, -234, -239, -139, -235, -218, -70, -1, -133, -32, -132, -90, -6, -35, -149, -128, -231, -107, -217, -235, -157, -178, -224, -32, -132, -68, -158, -176, -68, -220, -35, -106, -219, -49, -191, -162, -136, -201, -175, -235, -253, -146, -163, -40, -0, -126, -228, -212, -109, -87, -168, -171, -21, -0, -200, -17, -39, -200, -87, -187, -44, -171, -144, -13, -36, -91, -229, -128, -100, -163, -18, -15, -164, -178, -71, -12, -122, -158, -18, -2, -72, -102, -170, -57, -136, -197, -160, -215, -148, -156, -60, -55, -95, -232, -41, -128, -205, -204, -60, -144, -32, -213, -67, -251, -25, -165, -61, -189, -208, -246, -179, -33, -215, -234, -12, -252, -65, -8, -97, -164, -0, -216, -18, -89, -146, -92, -75, -30, -146, -58, -56, -8, -33, -113, -149, -58, -207, -33, -210, -30, -209, -105, -59, -30, -86, -20, -49, -183, -80, -157, -238, -151, -28, -69, -1, -96, -195, -166, -77, -101, -48, -203, -116, -136, -0, -176, -6, -83, -135, -87, -208, -252, -76, -105, -142, -170, -160, -177, -177, -3, -183, -45, -169, -127, -51, -121, -54, -43, -123, -74, -240, -195, -10, -126, -239, -83, -154, -57, -52, -179, -3, -120, -123, -21, -173, -210, -219, -109, -192, -147, -200, -92, -156, -174, -99, -248, -217, -117, -109, -163, -129, -193, -32, -132, -224, -8, -0, -45, -179, -22, -78, -31, -163, -65, -16, -66, -34, -79, -88, -34, -236, -17, -61, -237, -198, -244, -138, -162, -7, -126, -81, -239, -55, -6, -16, -19, -93, -59, -25, -95, -89, -65, -103, -29, -100, -30, -163, -36, -19, -53, -146, -238, -28, -224, -43, -21, -124, -118, -37, -199, -29, -84, -68, -31, -210, -190, -221, -165, -180, -191, -43, -163, -83, -90, -107, -102, -124, -19, -225, -74, -85, -187, -13, -248, -162, -254, -173, -204, -157, -216, -11, -232, -41, -8, -97, -236, -9, -75, -132, -61, -98, -129, -223, -115, -24, -227, -43, -138, -152, -252, -250, -184, -223, -24, -32, -143, -91, -88, -107, -157, -71, -190, -196, -247, -186, -33, -147, -39, -253, -152, -173, -207, -206, -205, -175, -184, -30, -146, -123, -241, -225, -194, -152, -57, -160, -230, -154, -111, -34, -63, -13, -56, -133, -193, -188, -136, -235, -145, -7, -87, -157, -67, -133, -16, -243, -240, -182, -219, -128, -251, -245, -111, -183, -216, -254, -109, -65, -15, -65, -8, -137, -56, -97, -137, -180, -71, -44, -240, -28, -15, -43, -138, -152, -91, -168, -232, -247, -27, -3, -228, -113, -40, -107, -175, -77, -110, -121, -120, -102, -73, -253, -74, -136, -167, -168, -197, -124, -228, -52, -203, -245, -228, -91, -136, -108, -57, -118, -116, -198, -204, -64, -134, -170, -2, -223, -119, -21, -120, -220, -166, -215, -113, -189, -1, -167, -226, -232, -202, -2, -239, -253, -89, -136, -224, -69, -255, -174, -83, -223, -170, -39, -16, -49, -8, -33, -145, -39, -44, -145, -246, -136, -5, -250, -49, -189, -162, -232, -129, -95, -212, -251, -141, -1, -100, -91, -99, -221, -118, -7, -142, -155, -61, -244, -214, -207, -96, -30, -37, -177, -27, -144, -204, -86, -159, -68, -20, -216, -54, -174, -197, -99, -136, -99, -219, -161, -56, -161, -191, -145, -109, -133, -61, -234, -251, -118, -205, -181, -215, -64, -132, -232, -101, -72, -152, -54, -27, -174, -237, -98, -100, -101, -209, -54, -94, -134, -77, -102, -251, -239, -122, -234, -113, -2, -34, -78, -88, -34, -238, -17, -29, -218, -49, -191, -162, -136, -201, -175, -143, -251, -77, -72, -240, -34, -246, -132, -37, -226, -30, -209, -161, -27, -15, -43, -138, -152, -91, -168, -232, -247, -155, -144, -224, -69, -236, -9, -75, -196, -61, -162, -210, -140, -249, -21, -69, -76, -126, -125, -220, -111, -66, -66, -41, -98, -78, -88, -250, -217, -35, -142, -249, -21, -69, -76, -126, -125, -220, -111, -194, -4, -3, -225, -231, -154, -209, -39, -108, -108, -196, -20, -80, -74, -19, -123, -203, -19, -155, -95, -212, -251, -13, -1, -162, -48, -179, -86, -119, -165, -249, -246, -144, -252, -136, -32, -182, -250, -107, -84, -208, -21, -177, -16, -57, -206, -187, -4, -177, -247, -95, -181, -97, -255, -214, -82, -62, -39, -20, -202, -47, -3, -30, -172, -104, -183, -10, -185, -210, -240, -131, -21, -116, -239, -82, -154, -39, -129, -85, -60, -245, -43, -146, -219, -44, -236, -81, -193, -103, -71, -165, -185, -135, -190, -3, -240, -208, -49, -96, -228, -120, -0, -227, -96, -69, -17, -147, -95, -31, -247, -27, -10, -242, -179, -241, -42, -75, -187, -131, -148, -230, -212, -26, -94, -22, -54, -247, -226, -127, -200, -163, -254, -160, -99, -118, -32, -155, -117, -5, -191, -237, -181, -221, -23, -157, -178, -12, -217, -86, -253, -179, -166, -237, -183, -181, -109, -105, -92, -65, -114, -205, -254, -129, -21, -52, -86, -72, -60, -130, -39, -211, -51, -18, -142, -204, -190, -223, -119, -135, -222, -91, -107, -208, -49, -96, -228, -68, -5, -241, -87, -20, -67, -255, -98, -247, -1, -36, -98, -51, -136, -243, -210, -64, -40, -50, -96, -9, -242, -120, -123, -111, -170, -225, -101, -81, -52, -65, -222, -20, -248, -179, -214, -45, -160, -196, -4, -216, -195, -239, -11, -218, -102, -123, -167, -204, -70, -50, -174, -244, -182, -100, -228, -42, -224, -205, -158, -122, -27, -69, -107, -186, -111, -98, -23, -104, -109, -223, -127, -229, -169, -179, -130, -198, -155, -216, -53, -58, -232, -24, -48, -114, -34, -130, -200, -95, -216, -216, -252, -70, -27, -136, -89, -44, -120, -108, -227, -129, -29, -180, -110, -10, -53, -153, -145, -203, -4, -128, -214, -101, -192, -201, -90, -127, -23, -97, -137, -69, -143, -87, -250, -181, -157, -178, -119, -106, -217, -71, -3, -218, -219, -201, -57, -224, -17, -75, -190, -242, -25, -136, -150, -236, -161, -93, -151, -220, -7, -96, -43, -167, -124, -67, -196, -154, -113, -38, -195, -178, -206, -164, -99, -192, -200, -132, -132, -34, -200, -191, -180, -103, -120, -234, -78, -209, -186, -111, -5, -240, -41, -21, -0, -90, -191, -6, -185, -147, -207, -7, -2, -248, -93, -13, -60, -84, -40, -179, -118, -23, -181, -81, -131, -24, -185, -10, -112, -3, -162, -174, -163, -2, -124, -22, -129, -31, -77, -96, -95, -229, -243, -63, -59, -7, -201, -205, -189, -27, -133, -224, -31, -115, -32, -82, -60, -123, -135, -95, -148, -56, -251, -9, -195, -1, -226, -33, -103, -93, -110, -215, -116, -202, -87, -213, -242, -133, -192, -122, -1, -124, -42, -5, -128, -210, -216, -128, -156, -94, -167, -29, -90, -160, -166, -79, -118, -21, -112, -170, -83, -102, -19, -122, -252, -180, -238, -158, -156, -54, -75, -2, -215, -104, -187, -47, -145, -235, -6, -38, -19, -176, -154, -137, -2, -58, -166, -61, -42, -225, -25, -45, -158, -189, -195, -179, -83, -156, -125, -82, -26, -176, -74, -32, -91, -16, -235, -82, -91, -186, -5, -1, -54, -82, -154, -218, -173, -7, -185, -59, -243, -126, -78, -153, -77, -144, -113, -118, -85, -91, -135, -222, -162, -74, -0, -236, -175, -52, -94, -227, -40, -224, -88, -253, -119, -186, -210, -93, -227, -148, -29, -139, -172, -32, -30, -118, -203, -106, -250, -100, -87, -1, -11, -144, -37, -251, -242, -200, -137, -204, -28, -26, -186, -244, -34, -122, -131, -133, -58, -71, -238, -69, -182, -4, -175, -107, -194, -163, -53, -136, -148, -246, -168, -192, -51, -106, -60, -123, -229, -25, -35, -206, -254, -132, -73, -3, -214, -22, -228, -138, -169, -82, -37, -36, -240, -93, -165, -249, -99, -0, -191, -173, -149, -246, -122, -167, -204, -10, -226, -247, -214, -180, -173, -68, -129, -214, -134, -227, -122, -162, -134, -167, -141, -219, -183, -135, -83, -182, -188, -142, -173, -63, -212, -221, -79, -129, -151, -93, -5, -28, -65, -190, -218, -109, -101, -73, -73, -110, -215, -1, -240, -235, -54, -60, -218, -92, -52, -90, -218, -163, -66, -187, -232, -241, -236, -137, -16, -103, -95, -249, -244, -146, -6, -172, -112, -141, -12, -9, -38, -81, -171, -87, -33, -242, -234, -139, -142, -201, -88, -201, -163, -233, -122, -143, -33, -245, -222, -166, -40, -77, -80, -134, -40, -242, -208, -90, -47, -37, -87, -92, -62, -140, -19, -59, -176, -164, -157, -181, -122, -180, -184, -214, -41, -187, -164, -64, -107, -39, -246, -188, -26, -158, -7, -40, -157, -187, -119, -183, -1, -77, -188, -201, -58, -42, -120, -217, -85, -192, -44, -125, -38, -243, -105, -169, -180, -3, -246, -113, -238, -179, -113, -0, -211, -54, -23, -140, -154, -246, -104, -60, -129, -142, -105, -192, -60, -147, -118, -46, -98, -172, -113, -1, -226, -137, -182, -159, -62, -175, -223, -214, -240, -9, -89, -125, -61, -68, -33, -204, -86, -13, -207, -78, -201, -88, -129, -101, -144, -179, -105, -240, -8, -124, -224, -245, -90, -231, -61, -222, -43, -225, -249, -121, -109, -115, -32, -185, -178, -205, -27, -135, -175, -164, -189, -69, -213, -22, -224, -163, -74, -83, -25, -172, -22, -9, -171, -182, -0, -120, -154, -83, -182, -167, -182, -13, -118, -119, -119, -218, -126, -219, -233, -95, -171, -47, -55, -226, -225, -249, -48, -162, -245, -159, -133, -108, -71, -106, -117, -35, -157, -64, -15, -105, -143, -38, -2, -60, -147, -246, -14, -157, -180, -247, -144, -251, -118, -131, -124, -201, -189, -249, -223, -148, -79, -211, -213, -87, -168, -87, -98, -231, -100, -172, -192, -225, -122, -221, -95, -120, -234, -236, -17, -90, -169, -129, -143, -167, -205, -106, -58, -206, -46, -35, -95, -254, -7, -235, -131, -2, -5, -128, -205, -72, -52, -201, -83, -231, -238, -245, -31, -211, -177, -236, -150, -93, -171, -109, -255, -68, -192, -254, -191, -192, -123, -21, -167, -127, -175, -14, -109, -87, -224, -113, -168, -182, -255, -46, -121, -124, -197, -147, -218, -240, -106, -114, -209, -232, -105, -143, -148, -46, -218, -146, -54, -38, -175, -24, -40, -76, -218, -211, -128, -13, -11, -245, -27, -147, -235, -24, -230, -80, -72, -78, -233, -208, -181, -89, -125, -221, -194, -144, -142, -106, -201, -141, -89, -30, -193, -209, -68, -35, -123, -101, -235, -132, -84, -106, -226, -91, -194, -243, -68, -125, -127, -139, -128, -203, -26, -182, -181, -168, -18, -0, -231, -42, -205, -128, -209, -26, -45, -16, -187, -127, -21, -109, -55, -66, -86, -144, -15, -34, -38, -194, -107, -144, -219, -6, -116, -218, -238, -214, -93, -184, -143, -180, -71, -209, -20, -138, -49, -121, -41, -191, -78, -194, -132, -145, -147, -246, -119, -120, -38, -45, -240, -33, -173, -183, -198, -60, -215, -151, -208, -181, -93, -125, -5, -229, -180, -139, -1, -228, -24, -10, -156, -21, -31, -240, -97, -45, -107, -52, -129, -181, -173, -221, -138, -64, -243, -112, -102, -22, -101, -118, -0, -27, -147, -167, -219, -42, -53, -9, -38, -87, -72, -126, -163, -80, -254, -16, -112, -121, -147, -62, -53, -233, -95, -77, -91, -27, -219, -97, -31, -167, -236, -123, -90, -118, -126, -219, -62, -133, -92, -56, -106, -218, -35, -226, -230, -81, -143, -170, -156, -36, -130, -48, -33, -159, -180, -179, -240, -56, -174, -32, -146, -251, -97, -29, -136, -111, -35, -159, -180, -3, -138, -50, -198, -199, -234, -235, -255, -244, -154, -238, -57, -183, -253, -202, -238, -21, -202, -199, -105, -187, -190, -182, -157, -14, -172, -208, -176, -173, -133, -207, -18, -240, -233, -228, -138, -194, -201, -84, -175, -166, -62, -169, -116, -174, -80, -123, -166, -150, -5, -47, -251, -155, -244, -175, -166, -157, -213, -167, -220, -204, -200, -248, -133, -171, -146, -27, -26, -213, -70, -24, -110, -5, -34, -166, -61, -34, -162, -66, -49, -38, -47, -165, -137, -34, -76, -28, -26, -239, -209, -23, -121, -84, -216, -159, -233, -111, -27, -231, -221, -183, -36, -109, -187, -250, -42, -85, -90, -18, -127, -197, -244, -76, -125, -110, -51, -145, -176, -213, -107, -33, -194, -173, -109, -68, -98, -155, -246, -186, -241, -68, -115, -250, -239, -102, -36, -90, -2, -216, -22, -184, -82, -235, -102, -81, -51, -1, -129, -159, -43, -237, -115, -157, -50, -155, -29, -168, -117, -64, -78, -95, -255, -2, -218, -44, -129, -132, -41, -3, -143, -179, -15, -240, -45, -173, -187, -145, -62, -182, -126, -68, -76, -123, -68, -68, -133, -98, -100, -94, -49, -5, -147, -157, -180, -251, -123, -234, -172, -119, -217, -221, -232, -215, -141, -138, -44, -49, -180, -95, -125, -61, -94, -82, -223, -215, -113, -174, -53, -73, -221, -25, -216, -91, -255, -127, -98, -72, -219, -2, -159, -165, -16, -129, -4, -240, -178, -22, -237, -45, -236, -49, -224, -181, -228, -46, -211, -32, -199, -203, -175, -8, -224, -115, -62, -98, -155, -146, -57, -101, -246, -196, -230, -13, -77, -251, -229, -233, -95, -19, -1, -176, -151, -182, -185, -180, -164, -126, -37, -36, -38, -39, -120, -18, -157, -116, -6, -17, -211, -30, -17, -113, -73, -27, -153, -87, -76, -97, -226, -157, -180, -200, -215, -209, -166, -198, -122, -171, -83, -110, -245, -1, -143, -122, -120, -181, -93, -125, -45, -240, -212, -245, -118, -156, -75, -158, -24, -227, -120, -224, -239, -250, -255, -70, -19, -5, -57, -86, -180, -167, -10, -23, -52, -105, -235, -240, -40, -98, -54, -114, -234, -114, -38, -240, -113, -2, -35, -24, -33, -123, -253, -127, -23, -202, -78, -80, -158, -149, -227, -35, -176, -127, -65, -2, -0, -153, -220, -86, -89, -92, -149, -233, -200, -206, -185, -135, -136, -173, -240, -38, -98, -218, -35, -34, -42, -20, -35, -243, -138, -41, -76, -188, -147, -150, -252, -216, -230, -228, -66, -121, -213, -150, -169, -237, -234, -107, -154, -167, -174, -183, -227, -92, -68, -184, -216, -112, -241, -179, -144, -175, -120, -169, -128, -113, -218, -173, -132, -40, -64, -175, -32, -183, -41, -120, -130, -0, -15, -199, -132, -126, -225, -186, -93, -254, -69, -255, -190, -57, -100, -224, -24, -99, -172, -239, -243, -233, -30, -18, -235, -86, -121, -119, -96, -63, -108, -128, -145, -103, -246, -204, -203, -122, -118, -133, -106, -120, -175, -212, -191, -62, -11, -193, -233, -250, -247, -233, -182, -0, -241, -28, -219, -215, -24, -243, -152, -49, -166, -184, -135, -92, -174, -208, -206, -197, -3, -250, -119, -221, -192, -126, -217, -120, -241, -119, -121, -234, -172, -146, -241, -156, -44, -203, -30, -242, -212, -63, -133, -44, -203, -30, -53, -198, -88, -251, -251, -218, -19, -133, -44, -203, -230, -24, -99, -78, -49, -198, -172, -105, -228, -190, -79, -200, -178, -44, -228, -136, -108, -145, -49, -102, -101, -99, -204, -22, -198, -152, -101, -140, -140, -181, -151, -100, -89, -118, -71, -64, -219, -132, -30, -225, -10, -128, -51, -140, -76, -178, -101, -141, -49, -135, -213, -180, -251, -137, -145, -1, -125, -179, -49, -230, -175, -158, -122, -155, -81, -40, -200, -218, -204, -24, -51, -75, -255, -250, -194, -28, -197, -228, -21, -83, -152, -216, -201, -181, -142, -49, -198, -32, -75, -232, -99, -140, -49, -75, -26, -99, -246, -203, -178, -172, -24, -74, -202, -78, -218, -251, -61, -188, -174, -214, -191, -161, -89, -100, -172, -64, -242, -153, -81, -199, -20, -114, -3, -200, -178, -236, -19, -89, -142, -111, -7, -182, -153, -145, -101, -217, -58, -89, -150, -45, -147, -101, -217, -74, -89, -150, -189, -61, -203, -178, -59, -3, -251, -151, -208, -35, -158, -18, -0, -89, -150, -205, -53, -198, -124, -220, -24, -179, -208, -24, -179, -43, -21, -105, -143, -140, -49, -187, -25, -99, -230, -26, -99, -62, -146, -101, -217, -66, -15, -223, -25, -250, -119, -57, -79, -157, -15, -85, -95, -199, -152, -188, -98, -10, -19, -59, -249, -236, -196, -217, -215, -200, -228, -59, -47, -203, -50, -95, -54, -88, -75, -119, -165, -167, -174, -237, -234, -107, -192, -175, -222, -196, -21, -114, -9, -139, -57, -70, -68, -94, -201, -178, -236, -28, -99, -204, -123, -141, -49, -79, -24, -99, -118, -49, -198, -76, -193, -73, -123, -100, -100, -201, -185, -171, -49, -230, -65, -99, -204, -142, -89, -150, -149, -125, -101, -218, -46, -105, -125, -95, -199, -152, -188, -98, -10, -147, -127, -232, -223, -29, -16, -141, -243, -119, -140, -8, -150, -189, -139, -132, -1, -91, -166, -54, -171, -175, -27, -77, -46, -56, -92, -196, -20, -114, -227, -2, -136, -157, -197, -41, -148, -28, -143, -33, -217, -129, -254, -136, -39, -100, -151, -214, -23, -209, -58, -200, -40, -18, -196, -117, -111, -224, -31, -192, -125, -136, -237, -197, -52, -228, -136, -239, -135, -56, -14, -113, -37, -237, -59, -185, -168, -211, -209, -249, -203, -50, -233, -148, -246, -136, -184, -10, -197, -152, -188, -98, -158, -116, -44, -67, -158, -196, -209, -42, -210, -6, -142, -4, -149, -182, -54, -83, -44, -205, -146, -78, -206, -162, -228, -152, -139, -136, -199, -185, -227, -5, -58, -232, -209, -231, -252, -101, -224, -6, -125, -39, -147, -145, -99, -189, -83, -181, -222, -39, -48, -125, -199, -138, -173, -130, -140, -34, -169, -207, -111, -119, -218, -77, -69, -20, -159, -55, -146, -91, -38, -86, -230, -112, -164, -163, -139, -58, -29, -157, -191, -162, -128, -136, -121, -212, -35, -243, -138, -38, -76, -180, -126, -123, -231, -197, -78, -7, -54, -45, -212, -55, -202, -20, -75, -120, -210, -201, -109, -43, -120, -68, -19, -114, -49, -64, -216, -241, -226, -234, -192, -33, -136, -127, -195, -92, -202, -225, -245, -55, -64, -162, -232, -252, -202, -161, -187, -29, -17, -162, -147, -157, -178, -83, -40, -113, -55, -118, -104, -90, -7, -25, -5, -94, -64, -46, -124, -255, -11, -108, -83, -168, -95, -11, -137, -16, -84, -155, -48, -148, -14, -46, -234, -68, -112, -254, -234, -12, -34, -230, -81, -143, -204, -43, -154, -48, -81, -154, -103, -146, -219, -3, -128, -56, -108, -220, -197, -160, -197, -93, -112, -166, -88, -6, -87, -95, -11, -201, -133, -204, -69, -212, -251, -40, -68, -21, -114, -129, -60, -90, -135, -144, -215, -251, -181, -95, -205, -219, -129, -99, -128, -163, -129, -91, -157, -231, -119, -55, -226, -33, -87, -122, -239, -228, -246, -242, -199, -49, -210, -97, -201, -90, -28, -126, -161, -162, -173, -69, -171, -32, -163, -74, -99, -45, -16, -175, -37, -66, -204, -126, -58, -186, -168, -143, -58, -136, -152, -71, -61, -22, -47, -34, -10, -19, -165, -57, -70, -105, -14, -71, -246, -138, -255, -209, -151, -22, -37, -83, -172, -115, -29, -43, -184, -102, -2, -149, -186, -16, -34, -11, -185, -192, -254, -29, -71, -203, -16, -242, -192, -79, -245, -250, -255, -194, -49, -224, -65, -246, -237, -103, -107, -93, -109, -48, -76, -100, -245, -48, -3, -88, -190, -80, -158, -33, -1, -54, -7, -220, -130, -29, -26, -139, -86, -65, -70, -201, -45, -63, -33, -32, -136, -232, -132, -1, -17, -243, -168, -199, -226, -69, -92, -193, -180, -2, -146, -66, -189, -215, -220, -121, -200, -18, -215, -46, -103, -47, -5, -158, -94, -65, -27, -85, -200, -5, -246, -175, -117, -8, -121, -114, -147, -224, -29, -61, -117, -111, -214, -186, -255, -5, -244, -97, -54, -37, -201, -57, -144, -85, -209, -204, -138, -182, -22, -173, -130, -140, -34, -43, -22, -40, -49, -227, -29, -22, -144, -109, -198, -65, -136, -192, -155, -129, -8, -247, -255, -34, -65, -74, -26, -101, -75, -50, -192, -243, -145, -101, -213, -165, -200, -87, -109, -46, -98, -111, -125, -35, -98, -6, -250, -57, -28, -39, -138, -10, -62, -209, -242, -168, -199, -226, -69, -68, -193, -52, -44, -32, -49, -16, -173, -91, -241, -5, -84, -36, -153, -32, -162, -144, -11, -236, -91, -107, -231, -20, -242, -61, -255, -102, -158, -186, -205, -180, -110, -150, -175, -109, -129, -246, -118, -68, -219, -62, -32, -28, -17, -13, -252, -109, -21, -109, -67, -4, -64, -105, -144, -81, -157, -112, -0, -223, -173, -235, -103, -9, -239, -5, -250, -111, -243, -10, -154, -45, -108, -39, -75, -234, -95, -69, -110, -101, -57, -11, -9, -117, -126, -13, -249, -202, -229, -30, -42, -2, -210, -184, -140, -150, -71, -150, -116, -118, -223, -185, -0, -89, -94, -93, -142, -104, -87, -109, -148, -88, -232, -224, -49, -53, -218, -32, -162, -96, -26, -22, -16, -51, -95, -27, -101, -168, -50, -172, -58, -227, -68, -200, -145, -231, -196, -27, -176, -68, -36, -79, -206, -225, -59, -210, -45, -210, -218, -40, -58, -199, -48, -210, -149, -214, -78, -220, -210, -12, -63, -206, -243, -168, -18, -0, -165, -65, -70, -157, -231, -252, -209, -186, -126, -118, -184, -126, -169, -0, -64, -20, -125, -246, -68, -234, -199, -56, -66, -16, -153, -207, -214, -235, -241, -14, -10, -91, -164, -34, -163, -21, -201, -195, -33, -61, -12, -124, -134, -194, -210, -1, -89, -98, -110, -143, -68, -62, -45, -93, -138, -38, -244, -3, -96, -23, -2, -61, -240, -232, -81, -200, -21, -132, -72, -41, -159, -170, -129, -171, -245, -214, -49, -104, -50, -78, -0, -77, -36, -169, -198, -127, -237, -164, -174, -233, -75, -230, -240, -1, -249, -96, -157, -236, -180, -135, -138, -175, -115, -224, -4, -44, -13, -50, -74, -67, -103, -174, -150, -215, -175, -18, -0, -214, -255, -165, -204, -147, -48, -67, -20, -211, -0, -159, -173, -234, -136, -117, -150, -185, -135, -228, -168, -145, -80, -1, -70, -162, -42, -225, -103, -157, -0, -88, -93, -39, -44, -136, -128, -186, -65, -133, -129, -221, -26, -76, -193, -73, -217, -85, -194, -227, -56, -165, -253, -13, -162, -140, -189, -17, -89, -6, -223, -164, -191, -109, -236, -127, -239, -185, -122, -224, -4, -252, -168, -210, -12, -4, -25, -37, -63, -254, -251, -112, -85, -63, -43, -120, -119, -21, -0, -151, -106, -213, -167, -42, -218, -127, -86, -105, -46, -40, -35, -120, -137, -211, -145, -237, -189, -68, -9, -9, -10, -29, -39, -139, -16, -101, -211, -92, -10, -49, -7, -28, -186, -74, -1, -160, -52, -171, -146, -123, -125, -46, -64, -244, -18, -55, -35, -231, -230, -171, -5, -244, -229, -173, -192, -31, -40, -183, -4, -92, -22, -73, -178, -234, -141, -242, -27, -56, -1, -171, -130, -140, -90, -219, -139, -218, -4, -174, -29, -174, -95, -37, -0, -108, -44, -132, -82, -215, -108, -224, -141, -101, -2, -204, -18, -28, -166, -4, -215, -182, -185, -137, -16, -208, -33, -149, -23, -49, -76, -28, -19, -162, -193, -25, -180, -223, -210, -191, -139, -86, -90, -155, -0, -0, -32, -0, -73, -68, -65, -84, -222, -140, -196, -129, -2, -224, -215, -136, -206, -105, -167, -254, -122, -92, -142, -192, -9, -88, -21, -100, -212, -218, -94, -84, -166, -18, -239, -120, -253, -42, -1, -96, -149, -195, -85, -39, -86, -175, -84, -154, -1, -119, -116, -75, -96, -247, -254, -223, -111, -115, -19, -33, -160, -67, -42, -47, -122, -48, -113, -108, -34, -144, -234, -94, -18, -178, -207, -178, -3, -225, -34, -156, -56, -243, -139, -35, -156, -231, -241, -108, -242, -0, -40, -3, -105, -189, -3, -5, -192, -44, -228, -200, -106, -83, -106, -50, -3, -247, -129, -128, -119, -91, -25, -100, -20, -120, -175, -214, -45, -4, -158, -23, -251, -250, -74, -83, -37, -0, -108, -164, -160, -170, -24, -150, -219, -41, -205, -64, -64, -154, -34, -147, -221, -155, -222, -64, -8, -232, -152, -202, -139, -30, -76, -28, -105, -32, -144, -2, -6, -137, -53, -104, -249, -175, -237, -99, -5, -143, -78, -138, -51, -15, -253, -174, -74, -94, -155, -89, -55, -128, -215, -55, -156, -126, -174, -82, -65, -103, -177, -62, -185, -130, -236, -6, -10, -203, -240, -144, -123, -97, -164, -25, -175, -139, -25, -136, -113, -85, -109, -134, -223, -46, -168, -122, -183, -4, -4, -25, -69, -140, -150, -172, -53, -227, -165, -64, -168, -179, -89, -237, -245, -29, -154, -42, -1, -112, -145, -86, -149, -42, -248, -144, -99, -123, -128, -139, -203, -8, -236, -50, -194, -171, -201, -44, -121, -65, -193, -241, -210, -137, -148, -202, -43, -22, -104, -40, -144, -106, -6, -137, -53, -55, -189, -3, -40, -117, -171, -45, -60, -186, -214, -138, -179, -2, -173, -59, -248, -158, -164, -194, -62, -32, -128, -151, -107, -107, -0, -97, -2, -96, -83, -253, -125, -158, -254, -254, -92, -129, -174, -78, -9, -248, -2, -68, -121, -103, -147, -97, -62, -162, -255, -30, -103, -100, -98, -149, -248, -113, -240, -6, -239, -165, -117, -144, -81, -36, -196, -184, -85, -92, -94, -173, -109, -151, -40, -208, -108, -4, -236, -27, -114, -125, -15, -77, -149, -0, -176, -153, -150, -188, -222, -185, -140, -60, -5, -248, -82, -217, -5, -172, -34, -225, -35, -37, -245, -151, -56, -255, -174, -45, -235, -204, -120, -1, -13, -5, -82, -217, -75, -34, -15, -142, -249, -32, -37, -138, -176, -2, -143, -40, -138, -51, -135, -246, -51, -74, -106, -99, -244, -253, -40, -228, -126, -60, -124, -150, -67, -180, -230, -247, -146, -159, -41, -135, -8, -128, -23, -235, -239, -23, -35, -10, -188, -199, -113, -194, -164, -215, -12, -220, -237, -17, -205, -255, -21, -192, -11, -61, -245, -43, -146, -39, -30, -237, -197, -89, -169, -112, -47, -93, -131, -140, -190, -9, -57, -102, -181, -120, -20, -49, -198, -185, -66, -199, -90, -217, -115, -240, -142, -173, -2, -77, -213, -115, -92, -158, -252, -35, -240, -115, -156, -179, -126, -125, -175, -246, -136, -244, -94, -74, -86, -167, -6, -152, -164, -68, -7, -4, -220, -104, -163, -37, -106, -27, -32, -103, -216, -118, -223, -95, -154, -117, -6, -217, -159, -129, -76, -168, -129, -248, -252, -17, -251, -51, -240, -146, -144, -189, -223, -66, -228, -203, -235, -205, -250, -83, -194, -227, -91, -250, -183, -181, -226, -76, -233, -86, -68, -4, -207, -253, -200, -82, -245, -106, -228, -75, -245, -172, -22, -247, -247, -11, -189, -228, -62, -206, -96, -13, -17, -0, -238, -243, -56, -82, -203, -142, -114, -202, -170, -6, -174, -61, -254, -219, -164, -226, -58, -171, -42, -77, -111, -238, -172, -12, -162, -85, -144, -81, -229, -181, -18, -226, -130, -124, -62, -178, -181, -156, -175, -255, -30, -68, -44, -56, -7, -188, -52, -125, -207, -210, -67, -83, -183, -146, -122, -33, -185, -11, -243, -108, -68, -240, -216, -241, -128, -94, -191, -60, -2, -51, -249, -126, -216, -191, -71, -8, -236, -12, -121, -134, -84, -128, -15, -86, -240, -120, -151, -210, -60, -89, -54, -208, -200, -205, -85, -171, -150, -203, -7, -41, -205, -169, -101, -52, -49, -80, -124, -73, -136, -82, -101, -46, -34, -164, -182, -109, -200, -163, -179, -226, -76, -233, -236, -209, -212, -167, -244, -247, -219, -245, -247, -145, -13, -239, -109, -7, -109, -247, -111, -196, -239, -192, -42, -70, -155, -10, -128, -53, -144, -21, -192, -83, -102, -173, -53, -99, -197, -46, -153, -253, -95, -37, -243, -212, -22, -1, -96, -177, -141, -31, -232, -60, -239, -82, -171, -76, -224, -13, -74, -227, -11, -78, -99, -105, -86, -65, -236, -254, -255, -139, -40, -85, -103, -34, -102, -202, -223, -163, -238, -227, -232, -92, -0, -106, -150, -197, -117, -3, -148, -60, -67, -170, -215, -57, -67, -105, -46, -86, -154, -3, -43, -104, -182, -81, -26, -111, -230, -89, -100, -159, -102, -67, -96, -15, -76, -36, -165, -137, -178, -146, -112, -7, -60, -226, -170, -57, -29, -249, -250, -15, -36, -113, -168, -184, -134, -69, -12, -197, -217, -154, -218, -135, -187, -112, -252, -220, -17, -201, -63, -143, -64, -67, -46, -196, -16, -231, -126, -229, -181, -161, -150, -89, -115, -239, -70, -2, -64, -203, -173, -178, -233, -124, -253, -253, -162, -178, -123, -33, -223, -95, -123, -93, -117, -145, -252, -5, -151, -43, -205, -183, -67, -238, -103, -60, -130, -124, -245, -253, -241, -10, -26, -155, -197, -168, -116, -78, -197, -232, -200, -191, -157, -65, -89, -234, -74, -90, -55, -64, -25, -185, -10, -24, -8, -195, -68, -158, -108, -114, -58, -53, -74, -43, -100, -95, -10, -254, -0, -31, -246, -203, -53, -133, -138, -227, -35, -34, -172, -36, -156, -1, -191, -3, -185, -208, -241, -133, -247, -170, -186, -23, -139, -78, -138, -51, -165, -177, -246, -221, -123, -22, -202, -173, -253, -124, -80, -54, -89, -114, -47, -183, -61, -156, -50, -171, -124, -107, -35, -0, -150, -210, -241, -3, -240, -30, -36, -194, -141, -247, -94, -16, -197, -153, -93, -162, -254, -27, -217, -171, -254, -16, -248, -37, -162, -217, -182, -10, -201, -83, -41, -9, -230, -177, -56, -128, -60, -148, -252, -85, -248, -79, -25, -50, -242, -172, -65, -223, -235, -179, -35, -27, -146, -31, -7, -222, -141, -56, -64, -12, -228, -109, -67, -60, -143, -234, -6, -168, -93, -5, -252, -203, -83, -103, -39, -100, -173, -194, -10, -248, -130, -210, -14, -4, -192, -68, -60, -221, -160, -230, -248, -139, -56, -43, -9, -139, -251, -116, -130, -44, -212, -191, -193, -137, -49, -28, -30, -173, -21, -103, -90, -191, -33, -242, -149, -190, -181, -120, -63, -206, -96, -89, -72, -141, -247, -23, -185, -121, -235, -31, -11, -229, -22, -141, -5, -128, -214, -89, -55, -222, -41, -228, -57, -0, -203, -238, -101, -19, -100, -251, -121, -51, -34, -12, -22, -32, -219, -194, -73, -136, -99, -207, -152, -241, -200, -236, -11, -136, -223, -131, -13, -44, -115, -56, -206, -156, -67, -244, -60, -86, -183, -242, -8, -53, -102, -209, -49, -58, -243, -60, -242, -37, -9, -58, -208, -110, -38, -207, -229, -126, -47, -206, -241, -76, -5, -31, -119, -21, -176, -165, -83, -190, -14, -34, -217, -103, -81, -227, -43, -174, -244, -171, -35, -75, -248, -249, -56, -171, -18, -68, -57, -52, -71, -7, -250, -122, -1, -124, -58, -173, -36, -156, -231, -49, -21, -73, -189, -101, -87, -12, -247, -16, -232, -107, -237, -155, -52, -52, -84, -156, -105, -189, -141, -80, -227, -77, -37, -134, -228, -5, -4, -143, -208, -116, -104, -214, -215, -65, -119, -63, -133, -85, -152, -211, -207, -86, -2, -64, -235, -255, -162, -245, -7, -215, -141, -149, -97, -129, -6, -49, -15, -128, -79, -59, -247, -88, -233, -121, -25, -3, -58, -166, -108, -112, -22, -87, -137, -103, -221, -121, -31, -167, -16, -102, -172, -207, -206, -44, -1, -188, -31, -89, -122, -77, -33, -151, -204, -51, -144, -227, -134, -115, -244, -197, -122, -163, -172, -58, -124, -236, -42, -192, -205, -40, -107, -7, -196, -79, -27, -244, -231, -183, -218, -102, -63, -167, -236, -83, -90, -118, -118, -85, -91, -135, -190, -211, -74, -194, -25, -12, -175, -210, -223, -75, -147, -71, -112, -13, -50, -70, -242, -77, -26, -154, -43, -206, -182, -68, -4, -240, -13, -148, -11, -171, -140, -92, -136, -15, -28, -95, -233, -251, -189, -72, -249, -248, -148, -144, -22, -93, -4, -192, -198, -228, -74, -82, -239, -189, -244, -1, -58, -132, -39, -115, -120, -188, -72, -199, -250, -52, -253, -55, -3, -120, -65, -191, -61, -55, -6, -217, -46, -253, -28, -57, -29, -153, -173, -215, -189, -30, -217, -34, -4, -127, -249, -233, -24, -89, -56, -26, -200, -87, -1, -11, -144, -101, -235, -242, -200, -249, -234, -28, -224, -217, -13, -248, -216, -60, -238, -215, -59, -101, -246, -38, -223, -27, -200, -163, -211, -74, -162, -100, -242, -110, -66, -46, -181, -107, -61, -193, -202, -38, -13, -205, -20, -103, -255, -12, -185, -111, -100, -255, -13, -112, -174, -167, -206, -250, -200, -123, -67, -144, -59, -253, -108, -45, -0, -148, -230, -71, -14, -221, -176, -4, -192, -113, -180, -12, -79, -166, -237, -215, -66, -162, -20, -77, -3, -94, -13, -188, -14, -153, -136, -119, -16, -176, -98, -29, -11, -160, -99, -100, -225, -216, -157, -177, -171, -128, -35, -200, -151, -85, -71, -213, -183, -28, -224, -99, -61, -174, -94, -138, -216, -140, -131, -196, -44, -8, -86, -14, -209, -97, -37, -81, -54, -224, -201, -181, -179, -79, -226, -248, -179, -55, -228, -17, -172, -56, -139, -1, -242, -227, -183, -91, -116, -114, -20, -255, -89, -88, -215, -92, -95, -88, -116, -239, -189, -20, -104, -86, -162, -194, -0, -166, -15, -208, -33, -60, -153, -182, -185, -10, -89, -245, -190, -216, -41, -123, -41, -114, -218, -226, -203, -196, -52, -38, -65, -135, -200, -194, -177, -59, -98, -87, -1, -179, -244, -193, -206, -175, -155, -40, -37, -124, -172, -185, -227, -129, -228, -246, -234, -63, -110, -200, -163, -245, -74, -162, -106, -192, -3, -103, -105, -221, -37, -84, -236, -51, -107, -120, -4, -43, -206, -186, -130, -22, -232, -163, -31, -37, -125, -235, -20, -215, -142, -14, -225, -201, -22, -55, -48, -86, -34, -11, -147, -175, -2, -0, -126, -221, -146, -199, -106, -200, -190, -200, -42, -35, -161, -197, -190, -140, -150, -43, -137, -154, -201, -187, -38, -249, -146, -235, -235, -109, -120, -104, -253, -152, -80, -156, -57, -253, -44, -221, -2, -244, -116, -221, -56, -113, -237, -18, -226, -129, -14, -62, -252, -14, -143, -85, -156, -65, -245, -234, -14, -125, -57, -17, -57, -149, -88, -4, -92, -214, -146, -71, -171, -149, -68, -192, -228, -125, -155, -214, -207, -167, -100, -201, -21, -192, -99, -84, -20, -103, -158, -126, -12, -93, -0, -16, -41, -174, -29, -221, -87, -16, -209, -34, -235, -34, -167, -103, -181, -122, -135, -26, -30, -157, -231, -95, -39, -208, -193, -135, -191, -192, -167, -114, -240, -7, -242, -216, -202, -225, -243, -177, -150, -60, -90, -173, -36, -66, -250, -143, -232, -57, -64, -246, -214, -3, -131, -52, -144, -199, -208, -21, -103, -158, -62, -140, -134, -0, -232, -28, -215, -142, -142, -43, -136, -174, -237, -61, -252, -126, -5, -124, -38, -148, -190, -132, -71, -163, -249, -71, -228, -208, -224, -157, -124, -248, -11, -188, -106, -7, -127, -0, -15, -187, -55, -158, -142, -199, -64, -169, -1, -159, -206, -43, -137, -150, -215, -13, -17, -0, -67, -87, -156, -121, -250, -208, -73, -0, -208, -34, -65, -38, -29, -227, -218, -209, -113, -5, -209, -181, -125, -31, -104, -58, -255, -136, -189, -133, -34, -162, -15, -127, -36, -1, -96, -253, -238, -143, -237, -216, -151, -206, -43, -137, -4, -63, -104, -153, -32, -147, -142, -113, -237, -232, -184, -130, -232, -218, -190, -15, -52, -153, -127, -99, -81, -128, -21, -59, -216, -73, -0, -32, -71, -101, -119, -42, -143, -114, -183, -198, -48, -94, -81, -86, -18, -9, -35, -65, -135, -4, -153, -116, -140, -107, -71, -247, -21, -68, -140, -21, -72, -40, -46, -41, -187, -70, -91, -208, -82, -128, -141, -139, -35, -19, -68, -67, -127, -168, -49, -102, -125, -99, -204, -133, -89, -150, -117, -61, -143, -181, -38, -180, -167, -100, -89, -54, -163, -35, -175, -4, -35, -3, -204, -24, -115, -130, -49, -102, -101, -99, -204, -117, -198, -152, -173, -179, -44, -27, -225, -186, -154, -101, -217, -131, -198, -24, -111, -10, -117, -99, -204, -52, -99, -204, -106, -198, -152, -42, -129, -188, -162, -67, -91, -132, -205, -204, -124, -99, -69, -123, -123, -252, -235, -91, -6, -119, -109, -191, -192, -24, -19, -154, -26, -172, -52, -79, -97, -7, -216, -128, -170, -222, -116, -112, -89, -150, -129, -56, -136, -189, -220, -24, -243, -110, -99, -204, -207, -70, -16, -16, -33, -61, -81, -21, -28, -233, -23, -180, -2, -64, -246, -194, -215, -171, -212, -178, -251, -154, -39, -232, -152, -175, -128, -136, -43, -137, -132, -28, -116, -76, -144, -73, -199, -184, -118, -116, -95, -65, -116, -143, -172, -27, -1, -180, -84, -226, -209, -53, -52, -120, -200, -4, -237, -34, -0, -154, -2, -73, -188, -121, -47, -185, -2, -233, -76, -2, -114, -17, -214, -240, -92, -134, -60, -60, -210, -5, -145, -186, -106, -121, -23, -49, -23, -217, -147, -253, -3, -248, -24, -53, -6, -42, -228, -129, -39, -75, -51, -225, -146, -251, -51, -84, -126, -105, -104, -17, -66, -189, -235, -251, -167, -99, -130, -76, -58, -198, -181, -163, -99, -100, -220, -174, -237, -99, -128, -14, -74, -60, -186, -10, -176, -174, -3, -96, -172, -130, -158, -86, -18, -158, -235, -88, -216, -184, -114, -87, -58, -215, -3, -9, -128, -82, -26, -45, -150, -220, -142, -224, -94, -252, -57, -232, -151, -32, -87, -174, -13, -228, -209, -43, -208, -54, -14, -161, -222, -245, -253, -211, -61, -65, -102, -167, -184, -118, -116, -95, -65, -116, -143, -172, -59, -72, -255, -107, -2, -143, -1, -233, -126, -138, -209, -77, -128, -117, -29, -0, -99, -21, -244, -176, -146, -40, -185, -206, -192, -243, -67, -38, -237, -174, -228, -19, -242, -59, -21, -237, -109, -30, -123, -240, -231, -160, -183, -2, -226, -22, -106, -98, -231, -211, -34, -132, -122, -215, -247, -79, -199, -4, -153, -202, -163, -117, -92, -59, -186, -175, -32, -186, -71, -214, -29, -108, -115, -51, -112, -116, -32, -109, -215, -83, -140, -110, -2, -172, -235, -0, -152, -232, -168, -122, -126, -192, -207, -180, -174, -74, -193, -100, -128, -15, -41, -221, -149, -158, -186, -127, -104, -221, -39, -98, -246, -219, -225, -223, -233, -253, -211, -49, -65, -166, -195, -167, -85, -92, -59, -186, -175, -32, -186, -71, -214, -237, -118, -223, -93, -79, -33, -186, -9, -176, -174, -3, -192, -211, -153, -115, -129, -29, -156, -178, -231, -33, -174, -154, -222, -32, -22, -227, -29, -85, -207, -15, -137, -173, -0, -48, -167, -134, -135, -171, -160, -220, -202, -41, -223, -4, -49, -90, -122, -136, -158, -50, -50, -119, -125, -255, -116, -76, -144, -25, -3, -116, -140, -140, -219, -181, -125, -199, -190, -119, -181, -131, -232, -38, -192, -186, -14, -128, -2, -221, -1, -228, -246, -203, -23, -35, -89, -135, -23, -34, -231, -238, -223, -174, -106, -235, -240, -24, -106, -68, -150, -174, -168, -122, -126, -72, -104, -105, -0, -223, -241, -85, -145, -214, -222, -247, -31, -157, -50, -251, -242, -170, -2, -150, -116, -58, -197, -233, -250, -254, -233, -152, -32, -51, -22, -232, -18, -25, -55, -66, -251, -14, -253, -238, -124, -10, -65, -23, -1, -214, -117, -0, -120, -104, -119, -33, -247, -59, -7, -177, -189, -175, -205, -244, -170, -109, -71, -37, -34, -75, -23, -84, -61, -63, -36, -200, -37, -4, -68, -47, -66, -226, -251, -63, -132, -8, -204, -231, -34, -49, -225, -166, -233, -75, -172, -90, -2, -119, -157, -192, -93, -219, -119, -74, -144, -57, -209, -65, -164, -83, -8, -218, -10, -176, -88, -2, -0, -216, -131, -60, -28, -213, -125, -136, -115, -203, -44, -100, -9, -91, -27, -189, -135, -150, -17, -89, -136, -247, -5, -12, -66, -69, -251, -162, -18, -112, -119, -68, -186, -47, -162, -66, -186, -23, -120, -89, -147, -231, -159, -144, -103, -254, -169, -140, -236, -210, -245, -253, -69, -184, -255, -78, -9, -50, -39, -58, -232, -225, -20, -34, -4, -125, -88, -2, -46, -105, -140, -153, -103, -140, -217, -205, -136, -165, -221, -124, -36, -212, -213, -89, -198, -152, -223, -0, -87, -102, -89, -54, -165, -162, -253, -95, -141, -49, -153, -49, -102, -171, -44, -203, -38, -25, -99, -12, -176, -181, -49, -230, -116, -173, -43, -219, -131, -217, -64, -28, -3, -97, -149, -3, -225, -211, -190, -190, -86, -255, -94, -103, -140, -153, -25, -200, -231, -56, -96, -166, -49, -102, -89, -99, -204, -6, -70, -172, -219, -102, -24, -99, -246, -200, -178, -204, -171, -160, -241, -224, -23, -198, -152, -47, -25, -99, -62, -102, -140, -121, -208, -24, -179, -200, -24, -211, -40, -248, -73, -7, -84, -221, -235, -242, -198, -152, -50, -1, -115, -134, -49, -230, -14, -35, -247, -124, -28, -176, -125, -150, -101, -179, -122, -232, -223, -226, -138, -63, -25, -99, -94, -103, -140, -249, -160, -41, -90, -233, -25, -81, -226, -105, -157, -49, -198, -252, -37, -250, -213, -187, -126, -65, -2, -248, -31, -170, -77, -91, -103, -243, -173, -225, -31, -189, -255, -33, -60, -61, -180, -69, -60, -66, -96, -96, -70, -242, -48, -221, -85, -240, -234, -67, -186, -222, -127, -140, -231, -71, -135, -4, -153, -19, -29, -140, -242, -41, -132, -33, -82, -122, -162, -138, -182, -171, -144, -159, -21, -15, -229, -28, -222, -67, -51, -12, -1, -224, -110, -1, -206, -215, -178, -61, -170, -218, -58, -244, -59, -50, -50, -9, -171, -197, -181, -78, -153, -55, -19, -81, -215, -251, -143, -245, -252, -104, -153, -32, -179, -41, -144, -60, -123, -219, -118, -228, -241, -44, -228, -136, -246, -118, -196, -86, -227, -17, -36, -188, -155, -55, -55, -132, -182, -9, -70, -139, -254, -140, -218, -41, -132, -33, -82, -122, -34, -224, -53, -136, -214, -255, -110, -228, -107, -48, -93, -95, -254, -231, -145, -68, -15, -0, -159, -247, -180, -27, -85, -45, -118, -9, -125, -45, -207, -42, -90, -224, -37, -200, -158, -248, -65, -90, -72, -237, -174, -215, -247, -208, -12, -229, -249, -209, -34, -65, -102, -83, -144, -107, -205, -91, -9, -2, -196, -215, -222, -42, -222, -166, -33, -147, -237, -86, -231, -57, -120, -141, -182, -24, -41, -160, -203, -254, -61, -25, -58, -198, -60, -252, -71, -229, -20, -194, -16, -33, -61, -17, -240, -85, -242, -164, -33, -211, -148, -151, -43, -193, -30, -208, -191, -255, -240, -180, -29, -51, -3, -184, -9, -207, -58, -90, -36, -42, -12, -192, -192, -190, -206, -195, -99, -92, -111, -1, -74, -218, -188, -4, -249, -154, -109, -90, -79, -29, -14, -196, -165, -251, -23, -228, -99, -43, -88, -16, -32, -2, -234, -30, -109, -247, -45, -70, -230, -86, -220, -1, -249, -104, -65, -131, -172, -79, -5, -254, -151, -52, -121, -70, -99, -2, -116, -76, -79, -68, -126, -68, -177, -200, -243, -80, -159, -142, -8, -7, -139, -123, -60, -237, -199, -220, -0, -14, -225, -89, -71, -11, -60, -83, -7, -212, -2, -60, -145, -112, -10, -180, -227, -126, -11, -224, -105, -243, -7, -29, -19, -231, -19, -89, -8, -40, -255, -53, -17, -239, -57, -107, -136, -84, -43, -8, -128, -47, -42, -173, -55, -115, -18, -240, -93, -173, -255, -83, -203, -62, -141, -63, -1, -96, -204, -83, -3, -176, -85, -122, -34, -242, -208, -216, -165, -81, -127, -129, -19, -44, -111, -79, -221, -152, -27, -192, -33, -60, -67, -104, -129, -175, -105, -221, -69, -33, -215, -141, -125, -125, -135, -102, -52, -4, -192, -187, -17, -223, -4, -128, -107, -67, -219, -53, -5, -242, -85, -223, -159, -124, -149, -89, -42, -8, -128, -11, -149, -198, -107, -181, -136, -232, -49, -0, -166, -182, -236, -203, -248, -20, -0, -198, -24, -67, -203, -244, -68, -72, -104, -109, -128, -247, -87, -208, -188, -85, -105, -230, -122, -234, -198, -220, -0, -142, -53, -1, -145, -21, -208, -93, -90, -255, -65, -95, -251, -62, -175, -239, -208, -12, -93, -0, -56, -109, -47, -5, -158, -104, -218, -174, -197, -117, -150, -5, -62, -129, -56, -67, -129, -223, -110, -222, -245, -210, -172, -194, -194, -150, -125, -24, -55, -2, -96, -192, -14, -32, -203, -178, -59, -141, -49, -109, -76, -111, -173, -146, -235, -222, -10, -154, -39, -245, -239, -99, -45, -248, -143, -91, -100, -89, -54, -27, -241, -243, -255, -157, -49, -230, -71, -192, -153, -197, -104, -57, -17, -48, -211, -200, -57, -125, -85, -228, -87, -107, -137, -57, -148, -40, -72, -136, -99, -16, -70, -108, -7, -182, -52, -198, -252, -97, -24, -215, -213, -107, -90, -248, -140, -146, -236, -88, -253, -175, -233, -231, -89, -124, -222, -24, -51, -16, -80, -213, -17, -10, -27, -103, -89, -118, -91, -27, -198, -192, -134, -198, -152, -111, -24, -99, -182, -55, -198, -172, -110, -196, -78, -228, -76, -99, -204, -1, -89, -150, -61, -220, -174, -187, -17, -64, -190, -244, -218, -165, -130, -230, -195, -74, -243, -119, -79, -221, -152, -251, -130, -133, -240, -28, -43, -32, -210, -41, -78, -228, -62, -205, -64, -180, -245, -83, -144, -227, -182, -222, -130, -81, -34, -91, -128, -47, -57, -227, -112, -10, -176, -15, -176, -172, -135, -214, -58, -222, -148, -154, -221, -246, -212, -71, -139, -141, -90, -182, -127, -41, -185, -158, -110, -6, -146, -241, -218, -42, -65, -239, -35, -32, -75, -118, -111, -32, -79, -89, -253, -199, -10, -154, -191, -41, -205, -94, -158, -186, -174, -2, -32, -186, -29, -195, -56, -19, -0, -157, -79, -113, -198, -35, -16, -37, -224, -247, -200, -109, -76, -110, -70, -204, -175, -75, -173, -92, -201, -93, -111, -191, -48, -228, -190, -182, -22, -0, -136, -89, -185, -77, -113, -127, -2, -42, -76, -129, -149, -17, -69, -43, -104, -114, -217, -81, -1, -114, -220, -99, -207, -102, -191, -138, -35, -121, -145, -125, -217, -119, -156, -23, -52, -224, -210, -26, -65, -0, -68, -255, -2, -142, -51, -1, -208, -233, -20, -103, -188, -1, -57, -6, -252, -57, -249, -23, -112, -18, -226, -118, -93, -25, -44, -69, -219, -238, -167, -109, -174, -199, -19, -125, -169, -47, -116, -20, -0, -111, -215, -182, -247, -2, -79, -43, -212, -45, -79, -158, -154, -238, -85, -49, -59, -124, -24, -13, -98, -160, -3, -31, -32, -63, -49, -152, -129, -156, -32, -92, -77, -254, -117, -190, -7, -120, -97, -73, -219, -174, -2, -32, -250, -23, -112, -60, -9, -0, -99, -186, -157, -226, -140, -55, -144, -127, -108, -174, -4, -222, -233, -123, -231, -21, -109, -87, -64, -182, -8, -32, -17, -162, -54, -44, -212, -63, -7, -81, -36, -238, -80, -198, -163, -101, -159, -187, -8, -0, -43, -192, -189, -246, -36, -228, -89, -169, -126, -208, -189, -167, -57, -83, -104, -24, -195, -28, -9, -252, -113, -4, -249, -41, -130, -141, -106, -250, -29, -186, -231, -155, -175, -18, -0, -209, -191, -128, -93, -5, -0, -34, -248, -162, -41, -153, -144, -124, -134, -223, -167, -58, -55, -94, -171, -83, -156, -241, -6, -196, -123, -238, -205, -29, -218, -111, -70, -126, -50, -3, -242, -5, -157, -66, -110, -4, -4, -145, -227, -80, -56, -124, -159, -131, -28, -143, -90, -171, -65, -155, -190, -252, -45, -21, -109, -255, -163, -109, -119, -43, -169, -223, -93, -235, -207, -139, -221, -225, -232, -73, -12, -74, -174, -213, -121, -15, -79, -228, -47, -96, -4, -1, -112, -59, -208, -72, -219, -203, -96, -88, -232, -39, -245, -30, -190, -133, -44, -119, -103, -0, -207, -106, -211, -159, -190, -129, -68, -93, -62, -7, -103, -27, -230, -43, -107, -200, -115, -91, -34, -71, -112, -118, -120, -175, -12, -124, -5, -89, -69, -76, -67, -140, -181, -30, -211, -201, -246, -157, -216, -2, -211, -25, -79, -63, -212, -191, -247, -1, -151, -147, -7, -3, -5, -143, -153, -188, -182, -181, -202, -77, -239, -252, -32, -143, -22, -52, -96, -100, -215, -181, -195, -195, -18, -0, -177, -124, -17, -162, -125, -1, -35, -8, -128, -127, -211, -32, -76, -54, -254, -176, -208, -147, -28, -1, -6, -112, -112, -155, -190, -12, -3, -120, -150, -169, -190, -178, -64, -94, -219, -34, -198, -60, -208, -99, -28, -254, -97, -194, -121, -135, -115, -113, -98, -99, -32, -219, -83, -27, -247, -97, -30, -158, -120, -10, -228, -186, -14, -111, -206, -5, -224, -101, -90, -31, -239, -88, -115, -200, -2, -96, -92, -107, -177, -129, -53, -144, -200, -174, -155, -57, -101, -167, -227, -152, -147, -34, -169, -179, -190, -136, -39, -203, -43, -213, -97, -161, -63, -224, -12, -158, -41, -140, -193, -116, -102, -228, -71, -188, -215, -160, -10, -96, -95, -89, -0, -31, -119, -226, -207, -70, -236, -253, -215, -239, -181, -243, -67, -130, -243, -14, -189, -193, -93, -200, -79, -201, -14, -241, -212, -217, -188, -138, -155, -149, -180, -125, -177, -214, -183, -50, -94, -50, -192, -15, -8, -199, -59, -91, -93, -164, -250, -250, -227, -90, -139, -141, -248, -194, -91, -220, -141, -164, -117, -190, -88, -255, -29, -73, -174, -116, -2, -216, -201, -211, -222, -27, -22, -26, -241, -37, -176, -86, -150, -118, -25, -232, -93, -38, -142, -22, -128, -205, -145, -47, -212, -116, -244, -235, -229, -43, -171, -225, -225, -78, -252, -39, -16, -93, -199, -154, -253, -247, -126, -120, -112, -222, -255, -219, -74, -234, -109, -236, -200, -171, -61, -117, -118, -107, -91, -183, -2, -8, -13, -92, -51, -192, -224, -19, -12, -186, -53, -162, -147, -178, -88, -254, -186, -138, -155, -11, -66, -73, -31, -198, -173, -22, -27, -17, -0, -255, -208, -254, -22, -149, -73, -211, -181, -236, -106, -100, -63, -60, -224, -111, -142, -39, -44, -52, -178, -234, -177, -225, -192, -143, -5, -246, -213, -255, -15, -100, -215, -85, -250, -205, -41, -28, -17, -57, -117, -75, -81, -227, -75, -142, -104, -199, -191, -174, -207, -221, -26, -241, -60, -140, -8, -177, -239, -80, -208, -150, -107, -155, -149, -200, -205, -110, -63, -92, -86, -86, -113, -77, -119, -226, -79, -69, -4, -225, -74, -85, -109, -198, -43, -156, -241, -80, -246, -21, -127, -189, -214, -15, -152, -76, -35, -250, -2, -168, -215, -1, -60, -16, -187, -195, -65, -91, -0, -36, -40, -100, -240, -191, -10, -62, -139, -133, -22, -27, -145, -230, -243, -16, -79, -184, -218, -88, -254, -228, -214, -105, -175, -119, -202, -108, -172, -247, -91, -144, -179, -222, -29, -244, -183, -47, -44, -244, -11, -17, -69, -214, -63, -138, -66, -0, -88, -26, -217, -142, -204, -161, -228, -156, -24, -81, -62, -222, -172, -252, -23, -234, -4, -190, -18, -39, -136, -7, -254, -132, -26, -43, -147, -135, -50, -255, -112, -89, -89, -201, -53, -237, -196, -191, -3, -209, -239, -120, -133, -87, -23, -32, -31, -149, -243, -200, -181, -237, -23, -163, -43, -88, -116, -75, -217, -128, -215, -154, -228, -202, -234, -43, -90, -244, -197, -98, -227, -146, -250, -170, -220, -133, -54, -102, -96, -153, -3, -147, -61, -5, -184, -172, -105, -191, -234, -58, -60, -20, -29, -64, -87, -32, -57, -7, -64, -190, -164, -3, -86, -96, -200, -23, -208, -126, -101, -75, -61, -22, -11, -109, -26, -217, -65, -56, -237, -94, -128, -172, -100, -78, -1, -206, -70, -4, -153, -87, -234, -59, -109, -70, -132, -133, -70, -142, -169, -230, -32, -66, -228, -101, -90, -246, -170, -138, -1, -178, -52, -112, -134, -214, -63, -37, -4, -16, -45, -252, -153, -90, -126, -22, -37, -123, -113, -224, -120, -165, -185, -141, -66, -4, -102, -196, -200, -235, -8, -74, -162, -58, -35, -38, -170, -179, -25, -185, -5, -24, -40, -243, -180, -179, -58, -143, -219, -145, -21, -104, -84, -1, -64, -158, -72, -3, -100, -251, -228, -70, -37, -250, -50, -170, -88, -107, -192, -239, -40, -135, -215, -128, -30, -39, -160, -189, -133, -55, -232, -13, -249, -87, -252, -113, -79, -221, -33, -90, -119, -120, -73, -219, -95, -104, -125, -101, -240, -216, -54, -29, -30, -47, -2, -96, -57, -96, -178, -246, -249, -32, -79, -253, -247, -181, -110, -18, -129, -201, -53, -218, -220, -63, -226, -249, -103, -83, -124, -109, -79, -254, -213, -190, -145, -234, -243, -123, -27, -157, -230, -77, -192, -211, -28, -30, -95, -118, -104, -42, -195, -66, -35, -66, -224, -207, -74, -115, -54, -242, -37, -254, -171, -254, -46, -157, -252, -218, -214, -158, -62, -148, -250, -114, -212, -220, -183, -253, -2, -185, -74, -192, -129, -178, -66, -155, -101, -17, -123, -125, -171, -31, -153, -138, -184, -244, -118, -222, -2, -0, -27, -147, -199, -39, -252, -63, -84, -177, -140, -108, -171, -62, -70, -174, -84, -11, -245, -11, -121, -185, -182, -121, -156, -246, -167, -66, -22, -239, -40, -169, -255, -148, -214, -95, -229, -169, -179, -249, -29, -239, -163, -218, -18, -208, -171, -95, -104, -5, -101, -56, -46, -4, -128, -49, -198, -0, -155, -34, -75, -180, -69, -192, -118, -78, -249, -246, -90, -54, -29, -216, -164, -1, -191, -198, -247, -79, -158, -6, -236, -78, -29, -108, -25, -112, -131, -150, -253, -178, -162, -221, -83, -97, -161, -129, -159, -234, -255, -207, -199, -49, -109, -37, -32, -44, -52, -242, -197, -183, -66, -192, -218, -199, -87, -78, -126, -109, -103, -151, -182, -91, -85, -209, -213, -240, -176, -249, -15, -126, -86, -85, -230, -105, -183, -52, -34, -44, -236, -22, -228, -9, -196, -22, -162, -181, -18, -144, -60, -8, -173, -215, -48, -134, -60, -54, -69, -168, -0, -184, -17, -17, -80, -91, -118, -232, -147, -197, -175, -74, -234, -207, -211, -250, -31, -150, -212, -91, -189, -220, -111, -200, -125, -1, -86, -4, -78, -213, -242, -235, -9, -48, -133, -110, -210, -225, -237, -232, -43, -8, -97, -79, -0, -62, -168, -15, -99, -42, -178, -175, -93, -139, -124, -217, -87, -26, -171, -160, -132, -23, -52, -23, -0, -107, -35, -58, -140, -253, -157, -178, -143, -35, -91, -148, -117, -42, -218, -89, -5, -223, -221, -136, -176, -122, -12, -88, -215, -169, -15, -78, -78, -137, -40, -243, -236, -201, -193, -163, -192, -138, -1, -253, -182, -118, -24, -165, -9, -76, -3, -120, -44, -139, -40, -57, -247, -170, -42, -171, -104, -111, -147, -169, -218, -213, -207, -44, -125, -150, -235, -183, -232, -203, -53, -202, -99, -159, -146, -250, -157, -155, -8, -128, -24, -112, -4, -192, -2, -36, -252, -155, -187, -42, -249, -63, -173, -155, -75, -137, -169, -48, -162, -31, -179, -39, -65, -51, -16, -161, -100, -149, -230, -143, -83, -19, -113, -106, -204, -67, -7, -192, -62, -136, -1, -141, -61, -18, -244, -162, -134, -143, -205, -85, -127, -142, -254, -131, -22, -123, -35, -109, -55, -44, -59, -136, -21, -200, -21, -103, -224, -40, -123, -104, -16, -22, -154, -145, -219, -0, -43, -4, -254, -130, -19, -162, -173, -164, -157, -141, -90, -52, -15, -89, -138, -46, -89, -69, -223, -39, -116, -66, -188, -19, -81, -66, -66, -11, -67, -32, -242, -16, -97, -222, -184, -126, -136, -94, -163, -118, -44, -197, -132, -243, -110, -109, -170, -239, -7, -24, -180, -4, -172, -212, -57, -33, -31, -152, -35, -17, -191, -154, -185, -250, -247, -215, -140, -119, -91, -9, -125, -233, -127, -28, -152, -233, -57, -30, -66, -20, -84, -15, -213, -189, -52, -100, -15, -125, -157, -211, -246, -42, -234, -151, -192, -163, -106, -7, -161, -125, -248, -142, -115, -141, -89, -72, -44, -192, -38, -201, -45, -93, -69, -224, -25, -200, -222, -240, -116, -253, -93, -41, -4, -180, -173, -213, -23, -128, -8, -163, -175, -50, -202, -103, -241, -192, -155, -105, -24, -74, -77, -219, -217, -108, -197, -175, -40, -169, -223, -216, -222, -104, -5, -143, -198, -97, -195, -107, -250, -100, -241, -44, -100, -165, -243, -31, -100, -91, -58, -29, -9, -83, -22, -111, -255, -62, -222, -64, -158, -65, -183, -12, -159, -80, -186, -79, -84, -189, -52, -135, -159, -155, -92, -116, -239, -0, -250, -78, -118, -16, -93, -1, -60, -3, -249, -106, -205, -5, -14, -70, -150, -228, -51, -144, -37, -222, -36, -234, -211, -99, -219, -163, -62, -244, -239, -210, -78, -185, -141, -201, -119, -38, -213, -66, -96, -9, -125, -110, -246, -204, -25, -29, -248, -63, -165, -143, -100, -20, -61, -130, -124, -5, -89, -22, -195, -114, -51, -123, -131, -37, -245, -173, -194, -134, -215, -244, -201, -98, -192, -158, -98, -194, -131, -220, -12, -178, -12, -193, -2, -0, -241, -182, -122, -2, -209, -218, -46, -68, -246, -211, -165, -251, -239, -10, -62, -48, -188, -45, -192, -114, -58, -201, -127, -212, -178, -253, -243, -145, -125, -224, -83, -147, -223, -169, -91, -10, -9, -216, -50, -147, -18, -75, -50, -15, -253, -206, -228, -138, -73, -16, -5, -221, -184, -177, -204, -35, -95, -1, -238, -89, -82, -255, -182, -50, -1, -64, -79, -97, -195, -157, -103, -217, -42, -34, -80, -239, -64, -236, -217, -191, -133, -72, -187, -39, -145, -165, -231, -36, -36, -81, -65, -165, -253, -57, -34, -81, -15, -71, -52, -222, -51, -144, -51, -224, -155, -144, -4, -151, -181, -222, -107, -228, -202, -141, -78, -2, -0, -88, -18, -49, -246, -0, -49, -30, -178, -231, -167, -23, -208, -80, -67, -170, -237, -198, -211, -41, -200, -243, -139, -147, -223, -169, -91, -146, -22, -74, -34, -36, -144, -171, -221, -79, -7, -217, -79, -140, -5, -144, -159, -164, -120, -243, -232, -145, -155, -150, -251, -4, -64, -47, -97, -195, -199, -180, -0, -0, -62, -68, -190, -108, -90, -128, -236, -123, -220, -165, -224, -245, -128, -55, -224, -164, -78, -124, -155, -16, -100, -14, -98, -185, -118, -143, -83, -246, -48, -53, -199, -111, -140, -76, -37, -238, -67, -168, -0, -248, -166, -210, -79, -66, -52, -208, -203, -146, -107, -184, -191, -214, -240, -153, -192, -56, -18, -0, -125, -193, -62, -115, -224, -161, -209, -238, -75, -40, -16, -97, -104, -141, -171, -246, -41, -212, -189, -7, -81, -118, -226, -27, -75, -244, -20, -54, -220, -25, -203, -209, -5, -64, -103, -222, -58, -137, -231, -81, -56, -127, -5, -94, -65, -254, -117, -246, -102, -168, -37, -143, -199, -254, -49, -70, -122, -177, -61, -31, -57, -170, -128, -18, -73, -236, -208, -186, -46, -175, -173, -4, -0, -146, -78, -124, -1, -34, -76, -94, -236, -148, -111, -174, -101, -243, -9, -76, -209, -173, -237, -146, -0, -48, -198, -0, -175, -213, -103, -49, -16, -206, -61, -160, -109, -48, -2, -120, -185, -122, -157, -218, -96, -29, -228, -71, -171, -144, -251, -222, -219, -165, -189, -253, -138, -251, -4, -64, -47, -97, -195, -157, -118, -99, -82, -0, -44, -65, -121, -184, -174, -189, -148, -249, -93, -21, -109, -189, -91, -4, -224, -29, -218, -182, -210, -79, -153, -145, -222, -114, -62, -84, -10, -0, -100, -223, -118, -135, -210, -250, -108, -214, -247, -215, -186, -219, -9, -56, -23, -215, -54, -227, -206, -14, -162, -15, -56, -239, -255, -198, -22, -109, -67, -114, -233, -93, -66, -141, -160, -5, -94, -132, -108, -45, -167, -233, -191, -25, -20, -76, -150, -75, -218, -189, -13, -249, -162, -91, -109, -251, -127, -128, -247, -2, -235, -218, -129, -229, -105, -99, -87, -14, -215, -117, -233, -179, -135, -111, -183, -73, -58, -74, -188, -173, -70, -20, -96, -94, -139, -182, -219, -106, -219, -1, -251, -230, -2, -221, -239, -171, -231, -127, -173, -0, -56, -73, -233, -46, -194, -179, -215, -71, -132, -148, -85, -106, -157, -232, -169, -247, -157, -2, -148, -253, -235, -124, -10, -128, -8, -151, -179, -144, -175, -205, -28, -68, -195, -124, -8, -125, -38, -128, -44, -239, -203, -230, -200, -113, -215, -43, -139, -207, -14, -177, -162, -180, -95, -196, -74, -3, -164, -30, -251, -183, -22, -114, -44, -57, -13, -89, -229, -189, -14, -17, -0, -119, -0, -107, -181, -228, -185, -161, -29, -88, -158, -186, -81, -9, -27, -222, -5, -125, -11, -0, -187, -231, -169, -74, -250, -81, -214, -214, -58, -41, -84, -42, -144, -24, -233, -79, -223, -88, -0, -116, -5, -67, -180, -3, -32, -95, -141, -128, -124, -149, -110, -38, -215, -129, -220, -71, -3, -115, -229, -24, -32, -119, -50, -2, -153, -100, -215, -33, -138, -96, -55, -205, -247, -31, -168, -8, -181, -221, -115, -255, -174, -66, -86, -136, -238, -182, -238, -165, -72, -76, -191, -1, -187, -249, -64, -158, -27, -217, -27, -243, -212, -141, -74, -216, -240, -46, -112, -222, -211, -6, -125, -48, -183, -17, -71, -143, -9, -164, -95, -6, -217, -255, -255, -4, -81, -4, -94, -67, -137, -39, -89, -161, -221, -215, -201, -21, -135, -69, -244, -42, -0, -134, -5, -228, -43, -187, -72, -39, -252, -7, -200, -205, -65, -159, -129, -216, -254, -163, -19, -208, -231, -209, -248, -74, -224, -159, -200, -215, -111, -22, -98, -26, -252, -85, -224, -185, -14, -205, -218, -192, -111, -27, -246, -105, -69, -36, -54, -222, -121, -200, -22, -105, -58, -162, -15, -186, -15, -49, -40, -122, -87, -135, -251, -13, -70, -219, -107, -180, -236, -87, -149, -0, -24, -149, -176, -225, -93, -224, -60, -198, -117, -128, -93, -16, -33, -22, -20, -112, -180, -142, -241, -115, -144, -37, -234, -2, -2, -150, -23, -133, -119, -58, -9, -177, -121, -14, -126, -136, -136, -100, -63, -88, -7, -222, -223, -201, -221, -124, -23, -23, -1, -96, -183, -42, -62, -143, -197, -101, -201, -245, -24, -239, -241, -212, -91, -69, -230, -181, -140, -52, -74, -1, -81, -182, -94, -133, -76, -220, -57, -195, -185, -155, -122, -16, -73, -7, -208, -67, -191, -170, -4, -192, -168, -132, -13, -239, -2, -103, -28, -88, -147, -241, -224, -128, -163, -85, -76, -151, -6, -46, -211, -198, -65, -241, -198, -145, -175, -215, -36, -196, -1, -103, -33, -114, -58, -112, -8, -29, -44, -201, -22, -51, -1, -112, -183, -222, -207, -75, -74, -234, -109, -16, -144, -147, -61, -117, -39, -1, -207, -116, -126, -111, -4, -124, -67, -5, -194, -60, -196, -208, -231, -92, -26, -156, -116, -140, -7, -16, -217, -36, -87, -121, -150, -10, -0, -173, -111, -21, -54, -92, -203, -175, -211, -255, -55, -10, -253, -221, -165, -207, -78, -159, -230, -225, -56, -189, -17, -16, -112, -180, -234, -130, -191, -210, -134, -151, -208, -98, -255, -135, -44, -107, -191, -174, -23, -190, -159, -246, -57, -209, -22, -39, -1, -96, -207, -159, -159, -81, -82, -111, -143, -220, -90, -37, -143, -92, -220, -64, -15, -38, -185, -202, -183, -82, -0, -40, -77, -227, -176, -225, -202, -114, -22, -178, -53, -131, -24, -95, -226, -128, -62, -59, -188, -27, -7, -28, -45, -187, -216, -193, -218, -224, -150, -178, -193, -218, -160, -227, -214, -56, -231, -172, -150, -237, -163, -11, -0, -26, -160, -130, -199, -70, -136, -95, -249, -125, -200, -210, -252, -126, -196, -87, -187, -84, -202, -234, -64, -2, -120, -126, -73, -253, -139, -180, -62, -118, -246, -224, -113, -7, -122, -50, -201, -237, -19, -206, -176, -105, -28, -250, -59, -128, -119, -136, -0, -104, -28, -112, -212, -71, -252, -37, -37, -190, -31, -71, -193, -212, -22, -228, -199, -136, -173, -34, -150, -106, -219, -216, -2, -96, -114, -205, -191, -59, -170, -250, -140, -28, -157, -89, -203, -201, -153, -136, -38, -223, -250, -102, -79, -163, -124, -137, -111, -51, -188, -120, -29, -148, -200, -5, -111, -212, -88, -248, -248, -49, -27, -89, -214, -158, -137, -196, -81, -240, -154, -74, -3, -171, -43, -237, -28, -58, -126, -12, -26, -246, -185, -23, -147, -220, -62, -225, -60, -219, -56, -95, -226, -145, -109, -67, -4, -128, -215, -220, -155, -138, -128, -163, -69, -66, -43, -41, -30, -2, -54, -109, -218, -201, -18, -158, -219, -40, -207, -39, -91, -182, -135, -33, -111, -1, -16, -255, -7, -128, -95, -148, -212, -159, -171, -245, -167, -161, -70, -80, -136, -242, -200, -122, -222, -157, -91, -210, -110, -31, -173, -191, -31, -71, -72, -32, -118, -10, -159, -33, -15, -85, -85, -105, -55, -209, -226, -126, -44, -174, -37, -87, -188, -93, -67, -110, -231, -15, -162, -120, -245, -230, -216, -67, -124, -206, -1, -190, -26, -179, -95, -53, -125, -238, -197, -36, -183, -79, -56, -207, -178, -251, -151, -120, -176, -109, -136, -0, -40, -139, -191, -88, -26, -112, -212, -37, -218, -133, -220, -123, -46, -90, -66, -76, -242, -0, -8, -173, -242, -150, -105, -219, -161, -9, -0, -196, -51, -239, -17, -125, -22, -94, -215, -77, -114, -31, -253, -77, -10, -229, -207, -215, -242, -178, -149, -195, -146, -72, -188, -62, -144, -109, -195, -69, -200, -23, -248, -78, -189, -158, -77, -144, -50, -37, -242, -61, -89, -108, -81, -40, -95, -18, -216, -155, -252, -248, -213, -123, -228, -135, -156, -206, -128, -40, -49, -135, -18, -44, -132, -158, -76, -114, -251, -132, -211, -167, -198, -161, -191, -29, -154, -98, -36, -227, -75, -16, -235, -197, -245, -44, -243, -138, -235, -54, -10, -56, -234, -198, -155, -219, -193, -24, -115, -146, -49, -102, -166, -49, -102, -199, -44, -203, -130, -82, -104, -107, -219, -115, -145, -44, -48, -43, -23, -202, -215, -0, -14, -52, -198, -88, -103, -12, -175, -47, -193, -24, -196, -158, -198, -152, -213, -141, -49, -103, -100, -89, -118, -123, -9, -205, -52, -253, -187, -160, -80, -190, -72, -255, -122, -95, -112, -150, -101, -11, -141, -49, -59, -25, -99, -190, -96, -140, -153, -108, -140, -121, -153, -49, -102, -107, -99, -204, -29, -198, -152, -237, -140, -49, -147, -148, -244, -250, -86, -61, -111, -136, -44, -203, -22, -102, -89, -246, -75, -99, -204, -63, -181, -104, -235, -18, -186, -107, -140, -49, -151, -25, -99, -214, -53, -198, -120, -131, -90, -246, -0, -59, -158, -254, -107, -140, -185, -180, -226, -95, -235, -80, -216, -200, -138, -237, -54, -59, -118, -129, -85, -145, -237, -223, -192, -41, -76, -67, -148, -29, -197, -206, -210, -191, -222, -0, -177, -72, -68, -160, -191, -25, -99, -94, -175, -180, -55, -27, -99, -54, -50, -198, -156, -106, -140, -217, -55, -224, -186, -235, -151, -148, -91, -157, -147, -127, -60, -147, -239, -95, -167, -82, -189, -55, -30, -152, -196, -142, -244, -89, -132, -40, -109, -254, -135, -124, -209, -236, -87, -101, -1, -78, -116, -219, -166, -80, -30, -67, -89, -1, -32, -190, -240, -246, -248, -231, -213, -21, -116, -39, -42, -205, -247, -10, -229, -54, -241, -99, -144, -209, -148, -135, -175, -221, -215, -182, -142, -205, -87, -194, -215, -194, -187, -178, -67, -18, -143, -0, -28, -86, -193, -99, -87, -165, -137, -151, -129, -182, -2, -12, -193, -36, -23, -209, -107, -0, -188, -86, -127, -219, -232, -187, -179, -91, -242, -179, -104, -19, -250, -187, -117, -36, -99, -231, -186, -173, -2, -142, -54, -209, -138, -159, -230, -105, -187, -35, -162, -13, -159, -132, -156, -151, -206, -71, -20, -97, -147, -17, -43, -194, -78, -193, -10, -245, -186, -195, -18, -0, -187, -233, -245, -42, -147, -122, -34, -203, -49, -235, -37, -249, -83, -96, -3, -242, -173, -206, -245, -180, -8, -158, -161, -47, -218, -186, -46, -87, -230, -17, -104, -193, -219, -98, -64, -0, -32, -250, -7, -27, -64, -99, -192, -0, -201, -161, -91, -218, -185, -103, -175, -227, -88, -76, -48, -4, -147, -92, -6, -87, -0, -171, -32, -43, -128, -83, -90, -242, -179, -104, -19, -250, -187, -117, -36, -99, -231, -186, -173, -2, -142, -38, -152, -167, -30, -150, -141, -72, -91, -107, -254, -138, -72, -236, -171, -25, -137, -95, -2, -171, -180, -188, -190, -245, -186, -59, -191, -77, -251, -26, -222, -3, -2, -0, -49, -217, -222, -20, -56, -70, -235, -254, -76, -137, -18, -208, -105, -99, -227, -22, -30, -25, -187, -143, -158, -107, -141, -103, -147, -220, -198, -95, -98, -58, -68, -50, -214, -226, -7, -201, -117, -72, -141, -3, -142, -78, -120, -32, -209, -111, -64, -108, -31, -106, -163, -7, -233, -132, -189, -23, -145, -186, -214, -113, -230, -6, -26, -154, -136, -34, -193, -59, -191, -170, -124, -102, -81, -178, -124, -236, -2, -170, -113, -17, -162, -195, -169, -156, -252, -202, -231, -89, -200, -57, -246, -116, -122, -142, -19, -200, -248, -54, -201, -109, -252, -37, -166, -67, -36, -99, -45, -254, -147, -254, -63, -5, -28, -109, -3, -114, -119, -225, -79, -214, -208, -45, -1, -252, -86, -105, -47, -70, -52, -255, -203, -34, -73, -61, -158, -64, -116, -31, -7, -86, -180, -255, -53, -146, -190, -235, -207, -72, -8, -116, -27, -12, -229, -1, -122, -74, -126, -234, -12, -76, -247, -24, -240, -74, -36, -142, -224, -2, -189, -247, -215, -4, -242, -58, -89, -121, -125, -174, -143, -190, -22, -174, -213, -202, -36, -55, -128, -175, -133, -111, -34, -214, -90, -9, -6, -240, -109, -28, -250, -155, -8, -145, -140, -19, -90, -2, -241, -51, -7, -9, -95, -86, -153, -58, -140, -220, -162, -235, -66, -10, -81, -118, -145, -175, -210, -157, -90, -255, -145, -146, -246, -199, -32, -147, -126, -1, -114, -212, -117, -33, -98, -244, -210, -219, -23, -213, -25, -124, -190, -99, -192, -143, -146, -7, -80, -173, -253, -154, -146, -43, -202, -110, -33, -96, -213, -208, -21, -180, -48, -201, -13, -224, -217, -183, -0, -104, -28, -250, -155, -142, -145, -140, -163, -0, -241, -40, -67, -59, -28, -108, -245, -165, -15, -237, -24, -68, -241, -247, -184, -182, -191, -9, -113, -96, -241, -90, -197, -105, -187, -189, -169, -135, -215, -168, -166, -192, -103, -101, -100, -207, -120, -33, -50, -169, -230, -107, -31, -130, -246, -170, -228, -201, -52, -106, -181, -239, -228, -169, -175, -203, -94, -148, -205, -225, -55, -102, -236, -249, -157, -103, -89, -119, -10, -16, -116, -164, -134, -172, -36, -96, -12, -45, -191, -155, -192, -121, -30, -125, -9, -128, -198, -161, -191, -233, -16, -201, -56, -26, -144, -244, -67, -54, -209, -96, -80, -54, -29, -196, -44, -214, -218, -184, -207, -69, -20, -105, -215, -144, -47, -211, -230, -1, -59, -149, -180, -253, -134, -210, -84, -29, -63, -86, -30, -169, -33, -150, -134, -110, -84, -225, -7, -181, -221, -20, -224, -204, -128, -254, -111, -138, -44, -219, -103, -19, -32, -244, -200, -29, -122, -188, -209, -142, -129, -103, -107, -253, -88, -114, -203, -173, -19, -0, -31, -209, -250, -89, -190, -122, -15, -253, -158, -74, -223, -202, -191, -99, -180, -49, -4, -1, -208, -88, -219, -78, -135, -72, -198, -81, -1, -108, -73, -30, -87, -255, -229, -1, -244, -103, -105, -191, -46, -99, -100, -64, -209, -229, -200, -143, -46, -38, -151, -180, -181, -254, -203, -94, -205, -103, -192, -181, -55, -35, -183, -202, -251, -13, -133, -227, -41, -2, -180, -199, -228, -49, -7, -74, -147, -119, -22, -232, -173, -75, -175, -247, -235, -135, -88, -84, -2, -220, -20, -118, -23, -253, -35, -64, -0, -216, -19, -136, -202, -216, -141, -14, -253, -211, -17, -47, -189, -133, -244, -17, -133, -166, -103, -140, -81, -1, -208, -58, -146, -113, -116, -168, -16, -120, -0, -184, -33, -128, -214, -106, -47, -7, -98, -229, -145, -155, -144, -122, -191, -134, -192, -41, -90, -223, -202, -186, -140, -252, -88, -165, -84, -233, -86, -211, -126, -29, -125, -176, -139, -8, -244, -208, -34, -63, -175, -189, -157, -130, -177, -16, -98, -19, -97, -87, -80, -251, -151, -241, -24, -54, -2, -4, -128, -117, -253, -190, -188, -1, -79, -123, -228, -116, -104, -188, -158, -14, -7, -99, -81, -0, -104, -251, -170, -72, -198, -95, -71, -21, -198, -109, -120, -247, -134, -154, -135, -121, -144, -214, -253, -179, -164, -237, -249, -90, -95, -187, -210, -240, -180, -125, -177, -182, -157, -66, -77, -242, -203, -10, -30, -118, -50, -255, -185, -65, -155, -21, -201, -189, -250, -64, -190, -132, -55, -50, -50, -161, -233, -25, -4, -158, -93, -3, -107, -146, -167, -231, -190, -34, -176, -77, -211, -16, -217, -22, -69, -37, -96, -6, -236, -65, -174, -129, -174, -205, -226, -235, -180, -125, -46, -185, -239, -200, -114, -161, -237, -106, -250, -215, -118, -226, -108, -139, -124, -76, -110, -211, -73, -50, -3, -49, -170, -58, -20, -143, -14, -170, -102, -204, -142, -154, -0, -80, -30, -222, -72, -198, -90, -119, -93, -155, -228, -11, -222, -105, -0, -0, -32, -0, -73, -68, -65, -84, -126, -245, -10, -223, -77, -35, -103, -184, -54, -55, -252, -109, -192, -179, -75, -218, -94, -175, -52, -222, -250, -154, -235, -30, -168, -109, -127, -210, -178, -223, -171, -146, -235, -41, -26, -69, -250, -69, -172, -226, -246, -65, -142, -207, -30, -211, -9, -244, -40, -18, -179, -239, -169, -88, -127, -129, -188, -142, -210, -62, -60, -0, -60, -39, -128, -190, -113, -136, -108, -231, -29, -185, -199, -128, -151, -147, -103, -16, -6, -57, -222, -107, -154, -61, -233, -47, -218, -246, -227, -77, -218, -85, -244, -175, -205, -210, -217, -21, -134, -183, -35, -177, -18, -239, -33, -55, -157, -29, -88, -29, -86, -93, -143, -252, -195, -50, -42, -2, -96, -84, -65, -139, -240, -75, -197, -155, -70, -190, -12, -54, -106, -203, -239, -168, -176, -140, -115, -6, -224, -13, -228, -122, -135, -25, -136, -34, -241, -48, -96, -227, -138, -182, -214, -183, -250, -3, -136, -34, -240, -92, -228, -4, -194, -102, -214, -221, -151, -81, -138, -100, -27, -10, -36, -86, -194, -66, -237, -119, -173, -7, -38, -45, -67, -100, -227, -199, -34, -114, -161, -181, -107, -203, -254, -91, -151, -220, -255, -182, -105, -239, -233, -95, -27, -1, -96, -141, -176, -182, -43, -148, -175, -130, -152, -118, -15, -40, -107, -201, -133, -195, -139, -60, -117, -54, -151, -197, -216, -250, -210, -246, -13, -90, -134, -95, -42, -190, -60, -242, -188, -238, -181, -54, -220, -192, -57, -74, -251, -24, -18, -84, -99, -50, -249, -30, -26, -68, -8, -121, -191, -46, -228, -86, -98, -135, -32, -95, -224, -123, -144, -47, -210, -69, -228, -10, -147, -191, -50, -138, -185, -238, -235, -128, -108, -29, -166, -2, -91, -6, -210, -71, -15, -145, -61, -22, -208, -81, -0, -216, -49, -251, -170, -6, -109, -236, -24, -123, -183, -167, -238, -232, -9, -39, -0, -232, -16, -126, -201, -35, -0, -172, -86, -62, -40, -35, -47, -158, -76, -61, -192, -243, -200, -45, -206, -22, -1, -219, -122, -104, -158, -112, -234, -191, -228, -78, -116, -196, -108, -210, -30, -13, -214, -166, -7, -79, -24, -93, -116, -20, -0, -214, -131, -114, -26, -18, -200, -165, -54, -89, -8, -185, -221, -199, -21, -56, -33, -235, -17, -109, -187, -213, -196, -79, -40, -1, -208, -58, -252, -146, -71, -0, -156, -161, -191, -255, -71, -71, -175, -54, -135, -215, -217, -158, -58, -171, -184, -250, -105, -73, -91, -107, -100, -84, -233, -217, -151, -48, -250, -112, -198, -208, -250, -72, -120, -178, -43, -244, -67, -242, -36, -178, -69, -169, -212, -209, -0, -7, -56, -60, -230, -35, -137, -76, -94, -91, -65, -255, -26, -103, -252, -204, -64, -156, -186, -172, -201, -241, -183, -24, -35, -218, -118, -2, -17, -227, -66, -173, -195, -47, -57, -253, -176, -2, -96, -53, -196, -198, -29, -100, -41, -254, -77, -90, -46, -195, -145, -99, -53, -240, -251, -79, -91, -205, -185, -55, -12, -54, -98, -224, -3, -53, -161, -200, -144, -213, -198, -137, -200, -82, -124, -62, -178, -31, -191, -4, -201, -224, -227, -205, -136, -172, -237, -90, -5, -147, -64, -146, -119, -124, -13, -81, -194, -77, -211, -107, -78, -69, -132, -221, -86, -177, -219, -141, -7, -56, -99, -200, -166, -115, -191, -27, -217, -130, -218, -119, -60, -159, -146, -248, -12, -192, -203, -144, -45, -228, -99, -200, -135, -234, -50, -135, -223, -241, -192, -178, -37, -237, -222, -128, -156, -66, -77, -67, -38, -252, -53, -168, -249, -54, -37, -218, -246, -226, -88, -47, -212, -133, -132, -236, -170, -132, -167, -93, -85, -46, -5, -107, -143, -210, -42, -212, -94, -241, -66, -173, -195, -47, -249, -30, -10, -162, -37, -255, -6, -121, -128, -131, -179, -129, -167, -181, -232, -151, -245, -128, -26, -8, -208, -128, -40, -42, -161, -160, -252, -113, -234, -159, -161, -245, -165, -89, -109, -17, -187, -118, -235, -140, -51, -23, -209, -67, -184, -105, -209, -75, -99, -236, -211, -50, -152, -4, -185, -185, -245, -124, -228, -20, -228, -42, -242, -237, -204, -2, -224, -245, -49, -219, -85, -244, -227, -45, -218, -54, -232, -232, -209, -211, -126, -64, -168, -35, -19, -174, -18, -37, -188, -44, -230, -3, -187, -56, -229, -171, -59, -247, -93, -182, -58, -181, -31, -155, -55, -59, -101, -219, -144, -7, -118, -245, -174, -16, -219, -192, -233, -103, -83, -1, -112, -82, -200, -191, -6, -253, -88, -146, -252, -4, -173, -117, -176, -29, -151, -97, -235, -140, -168, -53, -15, -101, -11, -114, -79, -168, -160, -228, -34, -133, -246, -239, -212, -182, -183, -122, -234, -172, -17, -145, -247, -1, -144, -11, -143, -187, -43, -248, -219, -243, -252, -75, -113, -204, -128, -17, -171, -172, -255, -171, -233, -91, -171, -96, -18, -136, -78, -229, -211, -56, -206, -63, -136, -213, -164, -221, -151, -150, -5, -20, -109, -213, -174, -162, -31, -246, -20, -197, -187, -234, -83, -154, -181, -17, -99, -171, -199, -245, -126, -183, -208, -242, -221, -128, -251, -60, -244, -239, -69, -242, -44, -22, -255, -253, -81, -175, -229, -181, -52, -116, -198, -208, -81, -158, -58, -235, -7, -255, -96, -73, -91, -43, -192, -87, -45, -148, -111, -169, -229, -211, -124, -237, -218, -160, -102, -172, -183, -182, -31, -104, -209, -15, -107, -185, -121, -55, -45, -62, -172, -62, -134, -173, -195, -47, -85, -61, -20, -173, -183, -166, -177, -222, -244, -226, -53, -188, -173, -153, -241, -64, -116, -94, -29, -108, -32, -202, -203, -1, -15, -62, -224, -251, -90, -95, -154, -43, -143, -124, -133, -210, -58, -99, -75, -44, -144, -123, -36, -54, -90, -210, -181, -105, -135, -100, -199, -93, -132, -28, -161, -121, -151, -200, -74, -119, -28, -178, -23, -159, -172, -244, -139, -244, -255, -179, -8, -207, -21, -185, -20, -249, -87, -220, -235, -106, -237, -140, -161, -1, -111, -57, -242, -92, -9, -197, -248, -139, -182, -222, -58, -103, -125, -177, -80, -190, -154, -150, -63, -28, -210, -207, -16, -140, -5, -1, -128, -196, -143, -176, -10, -238, -82, -225, -221, -148, -105, -235, -240, -75, -1, -2, -96, -11, -173, -247, -45, -227, -255, -12, -236, -4, -172, -84, -40, -127, -22, -185, -121, -234, -116, -96, -125, -79, -219, -165, -200, -61, -168, -46, -70, -181, -191, -136, -117, -219, -222, -200, -228, -94, -68, -197, -241, -144, -211, -247, -129, -243, -224, -97, -131, -138, -156, -245, -177, -219, -1, -63, -214, -38, -3, -57, -10, -11, -116, -83, -129, -15, -232, -255, -95, -140, -172, -2, -108, -154, -171, -160, -212, -220, -136, -82, -13, -224, -156, -10, -26, -139, -1, -165, -113, -221, -196, -66, -44, -231, -108, -252, -201, -19, -201, -117, -81, -54, -191, -197, -9, -33, -253, -12, -188, -151, -210, -177, -94, -215, -207, -136, -125, -248, -182, -94, -230, -26, -98, -185, -99, -211, -33, -252, -146, -251, -80, -244, -101, -63, -203, -169, -91, -154, -220, -34, -112, -96, -175, -233, -180, -93, -132, -68, -215, -185, -158, -145, -250, -136, -71, -41, -217, -227, -107, -251, -231, -147, -239, -217, -231, -33, -123, -120, -55, -88, -68, -229, -254, -200, -161, -107, -44, -0, -104, -153, -179, -14, -17, -92, -31, -68, -142, -57, -39, -33, -210, -124, -58, -185, -86, -186, -108, -160, -183, -106, -231, -225, -179, -28, -121, -32, -144, -245, -234, -250, -26, -194, -179, -162, -253, -75, -245, -189, -60, -65, -197, -177, -176, -59, -134, -60, -117, -33, -105, -188, -94, -67, -190, -202, -88, -132, -40, -74, -23, -33, -218, -253, -213, -187, -220, -67, -204, -126, -70, -184, -254, -51, -201, -21, -163, -241, -130, -165, -210, -33, -252, -146, -251, -80, -156, -23, -112, -51, -98, -16, -100, -39, -243, -130, -146, -182, -111, -67, -162, -235, -220, -138, -44, -43, -109, -176, -135, -203, -144, -211, -131, -218, -224, -154, -136, -178, -239, -80, -229, -49, -87, -219, -159, -77, -64, -242, -72, -167, -239, -141, -4, -0, -237, -141, -166, -214, -36, -247, -165, -183, -207, -197, -70, -82, -182, -171, -25, -159, -18, -169, -85, -187, -146, -62, -216, -253, -227, -233, -77, -238, -185, -41, -144, -152, -131, -54, -198, -226, -110, -53, -180, -79, -141, -33, -79, -93, -240, -196, -2, -94, -133, -172, -28, -109, -148, -235, -203, -112, -18, -170, -118, -69, -172, -126, -118, -184, -190, -141, -223, -248, -215, -62, -152, -119, -201, -136, -138, -62, -128, -189, -244, -161, -79, -67, -36, -255, -20, -100, -153, -223, -200, -214, -190, -111, -144, -107, -240, -189, -168, -105, -219, -197, -104, -234, -55, -90, -55, -21, -49, -58, -121, -186, -83, -87, -165, -69, -110, -213, -174, -164, -255, -255, -85, -242, -202, -47, -8, -29, -143, -29, -17, -229, -31, -4, -164, -238, -114, -199, -144, -167, -174, -241, -196, -66, -78, -15, -78, -213, -102, -165, -91, -143, -166, -168, -233, -103, -175, -17, -123, -128, -23, -34, -130, -127, -1, -125, -69, -101, -166, -135, -240, -75, -99, -17, -136, -143, -194, -105, -206, -11, -61, -71, -127, -159, -134, -39, -244, -121, -161, -109, -23, -163, -41, -187, -106, -24, -8, -23, -70, -158, -67, -209, -39, -0, -90, -181, -243, -208, -110, -173, -164, -33, -110, -222, -173, -143, -29, -17, -197, -164, -13, -150, -26, -178, -138, -171, -154, -88, -173, -190, -172, -136, -16, -128, -192, -32, -39, -129, -60, -45, -124, -62, -4, -189, -70, -236, -33, -63, -181, -57, -186, -15, -254, -19, -18, -85, -47, -180, -162, -77, -23, -163, -41, -123, -234, -176, -173, -167, -206, -38, -21, -241, -9, -128, -86, -237, -60, -180, -246, -171, -248, -169, -0, -218, -182, -199, -149, -79, -71, -182, -128, -224, -177, -181, -47, -105, -211, -74, -0, -32, -142, -103, -251, -81, -80, -18, -35, -129, -91, -191, -172, -205, -188, -193, -104, -218, -192, -233, -167, -207, -135, -224, -136, -190, -4, -0, -121, -66, -145, -25, -44, -38, -31, -225, -49, -129, -150, -2, -160, -139, -209, -148, -221, -175, -31, -135, -42, -216, -116, -176, -238, -69, -133, -50, -175, -109, -187, -2, -143, -103, -145, -39, -109, -25, -240, -191, -104, -112, -255, -149, -199, -142, -136, -98, -20, -224, -196, -6, -60, -45, -154, -10, -128, -45, -156, -182, -83, -145, -149, -235, -21, -228, -43, -166, -57, -56, -6, -66, -93, -225, -92, -171, -232, -67, -240, -46, -122, -138, -216, -163, -239, -217, -234, -127, -190, -21, -147, -247, -168, -163, -234, -197, -7, -180, -253, -56, -18, -94, -123, -153, -170, -178, -138, -246, -75, -59, -215, -127, -126, -29, -189, -211, -174, -139, -209, -212, -78, -133, -1, -123, -53, -185, -13, -198, -126, -168, -34, -54, -86, -187, -2, -15, -107, -47, -255, -243, -208, -123, -45, -225, -83, -122, -236, -136, -36, -191, -180, -39, -58, -193, -9, -82, -170, -198, -1, -213, -2, -96, -57, -224, -115, -250, -188, -109, -92, -134, -199, -16, -229, -227, -49, -68, -202, -112, -237, -233, -39, -228, -62, -4, -174, -242, -124, -126, -221, -123, -104, -113, -205, -143, -40, -255, -7, -0, -111, -78, -193, -113, -139, -170, -23, -31, -208, -214, -198, -19, -60, -162, -170, -172, -162, -253, -122, -206, -245, -87, -171, -163, -119, -218, -117, -202, -89, -135, -248, -155, -95, -142, -40, -11, -167, -33, -166, -172, -239, -214, -186, -147, -203, -6, -80, -219, -118, -90, -239, -166, -245, -10, -18, -118, -180, -56, -118, -116, -38, -195, -121, -248, -45, -2, -127, -0, -172, -27, -246, -164, -198, -30, -156, -241, -178, -23, -114, -234, -51, -15, -17, -118, -135, -33, -6, -58, -255, -139, -41, -0, -144, -237, -148, -85, -56, -119, -10, -186, -210, -244, -194, -0, -199, -55, -108, -179, -153, -14, -208, -25, -192, -5, -84, -4, -243, -40, -92, -167, -173, -0, -88, -150, -124, -105, -180, -91, -89, -89, -69, -123, -155, -171, -189, -209, -30, -145, -33, -228, -172, -139, -13, -36, -104, -10, -4, -154, -11, -211, -254, -184, -50, -4, -193, -126, -251, -99, -13, -206, -61, -84, -218, -79, -68, -188, -222, -87, -236, -24, -101, -152, -113, -45, -90, -10, -128, -203, -145, -229, -247, -54, -136, -86, -253, -162, -192, -235, -180, -18, -0, -218, -254, -121, -136, -192, -153, -137, -6, -201, -240, -149, -121, -218, -109, -133, -236, -229, -231, -18, -96, -47, -80, -104, -59, -30, -115, -214, -89, -135, -153, -218, -156, -135, -74, -31, -237, -216, -113, -113, -130, -51, -94, -135, -18, -9, -153, -60, -206, -228, -221, -52, -216, -106, -182, -189, -216, -26, -136, -59, -166, -53, -222, -88, -136, -44, -233, -206, -69, -114, -215, -85, -38, -59, -208, -9, -103, -131, -23, -238, -2, -76, -15, -184, -102, -39, -1, -160, -60, -62, -172, -60, -110, -69, -77, -138, -125, -101, -37, -215, -125, -130, -234, -116, -232, -3, -171, -3, -198, -89, -206, -58, -114, -167, -168, -187, -8, -252, -138, -16, -233, -216, -113, -113, -67, -140, -241, -218, -242, -122, -149, -136, -113, -161, -13, -200, -227, -243, -89, -239, -170, -39, -201, -205, -14, -161, -102, -201, -139, -104, -96, -207, -66, -206, -154, -207, -1, -46, -108, -112, -131, -27, -21, -202, -215, -118, -38, -217, -55, -107, -120, -216, -201, -126, -39, -185, -103, -222, -64, -89, -201, -117, -91, -61, -92, -122, -202, -89, -215, -7, -144, -92, -132, -0, -95, -105, -208, -38, -202, -177, -99, -108, -208, -62, -6, -67, -171, -140, -87, -30, -62, -222, -241, -58, -238, -1, -252, -93, -111, -236, -100, -228, -11, -7, -226, -219, -157, -33, -129, -53, -190, -10, -172, -80, -195, -99, -115, -228, -120, -100, -38, -226, -156, -83, -27, -103, -223, -247, -64, -245, -250, -54, -237, -118, -93, -86, -160, -231, -233, -75, -157, -141, -134, -127, -246, -149, -245, -1, -250, -203, -89, -119, -157, -254, -191, -113, -100, -28, -15, -191, -213, -180, -253, -156, -38, -3, -159, -8, -199, -142, -125, -128, -246, -49, -24, -26, -103, -188, -42, -225, -179, -216, -10, -0, -251, -213, -127, -174, -254, -134, -134, -58, -128, -150, -215, -29, -241, -64, -17, -205, -179, -21, -70, -103, -81, -177, -100, -101, -164, -194, -239, -163, -101, -101, -227, -9, -218, -239, -89, -136, -9, -46, -52, -136, -140, -19, -185, -31, -157, -143, -29, -27, -92, -43, -56, -47, -2, -45, -99, -48, -40, -109, -163, -140, -87, -37, -60, -22, -91, -1, -96, -131, -118, -188, -64, -127, -15, -91, -0, -172, -171, -191, -173, -179, -195, -21, -212, -156, -121, -146, -31, -249, -29, -85, -85, -54, -158, -224, -60, -143, -198, -145, -113, -122, -232, -75, -235, -99, -199, -134, -215, -105, -148, -23, -161, -227, -181, -130, -51, -94, -77, -40, -144, -107, -125, -207, -71, -210, -101, -5, -11, -0, -58, -56, -141, -56, -3, -126, -37, -242, -175, -222, -109, -132, -217, -143, -239, -133, -232, -26, -150, -169, -42, -27, -79, -112, -158, -71, -227, -200, -56, -227, -17, -52, -204, -139, -176, -56, -3, -120, -1, -146, -165, -249, -78, -68, -7, -243, -32, -178, -157, -252, -70, -9, -125, -20, -189, -134, -101, -182, -22, -185, -59, -171, -53, -105, -60, -139, -138, -104, -49, -158, -142, -180, -113, -26, -65, -7, -192, -251, -201, -131, -58, -116, -138, -36, -60, -158, -225, -8, -128, -198, -145, -113, -198, -35, -104, -144, -23, -161, -203, -135, -102, -172, -3, -9, -125, -103, -183, -225, -243, -17, -155, -11, -155, -113, -219, -235, -250, -75, -36, -189, -134, -203, -112, -5, -196, -189, -213, -77, -181, -253, -40, -146, -130, -107, -224, -40, -205, -105, -215, -58, -86, -157, -35, -0, -102, -59, -215, -236, -100, -170, -218, -7, -144, -147, -141, -63, -170, -144, -156, -141, -40, -58, -39, -35, -22, -96, -3, -251, -73, -231, -94, -26, -125, -213, -156, -118, -141, -34, -227, -180, -189, -222, -120, -2, -237, -131, -169, -142, -105, -193, -129, -164, -147, -183, -167, -71, -191, -64, -77, -168, -17, -5, -252, -22, -84, -40, -178, -137, -160, -215, -240, -49, -93, -74, -59, -243, -8, -249, -106, -224, -54, -26, -90, -63, -17, -16, -171, -206, -25, -184, -23, -0, -219, -59, -15, -98, -224, -11, -56, -90, -0, -118, -37, -95, -157, -216, -140, -173, -55, -145, -167, -151, -58, -204, -211, -166, -171, -0, -104, -27, -118, -122, -113, -22, -0, -109, -189, -19, -27, -11, -142, -182, -239, -193, -161, -9, -94, -206, -147, -39, -208, -253, -119, -248, -211, -24, -209, -62, -190, -94, -67, -59, -116, -60, -34, -157, -172, -251, -107, -35, -229, -19, -1, -177, -234, -138, -15, -26, -201, -84, -11, -226, -71, -62, -38, -220, -30, -201, -131, -78, -126, -169, -80, -190, -58, -240, -73, -60, -130, -177, -237, -132, -108, -59, -240, -34, -92, -207, -98, -158, -14, -166, -179, -245, -93, -84, -157, -194, -52, -254, -178, -182, -189, -191, -154, -123, -168, -243, -78, -108, -44, -56, -186, -244, -147, -134, -203, -121, -68, -225, -13, -99, -196, -110, -196, -24, -51, -242, -20, -0, -216, -68, -127, -151, -133, -116, -110, -29, -171, -206, -247, -160, -201, -67, -72, -159, -67, -172, -160, -135, -35, -175, -217, -52, -181, -182, -53, -138, -121, -69, -131, -107, -88, -12, -91, -0, -88, -204, -67, -38, -227, -57, -192, -158, -148, -196, -246, -115, -232, -109, -214, -224, -171, -16, -133, -156, -197, -69, -148, -164, -254, -102, -20, -190, -172, -37, -253, -104, -27, -76, -181, -84, -112, -116, -120, -15, -141, -151, -243, -228, -199, -171, -3, -17, -164, -70, -13, -218, -161, -227, -245, -255, -214, -231, -218, -151, -157, -167, -83, -172, -58, -223, -131, -70, -190, -172, -54, -208, -231, -126, -145, -239, -171, -77, -106, -109, -187, -2, -186, -140, -26, -115, -104, -167, -141, -197, -176, -5, -64, -217, -68, -190, -20, -207, -177, -170, -175, -159, -72, -210, -137, -221, -201, -183, -56, -7, -148, -244, -117, -216, -95, -214, -40, -65, -81, -29, -126, -85, -110, -205, -109, -223, -67, -227, -229, -60, -185, -123, -121, -155, -15, -76, -41, -66, -121, -89, -134, -199, -2, -219, -162, -199, -103, -202, -227, -120, -36, -20, -180, -205, -246, -251, -27, -79, -187, -78, -78, -35, -101, -15, -26, -209, -7, -44, -66, -190, -190, -47, -109, -116, -51, -229, -215, -106, -155, -90, -251, -185, -228, -89, -102, -108, -222, -185, -186, -120, -122, -22, -163, -182, -5, -96, -112, -34, -127, -183, -73, -63, -117, -76, -0, -220, -212, -240, -30, -250, -248, -178, -182, -254, -208, -208, -206, -173, -185, -109, -63, -27, -47, -231, -201, -87, -77, -219, -54, -104, -99, -97, -5, -126, -55, -7, -33, -135, -225, -28, -242, -227, -192, -69, -78, -249, -36, -60, -103, -243, -116, -116, -26, -169, -121, -208, -54, -118, -253, -77, -68, -8, -132, -64, -135, -212, -218, -200, -151, -237, -235, -140, -60, -33, -249, -95, -217, -75, -115, -104, -70, -93, -7, -64, -197, -68, -174, -105, -183, -187, -29, -19, -13, -239, -161, -143, -47, -107, -219, -96, -170, -93, -221, -154, -155, -246, -179, -241, -114, -30, -177, -244, -132, -6, -89, -172, -171, -222, -91, -43, -32, -190, -241, -127, -67, -150, -222, -118, -73, -50, -13, -56, -29, -217, -67, -150, -37, -88, -28, -147, -78, -35, -125, -1, -9, -172, -241, -254, -194, -160, -218, -217, -67, -215, -234, -5, -117, -24, -120, -165, -215, -163, -98, -34, -215, -180, -219, -77, -235, -158, -40, -233, -235, -48, -191, -172, -109, -131, -169, -182, -21, -28, -109, -251, -217, -102, -57, -127, -176, -182, -57, -191, -65, -155, -210, -247, -22, -5, -202, -252, -248, -0, -186, -49, -233, -52, -210, -55, -244, -30, -109, -16, -200, -41, -158, -250, -126, -95, -80, -131, -235, -81, -49, -145, -107, -218, -157, -160, -117, -255, -242, -212, -13, -251, -203, -218, -54, -152, -106, -91, -193, -81, -213, -207, -23, -87, -180, -107, -179, -156, -95, -151, -60, -143, -193, -33, -56, -241, -26, -145, -20, -110, -187, -86, -244, -111, -212, -5, -192, -208, -156, -70, -186, -160, -237, -192, -171, -225, -249, -82, -109, -214, -232, -203, -218, -7, -170, -174, -71, -245, -68, -30, -104, -135, -56, -84, -125, -158, -92, -119, -240, -78, -79, -187, -62, -190, -172, -85, -19, -171, -109, -48, -213, -182, -130, -195, -222, -187, -47, -252, -247, -59, -42, -218, -53, -94, -206, -107, -187, -247, -145, -219, -220, -204, -67, -156, -192, -102, -233, -239, -255, -120, -232, -75, -223, -119, -20, -40, -243, -227, -3, -105, -135, -226, -52, -210, -5, -53, -3, -175, -106, -192, -110, -128, -132, -151, -222, -160, -80, -254, -52, -114, -39, -150, -43, -43, -174, -55, -106, -2, -128, -176, -137, -108, -97, -149, -73, -215, -49, -50, -6, -196, -129, -37, -215, -107, -251, -101, -109, -59, -177, -218, -6, -83, -109, -43, -56, -172, -137, -173, -47, -252, -247, -209, -21, -237, -26, -47, -231, -157, -182, -155, -35, -185, -13, -239, -65, -182, -18, -51, -144, -147, -167, -15, -121, -104, -7, -222, -247, -98, -5, -106, -34, -215, -208, -208, -17, -194, -121, -96, -93, -194, -78, -63, -128, -104, -121, -175, -36, -15, -213, -52, -11, -216, -186, -226, -122, -195, -22, -0, -77, -39, -114, -17, -179, -144, -19, -143, -147, -80, -159, -251, -146, -118, -109, -191, -172, -173, -38, -150, -214, -55, -254, -208, -208, -94, -112, -216, -163, -204, -98, -248, -239, -247, -144, -239, -243, -125, -237, -26, -47, -231, -219, -192, -243, -222, -6, -16, -227, -58, -189, -130, -134, -185, -231, -11, -109, -27, -57, -66, -56, -207, -165, -75, -216, -233, -71, -145, -175, -198, -227, -136, -31, -192, -49, -192, -38, -53, -215, -27, -182, -0, -176, -8, -157, -200, -173, -250, -73, -251, -47, -107, -171, -137, -213, -5, -180, -19, -28, -175, -113, -238, -195, -134, -255, -182, -17, -160, -190, -133, -90, -250, -149, -92, -175, -209, -114, -190, -229, -61, -89, -196, -57, -6, -28, -13, -208, -49, -247, -60, -13, -28, -33, -156, -7, -54, -148, -164, -142, -109, -39, -214, -176, -175, -215, -161, -93, -219, -47, -107, -235, -137, -53, -108, -0, -111, -64, -220, -227, -167, -105, -191, -174, -65, -183, -60, -168, -0, -172, -104, -27, -188, -156, -111, -217, -183, -161, -142, -175, -94, -64, -156, -220, -243, -65, -142, -16, -73, -0, -196, -109, -167, -109, -219, -230, -55, -104, -61, -177, -198, -27, -144, -128, -172, -179, -233, -47, -65, -73, -52, -59, -128, -149, -129, -207, -34, -182, -223, -143, -35, -75, -151, -59, -145, -163, -174, -210, -188, -238, -5, -30, -63, -69, -246, -63, -191, -8, -164, -239, -148, -123, -190, -9, -70, -81, -0, -148, -46, -209, -208, -8, -202, -145, -175, -55, -52, -1, -144, -80, -15, -196, -106, -116, -145, -10, -188, -82, -33, -64, -190, -69, -216, -190, -170, -204, -169, -139, -46, -0, -174, -115, -152, -62, -142, -132, -8, -179, -150, -128, -15, -17, -96, -3, -79, -238, -241, -52, -51, -74, -167, -34, -162, -70, -0, -148, -30, -63, -69, -184, -94, -21, -62, -223, -195, -245, -146, -0, -24, -67, -0, -222, -77, -158, -133, -250, -218, -10, -186, -187, -138, -239, -193, -87, -230, -212, -69, -23, -0, -123, -0, -63, -193, -201, -59, -142, -36, -147, -188, -64, -47, -116, -106, -0, -143, -35, -145, -229, -78, -200, -254, -29, -90, -68, -191, -173, -153, -200, -85, -202, -188, -86, -199, -79, -139, -59, -22, -87, -1, -64, -131, -64, -163, -67, -234, -207, -165, -148, -88, -84, -106, -189, -61, -213, -90, -167, -170, -204, -169, -27, -206, -123, -3, -94, -168, -23, -122, -44, -50, -95, -104, -17, -253, -182, -131, -0, -104, -125, -252, -180, -56, -99, -49, -22, -0, -149, -129, -70, -145, -232, -87, -7, -33, -39, -37, -115, -144, -237, -238, -161, -212, -132, -190, -111, -216, -135, -157, -145, -21, -192, -1, -122, -141, -210, -108, -201, -72, -252, -5, -112, -76, -238, -125, -101, -78, -221, -208, -4, -192, -202, -118, -178, -70, -230, -107, -209, -40, -250, -109, -7, -1, -48, -244, -227, -167, -197, -25, -250, -184, -46, -215, -255, -175, -173, -19, -238, -110, -68, -195, -127, -123, -69, -187, -224, -88, -12, -200, -10, -244, -100, -100, -85, -248, -36, -240, -59, -2, -130, -196, -16, -16, -104, -84, -199, -195, -34, -36, -120, -201, -151, -201, -147, -167, -252, -182, -142, -127, -40, -144, -143, -217, -124, -228, -68, -228, -103, -84, -56, -180, -33, -39, -6, -211, -234, -202, -156, -58, -139, -126, -143, -1, -17, -141, -45, -212, -36, -208, -68, -206, -203, -33, -48, -96, -165, -115, -3, -141, -162, -223, -118, -16, -0, -227, -230, -248, -105, -60, -64, -159, -219, -61, -72, -26, -180, -41, -250, -123, -33, -162, -60, -190, -180, -164, -77, -112, -44, -6, -157, -252, -54, -83, -213, -249, -72, -128, -218, -121, -200, -74, -174, -82, -8, -16, -16, -104, -20, -73, -152, -242, -170, -66, -217, -121, -68, -254, -208, -133, -2, -241, -124, -189, -163, -174, -204, -169, -171, -69, -140, -78, -173, -64, -238, -240, -241, -165, -26, -218, -87, -148, -77, -218, -154, -27, -104, -20, -253, -214, -105, -215, -38, -160, -68, -47, -199, -79, -180, -76, -87, -165, -180, -65, -95, -68, -242, -56, -141, -62, -188, -172, -77, -191, -187, -64, -175, -59, -31, -17, -164, -255, -3, -222, -132, -38, -74, -197, -241, -11, -112, -232, -27, -197, -98, -64, -143, -17, -113, -50, -60, -35, -66, -124, -62, -112, -82, -79, -247, -116, -61, -48, -169, -15, -222, -227, -14, -72, -162, -208, -203, -245, -37, -92, -74, -69, -22, -92, -196, -2, -236, -159, -74, -91, -155, -161, -69, -219, -88, -180, -141, -126, -59, -148, -227, -188, -16, -208, -62, -93, -85, -163, -232, -68, -72, -172, -185, -67, -157, -103, -240, -123, -224, -67, -68, -136, -149, -208, -20, -78, -31, -30, -194, -217, -82, -85, -208, -55, -138, -197, -160, -207, -99, -96, -43, -1, -252, -133, -10, -101, -90, -91, -32, -62, -19, -139, -128, -157, -98, -243, -30, -119, -64, -210, -45, -93, -175, -47, -248, -106, -52, -174, -89, -5, -189, -85, -162, -205, -2, -158, -31, -120, -141, -86, -19, -121, -140, -10, -128, -198, -233, -170, -104, -25, -157, -72, -219, -90, -84, -42, -128, -60, -253, -170, -92, -153, -208, -192, -191, -194, -233, -195, -103, -171, -232, -218, -66, -251, -112, -139, -167, -252, -12, -60, -251, -98, -58, -172, -144, -144, -192, -174, -243, -129, -61, -99, -222, -195, -184, -5, -18, -204, -16, -224, -6, -106, -164, -59, -146, -4, -19, -100, -73, -61, -96, -176, -80, -209, -174, -15, -1, -16, -253, -60, -191, -47, -208, -45, -58, -81, -168, -0, -104, -180, -50, -161, -129, -127, -133, -211, -135, -94, -146, -175, -146, -7, -134, -117, -21, -196, -47, -71, -156, -144, -188, -71, -210, -180, -88, -33, -1, -223, -70, -20, -140, -111, -9, -236, -215, -146, -218, -230, -38, -196, -232, -237, -33, -42, -182, -122, -228, -130, -125, -131, -50, -154, -49, -7, -242, -176, -87, -219, -212, -208, -109, -133, -44, -155, -230, -1, -219, -53, -188, -70, -91, -1, -48, -225, -207, -243, -27, -8, -128, -54, -43, -147, -32, -255, -138, -170, -247, -87, -66, -223, -232, -11, -13, -172, -135, -248, -22, -44, -2, -206, -5, -206, -68, -4, -218, -163, -192, -179, -107, -174, -85, -251, -124, -116, -34, -255, -82, -39, -114, -208, -170, -85, -219, -125, -67, -121, -47, -66, -244, -70, -183, -0, -191, -175, -160, -183, -31, -211, -198, -201, -68, -201, -243, -100, -222, -83, -65, -211, -40, -194, -117, -232, -133, -237, -36, -27, -80, -230, -20, -232, -78, -87, -186, -111, -182, -184, -70, -233, -0, -98, -140, -156, -231, -59, -125, -44, -53, -89, -174, -24, -212, -79, -33, -70, -95, -74, -174, -217, -203, -25, -48, -1, -254, -21, -85, -239, -175, -162, -77, -163, -47, -52, -34, -4, -78, -35, -215, -145, -156, -74, -128, -89, -122, -200, -243, -65, -182, -18, -51, -129, -111, -34, -251, -127, -247, -95, -105, -106, -58, -242, -120, -153, -95, -112, -202, -202, -66, -174, -175, -134, -156, -70, -52, -122, -78, -78, -251, -255, -105, -91, -111, -162, -91, -90, -68, -184, -142, -10, -231, -230, -26, -101, -12, -210, -182, -165, -3, -136, -49, -114, -158, -239, -244, -177, -106, -9, -105, -17, -124, -30, -75, -0, -2, -251, -53, -106, -198, -59, -85, -239, -175, -65, -219, -190, -4, -88, -45, -255, -154, -199, -255, -137, -138, -118, -214, -205, -183, -46, -148, -252, -81, -228, -113, -1, -138, -152, -26, -112, -15, -171, -146, -155, -226, -15, -196, -23, -164, -131, -14, -201, -69, -87, -103, -28, -59, -1, -163, -90, -9, -214, -224, -96, -99, -204, -91, -141, -49, -47, -55, -198, -220, -13, -220, -108, -140, -89, -195, -24, -179, -158, -49, -230, -219, -198, -152, -47, -27, -99, -158, -214, -150, -57, -34, -205, -247, -51, -198, -236, -229, -20, -79, -7, -238, -50, -198, -252, -57, -203, -178, -50, -251, -253, -221, -179, -44, -187, -174, -225, -229, -174, -51, -198, -244, -226, -55, -161, -247, -49, -191, -164, -250, -229, -89, -150, -85, -234, -25, -22, -119, -100, -89, -214, -56, -225, -12, -146, -164, -198, -158, -134, -205, -171, -33, -127, -194, -24, -115, -184, -49, -102, -127, -253, -253, -75, -45, -51, -198, -152, -233, -1, -151, -219, -198, -24, -147, -25, -99, -174, -206, -178, -204, -103, -202, -252, -87, -173, -223, -42, -203, -178, -73, -218, -191, -173, -141, -49, -167, -107, -93, -187, -163, -97, -100, -95, -52, -27, -56, -60, -128, -246, -30, -149, -80, -3, -38, -187, -1, -109, -75, -191, -32, -140, -210, -121, -190, -182, -63, -217, -233, -219, -44, -196, -168, -228, -94, -253, -253, -231, -138, -251, -8, -254, -154, -181, -105, -211, -166, -61, -13, -150, -220, -52, -223, -163, -151, -190, -191, -88, -253, -111, -139, -62, -248, -35, -123, -255, -219, -125, -15, -39, -176, -47, -141, -158, -19, -98, -57, -8, -145, -34, -8, -53, -185, -176, -117, -65, -172, -61, -107, -69, -156, -127, -64, -38, -224, -250, -78, -249, -166, -136, -107, -240, -238, -189, -118, -54, -50, -144, -100, -36, -22, -135, -224, -164, -196, -2, -214, -199, -227, -206, -217, -102, -176, -117, -29, -160, -77, -219, -135, -210, -211, -76, -96, -180, -26, -216, -109, -250, -63, -22, -248, -3, -63, -208, -127, -22, -71, -217, -178, -192, -190, -52, -21, -0, -183, -234, -191, -202, -208, -120, -209, -65, -179, -21, -192, -218, -136, -253, -183, -197, -253, -228, -81, -97, -64, -189, -253, -198, -11, -200, -163, -231, -94, -220, -160, -77, -227, -193, -214, -117, -128, -54, -109, -223, -7, -253, -48, -5, -0, -13, -61, -251, -186, -62, -223, -64, -222, -161, -167, -31, -173, -159, -83, -95, -125, -114, -177, -68, -177, -32, -203, -178, -189, -179, -44, -123, -122, -150, -101, -159, -169, -107, -156, -101, -217, -84, -35, -123, -141, -195, -141, -49, -83, -140, -236, -197, -151, -48, -198, -92, -108, -140, -249, -156, -49, -102, -219, -166, -29, -26, -101, -88, -101, -75, -173, -9, -239, -68, -71, -150, -227, -182, -33, -92, -238, -0, -99, -204, -242, -198, -152, -169, -198, -152, -104, -65, -84, -18, -186, -43, -1, -77, -150, -101, -15, -25, -99, -62, -171, -255, -198, -59, -236, -17, -211, -141, -45, -218, -94, -75, -201, -118, -176, -141, -194, -41, -65, -128, -216, -34, -124, -220, -136, -2, -109, -199, -44, -203, -238, -26, -229, -46, -45, -86, -24, -90, -72, -174, -50, -32, -249, -6, -239, -48, -34, -225, -175, -204, -178, -204, -119, -228, -81, -171, -212, -43, -78, -178, -54, -109, -140, -49, -118, -207, -239, -77, -131, -94, -131, -54, -26, -253, -36, -52, -234, -241, -27, -99, -204, -195, -198, -152, -183, -182, -56, -101, -73, -168, -193, -168, -11, -0, -211, -108, -121, -215, -102, -146, -53, -105, -51, -199, -136, -16, -88, -174, -142, -208, -131, -49, -117, -12, -184, -184, -32, -203, -178, -168, -193, -52, -19, -2, -129, -132, -8, -251, -15, -240, -120, -143, -215, -168, -13, -220, -160, -116, -67, -81, -180, -33, -102, -179, -0, -123, -213, -83, -15, -183, -111, -218, -238, -29, -12, -106, -233, -63, -136, -115, -90, -17, -227, -122, -125, -42, -209, -198, -59, -255, -166, -10, -55, -52, -206, -4, -145, -82, -220, -199, -232, -147, -139, -1, -37, -160, -50, -220, -223, -24, -243, -43, -99, -204, -43, -141, -49, -247, -55, -232, -64, -211, -7, -110, -151, -119, -219, -141, -145, -229, -221, -213, -250, -119, -151, -74, -170, -81, -0, -98, -216, -115, -134, -49, -230, -11, -78, -241, -251, -141, -49, -39, -25, -99, -134, -103, -254, -153, -208, -20, -183, -234, -223, -49, -55, -166, -140, -41, -17, -0, -198, -152, -125, -245, -239, -39, -178, -44, -123, -97, -9, -77, -103, -100, -89, -182, -105, -150, -101, -107, -103, -89, -118, -117, -61, -245, -80, -96, -195, -65, -189, -17, -248, -46, -35, -227, -179, -173, -5, -188, -125, -148, -250, -101, -178, -44, -91, -144, -149, -99, -66, -91, -245, -89, -216, -21, -146, -83, -180, -127, -200, -10, -169, -103, -28, -235, -244, -229, -102, -196, -132, -189, -214, -20, -120, -84, -225, -124, -209, -155, -250, -246, -143, -153, -37, -93, -219, -62, -145, -59, -56, -129, -156, -61, -223, -64, -30, -154, -42, -74, -150, -214, -190, -159, -87, -215, -235, -141, -197, -247, -25, -192, -115, -40, -17, -147, -28, -158, -161, -91, -128, -12, -216, -95, -199, -209, -108, -196, -122, -213, -27, -50, -109, -88, -125, -114, -81, -167, -4, -12, -138, -239, -215, -4, -208, -74, -59, -63, -76, -236, -98, -140, -249, -146, -49, -102, -79, -99, -204, -250, -198, -24, -171, -132, -186, -195, -24, -83, -26, -213, -53, -161, -30, -192, -59, -140, -49, -110, -34, -213, -253, -129, -179, -140, -49, -167, -103, -89, -214, -41, -22, -95, -150, -101, -11, -140, -216, -198, -143, -41, -100, -89, -134, -17, -255, -149, -131, -71, -187, -47, -62, -140, -230, -41, -192, -176, -142, -205, -26, -181, -201, -178, -108, -190, -49, -230, -123, -198, -152, -239, -57, -194, -106, -165, -44, -203, -234, -28, -56, -142, -163, -36, -33, -74, -150, -101, -91, -213, -180, -93, -236, -65, -174, -195, -112, -241, -126, -253, -247, -114, -99, -76, -218, -198, -140, -2, -70, -83, -0, -12, -235, -216, -44, -198, -81, -91, -101, -108, -63, -69, -155, -229, -236, -132, -17, -26, -99, -245, -11, -221, -20, -139, -155, -125, -198, -128, -0, -96, -100, -18, -133, -97, -186, -249, -134, -160, -141, -208, -104, -211, -198, -24, -19, -246, -178, -59, -14, -136, -94, -117, -0, -125, -46, -185, -19, -22, -51, -0, -203, -3, -47, -3, -254, -165, -10, -133, -191, -53, -104, -27, -172, -212, -25, -150, -210, -172, -111, -69, -214, -88, -71, -27, -165, -24, -45, -237, -12, -198, -2, -144, -0, -26, -199, -106, -191, -79, -243, -212, -15, -96, -52, -250, -233, -244, -103, -105, -228, -68, -224, -78, -26, -4, -240, -232, -179, -67, -22, -55, -2, -95, -1, -130, -131, -106, -36, -1, -48, -254, -209, -70, -96, -140, -21, -0, -239, -69, -162, -83, -221, -133, -68, -225, -169, -18, -0, -87, -17, -51, -115, -78, -75, -0, -27, -58, -125, -26, -181, -227, -101, -215, -14, -224, -127, -70, -246, -202, -235, -25, -99, -94, -104, -196, -60, -55, -97, -130, -160, -15, -59, -3, -224, -37, -200, -209, -151, -215, -156, -151, -6, -97, -200, -107, -176, -185, -49, -230, -95, -198, -152, -16, -107, -187, -247, -103, -89, -182, -149, -79, -199, -226, -147, -124, -109, -251, -76, -189, -11, -243, -221, -198, -152, -43, -141, -49, -119, -26, -99, -46, -15, -232, -119, -255, -0, -86, -68, -242, -145, -1, -252, -179, -65, -187, -224, -175, -109, -197, -87, -166, -234, -161, -7, -243, -239, -210, -70, -219, -5, -199, -210, -71, -76, -152, -255, -3, -236, -209, -228, -26, -19, -5, -192, -31, -144, -184, -118, -231, -251, -132, -0, -13, -194, -144, -55, -184, -38, -84, -175, -0, -74, -207, -202, -201, -227, -56, -218, -73, -238, -27, -139, -65, -125, -166, -38, -57, -233, -152, -5, -176, -142, -243, -176, -86, -10, -108, -19, -60, -217, -28, -218, -54, -65, -52, -135, -33, -0, -130, -99, -233, -147, -39, -79, -1, -248, -98, -13, -223, -37, -144, -40, -67, -143, -35, -65, -83, -14, -68, -98, -204, -21, -233, -62, -15, -92, -134, -132, -230, -126, -20, -248, -59, -240, -188, -38, -247, -48, -86, -128, -100, -198, -61, -205, -190, -239, -18, -154, -160, -48, -228, -13, -174, -9, -45, -5, -128, -67, -91, -23, -146, -174, -178, -207, -4, -250, -184, -116, -5, -18, -54, -124, -54, -240, -75, -79, -221, -47, -144, -237, -208, -79, -219, -48, -182, -216, -176, -33, -125, -223, -58, -128, -94, -133, -134, -182, -107, -20, -75, -31, -201, -42, -3, -240, -64, -13, -223, -47, -34, -81, -139, -191, -160, -255, -230, -227, -137, -62, -139, -164, -146, -62, -20, -120, -63, -176, -59, -146, -120, -226, -162, -10, -190, -149, -202, -36, -74, -80, -255, -36, -226, -1, -73, -45, -87, -26, -98, -142, -128, -48, -228, -13, -174, -5, -61, -11, -128, -186, -62, -19, -144, -156, -180, -105, -159, -74, -218, -219, -240, -125, -3, -238, -235, -78, -221, -147, -109, -24, -55, -234, -88, -147, -201, -214, -132, -214, -211, -166, -20, -49, -174, -211, -6, -192, -243, -235, -6, -139, -210, -93, -11, -252, -213, -249, -253, -87, -60, -166, -197, -158, -118, -135, -81, -145, -177, -150, -26, -101, -18, -178, -162, -25, -248, -231, -161, -179, -193, -80, -127, -94, -211, -159, -208, -231, -191, -51, -178, -2, -56, -64, -175, -57, -20, -43, -74, -237, -78, -239, -2, -32, -6, -154, -244, -169, -164, -253, -145, -200, -10, -224, -23, -158, -58, -187, -58, -56, -178, -247, -142, -53, -153, -108, -195, -154, -152, -195, -66, -232, -96, -65, -210, -79, -29, -230, -252, -62, -12, -24, -176, -179, -64, -146, -178, -254, -12, -177, -29, -159, -129, -198, -134, -175, -224, -107, -87, -0, -141, -226, -193, -123, -248, -172, -6, -220, -167, -183, -242, -158, -10, -186, -80, -129, -50, -3, -89, -229, -76, -209, -251, -25, -138, -82, -121, -34, -9, -128, -24, -24, -11, -1, -65, -38, -10, -22, -153, -145, -150, -112, -101, -158, -152, -191, -49, -18, -19, -254, -48, -99, -204, -100, -99, -204, -206, -250, -207, -11, -53, -93, -30, -136, -162, -212, -20, -89, -150, -61, -134, -164, -150, -250, -147, -49, -230, -112, -224, -239, -89, -150, -13, -88, -41, -102, -89, -22, -116, -60, -156, -101, -217, -10, -93, -251, -52, -150, -225, -8, -135, -141, -139, -113, -17, -171, -234, -198, -26, -202, -6, -161, -117, -2, -170, -76, -13, -150, -208, -8, -183, -24, -99, -54, -113, -126, -63, -207, -228, -190, -226, -46, -182, -54, -198, -28, -159, -101, -217, -215, -178, -44, -251, -189, -49, -102, -152, -97, -161, -207, -48, -198, -220, -100, -140, -89, -219, -24, -19, -39, -207, -92, -194, -168, -0, -248, -152, -174, -46, -94, -92, -69, -87, -38, -0, -172, -212, -122, -83, -192, -133, -92, -30, -115, -3, -251, -183, -56, -225, -169, -136, -73, -192, -186, -21, -116, -71, -27, -99, -182, -3, -246, -3, -246, -51, -198, -108, -103, -114, -95, -113, -23, -119, -24, -99, -182, -69, -148, -128, -223, -51, -198, -188, -173, -174, -3, -177, -150, -146, -234, -185, -102, -253, -233, -247, -41, -188, -219, -113, -1, -181, -91, -40, -221, -194, -76, -32, -172, -84, -248, -27, -14, -196, -178, -202, -226, -250, -26, -218, -245, -148, -110, -33, -1, -199, -134, -14, -223, -96, -141, -126, -0, -207, -159, -43, -207, -63, -4, -208, -238, -66, -158, -179, -109, -149, -166, -215, -42, -225, -121, -182, -242, -59, -23, -209, -14, -15, -152, -206, -34, -126, -225, -223, -36, -63, -6, -252, -14, -254, -99, -192, -215, -144, -251, -142, -159, -13, -236, -90, -183, -23, -141, -185, -151, -68, -108, -65, -108, -142, -197, -215, -117, -229, -55, -86, -208, -228, -25, -17, -118, -10, -80, -202, -47, -244, -90, -49, -223, -155, -135, -247, -55, -149, -119, -80, -218, -115, -31, -131, -119, -2, -23, -227, -81, -84, -57, -52, -75, -3, -39, -233, -133, -74, -143, -170, -10, -109, -106, -209, -176, -159, -59, -107, -179, -251, -129, -213, -43, -232, -50, -68, -35, -237, -34, -150, -0, -120, -58, -98, -62, -125, -99, -155, -123, -136, -112, -253, -168, -3, -9, -248, -183, -242, -59, -48, -6, -191, -177, -128, -38, -207, -136, -197, -67, -0, -28, -162, -188, -223, -87, -69, -87, -170, -4, -204, -178, -236, -12, -51, -232, -191, -109, -153, -223, -103, -140, -185, -215, -136, -217, -240, -218, -70, -162, -233, -238, -23, -210, -177, -152, -238, -148, -72, -250, -114, -155, -193, -232, -179, -89, -150, -61, -90, -66, -183, -188, -145, -96, -30, -239, -50, -18, -247, -111, -99, -211, -102, -105, -84, -142, -21, -140, -49, -47, -50, -242, -60, -102, -26, -89, -198, -143, -103, -156, -103, -140, -121, -181, -49, -166, -242, -28, -59, -97, -76, -35, -104, -11, -208, -118, -143, -55, -199, -136, -221, -245, -211, -140, -49, -103, -25, -99, -94, -155, -249, -51, -152, -246, -141, -221, -141, -49, -207, -52, -198, -220, -108, -140, -249, -99, -5, -221, -95, -140, -76, -254, -11, -141, -236, -189, -99, -251, -116, -159, -98, -140, -249, -128, -17, -13, -250, -218, -89, -150, -85, -42, -94, -198, -1, -172, -0, -171, -210, -105, -36, -140, -50, -144, -179, -126, -128, -123, -60, -213, -43, -234, -223, -74, -1, -208, -234, -24, -48, -203, -178, -32, -11, -193, -62, -129, -36, -77, -180, -230, -183, -135, -168, -2, -171, -12, -63, -54, -198, -220, -99, -140, -249, -120, -150, -101, -243, -104, -224, -233, -24, -208, -143, -149, -141, -49, -175, -215, -159, -95, -201, -178, -172, -77, -82, -145, -177, -134, -135, -244, -239, -26, -163, -218, -139, -132, -58, -188, -74, -255, -254, -213, -83, -103, -39, -254, -138, -158, -186, -167, -48, -158, -237, -0, -94, -105, -140, -217, -192, -200, -145, -165, -215, -76, -215, -34, -203, -178, -179, -140, -172, -84, -44, -150, -46, -163, -109, -129, -167, -38, -73, -150, -101, -247, -70, -228, -59, -154, -120, -68, -255, -174, -54, -170, -189, -72, -40, -5, -176, -170, -17, -175, -93, -99, -140, -249, -181, -135, -164, -217, -22, -0, -152, -171, -203, -137, -168, -174, -137, -52, -243, -174, -219, -20, -201, -208, -123, -175, -246, -103, -10, -146, -173, -216, -103, -225, -182, -173, -254, -189, -34, -32, -94, -95, -159, -88, -172, -66, -68, -41, -150, -209, -191, -211, -70, -181, -23, -9, -85, -216, -198, -200, -216, -187, -186, -100, -251, -221, -120, -11, -48, -221, -24, -179, -186, -137, -255, -210, -223, -102, -140, -89, -214, -136, -146, -236, -82, -35, -82, -235, -185, -70, -246, -238, -69, -28, -98, -196, -16, -230, -116, -35, -6, -41, -27, -26, -73, -12, -185, -145, -49, -230, -141, -5, -218, -109, -244, -239, -121, -145, -251, -155, -144, -191, -155, -135, -71, -181, -23, -139, -57, -58, -42, -196, -223, -160, -127, -15, -45, -169, -111, -188, -5, -152, -102, -74, -4, -0, -226, -110, -248, -97, -99, -204, -113, -89, -150, -237, -19, -90, -167, -56, -203, -72, -236, -187, -201, -250, -123, -178, -145, -32, -8, -87, -122, -104, -63, -100, -228, -185, -60, -117, -244, -8, -60, -215, -248, -77, -93, -173, -143, -245, -120, -215, -184, -143, -69, -88, -247, -227, -74, -15, -199, -113, -138, -147, -81, -223, -133, -113, -30, -120, -117, -71, -35, -6, -123, -167, -150, -212, -55, -51, -4, -2, -174, -211, -45, -192, -241, -158, -186, -16, -183, -195, -168, -202, -47, -224, -105, -192, -235, -17, -191, -235, -223, -122, -234, -109, -178, -142, -183, -182, -224, -109, -209, -217, -14, -0, -216, -76, -121, -205, -235, -202, -171, -67, -31, -162, -158, -39, -3, -103, -41, -191, -178, -175, -203, -184, -3, -30, -84, -208, -14, -197, -14, -160, -79, -56, -91, -250, -202, -36, -36, -197, -45, -128, -49, -254, -45, -192, -137, -70, -190, -242, -191, -241, -212, -157, -96, -140, -249, -168, -254, -141, -130, -194, -131, -63, -215, -24, -227, -139, -184, -99, -39, -175, -247, -236, -127, -136, -216, -65, -255, -250, -236, -250, -199, -29, -144, -19, -146, -109, -245, -231, -57, -163, -216, -149, -168, -8, -89, -110, -147, -91, -161, -214, -158, -18, -85, -241, -27, -11, -161, -195, -179, -44, -91, -182, -158, -202, -1, -226, -159, -14, -61, -89, -127, -53, -145, -138, -192, -14, -136, -133, -223, -17, -218, -230, -32, -15, -205, -227, -90, -183, -181, -143, -71, -96, -95, -58, -173, -0, -16, -147, -93, -139, -82, -143, -189, -190, -17, -243, -139, -131, -248, -32, -128, -132, -189, -90, -166, -190, -197, -226, -131, -38, -171, -132, -197, -14, -72, -8, -104, -144, -204, -192, -125, -240, -111, -53, -72, -129, -11, -241, -68, -147, -1, -110, -85, -126, -141, -179, -174, -70, -20, -0, -143, -33, -230, -210, -239, -236, -194, -167, -43, -34, -11, -128, -75, -149, -215, -119, -99, -244, -45, -97, -108, -35, -116, -11, -48, -20, -0, -171, -100, -89, -246, -132, -243, -123, -73, -99, -204, -51, -140, -63, -71, -225, -253, -70, -78, -7, -188, -17, -103, -135, -129, -44, -203, -22, -171, -115, -114, -196, -113, -228, -53, -70, -18, -194, -44, -54, -251, -255, -132, -114, -184, -166, -192, -211, -10, -127, -71, -3, -83, -129, -51, -144, -212, -220, -95, -55, -198, -92, -96, -100, -130, -255, -202, -67, -107, -247, -107, -111, -240, -212, -77, -24, -48, -210, -243, -176, -52, -238, -94, -0, -159, -149, -141, -49, -71, -233, -207, -207, -186, -130, -56, -97, -2, -128, -220, -125, -176, -151, -36, -5, -33, -203, -84, -224, -32, -196, -163, -110, -38, -18, -106, -106, -50, -18, -76, -115, -32, -40, -6, -240, -70, -229, -55, -151, -134, -153, -107, -98, -109, -1, -198, -2, -128, -183, -235, -189, -60, -94, -79, -93, -201, -199, -134, -131, -111, -30, -67, -46, -97, -252, -3, -216, -87, -7, -192, -182, -61, -241, -143, -182, -79, -85, -126, -203, -34, -145, -87, -1, -118, -107, -217, -151, -113, -43, -0, -128, -187, -145, -248, -245, -243, -244, -94, -14, -24, -237, -62, -37, -140, -99, -144, -135, -16, -10, -201, -174, -210, -134, -127, -84, -1, -160, -60, -247, -87, -158, -147, -98, -241, -28, -47, -64, -148, -160, -243, -144, -64, -158, -95, -247, -173, -146, -18, -18, -198, -12, -122, -18, -0, -43, -34, -201, -51, -0, -106, -67, -103, -37, -36, -36, -140, -18, -250, -16, -0, -202, -247, -67, -202, -247, -238, -241, -188, -164, -79, -72, -24, -85, -32, -94, -119, -179, -129, -195, -235, -169, -91, -241, -15, -22, -0, -84, -164, -60, -42, -161, -63, -82, -121, -255, -174, -123, -79, -19, -18, -38, -32, -200, -109, -250, -123, -57, -254, -105, -40, -0, -122, -241, -47, -72, -72, -72, -24, -9, -215, -14, -224, -68, -35, -161, -190, -134, -146, -194, -169, -6, -39, -24, -233, -75, -52, -255, -130, -132, -132, -132, -81, -68, -95, -58, -128, -132, -132, -132, -132, -132, -132, -132, -137, -0, -93, -69, -148, -165, -3, -111, -156, -84, -36, -33, -97, -34, -99, -220, -165, -126, -74, -72, -72, -136, -135, -36, -0, -18, -18, -38, -48, -122, -21, -0, -85, -138, -63, -231, -168, -111, -251, -62, -251, -144, -144, -144, -80, -142, -209, -92, -1, -60, -92, -248, -155, -144, -144, -48, -100, -140, -5, -1, -240, -72, -177, -194, -177, -74, -60, -98, -200, -125, -74, -72, -152, -80, -24, -77, -1, -96, -39, -190, -111, -5, -176, -155, -145, -192, -140, -31, -30, -94, -119, -18, -18, -38, -30, -70, -123, -5, -48, -61, -203, -178, -185, -158, -58, -107, -149, -232, -139, -66, -156, -144, -144, -16, -9, -163, -153, -27, -240, -97, -227, -89, -254, -27, -99, -76, -150, -101, -123, -27, -99, -246, -30, -110, -119, -18, -18, -18, -162, -98, -152, -230, -191, -201, -16, -40, -33, -161, -57, -146, -29, -64, -66, -194, -4, -70, -18, -0, -9, -9, -19, -24, -73, -0, -36, -36, -76, -96, -36, -1, -144, -144, -48, -129, -145, -4, -64, -66, -194, -4, -198, -184, -19, -0, -73, -219, -159, -144, -16, -15, -227, -78, -0, -36, -36, -36, -196, -67, -18, -0, -9, -9, -19, -24, -73, -0, -36, -36, -76, -96, -36, -1, -144, -144, -48, -117, -14, -169, -49, -0, -0, -2, -74, -73, -68, -65, -84, -129, -145, -4, -64, -66, -194, -4, -70, -175, -206, -64, -89, -150, -101, -125, -242, -79, -72, -72, -232, -134, -180, -2, -72, -72, -152, -192, -72, -2, -32, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -225, -255, -219, -131, -3, -2, -0, -0, -0, -0, -33, -253, -95, -221, -17, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -208, -17, -112, -180, -144, -16, -164, -2, -78, -0, -0, -0, -0, -73, -69, -78, -68, -174, -66, -96, -130, -}; -/* clang-format on */ diff --git a/scene/resources/default_theme/font_lodpi.inc b/scene/resources/default_theme/font_lodpi.inc deleted file mode 100644 index d2f5851224..0000000000 --- a/scene/resources/default_theme/font_lodpi.inc +++ /dev/null @@ -1,13117 +0,0 @@ -/* clang-format off */ -static const int _lodpi_font_height=14; -static const int _lodpi_font_ascent=11; -static const int _lodpi_font_charcount=191; -static const int _lodpi_font_charrects[191][8]={ -/* charidx , ofs_x, ofs_y, size_x, size_y, valign, halign, advance */ -{64,72,34,10,11,1,1,12}, -{224,85,180,5,11,0,1,7}, -{192,32,16,11,13,-2,-1,9}, -{96,2,216,3,2,0,3,8}, -{160,0,0,0,0,11,0,4}, -{32,0,0,0,0,11,0,4}, -{33,65,234,2,10,1,1,4}, -{225,112,169,5,11,0,1,7}, -{193,17,16,11,13,-2,-1,9}, -{161,2,222,2,11,3,1,4}, -{65,2,16,11,10,1,-1,9}, -{97,76,188,5,8,3,1,7}, -{98,102,165,6,11,0,1,8}, -{226,72,143,6,11,0,1,7}, -{194,113,2,11,13,-2,-1,9}, -{66,46,109,7,10,1,1,9}, -{162,12,136,6,10,1,1,8}, -{34,49,187,5,4,1,1,6}, -{35,78,66,8,10,1,0,9}, -{163,22,167,6,10,1,1,8}, -{195,53,2,11,14,-3,-1,9}, -{227,2,155,6,12,-1,1,7}, -{67,68,115,7,10,1,1,8}, -{99,40,179,5,8,3,1,7}, -{228,121,169,5,11,0,1,7}, -{196,98,2,11,13,-2,-1,9}, -{36,102,137,6,12,0,1,8}, -{100,82,150,6,11,0,1,8}, -{68,90,66,8,10,1,1,10}, -{164,14,79,8,7,3,0,8}, -{37,2,30,10,10,1,1,12}, -{69,29,191,5,10,1,1,7}, -{165,79,98,7,10,1,0,8}, -{229,20,196,5,12,-1,1,7}, -{197,83,2,11,12,-1,-1,9}, -{101,32,124,6,8,3,1,8}, -{38,67,49,9,10,1,1,10}, -{70,101,105,6,10,1,1,7}, -{198,21,2,12,10,1,-1,12}, -{166,95,228,2,14,0,3,7}, -{102,2,201,5,11,0,0,4}, -{230,58,34,10,8,3,1,12}, -{71,66,65,8,10,1,1,10}, -{231,2,186,5,11,3,1,7}, -{199,57,97,7,13,1,1,8}, -{103,13,107,7,11,3,1,7}, -{167,112,131,6,11,0,0,7}, -{39,119,219,2,4,1,1,3}, -{72,54,65,8,10,1,1,10}, -{232,62,143,6,11,0,1,8}, -{200,47,195,5,13,-2,1,7}, -{40,93,212,4,12,1,1,4}, -{104,72,158,6,11,0,1,8}, -{168,77,217,4,2,0,2,8}, -{73,38,191,5,10,1,0,5}, -{169,44,34,10,10,1,1,12}, -{233,52,142,6,11,0,1,8}, -{201,56,197,5,13,-2,1,7}, -{41,51,226,3,12,1,0,4}, -{105,109,213,3,11,0,0,4}, -{106,101,213,4,14,0,-1,4}, -{74,92,195,5,13,1,-2,3}, -{202,65,202,5,13,-2,1,7}, -{42,108,80,7,6,0,0,8}, -{170,29,205,4,5,1,0,5}, -{234,12,181,6,11,0,1,8}, -{171,22,181,5,6,4,1,7}, -{43,101,94,7,7,3,0,8}, -{107,112,92,7,11,0,1,7}, -{203,83,200,5,13,-2,1,7}, -{235,2,171,6,11,0,1,8}, -{75,102,66,8,10,1,1,8}, -{44,107,231,2,3,9,1,4}, -{172,2,104,7,4,6,0,8}, -{108,113,228,2,11,0,1,4}, -{204,101,196,5,13,-2,0,5}, -{236,30,214,3,11,0,0,4}, -{76,22,124,6,10,1,1,7}, -{173,16,229,3,2,7,1,5}, -{45,123,201,3,2,7,1,5}, -{109,68,2,11,8,3,1,13}, -{205,11,211,5,13,-2,0,5}, -{237,37,214,3,11,0,1,4}, -{77,62,20,10,10,1,1,12}, -{46,101,231,2,2,9,1,4}, -{110,111,107,6,8,3,1,8}, -{206,20,212,5,13,-2,0,5}, -{238,11,196,5,11,0,-1,4}, -{174,30,33,10,10,1,1,12}, -{78,2,79,8,10,1,1,10}, -{175,35,111,7,1,-1,0,7}, -{111,102,153,6,8,3,1,8}, -{207,119,184,5,13,-2,0,5}, -{239,69,219,4,11,0,0,4}, -{79,41,66,9,10,1,1,11}, -{47,90,105,7,10,1,-1,5}, -{176,61,219,4,4,1,1,6}, -{112,32,150,6,11,3,1,8}, -{240,82,165,6,11,0,1,8}, -{208,86,33,9,10,1,0,10}, -{80,52,128,6,10,1,1,8}, -{48,42,135,6,10,1,1,8}, -{177,46,97,7,8,3,0,8}, -{113,22,152,6,11,3,1,8}, -{241,2,112,6,12,-1,1,8}, -{81,15,59,9,13,1,1,11}, -{209,74,80,8,14,-3,1,10}, -{49,45,212,4,10,1,2,8}, -{178,58,187,5,6,1,0,5}, -{114,85,217,4,8,3,1,5}, -{210,2,62,9,13,-2,1,11}, -{242,62,165,6,11,0,1,8}, -{82,35,97,7,10,1,1,8}, -{50,57,114,7,10,1,1,8}, -{179,53,214,4,6,1,0,5}, -{115,112,146,6,8,3,0,7}, -{211,106,49,9,13,-2,1,11}, -{243,52,172,6,11,0,1,8}, -{83,24,96,7,10,1,0,7}, -{51,22,138,6,10,1,1,8}, -{180,9,228,3,2,0,3,8}, -{116,67,188,5,10,1,0,5}, -{212,93,49,9,13,-2,1,11}, -{244,42,164,6,11,0,1,8}, -{84,13,93,7,10,1,0,7}, -{52,24,110,7,10,1,1,8}, -{53,92,119,6,10,1,1,8}, -{85,114,66,8,10,1,1,10}, -{213,2,44,9,14,-3,1,11}, -{117,42,123,6,8,3,1,8}, -{181,2,140,6,11,3,1,8}, -{245,12,165,6,12,-1,1,8}, -{54,82,121,6,10,1,1,8}, -{86,76,18,10,10,1,-1,8}, -{246,72,173,6,11,0,1,8}, -{214,80,49,9,13,-2,1,11}, -{182,68,98,7,13,0,1,9}, -{118,15,47,9,8,3,-1,7}, -{55,72,129,6,10,1,1,8}, -{87,2,2,15,10,1,-1,13}, -{119,37,2,12,8,3,-1,10}, -{247,90,94,7,7,3,0,8}, -{215,2,93,7,7,3,0,8}, -{183,77,223,2,2,5,1,4}, -{56,62,129,6,10,1,1,8}, -{88,90,19,10,10,1,-1,8}, -{216,99,33,9,12,0,1,11}, -{248,2,128,6,8,3,1,8}, -{120,119,80,7,8,3,0,7}, -{184,116,212,3,3,11,0,3}, -{89,28,65,9,10,1,-1,7}, -{217,38,80,8,13,-2,1,10}, -{249,52,157,6,11,0,1,8}, -{121,112,33,9,11,3,-1,7}, -{57,12,122,6,10,1,1,8}, -{185,23,229,3,6,1,0,5}, -{218,26,79,8,13,-2,1,10}, -{250,42,149,6,11,0,1,8}, -{90,32,136,6,10,1,1,8}, -{122,112,119,6,8,3,1,7}, -{58,89,229,2,8,3,1,4}, -{186,37,205,4,5,1,0,5}, -{219,50,80,8,13,-2,1,10}, -{91,58,227,3,12,1,1,4}, -{123,103,180,5,12,1,0,5}, -{251,12,150,6,11,0,1,8}, -{59,71,234,2,9,3,1,4}, -{187,31,181,5,6,4,1,7}, -{188,16,33,10,10,1,0,10}, -{124,83,229,2,14,0,3,7}, -{220,62,79,8,13,-2,1,10}, -{252,92,133,6,11,0,1,8}, -{92,97,80,7,10,1,-1,5}, -{60,92,153,6,7,3,1,8}, -{189,47,20,11,10,1,0,10}, -{253,28,47,9,14,0,-1,7}, -{221,54,48,9,13,-2,-1,7}, -{93,44,226,3,12,1,0,4}, -{125,110,196,5,12,1,0,5}, -{61,79,112,7,5,4,0,8}, -{190,104,19,10,10,1,0,10}, -{222,32,165,6,10,1,1,8}, -{254,102,119,6,14,0,1,8}, -{62,112,158,6,7,3,1,8}, -{94,86,80,7,6,1,0,7}, -{126,62,158,6,3,5,1,8}, -{223,82,135,6,11,0,1,8}, -{255,41,48,9,14,0,-1,7}, -{191,94,180,5,11,3,0,6}, -{63,74,202,5,10,1,0,6}, -{95,92,148,6,1,12,0,6}, -}; -static const int _lodpi_font_kerning_pair_count=0; -static const int _lodpi_font_kerning_pairs[1][3]={ -{0,0,0}, -}; -static const int _lodpi_font_img_width=128; -static const int _lodpi_font_img_height=256; -static const int _lodpi_font_img_data_size=12909; -static const unsigned char _lodpi_font_img_data[12909]={ -137, -80, -78, -71, -13, -10, -26, -10, -0, -0, -0, -13, -73, -72, -68, -82, -0, -0, -0, -128, -0, -0, -1, -0, -8, -6, -0, -0, -0, -123, -249, -126, -167, -0, -0, -32, -0, -73, -68, -65, -84, -120, -156, -237, -157, -119, -184, -30, -85, -181, -255, -191, -59, -128, -8, -210, -155, -82, -77, -232, -221, -2, -210, -164, -132, -26, -154, -5, -164, -131, -63, -224, -98, -65, -4, -68, -46, -8, -42, -23, -84, -80, -41, -94, -16, -21, -17, -197, -10, -22, -80, -17, -21, -4, -5, -65, -16, -41, -42, -185, -116, -19, -106, -128, -80, -46, -32, -33, -148, -208, -18, -62, -191, -63, -214, -122, -115, -230, -204, -217, -51, -179, -103, -222, -121, -223, -115, -146, -123, -190, -207, -115, -158, -51, -101, -237, -50, -243, -174, -217, -123, -237, -213, -182, -52, -138, -81, -140, -98, -20, -2, -150, -3, -222, -0, -54, -207, -93, -255, -32, -134, -247, -231, -174, -111, -237, -244, -203, -38, -212, -61, -31, -48, -149, -2, -68, -232, -87, -242, -91, -107, -102, -174, -253, -28, -184, -58, -115, -190, -178, -211, -172, -88, -243, -57, -199, -84, -220, -7, -216, -17, -184, -21, -120, -21, -184, -23, -216, -9, -56, -192, -143, -103, -0, -55, -1, -235, -36, -180, -245, -117, -224, -122, -224, -244, -58, -125, -244, -178, -247, -0, -239, -4, -222, -13, -220, -85, -179, -236, -55, -253, -57, -190, -89, -69, -59, -251, -101, -132, -16, -30, -151, -116, -171, -164, -157, -114, -52, -59, -74, -186, -94, -210, -118, -185, -235, -59, -73, -250, -123, -8, -225, -137, -132, -62, -237, -39, -233, -113, -73, -11, -248, -249, -206, -126, -188, -64, -230, -218, -108, -132, -16, -30, -145, -116, -175, -164, -205, -252, -129, -230, -149, -180, -161, -164, -21, -129, -133, -156, -108, -115, -73, -147, -67, -8, -143, -150, -53, -12, -28, -9, -76, -3, -206, -4, -198, -74, -186, -49, -161, -191, -103, -75, -58, -76, -210, -242, -178, -103, -191, -88, -210, -17, -146, -118, -151, -52, -78, -210, -51, -146, -190, -157, -80, -207, -102, -33, -132, -45, -37, -109, -157, -64, -155, -199, -204, -204, -241, -172, -212, -66, -192, -119, -37, -45, -41, -105, -13, -73, -75, -251, -121, -114, -225, -19, -128, -137, -185, -107, -15, -1, -59, -3, -255, -202, -93, -191, -11, -248, -108, -98, -189, -119, -2, -251, -250, -49, -64, -158, -153, -98, -101, -206, -1, -206, -247, -227, -109, -128, -75, -129, -31, -117, -70, -34, -224, -188, -20, -14, -7, -110, -241, -209, -226, -76, -224, -57, -224, -136, -10, -122, -128, -79, -100, -206, -215, -205, -247, -217, -71, -136, -23, -18, -218, -190, -223, -203, -222, -93, -69, -59, -34, -0, -172, -159, -29, -214, -129, -53, -128, -71, -252, -120, -42, -176, -188, -31, -175, -232, -15, -182, -118, -66, -157, -59, -1, -143, -250, -87, -92, -135, -1, -62, -216, -121, -113, -192, -89, -192, -97, -192, -65, -192, -185, -126, -237, -110, -114, -211, -82, -65, -61, -165, -67, -126, -132, -30, -96, -219, -204, -249, -88, -191, -54, -54, -115, -109, -115, -24, -58, -117, -69, -234, -186, -12, -248, -53, -240, -251, -58, -125, -240, -178, -59, -121, -187, -249, -17, -185, -167, -101, -59, -95, -252, -193, -126, -124, -100, -230, -43, -252, -17, -112, -144, -31, -127, -28, -184, -47, -177, -190, -107, -128, -207, -100, -206, -135, -160, -160, -220, -162, -192, -107, -254, -255, -62, -76, -46, -88, -14, -120, -16, -88, -204, -239, -45, -146, -216, -135, -228, -23, -226, -116, -155, -103, -206, -59, -12, -176, -66, -230, -90, -37, -3, -0, -227, -128, -151, -252, -99, -121, -41, -203, -64, -137, -125, -238, -134, -121, -26, -151, -21, -112, -54, -112, -177, -31, -255, -1, -216, -211, -143, -247, -7, -46, -244, -227, -75, -129, -175, -37, -212, -245, -110, -224, -5, -96, -177, -204, -53, -252, -7, -121, -115, -231, -175, -164, -252, -77, -192, -129, -192, -29, -153, -107, -119, -0, -7, -3, -55, -212, -120, -166, -228, -23, -210, -34, -3, -156, -14, -252, -218, -143, -47, -1, -78, -171, -209, -223, -198, -204, -211, -45, -227, -9, -216, -22, -19, -154, -22, -0, -158, -7, -22, -247, -235, -111, -5, -30, -199, -36, -250, -231, -201, -173, -22, -10, -234, -250, -121, -236, -139, -39, -97, -10, -240, -242, -39, -3, -55, -0, -95, -201, -92, -59, -195, -25, -227, -164, -196, -58, -106, -189, -144, -54, -24, -192, -25, -251, -25, -96, -15, -63, -223, -195, -207, -11, -153, -61, -87, -190, -27, -230, -105, -92, -182, -83, -193, -188, -206, -0, -31, -7, -110, -206, -221, -187, -29, -27, -9, -158, -162, -122, -57, -53, -22, -27, -166, -215, -206, -126, -237, -53, -25, -96, -75, -167, -223, -44, -115, -109, -187, -252, -181, -138, -58, -106, -189, -144, -150, -24, -224, -64, -255, -72, -22, -240, -243, -55, -3, -211, -129, -3, -19, -250, -219, -152, -121, -186, -101, -188, -108, -69, -63, -195, -164, -231, -47, -230, -174, -127, -205, -191, -190, -31, -36, -212, -113, -54, -240, -219, -200, -117, -72, -156, -2, -186, -69, -107, -47, -164, -126, -187, -127, -39, -142, -91, -18, -202, -118, -195, -60, -141, -203, -230, -43, -218, -219, -59, -252, -222, -220, -245, -9, -126, -253, -3, -21, -229, -23, -7, -94, -4, -198, -71, -238, -13, -65, -173, -206, -213, -64, -107, -47, -164, -94, -155, -239, -1, -102, -97, -43, -168, -236, -200, -183, -154, -95, -223, -176, -162, -124, -55, -204, -211, -184, -236, -92, -137, -225, -120, -33, -216, -106, -233, -55, -5, -247, -46, -5, -126, -88, -82, -182, -49, -243, -116, -203, -120, -115, -29, -134, -227, -133, -0, -75, -2, -47, -3, -91, -21, -220, -223, -214, -239, -47, -89, -112, -191, -27, -230, -105, -92, -118, -174, -196, -156, -246, -66, -186, -97, -158, -110, -25, -111, -174, -195, -232, -11, -105, -1, -116, -163, -78, -180, -242, -75, -1, -255, -11, -252, -180, -102, -185, -159, -123, -185, -165, -154, -180, -59, -138, -150, -64, -55, -234, -68, -43, -127, -5, -166, -195, -175, -171, -139, -159, -7, -51, -163, -254, -161, -73, -187, -163, -104, -1, -116, -171, -78, -156, -139, -1, -44, -3, -188, -14, -108, -19, -185, -183, -131, -223, -91, -102, -56, -250, -214, -4, -69, -95, -231, -39, -36, -93, -233, -182, -246, -63, -250, -121, -50, -186, -153, -62, -186, -157, -122, -122, -141, -16, -194, -83, -146, -174, -146, -180, -103, -228, -246, -158, -146, -254, -228, -52, -131, -0, -44, -136, -57, -121, -124, -46, -114, -239, -72, -191, -183, -96, -228, -222, -217, -152, -129, -238, -62, -224, -204, -204, -245, -177, -152, -133, -54, -201, -32, -150, -12, -90, -208, -158, -117, -51, -125, -212, -41, -235, -194, -222, -143, -129, -215, -114, -215, -43, -95, -142, -51, -217, -171, -184, -173, -35, -119, -239, -77, -192, -211, -80, -104, -169, -220, -215, -229, -148, -121, -50, -215, -230, -245, -50, -251, -148, -180, -185, -30, -102, -28, -219, -58, -115, -109, -99, -204, -79, -97, -221, -130, -50, -107, -250, -180, -184, -46, -240, -104, -230, -250, -133, -192, -167, -138, -218, -106, -12, -186, -212, -158, -117, -51, -125, -212, -45, -11, -252, -2, -179, -77, -204, -200, -93, -175, -124, -57, -206, -0, -55, -147, -113, -254, -200, -220, -219, -3, -184, -177, -132, -1, -22, -244, -119, -180, -77, -230, -218, -118, -217, -247, -86, -210, -238, -199, -128, -39, -129, -101, -157, -129, -31, -6, -62, -86, -81, -230, -58, -103, -174, -143, -248, -249, -187, -48, -103, -147, -55, -149, -149, -107, -4, -186, -212, -158, -49, -12, -150, -44, -96, -102, -230, -56, -233, -229, -248, -51, -29, -79, -206, -224, -229, -247, -254, -0, -28, -81, -196, -0, -78, -243, -35, -220, -57, -197, -207, -207, -3, -126, -148, -216, -223, -95, -96, -190, -130, -127, -0, -126, -145, -88, -102, -89, -224, -223, -126, -252, -39, -220, -195, -170, -85, -208, -189, -30, -123, -88, -44, -89, -57, -6, -72, -122, -57, -206, -0, -219, -98, -243, -235, -26, -153, -235, -203, -3, -255, -196, -220, -208, -202, -24, -96, -59, -124, -26, -240, -191, -167, -200, -120, -18, -85, -180, -189, -48, -54, -69, -61, -69, -197, -28, -142, -59, -189, -2, -107, -97, -158, -85, -219, -99, -14, -171, -1, -27, -253, -94, -43, -43, -95, -11, -116, -169, -61, -99, -152, -44, -89, -29, -6, -200, -188, -156, -147, -128, -73, -216, -240, -26, -157, -10, -156, -1, -182, -195, -124, -14, -190, -156, -185, -254, -121, -224, -112, -191, -87, -198, -0, -99, -128, -199, -156, -81, -182, -246, -31, -52, -105, -201, -235, -31, -218, -116, -108, -154, -27, -178, -154, -200, -209, -222, -130, -9, -128, -119, -2, -239, -243, -231, -219, -14, -88, -221, -127, -175, -25, -101, -229, -147, -65, -11, -218, -51, -134, -201, -146, -5, -204, -244, -47, -162, -243, -114, -54, -197, -28, -90, -22, -3, -166, -23, -148, -233, -48, -192, -106, -206, -40, -99, -188, -142, -187, -49, -107, -102, -41, -3, -120, -29, -103, -96, -186, -142, -179, -73, -116, -253, -118, -198, -254, -23, -240, -73, -224, -88, -108, -4, -90, -168, -186, -164, -4, -236, -7, -252, -17, -152, -31, -184, -26, -120, -27, -240, -122, -74, -217, -158, -131, -97, -182, -100, -117, -94, -78, -238, -218, -4, -224, -138, -2, -122, -112, -199, -20, -76, -224, -219, -22, -24, -143, -207, -201, -137, -12, -240, -14, -108, -249, -54, -25, -88, -191, -170, -143, -94, -230, -76, -224, -42, -103, -182, -121, -128, -127, -0, -149, -46, -230, -216, -202, -228, -126, -44, -86, -224, -28, -6, -166, -202, -153, -85, -101, -251, -2, -134, -209, -146, -149, -125, -57, -153, -107, -155, -2, -215, -82, -108, -121, -203, -50, -192, -161, -216, -114, -242, -2, -96, -123, -191, -86, -201, -0, -78, -119, -59, -112, -123, -21, -157, -211, -110, -9, -60, -75, -38, -152, -5, -243, -196, -126, -149, -204, -210, -176, -160, -236, -81, -192, -5, -126, -252, -47, -108, -138, -155, -228, -207, -49, -188, -90, -83, -134, -217, -146, -149, -125, -57, -126, -190, -13, -112, -17, -176, -112, -73, -153, -44, -3, -44, -14, -252, -219, -95, -236, -24, -191, -150, -196, -0, -169, -0, -222, -2, -60, -64, -68, -166, -193, -228, -144, -7, -129, -183, -20, -148, -93, -4, -147, -55, -198, -70, -238, -141, -140, -17, -96, -184, -16, -123, -57, -206, -48, -247, -101, -190, -146, -141, -34, -229, -102, -51, -128, -159, -255, -146, -140, -11, -92, -219, -12, -48, -226, -1, -108, -229, -28, -122, -47, -176, -113, -230, -250, -242, -152, -144, -212, -174, -202, -113, -20, -35, -11, -152, -20, -190, -13, -166, -139, -191, -46, -115, -253, -7, -192, -145, -195, -217, -183, -81, -244, -1, -216, -154, -244, -45, -192, -66, -248, -210, -9, -211, -63, -223, -75, -47, -84, -142, -163, -24, -89, -192, -140, -20, -29, -6, -120, -222, -175, -93, -78, -137, -113, -195, -105, -26, -27, -100, -156, -110, -39, -204, -0, -116, -149, -255, -93, -6, -76, -232, -254, -137, -134, -31, -12, -198, -12, -44, -170, -233, -68, -42, -236, -5, -5, -117, -189, -189, -224, -250, -82, -190, -138, -200, -135, -245, -111, -130, -197, -101, -44, -157, -218, -192, -13, -153, -41, -224, -111, -216, -154, -248, -159, -152, -150, -107, -136, -108, -144, -41, -215, -141, -65, -230, -100, -224, -123, -89, -9, -31, -88, -218, -175, -149, -70, -254, -212, -101, -28, -111, -107, -50, -240, -138, -255, -159, -0, -124, -26, -51, -204, -60, -5, -156, -75, -196, -28, -155, -41, -191, -14, -240, -83, -23, -56, -159, -198, -150, -157, -167, -149, -49, -184, -255, -240, -157, -24, -136, -177, -192, -7, -48, -101, -213, -223, -139, -218, -194, -116, -254, -223, -243, -118, -94, -5, -166, -248, -52, -252, -34, -176, -92, -65, -153, -139, -240, -24, -206, -204, -181, -115, -113, -187, -74, -18, -128, -205, -48, -201, -249, -126, -96, -139, -140, -76, -16, -149, -13, -34, -229, -107, -25, -100, -128, -93, -24, -80, -186, -128, -169, -70, -31, -7, -142, -245, -107, -223, -43, -250, -65, -155, -48, -14, -102, -194, -125, -15, -182, -228, -251, -152, -127, -145, -147, -176, -105, -110, -37, -255, -0, -162, -241, -142, -192, -110, -254, -110, -246, -194, -109, -19, -152, -99, -200, -119, -252, -171, -142, -46, -55, -253, -185, -182, -203, -93, -91, -8, -91, -242, -157, -16, -161, -95, -10, -19, -184, -127, -131, -41, -153, -22, -1, -54, -192, -84, -228, -143, -20, -49, -27, -3, -150, -200, -5, -253, -124, -126, -44, -186, -107, -151, -24, -125, -37, -128, -125, -112, -229, -2, -17, -217, -160, -160, -76, -45, -131, -12, -230, -50, -182, -148, -31, -131, -169, -53, -87, -195, -194, -211, -23, -244, -31, -244, -178, -72, -185, -44, -227, -172, -4, -252, -197, -31, -246, -119, -126, -173, -144, -113, -50, -117, -204, -139, -105, -24, -63, -150, -185, -182, -7, -240, -112, -132, -118, -172, -191, -252, -149, -10, -234, -186, -144, -140, -45, -33, -119, -111, -8, -3, -248, -245, -207, -17, -201, -250, -1, -252, -183, -51, -229, -152, -28, -237, -3, -152, -138, -250, -11, -5, -237, -4, -103, -170, -255, -231, -231, -123, -249, -199, -52, -79, -140, -190, -20, -152, -86, -237, -94, -92, -181, -73, -68, -54, -40, -40, -151, -55, -200, -148, -90, -171, -24, -156, -238, -5, -224, -67, -192, -113, -152, -110, -188, -163, -140, -185, -42, -82, -46, -203, -56, -191, -195, -244, -241, -99, -112, -61, -64, -9, -227, -172, -128, -133, -188, -61, -236, -127, -49, -188, -17, -41, -247, -117, -224, -56, -63, -94, -13, -83, -27, -79, -197, -134, -230, -21, -48, -245, -245, -189, -145, -114, -67, -144, -185, -247, -65, -224, -229, -72, -153, -201, -157, -182, -252, -124, -93, -76, -167, -177, -165, -191, -155, -59, -242, -101, -50, -180, -159, -7, -174, -241, -227, -203, -129, -83, -139, -104, -75, -1, -124, -138, -140, -93, -27, -147, -7, -182, -198, -101, -131, -146, -114, -121, -131, -76, -169, -181, -42, -194, -0, -87, -96, -115, -241, -58, -153, -235, -49, -6, -200, -150, -123, -14, -88, -61, -66, -19, -43, -247, -103, -204, -211, -104, -17, -103, -24, -176, -16, -184, -172, -237, -97, -136, -233, -25, -251, -34, -215, -246, -227, -107, -128, -195, -252, -248, -85, -44, -98, -122, -12, -240, -74, -164, -92, -39, -16, -118, -167, -124, -221, -192, -238, -192, -139, -145, -50, -175, -0, -123, -249, -241, -188, -152, -12, -118, -150, -159, -239, -89, -244, -46, -253, -254, -114, -152, -208, -183, -14, -230, -151, -184, -70, -17, -109, -33, -252, -229, -60, -202, -96, -93, -245, -32, -217, -160, -162, -124, -178, -181, -202, -127, -240, -37, -253, -24, -167, -61, -16, -56, -207, -175, -45, -69, -252, -75, -206, -51, -192, -106, -17, -154, -24, -3, -188, -4, -236, -234, -199, -227, -188, -205, -83, -202, -158, -199, -105, -95, -102, -96, -110, -125, -9, -120, -59, -54, -26, -130, -141, -150, -139, -3, -79, -23, -148, -133, -248, -20, -240, -37, -224, -214, -200, -245, -87, -24, -200, -199, -112, -2, -54, -34, -116, -204, -227, -123, -17, -25, -53, -114, -229, -127, -135, -77, -191, -133, -31, -106, -207, -64, -77, -107, -21, -102, -219, -254, -185, -31, -119, -24, -32, -96, -158, -50, -155, -96, -115, -249, -16, -199, -208, -28, -227, -252, -22, -243, -34, -26, -195, -192, -180, -80, -196, -56, -147, -176, -233, -226, -173, -216, -84, -112, -163, -51, -208, -222, -206, -16, -239, -5, -118, -143, -148, -123, -9, -88, -212, -143, -31, -194, -134, -227, -29, -176, -17, -96, -25, -44, -140, -254, -226, -130, -103, -28, -194, -0, -94, -230, -73, -224, -63, -35, -244, -147, -49, -47, -165, -245, -48, -33, -117, -211, -204, -189, -227, -129, -59, -99, -237, -228, -222, -41, -184, -235, -88, -95, -65, -3, -107, -21, -240, -85, -44, -16, -36, -43, -205, -47, -133, -45, -45, -79, -46, -40, -147, -101, -156, -149, -176, -37, -224, -52, -60, -137, -85, -9, -227, -108, -143, -205, -253, -211, -129, -111, -97, -67, -236, -209, -126, -237, -117, -108, -78, -143, -249, -7, -78, -196, -163, -156, -177, -161, -123, -138, -63, -215, -4, -224, -127, -48, -225, -108, -229, -130, -190, -130, -77, -1, -11, -96, -242, -194, -1, -152, -176, -118, -13, -145, -21, -18, -230, -91, -112, -15, -54, -141, -158, -150, -185, -62, -143, -51, -71, -244, -157, -100, -232, -118, -114, -134, -45, -52, -130, -245, -4, -116, -97, -173, -242, -31, -244, -10, -108, -202, -184, -218, -143, -75, -151, -47, -77, -24, -167, -41, -128, -255, -44, -98, -224, -132, -178, -89, -60, -143, -45, -169, -143, -2, -230, -43, -160, -95, -6, -147, -222, -95, -198, -150, -172, -11, -99, -115, -250, -149, -206, -116, -67, -150, -129, -206, -28, -11, -97, -194, -232, -93, -192, -25, -77, -250, -58, -199, -161, -9, -227, -52, -108, -103, -62, -255, -98, -127, -69, -198, -117, -219, -127, -172, -86, -29, -51, -177, -21, -204, -115, -152, -101, -242, -49, -76, -168, -155, -234, -35, -86, -52, -84, -206, -71, -150, -87, -188, -220, -15, -128, -249, -219, -236, -147, -168, -25, -218, -53, -55, -194, -153, -224, -211, -152, -84, -62, -13, -243, -29, -120, -0, -23, -88, -91, -110, -43, -26, -31, -208, -55, -144, -203, -172, -73, -196, -109, -122, -20, -115, -15, -98, -95, -247, -254, -146, -54, -240, -227, -219, -36, -213, -138, -238, -29, -197, -28, -142, -58, -67, -190, -207, -89, -175, -145, -243, -184, -193, -194, -157, -94, -45, -154, -183, -114, -180, -141, -194, -200, -71, -209, -3, -48, -216, -18, -213, -17, -64, -190, -77, -137, -73, -17, -211, -174, -125, -35, -119, -237, -219, -20, -172, -141, -35, -229, -107, -133, -145, -211, -48, -139, -246, -40, -163, -85, -128, -1, -75, -212, -165, -12, -88, -162, -214, -243, -31, -248, -62, -92, -25, -18, -41, -183, -11, -166, -194, -237, -228, -2, -126, -147, -11, -73, -61, -177, -233, -99, -214, -183, -14, -3, -36, -121, -227, -122, -185, -36, -70, -195, -20, -66, -19, -49, -69, -204, -237, -84, -104, -63, -115, -101, -107, -51, -39, -53, -82, -187, -231, -202, -181, -203, -208, -152, -37, -234, -94, -114, -22, -36, -76, -203, -54, -153, -98, -139, -215, -60, -62, -98, -236, -226, -231, -187, -59, -35, -69, -95, -52, -109, -57, -48, -244, -8, -152, -54, -243, -84, -96, -9, -76, -115, -152, -148, -19, -217, -203, -214, -98, -78, -224, -187, -152, -86, -114, -117, -76, -135, -145, -156, -218, -189, -238, -200, -153, -82, -225, -32, -75, -84, -238, -222, -241, -101, -28, -141, -41, -102, -126, -230, -199, -191, -161, -192, -108, -153, -161, -175, -229, -192, -208, -132, -105, -176, -168, -160, -105, -228, -220, -176, -49, -83, -247, -179, -37, -35, -218, -11, -25, -102, -222, -139, -2, -61, -255, -92, -7, -50, -150, -168, -200, -189, -82, -67, -4, -102, -38, -157, -142, -185, -135, -205, -160, -192, -118, -158, -161, -175, -237, -192, -80, -151, -105, -252, -254, -137, -152, -118, -44, -100, -174, -221, -70, -196, -25, -35, -115, -255, -39, -152, -61, -98, -2, -166, -149, -59, -172, -236, -89, -188, -76, -19, -217, -169, -47, -101, -146, -209, -13, -3, -56, -205, -95, -49, -15, -153, -63, -150, -209, -57, -109, -109, -7, -134, -134, -76, -179, -136, -127, -237, -239, -243, -243, -157, -177, -136, -227, -178, -128, -145, -245, -48, -211, -246, -27, -192, -222, -9, -207, -82, -91, -118, -234, -87, -153, -90, -192, -116, -205, -199, -23, -220, -75, -177, -68, -29, -236, -47, -46, -150, -58, -37, -70, -95, -203, -129, -161, -9, -211, -100, -218, -185, -193, -143, -255, -74, -193, -52, -231, -247, -223, -235, -12, -115, -54, -166, -233, -251, -25, -3, -206, -41, -135, -19, -113, -200, -160, -129, -236, -212, -175, -50, -181, -128, -37, -130, -190, -47, -210, -64, -146, -37, -170, -65, -123, -181, -29, -24, -234, -50, -141, -211, -45, -236, -95, -253, -103, -49, -169, -57, -26, -126, -229, -180, -119, -0, -95, -242, -227, -149, -24, -112, -200, -92, -206, -153, -237, -195, -145, -50, -181, -101, -167, -126, -149, -169, -133, -204, -16, -115, -21, -230, -138, -148, -181, -68, -117, -63, -196, -196, -219, -172, -229, -192, -208, -132, -105, -188, -220, -113, -24, -134, -216, -224, -115, -116, -47, -2, -187, -101, -206, -87, -198, -28, -100, -94, -0, -190, -95, -80, -166, -246, -212, -217, -175, -50, -41, -200, -238, -26, -246, -140, -164, -77, -36, -77, -145, -244, -39, -73, -255, -150, -101, -8, -123, -80, -182, -251, -85, -161, -67, -104, -23, -248, -158, -164, -237, -37, -37, -165, -109, -245, -157, -205, -174, -148, -116, -150, -108, -199, -178, -201, -137, -237, -116, -148, -82, -67, -210, -215, -231, -112, -163, -164, -227, -176, -101, -220, -56, -73, -155, -202, -118, -239, -154, -79, -210, -180, -196, -182, -230, -40, -204, -155, -61, -241, -45, -224, -62, -218, -199, -246, -103, -74, -154, -33, -233, -162, -26, -101, -190, -39, -233, -119, -170, -215, -207, -212, -32, -207, -131, -36, -125, -93, -198, -248, -11, -73, -154, -36, -233, -11, -146, -110, -146, -116, -45, -240, -74, -8, -33, -191, -130, -152, -34, -41, -234, -16, -226, -215, -239, -143, -92, -239, -87, -153, -222, -131, -226, -165, -73, -52, -89, -34, -93, -58, -48, -208, -192, -235, -133, -129, -93, -63, -86, -173, -211, -86, -98, -221, -181, -101, -167, -126, -149, -233, -57, -40, -95, -154, -60, -64, -102, -179, -168, -76, -153, -218, -14, -12, -45, -48, -77, -47, -25, -160, -182, -236, -212, -175, -50, -61, -7, -189, -94, -154, -12, -212, -215, -91, -175, -151, -46, -145, -25, -5, -31, -167, -190, -82, -167, -167, -101, -154, -62, -80, -82, -198, -110, -122, -189, -52, -25, -197, -240, -128, -196, -140, -221, -244, -104, -105, -50, -138, -254, -97, -222, -216, -197, -16, -194, -44, -73, -71, -181, -80, -127, -123, -73, -12, -71, -209, -19, -12, -50, -37, -82, -223, -216, -48, -69, -229, -75, -147, -41, -249, -139, -52, -180, -210, -213, -133, -11, -125, -177, -168, -156, -194, -220, -63, -152, -47, -195, -49, -152, -63, -192, -75, -152, -98, -232, -110, -160, -141, -143, -97, -100, -163, -66, -162, -47, -146, -76, -171, -150, -38, -255, -85, -208, -86, -19, -43, -93, -45, -230, -172, -203, -0, -254, -227, -95, -135, -5, -101, -236, -224, -140, -250, -86, -224, -253, -177, -122, -114, -253, -122, -202, -143, -151, -6, -30, -47, -233, -79, -7, -149, -201, -34, -98, -253, -199, -98, -45, -175, -35, -30, -195, -56, -147, -248, -30, -6, -105, -201, -174, -104, -102, -160, -40, -91, -154, -220, -73, -65, -6, -76, -106, -90, -233, -26, -50, -103, -93, -6, -56, -14, -75, -252, -80, -107, -4, -2, -118, -197, -147, -81, -98, -219, -202, -71, -211, -220, -123, -127, -146, -147, -69, -228, -251, -15, -124, -25, -27, -153, -138, -114, -4, -212, -122, -222, -14, -178, -83, -192, -174, -146, -190, -239, -243, -255, -108, -132, -16, -222, -144, -169, -106, -135, -108, -22, -25, -81, -31, -79, -147, -116, -151, -164, -151, -36, -109, -17, -66, -24, -18, -1, -235, -229, -158, -151, -244, -223, -146, -58, -43, -136, -207, -74, -58, -35, -132, -240, -66, -65, -63, -63, -43, -233, -85, -73, -31, -10, -33, -220, -30, -66, -120, -62, -132, -112, -167, -108, -131, -134, -55, -36, -125, -166, -160, -92, -29, -236, -39, -233, -188, -6, -42, -239, -13, -36, -77, -140, -28, -199, -240, -122, -8, -225, -149, -16, -194, -148, -16, -194, -111, -37, -109, -37, -105, -41, -73, -71, -151, -53, -0, -28, -42, -105, -15, -73, -59, -250, -187, -107, -31, -180, -36, -209, -99, -86, -186, -137, -249, -145, -36, -66, -87, -199, -74, -215, -196, -122, -86, -138, -8, -253, -12, -96, -191, -148, -103, -116, -250, -231, -252, -111, -38, -102, -44, -26, -116, -92, -208, -159, -58, -201, -34, -240, -175, -247, -3, -62, -250, -69, -115, -4, -37, -212, -159, -60, -2, -180, -133, -79, -75, -90, -69, -210, -37, -148, -36, -67, -242, -175, -253, -12, -73, -95, -145, -116, -122, -8, -225, -165, -146, -58, -223, -46, -233, -161, -130, -123, -15, -122, -123, -49, -236, -44, -105, -129, -220, -223, -206, -69, -93, -146, -217, -38, -102, -163, -140, -97, -66, -8, -139, -133, -16, -22, -147, -244, -164, -164, -181, -252, -248, -127, -37, -173, -238, -199, -169, -184, -167, -164, -255, -235, -75, -250, -153, -108, -116, -28, -146, -189, -164, -13, -100, -25, -96, -138, -90, -48, -54, -132, -16, -238, -149, -89, -248, -222, -144, -84, -149, -201, -58, -213, -74, -215, -20, -157, -33, -119, -246, -95, -73, -159, -30, -148, -148, -207, -53, -80, -198, -48, -194, -118, -20, -39, -132, -48, -21, -75, -224, -52, -203, -13, -106, -117, -48, -70, -210, -172, -130, -123, -235, -203, -166, -166, -147, -128, -229, -107, -214, -155, -220, -120, -7, -151, -73, -58, -36, -63, -116, -251, -249, -193, -146, -46, -77, -173, -52, -132, -240, -247, -16, -194, -110, -33, -132, -170, -60, -182, -169, -86, -186, -41, -234, -189, -37, -236, -18, -73, -135, -146, -145, -176, -203, -24, -198, -135, -249, -127, -73, -90, -214, -143, -39, -75, -90, -174, -51, -53, -212, -104, -247, -157, -94, -54, -134, -11, -93, -86, -184, -72, -210, -79, -200, -172, -154, -34, -152, -37, -51, -91, -231, -241, -38, -229, -70, -182, -44, -178, -12, -112, -170, -19, -95, -73, -70, -162, -151, -116, -185, -211, -69, -51, -104, -245, -9, -173, -49, -103, -9, -206, -144, -9, -175, -127, -194, -178, -164, -45, -138, -173, -74, -162, -95, -158, -15, -243, -231, -72, -58, -209, -143, -207, -148, -116, -82, -102, -106, -168, -4, -102, -49, -253, -152, -108, -152, -47, -195, -103, -36, -173, -36, -233, -216, -18, -154, -123, -37, -189, -59, -114, -125, -35, -191, -151, -212, -161, -254, -24, -27, -6, -218, -75, -178, -210, -209, -204, -122, -86, -91, -40, -194, -188, -154, -191, -137, -101, -5, -155, -233, -130, -225, -189, -20, -236, -235, -227, -237, -79, -240, -227, -203, -40, -217, -234, -206, -251, -83, -39, -89, -196, -160, -254, -99, -233, -239, -95, -6, -54, -200, -211, -250, -253, -61, -48, -167, -217, -3, -253, -119, -124, -27, -112, -144, -11, -165, -251, -23, -245, -107, -142, -65, -191, -153, -51, -177, -79, -79, -116, -218, -247, -227, -183, -149, -208, -102, -145, -146, -44, -98, -8, -3, -99, -241, -23, -147, -41, -78, -43, -255, -62, -44, -132, -253, -101, -103, -222, -155, -129, -15, -117, -243, -140, -163, -24, -197, -40, -70, -49, -138, -185, -31, -62, -231, -220, -157, -159, -147, -42, -132, -166, -43, -124, -94, -126, -210, -231, -230, -239, -2, -151, -39, -180, -53, -98, -66, -181, -105, -24, -110, -62, -183, -32, -175, -9, -92, -84, -210, -49, -53, -202, -63, -41, -105, -89, -73, -107, -75, -90, -75, -210, -91, -252, -90, -21, -46, -144, -45, -125, -134, -4, -90, -228, -65, -162, -119, -82, -174, -76, -157, -144, -235, -236, -26, -185, -72, -33, -83, -214, -86, -85, -184, -249, -10, -152, -89, -121, -205, -18, -154, -206, -190, -73, -235, -213, -108, -187, -52, -112, -21, -203, -99, -124, -117, -238, -218, -159, -201, -236, -175, -148, -189, -1, -240, -31, -46, -161, -142, -203, -92, -47, -27, -1, -30, -3, -86, -201, -156, -47, -82, -213, -169, -186, -32, -209, -59, -41, -67, -223, -56, -228, -58, -177, -254, -90, -57, -148, -128, -243, -49, -251, -72, -105, -194, -12, -108, -207, -133, -115, -203, -104, -156, -110, -33, -6, -194, -213, -254, -234, -255, -3, -17, -203, -171, -255, -30, -15, -2, -255, -225, -231, -135, -96, -206, -186, -67, -173, -174, -206, -0, -91, -250, -131, -93, -158, -185, -94, -198, -0, -79, -144, -73, -146, -136, -173, -207, -163, -241, -244, -116, -107, -175, -110, -25, -77, -251, -131, -237, -228, -185, -178, -191, -167, -231, -128, -35, -74, -104, -215, -240, -23, -62, -63, -102, -30, -127, -103, -9, -237, -70, -216, -154, -189, -106, -131, -141, -19, -48, -179, -245, -79, -49, -31, -138, -159, -98, -9, -58, -190, -88, -64, -191, -137, -223, -127, -167, -255, -31, -178, -231, -67, -135, -16, -108, -139, -182, -133, -176, -112, -168, -78, -170, -215, -50, -6, -248, -1, -150, -174, -117, -49, -255, -241, -127, -82, -244, -197, -121, -253, -117, -236, -243, -181, -126, -160, -6, -244, -181, -250, -147, -185, -95, -39, -135, -210, -175, -128, -143, -250, -241, -190, -68, -82, -216, -230, -232, -255, -9, -28, -94, -65, -179, -8, -182, -165, -252, -49, -216, -212, -114, -180, -159, -23, -250, -49, -96, -142, -39, -51, -136, -56, -232, -228, -31, -38, -184, -13, -255, -40, -73, -103, -71, -135, -138, -193, -120, -171, -164, -103, -100, -170, -198, -251, -100, -115, -104, -105, -110, -128, -26, -152, -39, -210, -191, -54, -233, -107, -193, -25, -230, -110, -111, -39, -123, -189, -136, -193, -54, -148, -244, -46, -13, -132, -189, -93, -36, -105, -53, -96, -147, -146, -102, -206, -145, -52, -36, -93, -109, -22, -238, -15, -48, -77, -246, -27, -29, -44, -179, -190, -78, -171, -240, -99, -24, -39, -233, -89, -255, -63, -8, -209, -23, -22, -66, -248, -181, -44, -69, -220, -41, -42, -49, -36, -132, -16, -118, -9, -33, -28, -18, -66, -88, -38, -132, -240, -182, -16, -194, -193, -33, -132, -29, -203, -30, -96, -14, -71, -29, -33, -249, -84, -73, -167, -116, -12, -98, -238, -88, -243, -21, -73, -101, -177, -18, -191, -144, -244, -54, -42, -118, -17, -149, -180, -177, -164, -137, -33, -132, -95, -74, -186, -69, -210, -123, -139, -8, -49, -175, -171, -237, -36, -189, -71, -210, -14, -120, -198, -244, -24, -225, -160, -33, -17, -203, -160, -253, -60, -182, -189, -74, -215, -115, -116, -131, -41, -96, -36, -210, -215, -18, -146, -155, -0, -115, -205, -251, -101, -75, -117, -45, -137, -201, -105, -157, -233, -124, -79, -63, -95, -162, -67, -83, -56, -100, -134, -16, -30, -146, -244, -85, -73, -95, -106, -163, -51, -115, -9, -238, -151, -116, -190, -164, -111, -245, -176, -141, -115, -37, -189, -159, -130, -13, -162, -106, -226, -219, -146, -238, -8, -33, -252, -74, -146, -124, -196, -184, -211, -175, -75, -170, -158, -51, -191, -38, -11, -19, -111, -3, -141, -236, -213, -61, -68, -147, -254, -204, -39, -233, -68, -73, -235, -119, -190, -170, -182, -17, -66, -184, -95, -210, -181, -50, -51, -113, -183, -117, -237, -29, -66, -152, -144, -187, -182, -67, -8, -97, -246, -86, -128, -217, -252, -0, -33, -132, -112, -117, -142, -248, -245, -16, -194, -58, -33, -132, -50, -71, -132, -84, -212, -181, -87, -215, -253, -129, -234, -210, -55, -177, -159, -215, -21, -146, -155, -226, -28, -73, -31, -197, -115, -47, -206, -21, -160, -166, -189, -26, -83, -209, -126, -62, -114, -253, -11, -46, -141, -119, -75, -95, -183, -63, -121, -25, -233, -114, -44, -151, -208, -248, -54, -101, -128, -185, -26, -212, -176, -87, -55, -248, -129, -106, -59, -68, -212, -236, -79, -158, -1, -90, -21, -146, -71, -17, -65, -157, -31, -168, -9, -125, -205, -190, -12, -89, -53, -96, -46, -237, -79, -14, -39, -3, -120, -191, -54, -175, -166, -76, -171, -108, -173, -252, -57, -17, -71, -68, -74, -208, -74, -71, -70, -32, -10, -24, -96, -62, -204, -130, -58, -231, -51, -0, -176, -10, -166, -99, -222, -193, -207, -183, -33, -183, -143, -95, -134, -182, -179, -207, -222, -26, -222, -129, -181, -41, -216, -119, -111, -20, -67, -129, -109, -170, -149, -36, -88, -99, -6, -173, -168, -219, -152, -223, -111, -117, -4, -216, -210, -153, -224, -243, -254, -127, -124, -5, -253, -88, -239, -64, -235, -169, -87, -230, -102, -164, -254, -248, -78, -75, -217, -135, -149, -101, -0, -204, -225, -244, -38, -114, -41, -117, -235, -118, -238, -24, -175, -52, -154, -53, -52, -71, -155, -196, -0, -88, -46, -225, -171, -49, -227, -197, -19, -152, -141, -255, -255, -252, -94, -68, -41, -72, -101, -0, -44, -136, -247, -55, -192, -239, -105, -178, -103, -176, -87, -182, -149, -127, -249, -157, -72, -217, -237, -43, -232, -83, -25, -224, -70, -44, -217, -243, -50, -152, -229, -234, -160, -146, -135, -129, -129, -125, -252, -206, -1, -22, -79, -232, -119, -233, -48, -233, -52, -75, -97, -142, -34, -83, -177, -56, -200, -135, -40, -216, -45, -188, -23, -192, -220, -192, -99, -136, -109, -33, -123, -97, -1, -237, -175, -34, -180, -96, -91, -204, -125, -195, -191, -254, -232, -182, -244, -41, -29, -28, -135, -205, -249, -219, -248, -249, -22, -126, -190, -86, -73, -153, -84, -6, -120, -153, -34, -35, -196, -96, -58, -48, -223, -249, -133, -49, -251, -248, -157, -120, -26, -250, -132, -114, -101, -95, -201, -34, -152, -59, -245, -213, -152, -235, -215, -226, -254, -124, -177, -212, -175, -11, -3, -23, -224, -210, -125, -22, -17, -218, -107, -129, -255, -206, -93, -59, -25, -207, -77, -156, -187, -62, -134, -220, -62, -197, -254, -55, -36, -225, -21, -38, -92, -118, -238, -131, -5, -169, -188, -153, -8, -147, -251, -253, -142, -7, -212, -16, -121, -173, -22, -200, -24, -57, -252, -188, -40, -104, -177, -115, -63, -149, -1, -238, -33, -205, -219, -5, -6, -175, -181, -247, -7, -42, -85, -209, -9, -12, -112, -10, -230, -189, -84, -24, -172, -154, -161, -61, -39, -247, -187, -79, -194, -116, -10, -147, -34, -180, -91, -96, -211, -90, -103, -43, -219, -249, -253, -163, -121, -127, -69, -27, -109, -203, -0, -191, -194, -188, -160, -174, -168, -83, -119, -215, -168, -193, -0, -59, -99, -27, -62, -92, -71, -137, -61, -60, -194, -0, -31, -6, -162, -193, -150, -212, -27, -38, -239, -166, -98, -19, -139, -12, -237, -99, -185, -250, -238, -242, -235, -81, -135, -81, -44, -215, -241, -201, -126, -124, -176, -51, -123, -233, -143, -64, -15, -86, -1, -216, -200, -245, -16, -240, -233, -148, -122, -91, -65, -42, -3, -56, -237, -234, -152, -144, -50, -139, -200, -208, -235, -52, -48, -48, -5, -108, -140, -165, -145, -249, -66, -1, -109, -157, -97, -242, -21, -96, -159, -88, -61, -17, -218, -153, -53, -25, -96, -19, -204, -71, -112, -17, -108, -47, -225, -131, -83, -218, -105, -11, -29, -6, -240, -227, -205, -49, -5, -216, -187, -250, -213, -120, -237, -101, -32, -182, -15, -79, -52, -214, -63, -242, -53, -31, -153, -88, -39, -148, -15, -147, -175, -144, -176, -249, -131, -211, -78, -175, -195, -0, -126, -239, -114, -76, -250, -158, -74, -36, -206, -175, -151, -200, -50, -128, -159, -127, -5, -155, -182, -10, -19, -110, -12, -43, -176, -164, -75, -209, -52, -39, -254, -48, -219, -249, -241, -223, -129, -66, -239, -155, -220, -8, -112, -169, -143, -26, -69, -35, -192, -36, -224, -115, -137, -253, -251, -83, -42, -3, -48, -48, -250, -228, -177, -66, -74, -91, -35, -10, -184, -85, -43, -134, -46, -235, -93, -10, -51, -208, -44, -141, -249, -200, -95, -6, -252, -174, -128, -54, -203, -0, -187, -97, -75, -209, -162, -68, -83, -117, -100, -128, -51, -177, -249, -177, -242, -235, -196, -146, -80, -61, -156, -194, -0, -126, -61, -59, -13, -237, -196, -156, -170, -13, -37, -190, -76, -89, -180, -5, -6, -88, -28, -219, -170, -165, -147, -71, -231, -18, -10, -162, -104, -115, -12, -16, -48, -225, -45, -233, -203, -173, -232, -195, -210, -152, -167, -243, -223, -176, -53, -243, -34, -216, -178, -183, -72, -22, -121, -51, -176, -1, -176, -97, -25, -3, -208, -197, -8, -224, -12, -254, -40, -17, -165, -77, -65, -157, -253, -183, -55, -116, -30, -176, -143, -237, -205, -102, -0, -63, -255, -48, -45, -37, -143, -4, -150, -7, -190, -143, -133, -176, -205, -194, -76, -185, -215, -38, -148, -235, -201, -8, -128, -229, -58, -40, -202, -163, -56, -187, -174, -236, -95, -74, -189, -173, -2, -155, -103, -135, -36, -71, -192, -180, -121, -127, -241, -151, -217, -193, -227, -100, -156, -13, -71, -50, -128, -139, -129, -75, -18, -105, -43, -133, -64, -191, -63, -136, -121, -43, -104, -87, -199, -150, -197, -149, -35, -97, -66, -155, -187, -96, -59, -156, -206, -192, -130, -86, -214, -79, -233, -67, -87, -192, -134, -244, -147, -252, -248, -68, -224, -250, -158, -55, -218, -34, -176, -41, -33, -41, -223, -94, -42, -3, -212, -108, -255, -44, -160, -112, -151, -148, -154, -12, -48, -25, -155, -170, -150, -193, -132, -225, -222, -255, -22, -206, -109, -219, -250, -241, -182, -68, -244, -217, -125, -232, -67, -22, -47, -99, -243, -233, -197, -84, -107, -47, -255, -138, -173, -48, -62, -153, -216, -78, -171, -12, -128, -89, -236, -166, -1, -91, -149, -208, -68, -81, -64, -247, -137, -204, -249, -142, -64, -81, -178, -205, -40, -154, -90, -228, -238, -151, -244, -65, -31, -242, -63, -168, -226, -44, -87, -131, -128, -205, -233, -80, -18, -35, -87, -19, -157, -60, -128, -111, -147, -101, -50, -93, -82, -210, -144, -85, -64, -22, -33, -132, -45, -66, -8, -27, -133, -16, -206, -105, -169, -15, -117, -177, -159, -164, -199, -66, -8, -215, -85, -208, -197, -114, -28, -198, -144, -117, -96, -125, -81, -182, -215, -81, -111, -225, -67, -206, -52, -204, -106, -55, -145, -4, -205, -19, -176, -32, -166, -217, -123, -190, -136, -1, -48, -189, -122, -244, -47, -66, -59, -100, -152, -196, -36, -235, -178, -132, -147, -195, -14, -204, -101, -173, -116, -244, -169, -57, -5, -100, -149, -64, -155, -199, -70, -138, -50, -148, -186, -29, -3, -99, -60, -164, -105, -16, -66, -8, -255, -196, -214, -212, -107, -132, -16, -30, -76, -108, -235, -51, -178, -221, -184, -10, -99, -223, -66, -8, -141, -184, -23, -83, -254, -140, -149, -237, -36, -86, -103, -7, -178, -174, -128, -57, -204, -68, -87, -18, -49, -87, -122, -44, -50, -119, -13, -73, -63, -233, -109, -207, -26, -130, -26, -177, -239, -88, -122, -179, -14, -158, -164, -68, -213, -138, -45, -193, -158, -192, -148, -66, -111, -180, -49, -5, -20, -76, -147, -23, -208, -212, -25, -162, -89, -31, -138, -76, -188, -209, -37, -27, -233, -57, -0, -250, -54, -2, -228, -101, -128, -253, -101, -25, -175, -37, -11, -14, -141, -166, -112, -1, -206, -145, -101, -239, -92, -213, -255, -206, -85, -121, -34, -201, -175, -74, -58, -199, -179, -139, -183, -105, -174, -236, -204, -147, -11, -75, -26, -47, -11, -234, -248, -122, -164, -191, -27, -98, -186, -250, -233, -88, -130, -234, -139, -40, -73, -233, -150, -138, -16, -194, -27, -249, -84, -180, -153, -148, -180, -249, -62, -44, -41, -105, -47, -101, -194, -178, -70, -28, -72, -116, -211, -242, -121, -121, -163, -204, -249, -38, -20, -164, -71, -197, -52, -111, -143, -227, -198, -9, -231, -218, -182, -70, -128, -188, -12, -176, -63, -240, -108, -132, -246, -66, -204, -81, -99, -101, -108, -13, -126, -45, -5, -121, -253, -157, -62, -201, -123, -136, -30, -169, -207, -135, -5, -222, -239, -36, -165, -2, -102, -207, -62, -210, -143, -23, -244, -23, -28, -141, -104, -5, -110, -192, -44, -84, -171, -250, -31, -152, -255, -126, -87, -201, -143, -11, -24, -224, -96, -224, -177, -132, -178, -239, -163, -216, -32, -85, -199, -123, -168, -214, -20, -48, -162, -225, -47, -52, -73, -169, -224, -95, -252, -173, -152, -243, -196, -67, -88, -166, -176, -168, -22, -176, -232, -11, -1, -254, -210, -66, -127, -59, -234, -210, -69, -49, -143, -230, -251, -129, -51, -19, -202, -126, -130, -72, -184, -152, -223, -75, -246, -30, -154, -171, -224, -47, -180, -43, -165, -66, -141, -118, -218, -22, -2, -103, -98, -185, -120, -78, -166, -98, -83, -73, -44, -5, -206, -125, -192, -199, -11, -238, -39, -123, -15, -205, -85, -240, -23, -185, -109, -230, -188, -182, -68, -89, -163, -157, -182, -20, -65, -117, -219, -158, -199, -71, -182, -223, -80, -224, -146, -69, -13, -239, -161, -134, -125, -56, -44, -50, -26, -198, -18, -85, -64, -66, -192, -7, -240, -59, -76, -86, -153, -138, -9, -184, -93, -233, -1, -94, -173, -83, -184, -9, -90, -10, -53, -175, -13, -76, -192, -253, -129, -164, -197, -101, -123, -239, -148, -189, -168, -94, -10, -113, -75, -73, -186, -66, -82, -214, -219, -169, -82, -110, -41, -66, -8, -97, -182, -3, -42, -240, -99, -21, -107, -12, -163, -152, -123, -226, -207, -75, -224, -95, -251, -119, -100, -75, -214, -9, -33, -132, -178, -253, -143, -166, -168, -120, -11, -151, -170, -118, -86, -150, -229, -1, -218, -82, -166, -150, -30, -31, -66, -184, -41, -71, -182, -148, -164, -7, -61, -17, -68, -107, -0, -54, -147, -244, -33, -89, -194, -206, -100, -212, -182, -5, -96, -82, -242, -143, -128, -127, -99, -10, -160, -202, -8, -162, -17, -128, -111, -200, -244, -4, -71, -200, -146, -48, -117, -86, -36, -177, -156, -124, -127, -144, -37, -103, -168, -229, -219, -135, -101, -50, -189, -78, -54, -122, -76, -144, -237, -115, -116, -103, -132, -116, -41, -217, -222, -66, -41, -216, -4, -19, -204, -103, -96, -249, -0, -163, -242, -141, -143, -110, -223, -146, -244, -213, -16, -194, -163, -117, -250, -157, -173, -100, -208, -156, -83, -36, -3, -0, -223, -193, -44, -106, -171, -57, -205, -35, -192, -1, -141, -26, -237, -65, -255, -74, -202, -198, -240, -145, -8, -109, -45, -239, -161, -76, -185, -255, -194, -54, -131, -172, -218, -45, -237, -50, -204, -59, -234, -53, -76, -63, -242, -109, -34, -153, -70, -188, -127, -183, -97, -190, -23, -107, -98, -130, -235, -23, -10, -234, -60, -212, -239, -247, -126, -87, -117, -239, -244, -142, -153, -243, -143, -18, -89, -46, -22, -9, -55, -5, -117, -158, -128, -229, -3, -126, -26, -56, -151, -226, -141, -20, -187, -82, -123, -166, -130, -6, -222, -67, -88, -46, -130, -74, -247, -53, -44, -115, -215, -170, -216, -82, -123, -107, -204, -97, -245, -194, -8, -29, -120, -138, -87, -63, -255, -52, -241, -76, -39, -75, -98, -163, -241, -46, -169, -207, -215, -21, -48, -117, -234, -123, -51, -231, -59, -0, -67, -146, -20, -166, -50, -0, -176, -31, -166, -88, -90, -23, -88, -9, -83, -28, -197, -180, -110, -125, -99, -128, -38, -192, -252, -29, -191, -136, -133, -179, -189, -128, -69, -233, -84, -42, -187, -176, -61, -25, -103, -68, -174, -67, -38, -95, -32, -166, -188, -26, -34, -164, -99, -35, -114, -105, -6, -210, -50, -52, -241, -7, -184, -94, -210, -17, -62, -52, -190, -93, -182, -251, -103, -179, -128, -68, -195, -17, -146, -190, -22, -66, -184, -43, -132, -240, -136, -76, -151, -191, -103, -23, -245, -13, -23, -22, -146, -180, -181, -164, -221, -36, -173, -40, -233, -101, -153, -141, -164, -10, -243, -73, -122, -170, -224, -222, -152, -220, -113, -140, -225, -63, -42, -105, -51, -6, -150, -130, -83, -233, -165, -91, -186, -127, -165, -87, -98, -246, -128, -59, -157, -235, -159, -137, -208, -165, -142, -0, -207, -50, -20, -67, -76, -208, -145, -17, -96, -251, -17, -54, -2, -188, -64, -38, -0, -22, -83, -31, -199, -70, -198, -227, -125, -180, -91, -26, -139, -198, -190, -15, -56, -37, -66, -7, -158, -103, -216, -207, -143, -161, -7, -251, -25, -204, -230, -48, -76, -175, -125, -134, -255, -32, -79, -99, -33, -221, -135, -228, -11, -132, -16, -30, -9, -33, -236, -24, -66, -88, -40, -132, -176, -158, -164, -87, -148, -186, -45, -89, -113, -31, -246, -209, -96, -207, -151, -216, -136, -242, -146, -6, -123, -187, -172, -17, -171, -44, -195, -68, -179, -48, -19, -244, -55, -72, -8, -49, -111, -1, -119, -201, -190, -252, -14, -22, -146, -121, -232, -228, -241, -30, -217, -40, -58, -85, -210, -121, -146, -126, -36, -41, -154, -233, -91, -210, -145, -152, -0, -184, -150, -164, -195, -212, -75, -63, -2, -224, -63, -157, -27, -215, -198, -50, -91, -19, -99, -128, -72, -185, -137, -192, -137, -145, -235, -169, -35, -192, -45, -120, -112, -101, -5, -221, -13, -152, -6, -111, -25, -76, -58, -159, -28, -27, -1, -188, -221, -78, -124, -225, -38, -152, -106, -247, -231, -17, -186, -113, -88, -166, -243, -206, -30, -192, -151, -18, -217, -159, -55, -50, -58, -81, -208, -238, -129, -152, -13, -97, -75, -108, -133, -116, -3, -240, -157, -170, -231, -42, -121, -94, -128, -207, -96, -246, -141, -151, -177, -125, -7, -122, -23, -118, -134, -5, -54, -30, -229, -199, -215, -96, -214, -176, -91, -34, -116, -243, -99, -238, -226, -75, -98, -142, -35, -83, -137, -108, -221, -86, -131, -1, -246, -194, -156, -80, -62, -4, -172, -136, -229, -5, -216, -45, -66, -247, -78, -103, -182, -87, -48, -139, -229, -174, -37, -12, -144, -141, -45, -56, -0, -152, -22, -161, -187, -25, -11, -169, -30, -135, -229, -71, -250, -5, -112, -107, -132, -110, -213, -220, -223, -215, -40, -112, -148, -193, -150, -130, -79, -97, -82, -249, -15, -169, -200, -253, -95, -6, -127, -142, -118, -114, -255, -36, -54, -56, -29, -147, -52, -183, -193, -252, -214, -118, -45, -120, -113, -27, -97, -107, -216, -255, -197, -162, -124, -242, -251, -237, -118, -232, -146, -24, -192, -105, -63, -130, -133, -99, -205, -116, -134, -250, -84, -23, -207, -145, -103, -128, -168, -137, -24, -155, -179, -179, -50, -197, -150, -84, -24, -191, -48, -91, -194, -20, -122, -104, -43, -200, -180, -149, -204, -0, -152, -60, -52, -209, -71, -138, -169, -36, -140, -168, -177, -74, -166, -97, -67, -231, -141, -152, -37, -240, -3, -68, -156, -43, -106, -212, -7, -67, -163, -91, -74, -211, -184, -180, -129, -76, -187, -139, -49, -32, -100, -157, -29, -161, -251, -33, -240, -107, -204, -58, -184, -16, -230, -49, -84, -234, -41, -12, -236, -141, -41, -137, -122, -174, -66, -175, -201, -0, -79, -98, -250, -152, -69, -177, -216, -198, -241, -77, -26, -252, -7, -22, -230, -220, -217, -135, -230, -104, -224, -239, -181, -43, -26, -168, -47, -134, -82, -151, -237, -54, -144, -105, -107, -38, -230, -171, -112, -10, -241, -173, -89, -231, -199, -194, -179, -158, -197, -134, -236, -243, -168, -222, -0, -234, -22, -224, -179, -45, -244, -177, -213, -157, -202, -188, -190, -243, -233, -38, -132, -14, -248, -148, -191, -184, -253, -49, -65, -240, -1, -224, -208, -110, -59, -215, -111, -248, -51, -164, -200, -30, -187, -251, -151, -179, -157, -63, -251, -179, -148, -7, -107, -116, -146, -47, -44, -217, -66, -31, -239, -200, -48, -192, -237, -45, -212, -55, -30, -115, -209, -159, -142, -109, -47, -91, -223, -153, -5, -203, -171, -251, -15, -127, -17, -211, -128, -47, -85, -125, -17, -253, -4, -22, -45, -124, -154, -247, -237, -25, -34, -137, -161, -157, -46, -149, -1, -38, -147, -209, -239, -99, -94, -66, -49, -227, -77, -231, -254, -111, -40, -217, -129, -12, -203, -104, -50, -209, -153, -228, -206, -70, -195, -112, -67, -120, -123, -7, -249, -135, -123, -11, -240, -219, -126, -53, -12, -53, -247, -186, -43, -168, -99, -8, -34, -116, -7, -99, -235, -250, -119, -97, -110, -107, -79, -1, -187, -23, -212, -151, -194, -0, -47, -98, -38, -212, -206, -249, -86, -68, -20, -55, -126, -111, -21, -159, -82, -10, -179, -112, -249, -87, -253, -77, -204, -135, -240, -43, -64, -212, -220, -235, -253, -107, -85, -165, -141, -9, -180, -59, -249, -241, -102, -244, -58, -56, -6, -56, -28, -19, -174, -240, -161, -39, -105, -79, -191, -130, -186, -242, -14, -149, -151, -17, -9, -156, -196, -150, -166, -95, -204, -156, -159, -76, -68, -7, -94, -131, -1, -174, -196, -214, -254, -227, -176, -117, -251, -31, -129, -95, -23, -208, -126, -11, -184, -170, -162, -190, -89, -157, -175, -30, -19, -66, -135, -232, -247, -51, -253, -171, -197, -0, -20, -120, -92, -103, -238, -159, -238, -31, -196, -193, -192, -73, -192, -53, -101, -244, -93, -193, -27, -121, -209, -135, -156, -21, -176, -124, -193, -155, -69, -232, -222, -135, -101, -5, -123, -17, -27, -182, -47, -7, -86, -175, -168, -123, -107, -108, -41, -51, -54, -114, -111, -42, -153, -204, -223, -152, -222, -160, -177, -39, -141, -247, -253, -82, -6, -166, -148, -11, -137, -236, -78, -234, -95, -244, -139, -84, -88, -219, -178, -63, -44, -230, -36, -251, -199, -42, -58, -63, -239, -154, -1, -156, -102, -87, -76, -249, -4, -137, -249, -144, -26, -1, -83, -194, -156, -154, -64, -247, -117, -224, -8, -76, -149, -185, -10, -54, -135, -22, -174, -42, -48, -85, -244, -109, -192, -87, -10, -238, -191, -204, -96, -159, -197, -109, -129, -215, -154, -61, -69, -79, -60, -184, -94, -0, -0, -18, -40, -73, -68, -65, -84, -251, -200, -49, -192, -115, -192, -7, -170, -232, -252, -188, -200, -239, -98, -69, -76, -178, -159, -226, -101, -30, -163, -64, -238, -201, -149, -251, -36, -53, -118, -111, -205, -218, -2, -82, -57, -115, -13, -73, -149, -203, -195, -16, -194, -81, -33, -132, -111, -134, -16, -38, -133, -16, -30, -144, -237, -23, -92, -54, -2, -252, -135, -108, -31, -194, -175, -22, -220, -207, -247, -101, -94, -73, -175, -87, -245, -99, -152, -48, -70, -233, -190, -121, -69, -116, -157, -60, -74, -239, -147, -244, -188, -164, -93, -36, -253, -45, -79, -132, -173, -253, -247, -195, -54, -201, -88, -92, -182, -181, -221, -16, -5, -94, -17, -154, -40, -52, -130, -226, -102, -201, -40, -48, -231, -142, -119, -201, -12, -30, -81, -243, -40, -230, -17, -115, -138, -164, -207, -135, -16, -138, -180, -113, -79, -203, -28, -58, -59, -88, -212, -175, -141, -68, -156, -45, -233, -187, -192, -191, -66, -8, -249, -101, -94, -146, -81, -75, -182, -41, -247, -255, -132, -16, -238, -4, -8, -33, -220, -86, -210, -222, -161, -50, -195, -82, -144, -52, -81, -210, -190, -181, -123, -92, -99, -104, -186, -139, -136, -249, -178, -164, -78, -48, -1, -233, -28, -10, -220, -165, -176, -181, -235, -109, -148, -44, -59, -49, -161, -237, -212, -204, -249, -233, -84, -11, -102, -125, -219, -166, -62, -55, -5, -4, -151, -47, -254, -26, -161, -75, -53, -106, -77, -192, -220, -237, -30, -197, -150, -150, -221, -229, -1, -174, -243, -0, -126, -94, -196, -0, -31, -193, -92, -164, -14, -192, -92, -167, -86, -195, -118, -167, -140, -213, -185, -152, -207, -101, -31, -196, -116, -253, -223, -143, -208, -140, -245, -249, -189, -116, -183, -76, -204, -104, -244, -12, -166, -60, -121, -47, -166, -175, -56, -168, -162, -204, -21, -88, -58, -150, -40, -99, -81, -99, -107, -122, -42, -182, -164, -143, -188, -191, -9, -192, -16, -239, -99, -18, -141, -90, -78, -59, -47, -102, -163, -152, -137, -41, -121, -26, -219, -72, -42, -17, -121, -128, -66, -135, -11, -44, -140, -60, -187, -12, -188, -60, -70, -151, -43, -179, -47, -145, -120, -60, -44, -173, -203, -101, -164, -133, -87, -127, -145, -1, -69, -213, -233, -116, -153, -24, -153, -196, -173, -233, -73, -216, -146, -62, -55, -2, -108, -134, -105, -23, -155, -121, -232, -14, -173, -251, -57, -10, -140, -115, -173, -193, -31, -32, -235, -236, -121, -120, -17, -3, -228, -202, -36, -41, -130, -176, -244, -48, -49, -171, -92, -20, -245, -159, -96, -80, -157, -149, -78, -166, -109, -35, -199, -0, -19, -177, -81, -114, -136, -215, -113, -141, -250, -254, -11, -27, -233, -222, -234, -117, -157, -69, -36, -99, -121, -107, -240, -7, -200, -206, -77, -15, -116, -243, -67, -248, -215, -250, -78, -44, -213, -250, -54, -36, -6, -110, -118, -11, -18, -157, -76, -157, -54, -197, -27, -121, -127, -76, -203, -247, -146, -255, -47, -220, -168, -185, -77, -96, -166, -246, -71, -49, -211, -251, -27, -254, -28, -27, -20, -208, -110, -129, -5, -235, -118, -204, -193, -73, -50, -90, -190, -18, -48, -109, -219, -11, -192, -189, -152, -121, -177, -27, -6, -184, -16, -211, -78, -189, -234, -204, -244, -101, -250, -16, -54, -141, -237, -154, -113, -72, -230, -124, -15, -224, -225, -8, -93, -170, -55, -242, -54, -152, -60, -180, -4, -182, -81, -228, -255, -244, -250, -25, -34, -125, -168, -210, -4, -62, -132, -237, -243, -180, -24, -150, -225, -116, -124, -147, -70, -160, -124, -111, -251, -57, -2, -164, -59, -153, -38, -49, -74, -174, -204, -22, -68, -92, -179, -107, -246, -47, -41, -9, -86, -174, -76, -21, -3, -60, -5, -28, -219, -77, -191, -134, -8, -129, -35, -21, -84, -44, -237, -48, -129, -105, -111, -42, -132, -202, -26, -140, -50, -214, -135, -227, -39, -241, -189, -4, -122, -241, -92, -221, -0, -19, -176, -103, -0, -127, -198, -183, -252, -105, -82, -73, -45, -6, -160, -98, -89, -84, -179, -221, -221, -72, -136, -129, -115, -250, -170, -165, -93, -170, -147, -105, -42, -163, -252, -5, -243, -143, -92, -15, -91, -218, -165, -134, -163, -245, -53, -112, -5, -147, -181, -78, -195, -150, -151, -69, -94, -198, -173, -53, -86, -185, -44, -170, -81, -23, -152, -67, -106, -101, -12, -92, -98, -125, -169, -78, -166, -169, -140, -50, -3, -247, -247, -119, -134, -25, -145, -12, -144, -105, -247, -195, -20, -152, -181, -251, -221, -145, -249, -177, -180, -104, -51, -48, -97, -235, -253, -177, -23, -226, -12, -144, -18, -3, -55, -104, -132, -42, -123, -193, -36, -56, -153, -214, -96, -148, -251, -49, -211, -243, -242, -62, -26, -140, -40, -6, -192, -4, -191, -15, -99, -182, -128, -37, -48, -79, -237, -59, -122, -217, -96, -210, -15, -129, -217, -165, -31, -194, -188, -84, -214, -197, -252, -214, -138, -24, -32, -37, -6, -46, -153, -1, -106, -60, -75, -10, -163, -124, -8, -243, -25, -124, -12, -83, -198, -12, -73, -1, -231, -116, -89, -134, -159, -236, -140, -92, -217, -63, -204, -185, -101, -136, -74, -155, -193, -129, -58, -207, -98, -190, -141, -177, -228, -147, -139, -98, -102, -247, -23, -48, -129, -242, -58, -224, -29, -169, -239, -160, -54, -106, -48, -192, -36, -50, -91, -190, -224, -169, -81, -10, -234, -203, -154, -121, -63, -16, -123, -201, -189, -96, -128, -54, -17, -97, -248, -127, -149, -245, -15, -155, -62, -47, -246, -15, -99, -136, -241, -134, -193, -129, -58, -107, -251, -113, -235, -62, -154, -85, -94, -176, -221, -248, -4, -142, -211, -224, -144, -177, -178, -140, -24, -43, -103, -142, -87, -171, -160, -29, -169, -216, -87, -150, -12, -243, -158, -16, -194, -93, -146, -138, -108, -6, -203, -97, -17, -67, -151, -72, -250, -173, -164, -117, -67, -8, -67, -34, -151, -36, -29, -144, -169, -239, -30, -89, -130, -201, -131, -218, -238, -244, -160, -31, -152, -92, -170, -88, -73, -55, -118, -81, -247, -171, -210, -32, -179, -241, -204, -18, -218, -38, -49, -112, -35, -45, -141, -91, -42, -195, -79, -150, -180, -130, -164, -119, -135, -16, -126, -26, -203, -197, -236, -88, -89, -210, -3, -185, -250, -74, -61, -170, -154, -160, -81, -170, -216, -28, -138, -126, -136, -71, -52, -248, -203, -46, -203, -187, -115, -129, -164, -203, -100, -182, -236, -107, -20, -73, -247, -170, -116, -59, -122, -45, -43, -95, -139, -72, -101, -248, -141, -100, -161, -227, -119, -96, -22, -213, -162, -140, -34, -111, -104, -112, -90, -221, -222, -123, -104, -167, -12, -249, -46, -104, -84, -26, -141, -128, -207, -97, -42, -229, -53, -49, -147, -241, -173, -37, -50, -64, -74, -58, -180, -36, -59, -186, -211, -86, -90, -249, -48, -255, -132, -231, -177, -37, -232, -62, -152, -4, -253, -229, -46, -250, -119, -87, -86, -136, -164, -66, -149, -142, -121, -55, -95, -238, -178, -210, -144, -20, -59, -216, -166, -22, -71, -103, -206, -163, -129, -58, -88, -112, -75, -212, -70, -80, -11, -36, -26, -61, -82, -127, -8, -76, -177, -242, -83, -204, -64, -113, -63, -190, -45, -125, -132, -46, -245, -5, -39, -219, -209, -83, -0, -156, -138, -5, -184, -238, -131, -249, -213, -191, -72, -36, -147, -119, -141, -254, -229, -25, -126, -98, -74, -255, -128, -77, -137, -120, -241, -2, -135, -248, -123, -235, -8, -129, -209, -64, -29, -76, -11, -120, -15, -150, -0, -59, -26, -167, -153, -4, -18, -141, -30, -77, -127, -8, -138, -87, -11, -73, -47, -56, -21, -249, -250, -138, -218, -109, -90, -95, -9, -93, -158, -225, -119, -239, -178, -221, -128, -237, -199, -52, -141, -138, -64, -29, -108, -201, -184, -191, -51, -242, -119, -128, -101, -155, -182, -219, -169, -176, -107, -163, -71, -164, -206, -126, -41, -70, -122, -193, -0, -71, -210, -239, -157, -185, -26, -0, -120, -19, -150, -243, -32, -57, -197, -111, -214, -43, -120, -182, -209, -67, -182, -11, -70, -95, -247, -192, -29, -225, -56, -92, -210, -33, -178, -93, -73, -158, -144, -229, -228, -27, -49, -192, -100, -158, -3, -36, -221, -46, -233, -53, -153, -160, -89, -187, -146, -70, -70, -143, -145, -134, -30, -141, -0, -61, -79, -162, -221, -20, -216, -74, -98, -18, -230, -86, -215, -60, -7, -51, -13, -141, -30, -253, -6, -245, -157, -51, -187, -74, -38, -229, -245, -245, -60, -137, -118, -83, -96, -78, -60, -155, -54, -45, -159, -21, -42, -30, -151, -180, -49, -150, -219, -174, -112, -99, -167, -130, -78, -52, -94, -163, -250, -11, -78, -205, -36, -242, -93, -89, -14, -222, -53, -36, -45, -77, -220, -10, -217, -11, -125, -65, -146, -60, -132, -37, -153, -56, -20, -216, -147, -72, -242, -199, -58, -72, -21, -62, -67, -8, -219, -68, -242, -17, -55, -106, -48, -201, -232, -225, -180, -201, -155, -75, -37, -180, -155, -204, -0, -137, -245, -181, -173, -47, -72, -158, -82, -176, -112, -181, -89, -152, -175, -193, -184, -46, -159, -35, -137, -1, -134, -5, -46, -9, -175, -236, -12, -240, -28, -112, -68, -1, -221, -170, -192, -245, -152, -110, -225, -62, -96, -255, -8, -77, -7, -165, -105, -221, -72, -183, -142, -181, -173, -47, -168, -195, -0, -151, -96, -145, -193, -151, -23, -140, -78, -117, -219, -237, -139, -3, -106, -109, -164, -14, -249, -152, -139, -210, -165, -88, -36, -238, -22, -192, -187, -35, -52, -144, -150, -214, -173, -85, -235, -88, -13, -134, -74, -102, -128, -12, -205, -130, -152, -71, -111, -227, -47, -216, -219, -61, -154, -68, -79, -169, -190, -33, -245, -197, -57, -237, -12, -96, -231, -138, -250, -96, -112, -86, -175, -253, -137, -103, -39, -155, -157, -198, -206, -207, -63, -77, -119, -83, -79, -219, -12, -181, -16, -230, -15, -240, -44, -166, -8, -186, -22, -248, -65, -23, -245, -65, -13, -79, -41, -170, -125, -37, -219, -201, -75, -84, -231, -197, -249, -67, -140, -175, -168, -47, -207, -0, -69, -105, -221, -166, -147, -9, -65, -195, -28, -71, -186, -201, -98, -214, -54, -67, -157, -137, -169, -208, -215, -240, -247, -114, -15, -5, -89, -66, -18, -235, -131, -4, -79, -169, -204, -253, -42, -95, -201, -234, -188, -68, -41, -92, -82, -231, -197, -213, -96, -128, -148, -180, -110, -211, -128, -236, -246, -40, -221, -166, -177, -107, -155, -161, -166, -228, -126, -176, -131, -40, -200, -18, -146, -88, -31, -12, -94, -126, -70, -61, -165, -186, -69, -158, -91, -178, -38, -204, -89, -5, -101, -146, -236, -212, -12, -172, -34, -254, -140, -185, -92, -149, -249, -3, -252, -65, -210, -51, -178, -188, -185, -23, -73, -138, -249, -184, -223, -39, -219, -242, -165, -131, -85, -84, -226, -56, -82, -53, -36, -170, -125, -115, -235, -91, -101, -90, -194, -14, -158, -145, -45, -73, -219, -66, -52, -91, -56, -9, -70, -188, -50, -154, -65, -15, -29, -66, -88, -63, -132, -112, -91, -8, -97, -98, -8, -161, -200, -175, -44, -233, -197, -133, -16, -58, -46, -214, -219, -134, -16, -230, -13, -33, -148, -229, -34, -216, -222, -105, -198, -133, -16, -78, -8, -33, -196, -50, -127, -156, -39, -233, -176, -206, -212, -35, -233, -147, -178, -77, -160, -138, -112, -129, -164, -159, -73, -42, -218, -233, -35, -153, -161, -18, -231, -207, -71, -37, -45, -151, -57, -31, -39, -115, -254, -232, -6, -41, -158, -82, -79, -200, -156, -104, -86, -148, -169, -240, -99, -106, -234, -20, -26, -137, -180, -88, -185, -84, -59, -245, -252, -62, -140, -149, -234, -165, -243, -50, -64, -9, -93, -178, -117, -44, -5, -36, -154, -91, -157, -182, -114, -254, -4, -190, -128, -173, -96, -58, -66, -219, -100, -42, -194, -215, -43, -250, -135, -183, -187, -38, -176, -22, -240, -32, -240, -153, -138, -50, -149, -70, -188, -66, -26, -210, -99, -229, -82, -237, -212, -251, -49, -32, -20, -205, -78, -182, -156, -240, -236, -93, -129, -244, -229, -93, -219, -12, -53, -63, -38, -8, -62, -225, -245, -157, -22, -107, -183, -70, -125, -175, -99, -91, -249, -150, -102, -11, -39, -33, -114, -41, -133, -70, -164, -7, -85, -38, -189, -56, -10, -208, -232, -109, -212, -0, -195, -164, -47, -24, -46, -144, -96, -196, -75, -161, -17, -137, -177, -114, -35, -29, -140, -112, -125, -65, -219, -32, -193, -136, -87, -70, -147, -223, -147, -38, -101, -231, -142, -145, -142, -182, -189, -105, -251, -226, -158, -221, -5, -82, -140, -120, -213, -52, -36, -198, -202, -245, -27, -62, -4, -127, -25, -91, -103, -191, -72, -197, -14, -89, -140, -112, -125, -65, -219, -32, -193, -136, -151, -66, -35, -18, -99, -229, -250, -13, -224, -40, -108, -101, -50, -1, -75, -56, -89, -149, -76, -42, -105, -149, -82, -163, -253, -70, -12, -69, -130, -169, -25, -115, -72, -189, -39, -161, -174, -9, -254, -129, -150, -230, -17, -232, -26, -212, -216, -185, -131, -132, -240, -112, -224, -91, -153, -227, -111, -84, -220, -31, -162, -253, -243, -235, -183, -147, -176, -33, -99, -134, -62, -121, -121, -151, -88, -95, -35, -134, -34, -205, -212, -188, -31, -80, -169, -43, -192, -18, -64, -156, -76, -127, -54, -191, -170, -6, -105, -89, -179, -182, -0, -206, -247, -227, -205, -128, -239, -69, -104, -190, -207, -64, -98, -165, -243, -137, -120, -182, -96, -75, -160, -104, -218, -213, -130, -190, -13, -155, -190, -160, -87, -240, -15, -109, -124, -63, -219, -236, -26, -152, -143, -218, -134, -249, -227, -28, -205, -134, -157, -57, -221, -143, -127, -31, -161, -121, -53, -59, -4, -247, -27, -77, -24, -138, -244, -24, -139, -13, -128, -202, -116, -183, -206, -0, -133, -155, -89, -116, -131, -70, -95, -6, -105, -105, -216, -230, -41, -56, -46, -163, -139, -169, -139, -31, -80, -205, -45, -209, -219, -68, -8, -129, -16, -194, -151, -66, -8, -139, -251, -223, -137, -37, -241, -124, -29, -164, -169, -94, -165, -93, -53, -120, -197, -50, -8, -152, -95, -193, -129, -50, -27, -77, -105, -254, -162, -146, -58, -90, -221, -158, -166, -142, -198, -112, -171, -204, -20, -48, -123, -58, -200, -209, -124, -31, -216, -210, -143, -207, -39, -226, -64, -129, -69, -20, -61, -142, -185, -91, -173, -74, -65, -86, -210, -145, -10, -10, -84, -175, -192, -58, -152, -215, -210, -129, -37, -101, -193, -164, -247, -198, -35, -32, -169, -219, -211, -144, -184, -220, -162, -70, -118, -45, -50, -187, -112, -17, -17, -22, -169, -16, -18, -253, -250, -60, -152, -38, -110, -42, -166, -208, -24, -178, -153, -196, -72, -3, -105, -234, -217, -75, -171, -158, -5, -179, -104, -30, -139, -41, -233, -150, -232, -93, -143, -149, -190, -220, -98, -4, -106, -12, -201, -8, -138, -20, -8, -141, -62, -143, -79, -194, -86, -57, -189, -203, -185, -171, -100, -245, -236, -244, -148, -121, -29, -203, -23, -12, -176, -69, -131, -126, -108, -130, -173, -253, -241, -81, -180, -216, -199, -144, -196, -229, -22, -137, -217, -181, -250, -9, -127, -200, -45, -242, -199, -57, -154, -77, -129, -5, -48, -199, -147, -194, -36, -74, -88, -142, -223, -117, -75, -238, -167, -216, -223, -83, -212, -179, -47, -146, -152, -151, -209, -127, -192, -241, -41, -180, -145, -178, -99, -189, -124, -212, -16, -151, -21, -2, -87, -151, -84, -232, -114, -148, -193, -100, -89, -86, -139, -87, -178, -127, -77, -58, -215, -34, -158, -245, -191, -252, -241, -108, -132, -16, -110, -10, -33, -188, -44, -105, -99, -21, -36, -190, -192, -172, -109, -71, -75, -90, -172, -164, -173, -20, -1, -47, -69, -61, -123, -147, -164, -99, -48, -185, -102, -107, -224, -240, -146, -54, -123, -15, -18, -151, -91, -140, -64, -141, -33, -150, -24, -105, -217, -252, -113, -132, -110, -83, -204, -89, -51, -186, -247, -31, -3, -123, -238, -60, -133, -201, -28, -165, -31, -4, -197, -2, -94, -138, -122, -118, -101, -44, -170, -231, -37, -76, -183, -80, -168, -134, -247, -169, -235, -44, -26, -200, -1, -85, -35, -64, -150, -240, -30, -224, -184, -196, -74, -187, -218, -235, -151, -8, -34, -52, -233, -243, -87, -90, -155, -219, -96, -49, -244, -11, -39, -244, -173, -208, -157, -155, -97, -200, -28, -138, -233, -11, -254, -66, -131, -152, -196, -58, -12, -208, -183, -229, -150, -119, -104, -208, -190, -194, -5, -116, -105, -157, -79, -107, -243, -101, -204, -148, -59, -201, -255, -162, -158, -74, -9, -12, -80, -41, -224, -141, -36, -84, -189, -195, -172, -226, -229, -44, -153, -99, -227, -143, -37, -45, -33, -233, -247, -254, -215, -43, -188, -222, -79, -217, -33, -132, -208, -86, -82, -169, -141, -36, -237, -229, -123, -249, -172, -221, -82, -157, -195, -134, -217, -66, -96, -8, -97, -86, -8, -225, -216, -16, -194, -10, -178, -132, -77, -61, -223, -233, -123, -14, -69, -227, -32, -218, -57, -6, -192, -210, -68, -182, -119, -105, -177, -254, -74, -25, -192, -233, -42, -167, -0, -90, -206, -8, -134, -109, -206, -176, -101, -201, -253, -228, -32, -218, -132, -182, -160, -197, -192, -216, -130, -54, -234, -77, -163, -192, -95, -49, -19, -232, -39, -11, -238, -39, -101, -9, -199, -76, -161, -215, -3, -167, -71, -238, -181, -38, -3, -144, -184, -239, -79, -42, -48, -111, -222, -51, -129, -69, -128, -229, -170, -75, -116, -213, -86, -33, -3, -84, -49, -54, -166, -71, -184, -25, -216, -190, -164, -254, -77, -48, -33, -29, -103, -216, -238, -130, -77, -169, -145, -37, -28, -183, -153, -3, -255, -136, -220, -75, -226, -252, -218, -220, -91, -94, -87, -210, -22, -114, -152, -13, -99, -18, -230, -149, -219, -120, -107, -218, -196, -62, -149, -49, -64, -41, -99, -99, -91, -218, -126, -25, -75, -233, -51, -242, -224, -35, -197, -117, -196, -13, -69, -173, -48, -0, -17, -148, -212, -85, -26, -59, -55, -28, -72, -125, -15, -37, -229, -199, -151, -61, -115, -207, -224, -29, -239, -54, -244, -185, -235, -41, -32, -181, -158, -145, -138, -34, -6, -32, -81, -7, -50, -34, -25, -128, -52, -93, -121, -229, -151, -75, -194, -252, -149, -242, -5, -213, -25, -37, -218, -0, -53, -236, -239, -101, -253, -175, -98, -254, -12, -205, -235, -192, -129, -64, -163, -101, -110, -62, -89, -244, -78, -88, -182, -203, -87, -129, -235, -155, -84, -168, -4, -93, -121, -136, -32, -66, -115, -115, -8, -97, -5, -191, -189, -124, -8, -97, -200, -198, -201, -53, -176, -179, -6, -187, -187, -247, -18, -41, -1, -182, -173, -32, -132, -48, -69, -210, -30, -50, -29, -78, -101, -36, -114, -229, -71, -128, -237, -85, -123, -18, -182, -9, -65, -97, -182, -201, -178, -17, -32, -71, -215, -122, -194, -201, -72, -63, -170, -70, -146, -202, -81, -194, -233, -170, -178, -143, -125, -17, -91, -33, -188, -132, -173, -148, -186, -86, -2, -181, -48, -2, -44, -129, -153, -231, -143, -37, -109, -251, -219, -78, -220, -226, -154, -157, -107, -121, -129, -104, -5, -73, -215, -132, -16, -166, -135, -16, -158, -80, -3, -208, -255, -132, -147, -93, -127, -221, -164, -101, -31, -123, -70, -22, -145, -252, -14, -89, -100, -241, -144, -200, -228, -126, -79, -55, -146, -214, -145, -237, -168, -126, -86, -8, -225, -153, -42, -226, -16, -194, -164, -206, -223, -144, -155, -88, -98, -35, -112, -75, -88, -89, -69, -101, -35, -0, -125, -212, -149, -167, -124, -221, -189, -248, -81, -48, -7, -214, -162, -93, -77, -146, -133, -210, -22, -70, -128, -174, -133, -192, -172, -42, -120, -23, -63, -220, -221, -213, -193, -77, -177, -145, -164, -175, -135, -16, -238, -84, -129, -93, -29, -248, -33, -112, -35, -22, -199, -119, -66, -228, -254, -246, -126, -255, -111, -180, -227, -14, -93, -58, -74, -52, -96, -146, -247, -72, -122, -168, -224, -222, -235, -41, -126, -18, -12, -56, -210, -118, -19, -101, -212, -110, -144, -106, -217, -151, -157, -74, -71, -194, -46, -91, -192, -124, -254, -127, -94, -226, -249, -128, -110, -195, -20, -29, -75, -2, -255, -172, -232, -71, -233, -23, -87, -99, -148, -72, -250, -114, -125, -100, -123, -30, -216, -175, -97, -91, -203, -96, -94, -73, -39, -97, -171, -155, -104, -230, -175, -170, -17, -0, -155, -255, -191, -65, -133, -207, -66, -45, -164, -50, -64, -69, -29, -41, -206, -16, -159, -192, -66, -157, -174, -35, -238, -80, -49, -37, -118, -92, -208, -223, -210, -47, -183, -6, -3, -164, -102, -43, -189, -25, -248, -97, -23, -253, -153, -142, -169, -113, -175, -39, -18, -43, -145, -161, -171, -98, -128, -23, -48, -231, -150, -230, -249, -129, -11, -30, -160, -231, -217, -41, -49, -149, -236, -155, -253, -75, -24, -178, -124, -193, -166, -134, -37, -48, -245, -237, -109, -93, -182, -85, -249, -117, -215, -100, -128, -153, -148, -251, -18, -116, -173, -152, -162, -109, -29, -126, -141, -134, -251, -197, -0, -231, -96, -6, -167, -223, -19, -223, -36, -114, -107, -76, -6, -184, -145, -76, -166, -172, -134, -109, -165, -142, -18, -165, -52, -53, -218, -234, -169, -117, -175, -167, -232, -23, -3, -140, -52, -212, -249, -114, -49, -69, -217, -222, -37, -245, -204, -81, -12, -208, -52, -52, -44, -201, -178, -54, -135, -33, -73, -122, -87, -181, -228, -61, -95, -183, -83, -192, -136, -7, -213, -89, -41, -91, -217, -89, -188, -162, -15, -251, -249, -124, -220, -181, -16, -212, -214, -151, -91, -103, -42, -1, -62, -6, -252, -185, -219, -54, -11, -234, -190, -24, -184, -164, -23, -117, -167, -52, -222, -218, -206, -226, -37, -109, -28, -136, -45, -55, -95, -109, -145, -1, -250, -102, -85, -196, -18, -99, -79, -163, -100, -163, -7, -239, -199, -195, -192, -123, -234, -220, -243, -251, -189, -241, -232, -162, -69, -87, -109, -74, -60, -94, -170, -166, -24, -103, -176, -85, -48, -75, -216, -16, -6, -192, -118, -61, -123, -8, -243, -2, -62, -51, -161, -47, -201, -95, -110, -27, -240, -175, -191, -52, -49, -68, -83, -6, -160, -194, -163, -171, -107, -80, -189, -62, -77, -181, -101, -23, -122, -188, -144, -232, -188, -225, -109, -196, -24, -96, -77, -175, -127, -93, -224, -209, -212, -103, -235, -23, -176, -192, -208, -115, -170, -41, -251, -136, -178, -47, -50, -71, -151, -106, -167, -46, -165, -105, -3, -69, -12, -224, -247, -174, -195, -242, -23, -124, -164, -151, -125, -104, -2, -204, -234, -250, -209, -4, -58, -40, -214, -184, -22, -222, -171, -131, -236, -23, -118, -128, -164, -159, -43, -109, -227, -230, -86, -80, -244, -16, -109, -60, -92, -8, -97, -43, -73, -235, -75, -58, -173, -155, -122, -98, -160, -194, -233, -195, -251, -191, -62, -166, -210, -126, -58, -82, -197, -210, -50, -235, -226, -176, -99, -118, -96, -72, -8, -97, -150, -164, -163, -74, -104, -231, -24, -0, -43, -134, -16, -30, -149, -25, -163, -26, -167, -108, -47, -65, -138, -211, -199, -143, -37, -157, -44, -41, -230, -21, -20, -100, -73, -183, -135, -29, -35, -198, -65, -178, -101, -252, -10, -184, -79, -210, -197, -138, -4, -111, -96, -18, -255, -195, -46, -40, -78, -136, -220, -47, -253, -194, -19, -179, -170, -255, -44, -132, -240, -235, -16, -66, -76, -216, -251, -183, -164, -158, -239, -108, -78, -66, -130, -173, -178, -20, -238, -115, -44, -66, -8, -27, -87, -144, -156, -39, -105, -127, -73, -79, -75, -186, -66, -150, -218, -61, -139, -54, -220, -186, -162, -123, -47, -59, -110, -147, -57, -150, -244, -26, -209, -80, -249, -44, -230, -74, -6, -72, -192, -24, -217, -143, -60, -83, -17, -205, -94, -8, -33, -187, -55, -112, -211, -31, -170, -76, -155, -120, -165, -164, -126, -164, -154, -171, -100, -128, -236, -222, -193, -147, -128, -189, -122, -222, -165, -22, -225, -14, -163, -77, -172, -133, -31, -151, -244, -11, -73, -87, -169, -32, -190, -175, -45, -41, -187, -0, -23, -72, -90, -145, -18, -115, -112, -75, -72, -103, -0, -89, -186, -182, -170, -117, -247, -38, -146, -110, -240, -211, -235, -138, -214, -248, -35, -29, -33, -132, -203, -67, -8, -111, -247, -29, -74, -174, -168, -91, -30, -184, -149, -72, -196, -83, -141, -246, -159, -147, -116, -156, -164, -83, -154, -214, -145, -216, -206, -86, -77, -125, -59, -27, -131, -26, -182, -236, -94, -46, -3, -187, -69, -197, -26, -124, -34, -112, -107, -147, -178, -13, -250, -176, -89, -183, -245, -164, -54, -54, -29, -88, -186, -47, -141, -205, -1, -24, -78, -38, -196, -92, -225, -198, -99, -187, -169, -22, -186, -231, -183, -129, -172, -16, -56, -70, -115, -192, -254, -0, -48, -160, -167, -207, -7, -148, -0, -127, -146, -116, -73, -8, -225, -59, -125, -239, -88, -187, -120, -80, -210, -235, -146, -142, -239, -219, -16, -142, -5, -60, -12, -91, -74, -214, -84, -96, -134, -144, -29, -178, -140, -144, -185, -183, -9, -102, -101, -91, -40, -114, -111, -54, -18, -219, -25, -246, -105, -168, -31, -200, -239, -24, -18, -29, -1, -24, -65, -14, -32, -238, -172, -17, -219, -86, -78, -33, -132, -155, -37, -61, -37, -105, -136, -199, -174, -99, -130, -122, -31, -26, -150, -12, -103, -178, -174, -77, -207, -221, -48, -107, -18, -3, -168, -100, -15, -190, -178, -47, -203, -231, -178, -31, -3, -209, -31, -172, -71, -248, -179, -236, -135, -142, -225, -181, -94, -230, -37, -42, -250, -65, -49, -155, -192, -196, -94, -181, -219, -13, -146, -100, -128, -16, -194, -78, -37, -117, -148, -77, -27, -231, -200, -18, -77, -237, -89, -191, -107, -141, -113, -155, -164, -227, -187, -173, -36, -22, -176, -218, -5, -222, -144, -134, -238, -250, -57, -18, -208, -181, -16, -24, -141, -51, -27, -184, -183, -143, -36, -1, -63, -174, -223, -181, -198, -120, -70, -150, -237, -108, -196, -32, -132, -240, -238, -58, -244, -190, -196, -36, -132, -176, -97, -238, -250, -169, -146, -246, -13, -33, -188, -189, -173, -190, -205, -113, -171, -128, -4, -228, -183, -182, -157, -19, -81, -212, -255, -117, -36, -253, -178, -205, -134, -178, -230, -224, -57, -253, -165, -117, -176, -148, -70, -136, -173, -189, -10, -69, -239, -188, -100, -196, -120, -151, -164, -99, -218, -236, -195, -220, -104, -14, -126, -135, -76, -14, -72, -134, -11, -111, -255, -47, -114, -125, -68, -9, -111, -158, -48, -163, -219, -13, -169, -7, -161, -47, -12, -80, -177, -115, -120, -219, -216, -70, -102, -228, -169, -131, -55, -36, -197, -182, -100, -107, -69, -120, -115, -219, -65, -97, -144, -235, -112, -98, -142, -51, -7, -251, -50, -43, -154, -116, -194, -173, -107, -43, -202, -150, -173, -117, -240, -134, -164, -151, -242, -23, -235, -10, -111, -37, -24, -177, -211, -235, -28, -199, -0, -146, -94, -46, -185, -247, -37, -73, -159, -117, -107, -91, -12, -111, -2, -222, -28, -209, -5, -204, -82, -124, -4, -104, -5, -69, -140, -228, -186, -147, -5, -134, -115, -191, -133, -57, -142, -1, -202, -132, -213, -16, -194, -206, -21, -197, -255, -216, -33, -205, -93, -143, -142, -0, -255, -23, -48, -199, -49, -64, -83, -84, -172, -114, -138, -100, -128, -57, -2, -221, -172, -224, -230, -198, -85, -64, -19, -204, -82, -100, -4, -0, -78, -165, -96, -71, -180, -185, -5, -255, -103, -70, -128, -50, -132, -16, -22, -45, -184, -213, -186, -226, -101, -20, -35, -16, -20, -196, -252, -187, -103, -211, -26, -53, -234, -105, -197, -186, -215, -79, -140, -142, -0, -134, -34, -141, -92, -173, -108, -105, -115, -145, -54, -117, -20, -163, -24, -197, -40, -70, -49, -138, -81, -140, -98, -20, -163, -24, -197, -40, -70, -49, -138, -81, -140, -98, -20, -163, -24, -197, -40, -70, -49, -138, -81, -204, -37, -248, -255, -129, -51, -157, -250, -111, -139, -157, -153, -0, -0, -0, -0, -73, -69, -78, -68, -174, -66, -96, -130, -}; -/* clang-format on */ diff --git a/scene/resources/default_theme/picker_cursor.png b/scene/resources/default_theme/picker_cursor.png Binary files differnew file mode 100644 index 0000000000..2f403492d2 --- /dev/null +++ b/scene/resources/default_theme/picker_cursor.png diff --git a/scene/resources/default_theme/radio_checked_disabled.png b/scene/resources/default_theme/radio_checked_disabled.png Binary files differnew file mode 100644 index 0000000000..72f08ecb96 --- /dev/null +++ b/scene/resources/default_theme/radio_checked_disabled.png diff --git a/scene/resources/default_theme/radio_unchecked_disabled.png b/scene/resources/default_theme/radio_unchecked_disabled.png Binary files differnew file mode 100644 index 0000000000..a8f4c1b555 --- /dev/null +++ b/scene/resources/default_theme/radio_unchecked_disabled.png diff --git a/scene/resources/default_theme/theme_data.h b/scene/resources/default_theme/theme_data.h index b905c9db69..190f2a03d9 100644 --- a/scene/resources/default_theme/theme_data.h +++ b/scene/resources/default_theme/theme_data.h @@ -14,6 +14,10 @@ static const unsigned char arrow_right_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x4, 0x0, 0x0, 0x0, 0xfc, 0x7c, 0x94, 0x6c, 0x0, 0x0, 0x0, 0x2e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x17, 0x3c, 0xf8, 0xf, 0x82, 0xf7, 0x13, 0x70, 0x48, 0x3c, 0xf8, 0xf2, 0x50, 0x1b, 0x43, 0x2, 0xa, 0xaf, 0xbe, 0xe0, 0xc6, 0x2e, 0xf1, 0xff, 0xe1, 0x7c, 0x12, 0x24, 0x10, 0x46, 0x11, 0xb6, 0x1c, 0xe1, 0x5c, 0xa, 0x0, 0x0, 0xe0, 0x14, 0x48, 0xb1, 0x3d, 0x1b, 0x7a, 0xf0, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; +static const unsigned char bar_arrow_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x14, 0x8, 0x4, 0x0, 0x0, 0x0, 0x2e, 0x6b, 0x75, 0xfc, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x1, 0x73, 0x52, 0x47, 0x42, 0x0, 0xae, 0xce, 0x1c, 0xe9, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xe, 0xc0, 0x0, 0x0, 0xe, 0xc0, 0x1, 0x6a, 0xd6, 0x89, 0x9, 0x0, 0x0, 0x0, 0x65, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x63, 0xfc, 0xcf, 0x80, 0x1f, 0x30, 0x8e, 0x20, 0x5, 0x8c, 0x38, 0x24, 0xff, 0x53, 0x5f, 0xc1, 0xb, 0xee, 0x9f, 0x53, 0x18, 0x18, 0xd8, 0x73, 0x24, 0xbe, 0x62, 0x55, 0x70, 0x5f, 0x83, 0x61, 0x15, 0xa3, 0x2e, 0x3, 0x3, 0xc3, 0xd, 0xe6, 0x30, 0xd9, 0xcb, 0x18, 0xa, 0x1e, 0xc6, 0xfd, 0x9f, 0xc6, 0xc0, 0xd, 0x35, 0xea, 0x3b, 0x63, 0x81, 0xfc, 0x2c, 0x14, 0x5, 0xf, 0x2a, 0x18, 0xda, 0xd1, 0x1c, 0x50, 0xa9, 0xd0, 0x1, 0x57, 0xf0, 0x10, 0x53, 0x9a, 0x81, 0x81, 0x81, 0xa1, 0x52, 0xbe, 0x83, 0x81, 0x81, 0xf1, 0x3f, 0x2e, 0x69, 0xa8, 0x12, 0x3a, 0x4, 0x14, 0x0, 0x7b, 0xda, 0x34, 0x1, 0xbb, 0xb5, 0x3e, 0x6c, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; + static const unsigned char bookmark_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, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x4, 0x73, 0x42, 0x49, 0x54, 0x8, 0x8, 0x8, 0x8, 0x7c, 0x8, 0x64, 0x88, 0x0, 0x0, 0x0, 0x57, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0xed, 0x93, 0x31, 0xa, 0xc0, 0x30, 0xc, 0x3, 0xa5, 0xd0, 0xff, 0x7f, 0x59, 0x1d, 0x8a, 0x42, 0x8, 0x9, 0x95, 0xc9, 0xd2, 0xa1, 0x9a, 0x8c, 0xf1, 0xdd, 0x62, 0x1b, 0x38, 0xc, 0x87, 0x5a, 0x5, 0xae, 0x79, 0xde, 0x2, 0x1, 0x80, 0x94, 0x39, 0x48, 0x76, 0x49, 0x17, 0xa4, 0xf0, 0x24, 0x61, 0x2b, 0x51, 0x8b, 0xfc, 0x82, 0xcf, 0xb, 0x48, 0x7a, 0xdf, 0x75, 0x81, 0xf, 0xe5, 0x29, 0xf7, 0x92, 0x6b, 0x3, 0x1a, 0x1e, 0xda, 0x7c, 0x3d, 0x77, 0x21, 0x7b, 0xa8, 0x74, 0x2e, 0xcb, 0xd, 0xc8, 0x75, 0x13, 0x28, 0x9, 0xed, 0xc2, 0xc8, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; @@ -42,6 +46,10 @@ static const unsigned char checked_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, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x8d, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x58, 0x56, 0x63, 0xb0, 0xaf, 0xb5, 0x38, 0x37, 0x40, 0x20, 0x20, 0x24, 0xb6, 0xb6, 0xb9, 0x57, 0x57, 0x5a, 0x20, 0x20, 0x24, 0x38, 0x36, 0x40, 0x20, 0x20, 0x25, 0x1e, 0x1e, 0x22, 0x1f, 0x1f, 0x23, 0x8b, 0x8b, 0x8d, 0xff, 0xff, 0xff, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x23, 0x23, 0x28, 0x42, 0x42, 0x47, 0xf8, 0xf8, 0xf8, 0xfe, 0xfe, 0xfe, 0x25, 0x25, 0x2a, 0x4e, 0x4e, 0x52, 0x26, 0x26, 0x2b, 0xc5, 0xc5, 0xc7, 0xaa, 0xaa, 0xab, 0xb8, 0xb8, 0xba, 0x5f, 0x5f, 0x63, 0x74, 0x74, 0x77, 0xed, 0xed, 0xed, 0x33, 0x33, 0x38, 0x8d, 0x8d, 0x8f, 0xb8, 0xb8, 0xb9, 0x35, 0x35, 0x39, 0x3a, 0x3a, 0x3e, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xfa, 0xb2, 0xb2, 0xb4, 0x45, 0x45, 0x49, 0x61, 0x61, 0x65, 0x8f, 0x8f, 0x92, 0x63, 0x63, 0x66, 0x2a, 0x2a, 0x2f, 0x40, 0x82, 0xb, 0xf6, 0x0, 0x0, 0x0, 0xf, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x7, 0x27, 0x50, 0x66, 0x68, 0x6a, 0x81, 0xb4, 0xfa, 0xdd, 0xfb, 0xfb, 0xb4, 0xfa, 0xb8, 0xf0, 0x7f, 0x59, 0x0, 0x0, 0x0, 0x7e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x5d, 0xca, 0x5, 0xb2, 0x2, 0x30, 0x18, 0x3, 0xe1, 0x4d, 0xed, 0xb9, 0x60, 0xf7, 0x3f, 0x20, 0xee, 0x4e, 0x99, 0xe0, 0xb0, 0x63, 0xfd, 0xbf, 0x86, 0xd7, 0x12, 0x72, 0x38, 0x69, 0x5b, 0x6b, 0x42, 0x45, 0xe5, 0xa, 0xab, 0x95, 0x41, 0x9f, 0x32, 0x20, 0x69, 0x2d, 0xbc, 0x50, 0x46, 0x3a, 0x10, 0x17, 0x5f, 0x49, 0x4, 0x7f, 0x90, 0x57, 0x89, 0xb7, 0xc5, 0x5f, 0x96, 0x17, 0x2e, 0x93, 0xcb, 0x8e, 0x3a, 0x83, 0xb, 0xc4, 0x8e, 0xd4, 0xff, 0x5c, 0x73, 0x83, 0x69, 0x9e, 0x95, 0xfc, 0x3b, 0xf4, 0x33, 0xe0, 0xf8, 0x61, 0xd3, 0xf1, 0x7d, 0x5d, 0x30, 0x7a, 0x6f, 0x89, 0xb, 0xd4, 0x5a, 0xe1, 0x40, 0xf, 0xfc, 0x34, 0x6c, 0xd2, 0x56, 0x80, 0xef, 0xfd, 0x9, 0xd2, 0x3a, 0x5e, 0x41, 0x15, 0x21, 0x77, 0x6, 0xc7, 0x6b, 0x47, 0x4e, 0x3a, 0x2f, 0x53, 0xb4, 0x10, 0xc7, 0x8c, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; +static const unsigned char checked_disabled_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, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x99, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x73, 0x72, 0x7b, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x82, 0x80, 0x8a, 0x90, 0x90, 0x93, 0x6a, 0x69, 0x70, 0x6a, 0x68, 0x70, 0x93, 0x93, 0x95, 0x58, 0x58, 0x5c, 0x58, 0x58, 0x5b, 0x7d, 0x7d, 0x7f, 0x58, 0x58, 0x5b, 0xa4, 0xa4, 0xa4, 0x9e, 0x9e, 0xa0, 0x9e, 0x9e, 0x9e, 0x9b, 0x9b, 0x9c, 0x9b, 0x9b, 0x9b, 0x9a, 0x9a, 0x9a, 0x99, 0x99, 0x99, 0x98, 0x98, 0x98, 0x97, 0x97, 0x97, 0x96, 0x96, 0x96, 0x95, 0x95, 0x95, 0x93, 0x93, 0x94, 0x8f, 0x8f, 0x8f, 0x86, 0x86, 0x88, 0x85, 0x85, 0x86, 0x82, 0x82, 0x83, 0x81, 0x81, 0x83, 0x7f, 0x7f, 0x81, 0x7c, 0x7c, 0x7e, 0x7a, 0x7a, 0x7d, 0x78, 0x78, 0x7b, 0x71, 0x71, 0x74, 0x68, 0x68, 0x6c, 0x66, 0x66, 0x6a, 0x65, 0x65, 0x68, 0x63, 0x63, 0x66, 0x5f, 0x5f, 0x63, 0x5c, 0x5c, 0x60, 0x5c, 0x5c, 0x5f, 0x5a, 0x5a, 0x5e, 0x59, 0x59, 0x5d, 0x59, 0x59, 0x5c, 0x58, 0x58, 0x5b, 0x57, 0x57, 0x5a, 0x56, 0x56, 0x59, 0x10, 0x13, 0xbb, 0xf, 0x0, 0x0, 0x0, 0x10, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x7, 0x27, 0x27, 0x50, 0x66, 0x68, 0x6a, 0x81, 0xb4, 0xb4, 0xdd, 0xfa, 0xfa, 0xfb, 0xfb, 0x5b, 0xd1, 0xf1, 0xe6, 0x0, 0x0, 0x0, 0x96, 0x49, 0x44, 0x41, 0x54, 0x78, 0x5e, 0x5d, 0x8f, 0xc9, 0x12, 0x82, 0x30, 0x14, 0x4, 0x87, 0x18, 0x50, 0x51, 0x44, 0x25, 0x42, 0x4, 0x77, 0xc4, 0x8d, 0x97, 0x0, 0xf9, 0xff, 0x8f, 0xb3, 0x88, 0xa4, 0x4a, 0xed, 0x63, 0x5f, 0xa6, 0x7, 0xf8, 0x7, 0x1e, 0xe3, 0x7e, 0x60, 0x19, 0x4f, 0x46, 0x1e, 0x0, 0x36, 0x8d, 0x4c, 0x67, 0x59, 0x65, 0x33, 0x6, 0x80, 0x47, 0xad, 0x56, 0x3d, 0xb7, 0x3c, 0x5d, 0x70, 0x0, 0xbe, 0xd1, 0x44, 0x65, 0x4d, 0x94, 0xc8, 0xc2, 0xf8, 0x0, 0x82, 0x4e, 0x91, 0x94, 0x15, 0x5d, 0xd2, 0xec, 0xde, 0x5, 0x83, 0x38, 0xc8, 0xe3, 0x63, 0x23, 0xce, 0xca, 0x9, 0x7a, 0x6e, 0xf3, 0x93, 0x48, 0x1a, 0x27, 0x14, 0x35, 0x3b, 0xb9, 0x5e, 0x56, 0xe4, 0x84, 0x22, 0xba, 0xa, 0x51, 0xd0, 0xb7, 0xa8, 0xcb, 0xfd, 0xcb, 0x9, 0x3b, 0xfb, 0x41, 0xdb, 0x59, 0x17, 0xa6, 0x94, 0x6e, 0xe3, 0x3e, 0x8c, 0x85, 0xf1, 0x90, 0x6e, 0xe6, 0x21, 0xfb, 0x39, 0xe7, 0x73, 0xe6, 0xfd, 0x5f, 0x7, 0xde, 0xc3, 0xb5, 0x16, 0x87, 0xb0, 0x9e, 0x42, 0x46, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; + static const unsigned char checker_bg_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, 0x0, 0x0, 0x0, 0x0, 0xe1, 0x64, 0xe1, 0x57, 0x0, 0x0, 0x0, 0x14, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xfc, 0xcf, 0xc0, 0xc0, 0xd0, 0x0, 0xc4, 0xf8, 0x18, 0xf5, 0x84, 0x19, 0x0, 0x9f, 0x5f, 0xa, 0x1, 0xf8, 0xef, 0x65, 0xf4, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; @@ -258,6 +266,10 @@ static const unsigned char panel_bg_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, 0x1, 0x3, 0x0, 0x0, 0x0, 0xfe, 0xc1, 0x2c, 0xc8, 0x0, 0x0, 0x0, 0x6, 0x50, 0x4c, 0x54, 0x45, 0x25, 0x25, 0x2a, 0x35, 0x32, 0x3b, 0x4a, 0x73, 0x58, 0x4a, 0x0, 0x0, 0x0, 0xa, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x40, 0x3, 0x0, 0x0, 0x10, 0x0, 0x1, 0xb3, 0xac, 0xe2, 0xd0, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; +static const unsigned char picker_cursor_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x4, 0x0, 0x0, 0x0, 0xfc, 0x7c, 0x94, 0x6c, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xe, 0xc3, 0x0, 0x0, 0xe, 0xc3, 0x1, 0xc7, 0x6f, 0xa8, 0x64, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x0, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x6e, 0x6b, 0x73, 0x63, 0x61, 0x70, 0x65, 0x2e, 0x6f, 0x72, 0x67, 0x9b, 0xee, 0x3c, 0x1a, 0x0, 0x0, 0x0, 0xb9, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3, 0x6d, 0x8f, 0x3d, 0x8a, 0xc2, 0x50, 0x18, 0x45, 0xcf, 0x6b, 0x92, 0x2a, 0x19, 0xd4, 0xa4, 0x72, 0x47, 0x3, 0x42, 0xc0, 0x9f, 0x55, 0x44, 0x17, 0x24, 0x88, 0xee, 0x24, 0x53, 0x4d, 0x7e, 0xa, 0xbf, 0x94, 0xd6, 0x71, 0x5, 0xf2, 0x5e, 0x7f, 0x2d, 0xa2, 0xa2, 0xe0, 0x29, 0xef, 0xb9, 0xcd, 0x1, 0x40, 0xb1, 0x76, 0x6a, 0x14, 0x14, 0xd4, 0x68, 0xab, 0x98, 0x11, 0xcd, 0xd5, 0xef, 0x9b, 0xac, 0x27, 0x10, 0x32, 0x3b, 0xb4, 0x32, 0xcd, 0xc7, 0x77, 0xff, 0xfb, 0xc7, 0xc0, 0x92, 0x84, 0x84, 0x82, 0xcb, 0xa2, 0x92, 0x29, 0x46, 0xbb, 0x7d, 0xc3, 0xc0, 0x94, 0x27, 0x13, 0x86, 0x63, 0xa7, 0x12, 0xb5, 0x59, 0xcf, 0x8a, 0x77, 0xd6, 0xb9, 0xa9, 0x46, 0xde, 0x5, 0x92, 0xf, 0x91, 0x3a, 0x2f, 0xff, 0x4d, 0xfc, 0x38, 0xaf, 0x1b, 0x6a, 0x33, 0xa3, 0xf8, 0x10, 0x9b, 0xfc, 0xac, 0x1a, 0x6d, 0xf, 0x2d, 0x17, 0x26, 0xaf, 0x79, 0xc6, 0xf5, 0xd4, 0xa9, 0x44, 0xb1, 0x6c, 0x51, 0x31, 0xb0, 0x26, 0x25, 0x65, 0xc3, 0xb5, 0xa8, 0x64, 0x8a, 0xc6, 0x40, 0x3b, 0x76, 0xb9, 0xb9, 0xe0, 0x42, 0x7e, 0x3e, 0x75, 0x8f, 0x40, 0x0, 0x45, 0x2a, 0x55, 0xcb, 0xcb, 0xeb, 0x5f, 0xa5, 0x22, 0x80, 0x3b, 0xa0, 0x2c, 0x6c, 0xa1, 0x40, 0x2f, 0xda, 0xfc, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; + static const unsigned char popup_bg_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, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0xa2, 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, 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, 0x3b, 0x3b, 0x43, 0x42, 0x42, 0x4b, 0x3e, 0x3e, 0x47, 0x3e, 0x3e, 0x46, 0x41, 0x41, 0x4a, 0x0, 0x0, 0x0, 0x3d, 0x3d, 0x45, 0x3b, 0x3b, 0x43, 0x3a, 0x3a, 0x42, 0x38, 0x38, 0x41, 0x37, 0x37, 0x3e, 0x36, 0x36, 0x3d, 0x35, 0x35, 0x3c, 0x0, 0x0, 0x0, 0x38, 0x38, 0x40, 0x38, 0x38, 0x40, 0x31, 0x31, 0x38, 0x34, 0x34, 0x3b, 0x34, 0x34, 0x3b, 0x39, 0x39, 0x3f, 0x31, 0x31, 0x38, 0x2f, 0x2f, 0x36, 0x2d, 0x2d, 0x33, 0x2c, 0x2c, 0x32, 0x2b, 0x2b, 0x31, 0x2a, 0x2a, 0x31, 0x2a, 0x2a, 0x30, 0x29, 0x29, 0x30, 0x29, 0x29, 0x2f, 0x28, 0x28, 0x2e, 0x28, 0x28, 0x2d, 0x27, 0x27, 0x2d, 0x27, 0x27, 0x2c, 0x29, 0x29, 0x2e, 0x26, 0x26, 0x2c, 0x36, 0xc6, 0xc8, 0x93, 0x0, 0x0, 0x0, 0x28, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x3, 0x5, 0x8, 0xa, 0xb, 0x4, 0x13, 0x19, 0x1f, 0x22, 0x23, 0x16, 0x27, 0x35, 0x3f, 0x45, 0x46, 0x94, 0xf5, 0xfa, 0xfb, 0xf5, 0x40, 0xfc, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0x1a, 0xf5, 0xf6, 0x95, 0xfa, 0xfb, 0xf4, 0x94, 0x71, 0xda, 0xac, 0x92, 0x0, 0x0, 0x0, 0x7f, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x65, 0x8f, 0x35, 0x82, 0xc3, 0x0, 0xc, 0x4, 0x77, 0x24, 0x85, 0xba, 0xe3, 0xff, 0xff, 0xee, 0xca, 0x74, 0x41, 0xdb, 0x32, 0xf3, 0x94, 0x82, 0x85, 0x10, 0x1d, 0x92, 0xb2, 0x3, 0x8e, 0x95, 0x77, 0x93, 0x6c, 0x28, 0xed, 0x15, 0x54, 0x67, 0xa6, 0x41, 0x3e, 0x8, 0x9c, 0xc3, 0xf4, 0xf2, 0xf6, 0x2a, 0x80, 0xf8, 0x44, 0x2d, 0x79, 0x2d, 0x20, 0xe0, 0x2, 0xa8, 0xc3, 0x2e, 0x6f, 0xc, 0x9e, 0x4c, 0x3c, 0x21, 0x4, 0xd8, 0xf0, 0x2, 0x28, 0x24, 0xcd, 0x3, 0xa9, 0x19, 0x64, 0xce, 0x83, 0x4c, 0x45, 0xe6, 0x69, 0x1a, 0xd8, 0xe9, 0x99, 0x96, 0x7f, 0x77, 0x37, 0x59, 0x83, 0xcc, 0xef, 0x7f, 0x89, 0x1f, 0x8e, 0xbf, 0x95, 0xd3, 0x1d, 0xf0, 0xff, 0x7a, 0x63, 0x7e, 0x86, 0xcb, 0x73, 0x8c, 0x5e, 0xee, 0xca, 0xb1, 0xad, 0x5f, 0x3, 0xaf, 0xdb, 0x49, 0x94, 0x4b, 0x90, 0x40, 0xdf, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; @@ -282,10 +294,18 @@ static const unsigned char radio_checked_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, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x42, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4, 0x3, 0x4, 0x9, 0x9, 0x9, 0x6, 0x6, 0x6, 0xa, 0xa, 0xb, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x7f, 0x7f, 0x82, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0x47, 0x47, 0x48, 0xd3, 0xd3, 0xd3, 0xa2, 0xa2, 0xa2, 0x79, 0x79, 0x79, 0x73, 0x73, 0x73, 0x1c, 0x1c, 0x1c, 0x3, 0x3, 0x3, 0x0, 0x0, 0x0, 0xd1, 0xa7, 0xf5, 0xaa, 0x0, 0x0, 0x0, 0xb, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b, 0x88, 0xd1, 0xf7, 0x64, 0xf6, 0x2, 0xb3, 0xed, 0xd7, 0x0, 0x0, 0x0, 0x63, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x6d, 0x4f, 0x55, 0x2, 0x43, 0x31, 0x8, 0x4b, 0xe8, 0x6a, 0xf7, 0xbf, 0xeb, 0xc, 0x9b, 0x6b, 0x1f, 0x9f, 0xc4, 0x89, 0xbf, 0xbb, 0x3f, 0x2a, 0x49, 0x64, 0xa6, 0x3e, 0x1e, 0x1c, 0x7c, 0x3e, 0xf2, 0x14, 0xb7, 0xc7, 0xac, 0xf1, 0x10, 0xa6, 0xe8, 0x1, 0x44, 0xad, 0x42, 0xb9, 0x33, 0x22, 0x43, 0x95, 0x68, 0x55, 0xa4, 0xdc, 0x1f, 0x1e, 0xa1, 0x67, 0xa2, 0x57, 0x96, 0x22, 0x0, 0xc2, 0x3d, 0xf5, 0x44, 0x8c, 0x8a, 0x5d, 0x21, 0x80, 0x74, 0x83, 0x1e, 0x97, 0xc7, 0x22, 0x59, 0x4c, 0xd7, 0xd8, 0xb5, 0x18, 0x4a, 0x7b, 0x57, 0x57, 0xdb, 0x1a, 0xf7, 0x77, 0x17, 0x3a, 0x56, 0x4e, 0x11, 0x6f, 0x82, 0x20, 0xde, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; +static const unsigned char radio_checked_disabled_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, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x45, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x3, 0x4, 0x9, 0x9, 0x9, 0x6, 0x6, 0x6, 0xa, 0xa, 0xb, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x7f, 0x7f, 0x82, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0x47, 0x47, 0x48, 0xd3, 0xd3, 0xd3, 0xa2, 0xa2, 0xa2, 0x79, 0x79, 0x79, 0x73, 0x73, 0x73, 0x1c, 0x1c, 0x1c, 0x3, 0x3, 0x3, 0x0, 0x0, 0x0, 0x83, 0xac, 0xe9, 0xaf, 0x0, 0x0, 0x0, 0x1, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x40, 0xe6, 0xd8, 0x66, 0x0, 0x0, 0x0, 0x3f, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3, 0x63, 0x60, 0x20, 0xe, 0xf0, 0x8, 0x2, 0x1, 0xf, 0x9c, 0x2b, 0x2c, 0x8, 0x5, 0xc2, 0x50, 0x1, 0xb0, 0x34, 0x58, 0x11, 0x5c, 0xbd, 0x10, 0x3f, 0x10, 0x8, 0xc1, 0x74, 0x1, 0x65, 0xf8, 0xc1, 0x0, 0xa6, 0x4, 0x28, 0x1, 0x11, 0xe0, 0xc1, 0x2d, 0x80, 0xa6, 0x5, 0xc3, 0x50, 0xc, 0x6b, 0x31, 0x1d, 0x86, 0xe1, 0x74, 0xfc, 0x0, 0x0, 0x1b, 0xc, 0x7, 0xb9, 0xa, 0x5e, 0x2e, 0x12, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; + static const unsigned char radio_unchecked_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, 0x2a, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4, 0x3, 0x4, 0x9, 0x9, 0x9, 0x6, 0x6, 0x6, 0xa, 0xa, 0xb, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x7f, 0x7f, 0x82, 0xd9, 0xd9, 0xd9, 0x47, 0x47, 0x48, 0x2b, 0x6e, 0xf2, 0xbf, 0x0, 0x0, 0x0, 0xb, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b, 0x88, 0xd1, 0xf7, 0x64, 0xf6, 0x2, 0xb3, 0xed, 0xd7, 0x0, 0x0, 0x0, 0x49, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x40, 0x2, 0x61, 0x15, 0xed, 0xa9, 0x20, 0x5a, 0x72, 0xf5, 0x99, 0x33, 0xbb, 0x26, 0x1, 0x19, 0x73, 0xcf, 0x0, 0xc1, 0x4d, 0x6, 0x6, 0xd6, 0x35, 0x20, 0xc6, 0xa9, 0x0, 0x6, 0xb6, 0x3d, 0x20, 0xc6, 0xe9, 0x4, 0x6, 0xf6, 0x33, 0x60, 0x50, 0xc0, 0xc0, 0x1, 0x61, 0x34, 0xc0, 0x19, 0x70, 0x29, 0xb8, 0x62, 0xb8, 0x76, 0x84, 0x81, 0xc, 0x96, 0x20, 0x2b, 0xa6, 0xc0, 0x2d, 0x45, 0x0, 0x0, 0x37, 0xca, 0x3d, 0x81, 0xb4, 0x84, 0xb6, 0x80, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; +static const unsigned char radio_unchecked_disabled_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, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0xf, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x54, 0x54, 0x54, 0xad, 0xad, 0xad, 0x80, 0x80, 0x81, 0x64, 0x64, 0x64, 0xf4, 0x17, 0x36, 0x28, 0x0, 0x0, 0x0, 0x2, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1b, 0xfc, 0xf6, 0x4, 0xd4, 0x0, 0x0, 0x0, 0x40, 0x49, 0x44, 0x41, 0x54, 0x78, 0x5e, 0x7d, 0x8d, 0xb1, 0x11, 0x80, 0x40, 0xc, 0xc3, 0x94, 0xd, 0x12, 0x60, 0x1, 0xc4, 0x4, 0xb0, 0x1, 0xfb, 0x2f, 0x45, 0xe5, 0x3b, 0xaa, 0x57, 0xe5, 0xc2, 0xb6, 0x20, 0xc0, 0xcc, 0xc, 0x40, 0xed, 0x7a, 0x37, 0x70, 0xa8, 0xbe, 0x50, 0x9b, 0xea, 0xd9, 0xd4, 0xa3, 0x7a, 0x35, 0xa5, 0xaa, 0xeb, 0x90, 0x72, 0xe6, 0x39, 0xfc, 0x2b, 0x22, 0xd, 0x1f, 0xe7, 0x64, 0xa, 0x5d, 0x6c, 0xfe, 0xc1, 0x3b, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; + static const unsigned char scroll_bg_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x3, 0x0, 0x0, 0x0, 0x61, 0xab, 0xac, 0xd5, 0x0, 0x0, 0x0, 0x45, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x40, 0x3e, 0x4a, 0x2a, 0x29, 0x2f, 0x20, 0x20, 0x24, 0x3f, 0x3e, 0x49, 0x1f, 0x1f, 0x24, 0x20, 0x20, 0x24, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x3f, 0x3e, 0x49, 0x3f, 0x3e, 0x49, 0x1e, 0x1e, 0x23, 0x20, 0x20, 0x25, 0x22, 0x22, 0x27, 0x23, 0x23, 0x27, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2a, 0x14, 0xee, 0x69, 0x20, 0x0, 0x0, 0x0, 0x11, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x4, 0x19, 0x40, 0x5d, 0x66, 0x28, 0x93, 0xf0, 0xfc, 0x94, 0xfc, 0xfd, 0x67, 0x1a, 0x96, 0x95, 0x1c, 0xf0, 0x43, 0x52, 0x0, 0x0, 0x0, 0x55, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x55, 0x8e, 0x45, 0x2, 0x80, 0x50, 0x10, 0x42, 0xc1, 0xee, 0xfb, 0x5f, 0xd4, 0xd6, 0xdf, 0xfd, 0x36, 0xd3, 0x3, 0x4, 0xd, 0x90, 0x6, 0xb2, 0x25, 0x39, 0xe0, 0xd2, 0xf9, 0xcb, 0x6a, 0x60, 0x6f, 0x27, 0xb7, 0xbc, 0x58, 0xb7, 0x53, 0x4d, 0x0, 0xf2, 0x3f, 0x5e, 0x36, 0x43, 0x5f, 0xc3, 0xf0, 0xdf, 0x17, 0xd7, 0xa6, 0xae, 0x60, 0x10, 0xff, 0x57, 0x16, 0xc5, 0x5a, 0xf1, 0x60, 0xe3, 0xe7, 0x5f, 0x37, 0x46, 0x74, 0xba, 0x9a, 0x16, 0xef, 0x37, 0x1c, 0x6f, 0x61, 0x47, 0x1, 0xa5, 0xc7, 0x32, 0x47, 0x38, 0x12, 0x92, 0xb1, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; @@ -430,6 +450,10 @@ static const unsigned char unchecked_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, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x33, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x38, 0x37, 0x40, 0x20, 0x20, 0x24, 0x20, 0x20, 0x24, 0x38, 0x36, 0x40, 0x20, 0x20, 0x25, 0x1e, 0x1e, 0x22, 0x1f, 0x1f, 0x23, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2a, 0x23, 0xc3, 0x49, 0x39, 0x0, 0x0, 0x0, 0xb, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x7, 0x27, 0x50, 0x66, 0x68, 0xb4, 0xfa, 0xfb, 0xb4, 0xfa, 0xa4, 0x7f, 0xe1, 0x5a, 0x0, 0x0, 0x0, 0x4f, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xb5, 0xcf, 0x45, 0x2, 0x80, 0x40, 0x8, 0x0, 0x40, 0x97, 0x66, 0xfb, 0xff, 0x9f, 0xb5, 0xdb, 0xb3, 0x73, 0xa4, 0x19, 0xbe, 0x2, 0x20, 0xf1, 0x8a, 0x10, 0xc2, 0x1c, 0x0, 0xd1, 0x94, 0x57, 0x49, 0x5, 0xe6, 0x0, 0x6a, 0xa9, 0x6d, 0x55, 0x8b, 0xe2, 0x1c, 0xa0, 0x54, 0xfb, 0xae, 0x26, 0x9a, 0x3, 0x9c, 0xdb, 0x11, 0x68, 0x99, 0xff, 0xa, 0x7c, 0xd6, 0xde, 0xf, 0x33, 0x9c, 0x3, 0xe0, 0x76, 0x9c, 0x1e, 0x1d, 0xbe, 0xcf, 0x7d, 0x4c, 0x93, 0xe2, 0x8, 0xa4, 0x66, 0x3c, 0xec, 0xed, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; +static const unsigned char unchecked_disabled_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, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x33, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x6a, 0x69, 0x70, 0x6a, 0x68, 0x70, 0x58, 0x58, 0x5c, 0x58, 0x58, 0x5b, 0x58, 0x58, 0x5b, 0x5c, 0x5c, 0x5f, 0x5a, 0x5a, 0x5e, 0x59, 0x59, 0x5d, 0x58, 0x58, 0x5b, 0x57, 0x57, 0x5a, 0x56, 0x56, 0x59, 0x27, 0xa1, 0xa6, 0x53, 0x0, 0x0, 0x0, 0xb, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x7, 0x27, 0x50, 0x66, 0x68, 0xb4, 0xb4, 0xfa, 0xfa, 0xfb, 0xc7, 0x8b, 0xf6, 0x7e, 0x0, 0x0, 0x0, 0x52, 0x49, 0x44, 0x41, 0x54, 0x78, 0x5e, 0xb5, 0xcf, 0x31, 0x12, 0xc0, 0x20, 0x8, 0x44, 0xd1, 0x15, 0x50, 0x83, 0x11, 0xe5, 0xfe, 0xa7, 0xcd, 0xe8, 0xc4, 0x22, 0xf4, 0x79, 0xe5, 0x36, 0x7c, 0x80, 0x8, 0x89, 0x58, 0xf2, 0x26, 0x4c, 0x9, 0x0, 0x15, 0xf5, 0xb9, 0xb9, 0x16, 0x2, 0xc0, 0x3a, 0xac, 0x6f, 0x36, 0x94, 0x1, 0x88, 0xdb, 0xfd, 0x32, 0x17, 0x0, 0x79, 0xf6, 0x33, 0xf4, 0x99, 0xff, 0x1a, 0xe2, 0xd9, 0x4f, 0x58, 0x5b, 0x61, 0x54, 0xdb, 0x49, 0xbf, 0xea, 0x4a, 0x8f, 0xcf, 0x45, 0xf, 0x4, 0x40, 0x7, 0x90, 0xb0, 0x7b, 0x47, 0x4, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; + static const unsigned char updown_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0x81, 0x83, 0xf6, 0xf6, 0x0, 0x0, 0x0, 0x57, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x8d, 0x4e, 0xb5, 0x1, 0xc0, 0x30, 0xc, 0xf3, 0x15, 0xfe, 0xff, 0x96, 0x64, 0xa, 0x6c, 0xca, 0x56, 0xd2, 0x25, 0x65, 0xe6, 0xc8, 0x8b, 0x49, 0x20, 0x79, 0x28, 0x95, 0x81, 0xa1, 0xd4, 0x7d, 0x4, 0xbb, 0xa1, 0x50, 0xea, 0x3c, 0xa6, 0x71, 0x98, 0x96, 0x69, 0x58, 0x31, 0xcc, 0xb7, 0xe5, 0x2f, 0x48, 0x63, 0x26, 0xf6, 0xa2, 0xd4, 0x18, 0xf9, 0x7, 0x2d, 0xe3, 0x46, 0x89, 0xb4, 0xd2, 0xf8, 0xa3, 0x68, 0xe3, 0xd7, 0x14, 0x20, 0xe6, 0xc3, 0x3d, 0xd8, 0xca, 0x5e, 0x94, 0x32, 0xd0, 0x3, 0x91, 0xba, 0x5f, 0x1b, 0x4a, 0x9b, 0x12, 0x62, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; diff --git a/scene/resources/default_theme/unchecked_disabled.png b/scene/resources/default_theme/unchecked_disabled.png Binary files differnew file mode 100644 index 0000000000..bef9316f58 --- /dev/null +++ b/scene/resources/default_theme/unchecked_disabled.png diff --git a/scene/resources/height_map_shape_3d.cpp b/scene/resources/height_map_shape_3d.cpp index 5593bb766f..de5da944bc 100644 --- a/scene/resources/height_map_shape_3d.cpp +++ b/scene/resources/height_map_shape_3d.cpp @@ -41,10 +41,10 @@ Vector<Vector3> HeightMapShape3D::get_debug_mesh_lines() const { Vector2 size(map_width - 1, map_depth - 1); Vector2 start = size * -0.5; - const real_t *r = map_data.ptr(); + const float *r = map_data.ptr(); // reserve some memory for our points.. - points.resize(((map_width - 1) * map_depth * 2) + (map_width * (map_depth - 1) * 2)); + points.resize(((map_width - 1) * map_depth * 2) + (map_width * (map_depth - 1) * 2) + ((map_width - 1) * (map_depth - 1) * 2)); // now set our points int r_offset = 0; @@ -65,6 +65,11 @@ Vector<Vector3> HeightMapShape3D::get_debug_mesh_lines() const { points.write[w_offset++] = Vector3(height.x, r[r_offset + map_width - 1], height.z + 1.0); } + if ((w != map_width - 1) && (d != map_depth - 1)) { + points.write[w_offset++] = Vector3(height.x + 1.0, r[r_offset], height.z); + points.write[w_offset++] = Vector3(height.x, r[r_offset + map_width - 1], height.z + 1.0); + } + height.x += 1.0; } @@ -100,7 +105,7 @@ void HeightMapShape3D::set_map_width(int p_new) { int new_size = map_width * map_depth; map_data.resize(map_width * map_depth); - real_t *w = map_data.ptrw(); + float *w = map_data.ptrw(); while (was_size < new_size) { w[was_size++] = 0.0; } @@ -124,7 +129,7 @@ void HeightMapShape3D::set_map_depth(int p_new) { int new_size = map_width * map_depth; map_data.resize(new_size); - real_t *w = map_data.ptrw(); + float *w = map_data.ptrw(); while (was_size < new_size) { w[was_size++] = 0.0; } @@ -146,8 +151,8 @@ void HeightMapShape3D::set_map_data(PackedFloat32Array p_new) { } // copy - real_t *w = map_data.ptrw(); - const real_t *r = p_new.ptr(); + float *w = map_data.ptrw(); + const float *r = p_new.ptr(); for (int i = 0; i < size; i++) { float val = r[i]; w[i] = val; @@ -189,7 +194,7 @@ void HeightMapShape3D::_bind_methods() { HeightMapShape3D::HeightMapShape3D() : Shape3D(PhysicsServer3D::get_singleton()->shape_create(PhysicsServer3D::SHAPE_HEIGHTMAP)) { map_data.resize(map_width * map_depth); - real_t *w = map_data.ptrw(); + float *w = map_data.ptrw(); w[0] = 0.0; w[1] = 0.0; w[2] = 0.0; diff --git a/scene/resources/height_map_shape_3d.h b/scene/resources/height_map_shape_3d.h index 6fc88cff90..1219791c56 100644 --- a/scene/resources/height_map_shape_3d.h +++ b/scene/resources/height_map_shape_3d.h @@ -39,8 +39,8 @@ class HeightMapShape3D : public Shape3D { int map_width = 2; int map_depth = 2; PackedFloat32Array map_data; - float min_height = 0.0; - float max_height = 0.0; + real_t min_height = 0.0; + real_t max_height = 0.0; protected: static void _bind_methods(); diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 9931757cc4..75dd417f7f 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -543,6 +543,9 @@ void BaseMaterial3D::_update_shader() { if (flags[FLAG_DISABLE_DEPTH_TEST]) { code += ",depth_test_disabled"; } + if (flags[FLAG_PARTICLE_TRAILS_MODE]) { + code += ",particle_trails"; + } if (shading_mode == SHADING_MODE_PER_VERTEX) { code += ",vertex_lighting"; } @@ -672,7 +675,7 @@ void BaseMaterial3D::_update_shader() { code += "uniform sampler2D texture_flowmap : hint_aniso," + texfilter_str + ";\n"; } if (features[FEATURE_AMBIENT_OCCLUSION]) { - code += "uniform sampler2D texture_ambient_occlusion : hint_white;\n"; + code += "uniform sampler2D texture_ambient_occlusion : hint_white, " + texfilter_str + ";\n"; code += "uniform vec4 ao_texture_channel;\n"; code += "uniform float ao_light_affect;\n"; } @@ -829,16 +832,26 @@ void BaseMaterial3D::_update_shader() { } if (flags[FLAG_UV1_USE_TRIPLANAR]) { - code += "\tuv1_power_normal=pow(abs(NORMAL),vec3(uv1_blend_sharpness));\n"; + if (flags[FLAG_UV1_USE_WORLD_TRIPLANAR]) { + code += "\tuv1_power_normal=pow(abs(mat3(WORLD_MATRIX) * NORMAL),vec3(uv1_blend_sharpness));\n"; + code += "\tuv1_triplanar_pos = (WORLD_MATRIX * vec4(VERTEX, 1.0f)).xyz * uv1_scale + uv1_offset;\n"; + } else { + code += "\tuv1_power_normal=pow(abs(NORMAL),vec3(uv1_blend_sharpness));\n"; + code += "\tuv1_triplanar_pos = VERTEX * uv1_scale + uv1_offset;\n"; + } code += "\tuv1_power_normal/=dot(uv1_power_normal,vec3(1.0));\n"; - code += "\tuv1_triplanar_pos = VERTEX * uv1_scale + uv1_offset;\n"; code += "\tuv1_triplanar_pos *= vec3(1.0,-1.0, 1.0);\n"; } if (flags[FLAG_UV2_USE_TRIPLANAR]) { - code += "\tuv2_power_normal=pow(abs(NORMAL), vec3(uv2_blend_sharpness));\n"; + if (flags[FLAG_UV2_USE_WORLD_TRIPLANAR]) { + code += "\tuv2_power_normal=pow(abs(mat3(WORLD_MATRIX) * NORMAL), vec3(uv2_blend_sharpness));\n"; + code += "\tuv2_triplanar_pos = (WORLD_MATRIX * vec4(VERTEX, 1.0f)).xyz * uv2_scale + uv2_offset;\n"; + } else { + code += "\tuv2_power_normal=pow(abs(NORMAL), vec3(uv2_blend_sharpness));\n"; + code += "\tuv2_triplanar_pos = VERTEX * uv2_scale + uv2_offset;\n"; + } code += "\tuv2_power_normal/=dot(uv2_power_normal,vec3(1.0));\n"; - code += "\tuv2_triplanar_pos = VERTEX * uv2_scale + uv2_offset;\n"; code += "\tuv2_triplanar_pos *= vec3(1.0,-1.0, 1.0);\n"; } @@ -1019,7 +1032,10 @@ void BaseMaterial3D::_update_shader() { if (features[FEATURE_REFRACTION]) { if (features[FEATURE_NORMAL_MAPPING]) { - code += "\tvec3 ref_normal = normalize( mix(NORMAL,TANGENT * NORMAL_MAP.x + BINORMAL * NORMAL_MAP.y + NORMAL * NORMAL_MAP.z,NORMAL_MAP_DEPTH) );\n"; + code += "\tvec3 unpacked_normal = NORMAL_MAP;\n"; + code += "\tunpacked_normal.xy = unpacked_normal.xy * 2.0 - 1.0;\n"; + code += "\tunpacked_normal.z = sqrt(max(0.0, 1.0 - dot(unpacked_normal.xy, unpacked_normal.xy)));\n"; + code += "\tvec3 ref_normal = normalize( mix(NORMAL,TANGENT * unpacked_normal.x + BINORMAL * unpacked_normal.y + NORMAL * unpacked_normal.z,NORMAL_MAP_DEPTH) );\n"; } else { code += "\tvec3 ref_normal = NORMAL;\n"; } @@ -1587,6 +1603,9 @@ void BaseMaterial3D::set_flag(Flags p_flag, bool p_enabled) { if (p_flag == FLAG_USE_SHADOW_TO_OPACITY || p_flag == FLAG_USE_TEXTURE_REPEAT || p_flag == FLAG_SUBSURFACE_MODE_SKIN || p_flag == FLAG_USE_POINT_SIZE) { notify_property_list_changed(); } + if (p_flag == FLAG_PARTICLE_TRAILS_MODE) { + update_configuration_warning(); + } _queue_shader_change(); } @@ -1703,7 +1722,7 @@ void BaseMaterial3D::_validate_property(PropertyInfo &property) const { property.usage = PROPERTY_USAGE_NOEDITOR; } - // you can only enable anti-aliasing (in mataerials) on alpha scissor and alpha hash + // you can only enable anti-aliasing (in materials) on alpha scissor and alpha hash const bool can_select_aa = (transparency == TRANSPARENCY_ALPHA_SCISSOR || transparency == TRANSPARENCY_ALPHA_HASH); // alpha anti aliasiasing is only enabled when you can select aa const bool alpha_aa_enabled = (alpha_antialiasing_mode != ALPHA_ANTIALIASING_OFF) && can_select_aa; @@ -1722,7 +1741,7 @@ void BaseMaterial3D::_validate_property(PropertyInfo &property) const { property.usage = 0; } - // we cant choose an antialiasing mode if alpha isnt possible + // we can't choose an antialiasing mode if alpha isn't possible if (property.name == "alpha_antialiasing_edge" && !alpha_aa_enabled) { property.usage = 0; } @@ -2167,6 +2186,8 @@ Shader::Mode BaseMaterial3D::get_shader_mode() const { } void BaseMaterial3D::_bind_methods() { + static_assert(sizeof(MaterialKey) == 16, "MaterialKey should be 16 bytes"); + ClassDB::bind_method(D_METHOD("set_albedo", "albedo"), &BaseMaterial3D::set_albedo); ClassDB::bind_method(D_METHOD("get_albedo"), &BaseMaterial3D::get_albedo); @@ -2501,7 +2522,7 @@ void BaseMaterial3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "uv2_world_triplanar"), "set_flag", "get_flag", FLAG_UV2_USE_WORLD_TRIPLANAR); ADD_GROUP("Sampling", "texture_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,MipmapNearest,MipmapLinear,MipmapNearestAniso,MipmapLinearAniso"), "set_texture_filter", "get_texture_filter"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,Nearest Mipmap,Linear Mipmap,Nearest Mipmap Aniso.,Linear Mipmap Aniso."), "set_texture_filter", "get_texture_filter"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "texture_repeat"), "set_flag", "get_flag", FLAG_USE_TEXTURE_REPEAT); ADD_GROUP("Shadows", ""); @@ -2524,6 +2545,7 @@ void BaseMaterial3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "fixed_size"), "set_flag", "get_flag", FLAG_FIXED_SIZE); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "use_point_size"), "set_flag", "get_flag", FLAG_USE_POINT_SIZE); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "point_size", PROPERTY_HINT_RANGE, "0.1,128,0.1"), "set_point_size", "get_point_size"); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "use_particle_trails"), "set_flag", "get_flag", FLAG_PARTICLE_TRAILS_MODE); ADD_GROUP("Proximity Fade", "proximity_fade_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "proximity_fade_enable"), "set_proximity_fade", "is_proximity_fade_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "proximity_fade_distance", PROPERTY_HINT_RANGE, "0,4096,0.01"), "set_proximity_fade_distance", "get_proximity_fade_distance"); @@ -2625,6 +2647,7 @@ void BaseMaterial3D::_bind_methods() { BIND_ENUM_CONSTANT(FLAG_USE_TEXTURE_REPEAT); BIND_ENUM_CONSTANT(FLAG_INVERT_HEIGHTMAP); BIND_ENUM_CONSTANT(FLAG_SUBSURFACE_MODE_SKIN); + BIND_ENUM_CONSTANT(FLAG_PARTICLE_TRAILS_MODE); BIND_ENUM_CONSTANT(FLAG_MAX); BIND_ENUM_CONSTANT(DIFFUSE_BURLEY); diff --git a/scene/resources/material.h b/scene/resources/material.h index 70452a5f74..ad1b7b3e33 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -235,6 +235,7 @@ public: FLAG_USE_TEXTURE_REPEAT, FLAG_INVERT_HEIGHTMAP, FLAG_SUBSURFACE_MODE_SKIN, + FLAG_PARTICLE_TRAILS_MODE, FLAG_MAX }; @@ -305,16 +306,15 @@ private: uint64_t roughness_channel : get_num_bits(TEXTURE_CHANNEL_MAX - 1); uint64_t emission_op : get_num_bits(EMISSION_OP_MAX - 1); uint64_t distance_fade : get_num_bits(DISTANCE_FADE_MAX - 1); - - // flag bitfield - uint64_t feature_mask : FEATURE_MAX - 1; - uint64_t flags : FLAG_MAX - 1; - // booleans uint64_t deep_parallax : 1; uint64_t grow : 1; uint64_t proximity_fade : 1; + // flag bitfield + uint32_t feature_mask; + uint32_t flags; + MaterialKey() { memset(this, 0, sizeof(MaterialKey)); } diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index 1a2b21299a..33ad15b938 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -579,6 +579,13 @@ Vector<Ref<Shape3D>> Mesh::convex_decompose() const { return ret; } +int Mesh::get_builtin_bind_pose_count() const { + return 0; +} +Transform Mesh::get_builtin_bind_pose(int p_index) const { + return Transform(); +} + Mesh::Mesh() { } @@ -1238,6 +1245,22 @@ StringName ArrayMesh::get_blend_shape_name(int p_index) const { return blend_shapes[p_index]; } +void ArrayMesh::set_blend_shape_name(int p_index, const StringName &p_name) { + ERR_FAIL_INDEX(p_index, blend_shapes.size()); + + StringName name = p_name; + int found = blend_shapes.find(name); + if (found != -1 && found != p_index) { + int count = 2; + do { + name = String(p_name) + " " + itos(count); + count++; + } while (blend_shapes.find(name) != -1); + } + + blend_shapes.write[p_index] = name; +} + void ArrayMesh::clear_blend_shapes() { ERR_FAIL_COND_MSG(surfaces.size(), "Can't set shape key count if surfaces are already created."); @@ -1378,7 +1401,7 @@ void ArrayMesh::regen_normal_maps() { } //dirty hack -bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, int p_index_count, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y, int *&r_cache_data, unsigned int &r_cache_size, bool &r_used_cache); +bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, int p_index_count, const uint8_t *p_cache_data, bool *r_use_cache, uint8_t **r_mesh_cache, int *r_mesh_cache_size, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y) = NULL; struct ArrayMeshLightmapSurface { Ref<Material> material; @@ -1388,28 +1411,28 @@ struct ArrayMeshLightmapSurface { }; Error ArrayMesh::lightmap_unwrap(const Transform &p_base_transform, float p_texel_size) { - int *cache_data = nullptr; - unsigned int cache_size = 0; - bool use_cache = false; // Don't use cache - return lightmap_unwrap_cached(cache_data, cache_size, use_cache, p_base_transform, p_texel_size); + Vector<uint8_t> null_cache; + return lightmap_unwrap_cached(p_base_transform, p_texel_size, null_cache, null_cache, false); } -Error ArrayMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cache_size, bool &r_used_cache, const Transform &p_base_transform, float p_texel_size) { +Error ArrayMesh::lightmap_unwrap_cached(const Transform &p_base_transform, float p_texel_size, const Vector<uint8_t> &p_src_cache, Vector<uint8_t> &r_dst_cache, bool p_generate_cache) { ERR_FAIL_COND_V(!array_mesh_lightmap_unwrap_callback, ERR_UNCONFIGURED); ERR_FAIL_COND_V_MSG(blend_shapes.size() != 0, ERR_UNAVAILABLE, "Can't unwrap mesh with blend shapes."); - Vector<float> vertices; - Vector<float> normals; - Vector<int> indices; - Vector<float> uv; - Vector<Pair<int, int>> uv_indices; + LocalVector<float> vertices; + LocalVector<float> normals; + LocalVector<int> indices; + LocalVector<float> uv; + LocalVector<Pair<int, int>> uv_indices; Vector<ArrayMeshLightmapSurface> lightmap_surfaces; // Keep only the scale - Transform transform = p_base_transform; - transform.origin = Vector3(); - transform.looking_at(Vector3(1, 0, 0), Vector3(0, 1, 0)); + Basis basis = p_base_transform.get_basis(); + Vector3 scale = Vector3(basis.get_axis(0).length(), basis.get_axis(1).length(), basis.get_axis(2).length()); + + Transform transform; + transform.scale(scale); Basis normal_basis = transform.basis.inverse().transposed(); @@ -1423,14 +1446,12 @@ Error ArrayMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cach Array arrays = surface_get_arrays(i); s.material = surface_get_material(i); - SurfaceTool::create_vertex_array_from_triangle_arrays(arrays, s.vertices); + SurfaceTool::create_vertex_array_from_triangle_arrays(arrays, s.vertices, &s.format); - Vector<Vector3> rvertices = arrays[Mesh::ARRAY_VERTEX]; + PackedVector3Array rvertices = arrays[Mesh::ARRAY_VERTEX]; int vc = rvertices.size(); - const Vector3 *r = rvertices.ptr(); - Vector<Vector3> rnormals = arrays[Mesh::ARRAY_NORMAL]; - const Vector3 *rn = rnormals.ptr(); + PackedVector3Array rnormals = arrays[Mesh::ARRAY_NORMAL]; int vertex_ofs = vertices.size() / 3; @@ -1439,24 +1460,29 @@ Error ArrayMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cach uv_indices.resize(vertex_ofs + vc); for (int j = 0; j < vc; j++) { - Vector3 v = transform.xform(r[j]); - Vector3 n = normal_basis.xform(rn[j]).normalized(); - - vertices.write[(j + vertex_ofs) * 3 + 0] = v.x; - vertices.write[(j + vertex_ofs) * 3 + 1] = v.y; - vertices.write[(j + vertex_ofs) * 3 + 2] = v.z; - normals.write[(j + vertex_ofs) * 3 + 0] = n.x; - normals.write[(j + vertex_ofs) * 3 + 1] = n.y; - normals.write[(j + vertex_ofs) * 3 + 2] = n.z; - uv_indices.write[j + vertex_ofs] = Pair<int, int>(i, j); + Vector3 v = transform.xform(rvertices[j]); + Vector3 n = normal_basis.xform(rnormals[j]).normalized(); + + vertices[(j + vertex_ofs) * 3 + 0] = v.x; + vertices[(j + vertex_ofs) * 3 + 1] = v.y; + vertices[(j + vertex_ofs) * 3 + 2] = v.z; + normals[(j + vertex_ofs) * 3 + 0] = n.x; + normals[(j + vertex_ofs) * 3 + 1] = n.y; + normals[(j + vertex_ofs) * 3 + 2] = n.z; + uv_indices[j + vertex_ofs] = Pair<int, int>(i, j); } - Vector<int> rindices = arrays[Mesh::ARRAY_INDEX]; + PackedInt32Array rindices = arrays[Mesh::ARRAY_INDEX]; int ic = rindices.size(); + float eps = 1.19209290e-7F; // Taken from xatlas.h if (ic == 0) { for (int j = 0; j < vc / 3; j++) { - if (Face3(r[j * 3 + 0], r[j * 3 + 1], r[j * 3 + 2]).is_degenerate()) { + Vector3 p0 = transform.xform(rvertices[j * 3 + 0]); + Vector3 p1 = transform.xform(rvertices[j * 3 + 1]); + Vector3 p2 = transform.xform(rvertices[j * 3 + 2]); + + if ((p0 - p1).length_squared() < eps || (p1 - p2).length_squared() < eps || (p2 - p0).length_squared() < eps) { continue; } @@ -1466,15 +1492,18 @@ Error ArrayMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cach } } else { - const int *ri = rindices.ptr(); - for (int j = 0; j < ic / 3; j++) { - if (Face3(r[ri[j * 3 + 0]], r[ri[j * 3 + 1]], r[ri[j * 3 + 2]]).is_degenerate()) { + Vector3 p0 = transform.xform(rvertices[rindices[j * 3 + 0]]); + Vector3 p1 = transform.xform(rvertices[rindices[j * 3 + 1]]); + Vector3 p2 = transform.xform(rvertices[rindices[j * 3 + 2]]); + + if ((p0 - p1).length_squared() < eps || (p1 - p2).length_squared() < eps || (p2 - p0).length_squared() < eps) { continue; } - indices.push_back(vertex_ofs + ri[j * 3 + 0]); - indices.push_back(vertex_ofs + ri[j * 3 + 1]); - indices.push_back(vertex_ofs + ri[j * 3 + 2]); + + indices.push_back(vertex_ofs + rindices[j * 3 + 0]); + indices.push_back(vertex_ofs + rindices[j * 3 + 1]); + indices.push_back(vertex_ofs + rindices[j * 3 + 2]); } } @@ -1483,6 +1512,9 @@ Error ArrayMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cach //unwrap + bool use_cache = p_generate_cache; // Used to request cache generation and to know if cache was used + uint8_t *gen_cache; + int gen_cache_size; float *gen_uvs; int *gen_vertices; int *gen_indices; @@ -1491,17 +1523,16 @@ Error ArrayMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cach int size_x; int size_y; - bool ok = array_mesh_lightmap_unwrap_callback(p_texel_size, vertices.ptr(), normals.ptr(), vertices.size() / 3, indices.ptr(), indices.size(), &gen_uvs, &gen_vertices, &gen_vertex_count, &gen_indices, &gen_index_count, &size_x, &size_y, r_cache_data, r_cache_size, r_used_cache); + bool ok = array_mesh_lightmap_unwrap_callback(p_texel_size, vertices.ptr(), normals.ptr(), vertices.size() / 3, indices.ptr(), indices.size(), p_src_cache.ptr(), &use_cache, &gen_cache, &gen_cache_size, &gen_uvs, &gen_vertices, &gen_vertex_count, &gen_indices, &gen_index_count, &size_x, &size_y); if (!ok) { return ERR_CANT_CREATE; } - //remove surfaces clear_surfaces(); //create surfacetools for each surface.. - Vector<Ref<SurfaceTool>> surfaces_tools; + LocalVector<Ref<SurfaceTool>> surfaces_tools; for (int i = 0; i < lightmap_surfaces.size(); i++) { Ref<SurfaceTool> st; @@ -1512,11 +1543,12 @@ Error ArrayMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cach } print_verbose("Mesh: Gen indices: " + itos(gen_index_count)); + //go through all indices for (int i = 0; i < gen_index_count; i += 3) { - ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 0]], uv_indices.size(), ERR_BUG); - ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 1]], uv_indices.size(), ERR_BUG); - ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 2]], uv_indices.size(), ERR_BUG); + ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 0]], (int)uv_indices.size(), ERR_BUG); + ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 1]], (int)uv_indices.size(), ERR_BUG); + ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 2]], (int)uv_indices.size(), ERR_BUG); ERR_FAIL_COND_V(uv_indices[gen_vertices[gen_indices[i + 0]]].first != uv_indices[gen_vertices[gen_indices[i + 1]]].first || uv_indices[gen_vertices[gen_indices[i + 0]]].first != uv_indices[gen_vertices[gen_indices[i + 2]]].first, ERR_BUG); @@ -1526,48 +1558,53 @@ Error ArrayMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cach SurfaceTool::Vertex v = lightmap_surfaces[surface].vertices[uv_indices[gen_vertices[gen_indices[i + j]]].second]; if (lightmap_surfaces[surface].format & ARRAY_FORMAT_COLOR) { - surfaces_tools.write[surface]->set_color(v.color); + surfaces_tools[surface]->set_color(v.color); } if (lightmap_surfaces[surface].format & ARRAY_FORMAT_TEX_UV) { - surfaces_tools.write[surface]->set_uv(v.uv); + surfaces_tools[surface]->set_uv(v.uv); } if (lightmap_surfaces[surface].format & ARRAY_FORMAT_NORMAL) { - surfaces_tools.write[surface]->set_normal(v.normal); + surfaces_tools[surface]->set_normal(v.normal); } if (lightmap_surfaces[surface].format & ARRAY_FORMAT_TANGENT) { Plane t; t.normal = v.tangent; t.d = v.binormal.dot(v.normal.cross(v.tangent)) < 0 ? -1 : 1; - surfaces_tools.write[surface]->set_tangent(t); + surfaces_tools[surface]->set_tangent(t); } if (lightmap_surfaces[surface].format & ARRAY_FORMAT_BONES) { - surfaces_tools.write[surface]->set_bones(v.bones); + surfaces_tools[surface]->set_bones(v.bones); } if (lightmap_surfaces[surface].format & ARRAY_FORMAT_WEIGHTS) { - surfaces_tools.write[surface]->set_weights(v.weights); + surfaces_tools[surface]->set_weights(v.weights); } Vector2 uv2(gen_uvs[gen_indices[i + j] * 2 + 0], gen_uvs[gen_indices[i + j] * 2 + 1]); - surfaces_tools.write[surface]->set_uv2(uv2); + surfaces_tools[surface]->set_uv2(uv2); - surfaces_tools.write[surface]->add_vertex(v.vertex); + surfaces_tools[surface]->add_vertex(v.vertex); } } //generate surfaces - - for (int i = 0; i < surfaces_tools.size(); i++) { - surfaces_tools.write[i]->index(); - surfaces_tools.write[i]->commit(Ref<ArrayMesh>((ArrayMesh *)this), lightmap_surfaces[i].format); + for (unsigned int i = 0; i < surfaces_tools.size(); i++) { + surfaces_tools[i]->index(); + surfaces_tools[i]->commit(Ref<ArrayMesh>((ArrayMesh *)this), lightmap_surfaces[i].format); } set_lightmap_size_hint(Size2(size_x, size_y)); - if (!r_used_cache) { - //free stuff - ::free(gen_vertices); - ::free(gen_indices); - ::free(gen_uvs); + if (gen_cache_size > 0) { + r_dst_cache.resize(gen_cache_size); + memcpy(r_dst_cache.ptrw(), gen_cache, gen_cache_size); + memfree(gen_cache); + } + + if (!use_cache) { + // Cache was not used, free the buffers + memfree(gen_vertices); + memfree(gen_indices); + memfree(gen_uvs); } return OK; @@ -1590,6 +1627,7 @@ void ArrayMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("add_blend_shape", "name"), &ArrayMesh::add_blend_shape); ClassDB::bind_method(D_METHOD("get_blend_shape_count"), &ArrayMesh::get_blend_shape_count); ClassDB::bind_method(D_METHOD("get_blend_shape_name", "index"), &ArrayMesh::get_blend_shape_name); + ClassDB::bind_method(D_METHOD("set_blend_shape_name", "index", "name"), &ArrayMesh::set_blend_shape_name); ClassDB::bind_method(D_METHOD("clear_blend_shapes"), &ArrayMesh::clear_blend_shapes); ClassDB::bind_method(D_METHOD("set_blend_shape_mode", "mode"), &ArrayMesh::set_blend_shape_mode); ClassDB::bind_method(D_METHOD("get_blend_shape_mode"), &ArrayMesh::get_blend_shape_mode); diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h index 2ce519e644..aa830d7b50 100644 --- a/scene/resources/mesh.h +++ b/scene/resources/mesh.h @@ -141,6 +141,7 @@ public: 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; + virtual void set_blend_shape_name(int p_index, const StringName &p_name) = 0; Vector<Face3> get_faces() const; Ref<TriangleMesh> generate_triangle_mesh() const; @@ -164,6 +165,9 @@ public: Vector<Ref<Shape3D>> convex_decompose() const; + virtual int get_builtin_bind_pose_count() const; + virtual Transform get_builtin_bind_pose(int p_index) const; + Mesh(); }; @@ -223,6 +227,7 @@ public: void add_blend_shape(const StringName &p_name); int get_blend_shape_count() const override; StringName get_blend_shape_name(int p_index) const override; + void set_blend_shape_name(int p_index, const StringName &p_name) override; void clear_blend_shapes(); void set_blend_shape_mode(BlendShapeMode p_mode); @@ -258,7 +263,7 @@ public: void regen_normal_maps(); Error lightmap_unwrap(const Transform &p_base_transform = Transform(), float p_texel_size = 0.05); - Error lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cache_size, bool &r_used_cache, const Transform &p_base_transform = Transform(), float p_texel_size = 0.05); + Error lightmap_unwrap_cached(const Transform &p_base_transform, float p_texel_size, const Vector<uint8_t> &p_src_cache, Vector<uint8_t> &r_dst_cache, bool p_generate_cache = true); virtual void reload_from_file() override; diff --git a/scene/resources/navigation_mesh.cpp b/scene/resources/navigation_mesh.cpp index 8c12f59a00..0a25bb2ed1 100644 --- a/scene/resources/navigation_mesh.cpp +++ b/scene/resources/navigation_mesh.cpp @@ -92,6 +92,7 @@ uint32_t NavigationMesh::get_collision_mask() const { } void NavigationMesh::set_collision_mask_bit(int p_bit, bool p_value) { + ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision mask bit must be between 0 and 31 inclusive."); uint32_t mask = get_collision_mask(); if (p_value) { mask |= 1 << p_bit; @@ -102,6 +103,7 @@ void NavigationMesh::set_collision_mask_bit(int p_bit, bool p_value) { } bool NavigationMesh::get_collision_mask_bit(int p_bit) const { + ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision mask bit must be between 0 and 31 inclusive."); return get_collision_mask() & (1 << p_bit); } diff --git a/scene/resources/particles_material.cpp b/scene/resources/particles_material.cpp index 167540eb77..59e699326d 100644 --- a/scene/resources/particles_material.cpp +++ b/scene/resources/particles_material.cpp @@ -289,7 +289,7 @@ void ParticlesMaterial::_update_shader() { code += "}\n"; code += "\n"; - code += "void compute() {\n"; + code += "void start() {\n"; code += " uint base_number = NUMBER;\n"; code += " uint alt_seed = hash(base_number + uint(1) + RANDOM_SEED);\n"; code += " float angle_rand = rand_from_seed(alt_seed);\n"; @@ -305,90 +305,94 @@ void ParticlesMaterial::_update_shader() { code += " ivec2 emission_tex_size = textureSize(emission_texture_points, 0);\n"; code += " ivec2 emission_tex_ofs = ivec2(point % emission_tex_size.x, point / emission_tex_size.x);\n"; } - code += " float tv = 0.0;\n"; - code += " if (RESTART) {\n"; - if (tex_parameters[PARAM_ANGLE].is_valid()) { - code += " float tex_angle = textureLod(angle_texture, vec2(0.0, 0.0), 0.0).r;\n"; + code += " float tex_angle = textureLod(angle_texture, vec2(0.0, 0.0), 0.0).r;\n"; } else { - code += " float tex_angle = 0.0;\n"; + code += " float tex_angle = 0.0;\n"; } if (tex_parameters[PARAM_ANIM_OFFSET].is_valid()) { - code += " float tex_anim_offset = textureLod(anim_offset_texture, vec2(0.0, 0.0), 0.0).r;\n"; + code += " float tex_anim_offset = textureLod(anim_offset_texture, vec2(0.0, 0.0), 0.0).r;\n"; } else { - code += " float tex_anim_offset = 0.0;\n"; + code += " float tex_anim_offset = 0.0;\n"; } - code += " float spread_rad = spread * degree_to_rad;\n"; + code += " float spread_rad = spread * degree_to_rad;\n"; - code += " if (RESTART_VELOCITY) {\n"; + code += " if (RESTART_VELOCITY) {\n"; if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { - code += " float tex_linear_velocity = textureLod(linear_velocity_texture, vec2(0.0, 0.0), 0.0).r;\n"; + code += " float tex_linear_velocity = textureLod(linear_velocity_texture, vec2(0.0, 0.0), 0.0).r;\n"; } else { - code += " float tex_linear_velocity = 0.0;\n"; + code += " float tex_linear_velocity = 0.0;\n"; } if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { - code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad;\n"; - code += " angle1_rad += direction.x != 0.0 ? atan(direction.y, direction.x) : sign(direction.y) * (pi / 2.0);\n"; - code += " vec3 rot = vec3(cos(angle1_rad), sin(angle1_rad), 0.0);\n"; - code += " VELOCITY = rot * initial_linear_velocity * mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n"; + code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad;\n"; + code += " angle1_rad += direction.x != 0.0 ? atan(direction.y, direction.x) : sign(direction.y) * (pi / 2.0);\n"; + code += " vec3 rot = vec3(cos(angle1_rad), sin(angle1_rad), 0.0);\n"; + code += " VELOCITY = rot * initial_linear_velocity * mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n"; } else { //initiate velocity spread in 3D - code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad;\n"; - code += " float angle2_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad * (1.0 - flatness);\n"; - code += " angle1_rad += direction.z != 0.0 ? atan(direction.x, direction.z) : sign(direction.x) * (pi / 2.0);\n"; - code += " angle2_rad += direction.z != 0.0 ? atan(direction.y, abs(direction.z)) : (direction.x != 0.0 ? atan(direction.y, abs(direction.x)) : sign(direction.y) * (pi / 2.0));\n"; - code += " vec3 direction_xz = vec3(sin(angle1_rad), 0.0, cos(angle1_rad));\n"; - code += " vec3 direction_yz = vec3(0.0, sin(angle2_rad), cos(angle2_rad));\n"; - code += " direction_yz.z = direction_yz.z / max(0.0001,sqrt(abs(direction_yz.z))); // better uniform distribution\n"; - code += " vec3 vec_direction = vec3(direction_xz.x * direction_yz.z, direction_yz.y, direction_xz.z * direction_yz.z);\n"; - code += " vec_direction = normalize(vec_direction);\n"; - code += " VELOCITY = vec_direction * initial_linear_velocity * mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n"; + code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad;\n"; + code += " float angle2_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad * (1.0 - flatness);\n"; + code += " vec3 direction_xz = vec3(sin(angle1_rad), 0.0, cos(angle1_rad));\n"; + code += " vec3 direction_yz = vec3(0.0, sin(angle2_rad), cos(angle2_rad));\n"; + code += " direction_yz.z = direction_yz.z / max(0.0001,sqrt(abs(direction_yz.z))); // better uniform distribution\n"; + code += " vec3 spread_direction = vec3(direction_xz.x * direction_yz.z, direction_yz.y, direction_xz.z * direction_yz.z);\n"; + code += " vec3 direction_nrm = normalize(direction);\n"; + code += " // rotate spread to direction\n"; + code += " vec3 binormal = cross(vec3(0.0, 1.0, 0.0), direction_nrm);\n"; + code += " if (length(binormal) < 0.0001) {\n"; + code += " // direction is parallel to Y. Choose Z as the binormal.\n"; + code += " binormal = vec3(0.0, 0.0, 1.0);\n"; + code += " }\n"; + code += " binormal = normalize(binormal);\n"; + code += " vec3 normal = cross(binormal, direction_nrm);\n"; + code += " spread_direction = binormal * spread_direction.x + normal * spread_direction.y + direction_nrm * spread_direction.z;\n"; + code += " VELOCITY = spread_direction * initial_linear_velocity * mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n"; } - code += " }\n"; + code += " }\n"; - code += " float base_angle = (initial_angle + tex_angle) * mix(1.0, angle_rand, initial_angle_random);\n"; - code += " CUSTOM.x = base_angle * degree_to_rad;\n"; // angle - code += " CUSTOM.y = 0.0;\n"; // phase - code += " CUSTOM.w = (1.0 - lifetime_randomness * rand_from_seed(alt_seed));\n"; - code += " CUSTOM.z = (anim_offset + tex_anim_offset) * mix(1.0, anim_offset_rand, anim_offset_random);\n"; // animation offset (0-1) + code += " float base_angle = (initial_angle + tex_angle) * mix(1.0, angle_rand, initial_angle_random);\n"; + code += " CUSTOM.x = base_angle * degree_to_rad;\n"; // angle + code += " CUSTOM.y = 0.0;\n"; // phase + code += " CUSTOM.w = (1.0 - lifetime_randomness * rand_from_seed(alt_seed));\n"; + code += " CUSTOM.z = (anim_offset + tex_anim_offset) * mix(1.0, anim_offset_rand, anim_offset_random);\n"; // animation offset (0-1) - code += " if (RESTART_POSITION) {\n"; + code += " if (RESTART_POSITION) {\n"; switch (emission_shape) { case EMISSION_SHAPE_POINT: { //do none, identity (will later be multiplied by emission transform) - code += " TRANSFORM = mat4(vec4(1,0,0,0),vec4(0,1,0,0),vec4(0,0,1,0),vec4(0,0,0,1));\n"; + code += " TRANSFORM = mat4(vec4(1,0,0,0),vec4(0,1,0,0),vec4(0,0,1,0),vec4(0,0,0,1));\n"; } break; case EMISSION_SHAPE_SPHERE: { - code += " float s = rand_from_seed(alt_seed) * 2.0 - 1.0;\n"; - code += " float t = rand_from_seed(alt_seed) * 2.0 * pi;\n"; - code += " float radius = emission_sphere_radius * sqrt(1.0 - s * s);\n"; - code += " TRANSFORM[3].xyz = vec3(radius * cos(t), radius * sin(t), emission_sphere_radius * s);\n"; + code += " float s = rand_from_seed(alt_seed) * 2.0 - 1.0;\n"; + code += " float t = rand_from_seed(alt_seed) * 2.0 * pi;\n"; + code += " float radius = emission_sphere_radius * sqrt(1.0 - s * s);\n"; + code += " TRANSFORM[3].xyz = vec3(radius * cos(t), radius * sin(t), emission_sphere_radius * s);\n"; } break; case EMISSION_SHAPE_BOX: { - code += " TRANSFORM[3].xyz = vec3(rand_from_seed(alt_seed) * 2.0 - 1.0, rand_from_seed(alt_seed) * 2.0 - 1.0, rand_from_seed(alt_seed) * 2.0 - 1.0) * emission_box_extents;\n"; + code += " TRANSFORM[3].xyz = vec3(rand_from_seed(alt_seed) * 2.0 - 1.0, rand_from_seed(alt_seed) * 2.0 - 1.0, rand_from_seed(alt_seed) * 2.0 - 1.0) * emission_box_extents;\n"; } break; case EMISSION_SHAPE_POINTS: case EMISSION_SHAPE_DIRECTED_POINTS: { - code += " TRANSFORM[3].xyz = texelFetch(emission_texture_points, emission_tex_ofs, 0).xyz;\n"; + code += " TRANSFORM[3].xyz = texelFetch(emission_texture_points, emission_tex_ofs, 0).xyz;\n"; if (emission_shape == EMISSION_SHAPE_DIRECTED_POINTS) { if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { - code += " mat2 rotm;"; - code += " rotm[0] = texelFetch(emission_texture_normal, emission_tex_ofs, 0).xy;\n"; - code += " rotm[1] = rotm[0].yx * vec2(1.0, -1.0);\n"; - code += " if (RESTART_VELOCITY) VELOCITY.xy = rotm * VELOCITY.xy;\n"; + code += " mat2 rotm;"; + code += " rotm[0] = texelFetch(emission_texture_normal, emission_tex_ofs, 0).xy;\n"; + code += " rotm[1] = rotm[0].yx * vec2(1.0, -1.0);\n"; + code += " if (RESTART_VELOCITY) VELOCITY.xy = rotm * VELOCITY.xy;\n"; } else { - code += " vec3 normal = texelFetch(emission_texture_normal, emission_tex_ofs, 0).xyz;\n"; - code += " vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0);\n"; - code += " vec3 tangent = normalize(cross(v0, normal));\n"; - code += " vec3 bitangent = normalize(cross(tangent, normal));\n"; - code += " if (RESTART_VELOCITY) VELOCITY = mat3(tangent, bitangent, normal) * VELOCITY;\n"; + code += " vec3 normal = texelFetch(emission_texture_normal, emission_tex_ofs, 0).xyz;\n"; + code += " vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0);\n"; + code += " vec3 tangent = normalize(cross(v0, normal));\n"; + code += " vec3 bitangent = normalize(cross(tangent, normal));\n"; + code += " if (RESTART_VELOCITY) VELOCITY = mat3(tangent, bitangent, normal) * VELOCITY;\n"; } } } break; @@ -397,134 +401,144 @@ void ParticlesMaterial::_update_shader() { } } - code += " if (RESTART_VELOCITY) VELOCITY = (EMISSION_TRANSFORM * vec4(VELOCITY, 0.0)).xyz;\n"; - code += " TRANSFORM = EMISSION_TRANSFORM * TRANSFORM;\n"; + code += " if (RESTART_VELOCITY) VELOCITY = (EMISSION_TRANSFORM * vec4(VELOCITY, 0.0)).xyz;\n"; + code += " TRANSFORM = EMISSION_TRANSFORM * TRANSFORM;\n"; if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { - code += " VELOCITY.z = 0.0;\n"; - code += " TRANSFORM[3].z = 0.0;\n"; + code += " VELOCITY.z = 0.0;\n"; + code += " TRANSFORM[3].z = 0.0;\n"; } - code += " }\n"; + code += " }\n"; + code += "}\n\n"; - code += " } else {\n"; + code += "void process() {\n"; + code += " uint base_number = NUMBER;\n"; + code += " uint alt_seed = hash(base_number + uint(1) + RANDOM_SEED);\n"; + code += " float angle_rand = rand_from_seed(alt_seed);\n"; + code += " float scale_rand = rand_from_seed(alt_seed);\n"; + code += " float hue_rot_rand = rand_from_seed(alt_seed);\n"; + code += " float anim_offset_rand = rand_from_seed(alt_seed);\n"; + code += " float pi = 3.14159;\n"; + code += " float degree_to_rad = pi / 180.0;\n"; + code += "\n"; - code += " CUSTOM.y += DELTA / LIFETIME;\n"; - code += " tv = CUSTOM.y / CUSTOM.w;\n"; + code += " CUSTOM.y += DELTA / LIFETIME;\n"; + code += " float tv = CUSTOM.y / CUSTOM.w;\n"; if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { - code += " float tex_linear_velocity = textureLod(linear_velocity_texture, vec2(tv, 0.0), 0.0).r;\n"; + code += " float tex_linear_velocity = textureLod(linear_velocity_texture, vec2(tv, 0.0), 0.0).r;\n"; } else { - code += " float tex_linear_velocity = 0.0;\n"; + code += " float tex_linear_velocity = 0.0;\n"; } if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { if (tex_parameters[PARAM_ORBIT_VELOCITY].is_valid()) { - code += " float tex_orbit_velocity = textureLod(orbit_velocity_texture, vec2(tv, 0.0), 0.0).r;\n"; + code += " float tex_orbit_velocity = textureLod(orbit_velocity_texture, vec2(tv, 0.0), 0.0).r;\n"; } else { - code += " float tex_orbit_velocity = 0.0;\n"; + code += " float tex_orbit_velocity = 0.0;\n"; } } if (tex_parameters[PARAM_ANGULAR_VELOCITY].is_valid()) { - code += " float tex_angular_velocity = textureLod(angular_velocity_texture, vec2(tv, 0.0), 0.0).r;\n"; + code += " float tex_angular_velocity = textureLod(angular_velocity_texture, vec2(tv, 0.0), 0.0).r;\n"; } else { - code += " float tex_angular_velocity = 0.0;\n"; + code += " float tex_angular_velocity = 0.0;\n"; } if (tex_parameters[PARAM_LINEAR_ACCEL].is_valid()) { - code += " float tex_linear_accel = textureLod(linear_accel_texture, vec2(tv, 0.0), 0.0).r;\n"; + code += " float tex_linear_accel = textureLod(linear_accel_texture, vec2(tv, 0.0), 0.0).r;\n"; } else { - code += " float tex_linear_accel = 0.0;\n"; + code += " float tex_linear_accel = 0.0;\n"; } if (tex_parameters[PARAM_RADIAL_ACCEL].is_valid()) { - code += " float tex_radial_accel = textureLod(radial_accel_texture, vec2(tv, 0.0), 0.0).r;\n"; + code += " float tex_radial_accel = textureLod(radial_accel_texture, vec2(tv, 0.0), 0.0).r;\n"; } else { - code += " float tex_radial_accel = 0.0;\n"; + code += " float tex_radial_accel = 0.0;\n"; } if (tex_parameters[PARAM_TANGENTIAL_ACCEL].is_valid()) { - code += " float tex_tangent_accel = textureLod(tangent_accel_texture, vec2(tv, 0.0), 0.0).r;\n"; + code += " float tex_tangent_accel = textureLod(tangent_accel_texture, vec2(tv, 0.0), 0.0).r;\n"; } else { - code += " float tex_tangent_accel = 0.0;\n"; + code += " float tex_tangent_accel = 0.0;\n"; } if (tex_parameters[PARAM_DAMPING].is_valid()) { - code += " float tex_damping = textureLod(damping_texture, vec2(tv, 0.0), 0.0).r;\n"; + code += " float tex_damping = textureLod(damping_texture, vec2(tv, 0.0), 0.0).r;\n"; } else { - code += " float tex_damping = 0.0;\n"; + code += " float tex_damping = 0.0;\n"; } if (tex_parameters[PARAM_ANGLE].is_valid()) { - code += " float tex_angle = textureLod(angle_texture, vec2(tv, 0.0), 0.0).r;\n"; + code += " float tex_angle = textureLod(angle_texture, vec2(tv, 0.0), 0.0).r;\n"; } else { - code += " float tex_angle = 0.0;\n"; + code += " float tex_angle = 0.0;\n"; } if (tex_parameters[PARAM_ANIM_SPEED].is_valid()) { - code += " float tex_anim_speed = textureLod(anim_speed_texture, vec2(tv, 0.0), 0.0).r;\n"; + code += " float tex_anim_speed = textureLod(anim_speed_texture, vec2(tv, 0.0), 0.0).r;\n"; } else { - code += " float tex_anim_speed = 0.0;\n"; + code += " float tex_anim_speed = 0.0;\n"; } if (tex_parameters[PARAM_ANIM_OFFSET].is_valid()) { - code += " float tex_anim_offset = textureLod(anim_offset_texture, vec2(tv, 0.0), 0.0).r;\n"; + code += " float tex_anim_offset = textureLod(anim_offset_texture, vec2(tv, 0.0), 0.0).r;\n"; } else { - code += " float tex_anim_offset = 0.0;\n"; + code += " float tex_anim_offset = 0.0;\n"; } - code += " vec3 force = gravity;\n"; - code += " vec3 pos = TRANSFORM[3].xyz;\n"; + code += " vec3 force = gravity;\n"; + code += " vec3 pos = TRANSFORM[3].xyz;\n"; if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { - code += " pos.z = 0.0;\n"; - } - code += " // apply linear acceleration\n"; - code += " force += length(VELOCITY) > 0.0 ? normalize(VELOCITY) * (linear_accel + tex_linear_accel) * mix(1.0, rand_from_seed(alt_seed), linear_accel_random) : vec3(0.0);\n"; - code += " // apply radial acceleration\n"; - code += " vec3 org = EMISSION_TRANSFORM[3].xyz;\n"; - code += " vec3 diff = pos - org;\n"; - code += " force += length(diff) > 0.0 ? normalize(diff) * (radial_accel + tex_radial_accel) * mix(1.0, rand_from_seed(alt_seed), radial_accel_random) : vec3(0.0);\n"; - code += " // apply tangential acceleration;\n"; + code += " pos.z = 0.0;\n"; + } + code += " // apply linear acceleration\n"; + code += " force += length(VELOCITY) > 0.0 ? normalize(VELOCITY) * (linear_accel + tex_linear_accel) * mix(1.0, rand_from_seed(alt_seed), linear_accel_random) : vec3(0.0);\n"; + code += " // apply radial acceleration\n"; + code += " vec3 org = EMISSION_TRANSFORM[3].xyz;\n"; + code += " vec3 diff = pos - org;\n"; + code += " force += length(diff) > 0.0 ? normalize(diff) * (radial_accel + tex_radial_accel) * mix(1.0, rand_from_seed(alt_seed), radial_accel_random) : vec3(0.0);\n"; + code += " // apply tangential acceleration;\n"; if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { - code += " force += length(diff.yx) > 0.0 ? vec3(normalize(diff.yx * vec2(-1.0, 1.0)), 0.0) * ((tangent_accel + tex_tangent_accel) * mix(1.0, rand_from_seed(alt_seed), tangent_accel_random)) : vec3(0.0);\n"; + code += " force += length(diff.yx) > 0.0 ? vec3(normalize(diff.yx * vec2(-1.0, 1.0)), 0.0) * ((tangent_accel + tex_tangent_accel) * mix(1.0, rand_from_seed(alt_seed), tangent_accel_random)) : vec3(0.0);\n"; } else { - code += " vec3 crossDiff = cross(normalize(diff), normalize(gravity));\n"; - code += " force += length(crossDiff) > 0.0 ? normalize(crossDiff) * ((tangent_accel + tex_tangent_accel) * mix(1.0, rand_from_seed(alt_seed), tangent_accel_random)) : vec3(0.0);\n"; + code += " vec3 crossDiff = cross(normalize(diff), normalize(gravity));\n"; + code += " force += length(crossDiff) > 0.0 ? normalize(crossDiff) * ((tangent_accel + tex_tangent_accel) * mix(1.0, rand_from_seed(alt_seed), tangent_accel_random)) : vec3(0.0);\n"; } if (attractor_interaction_enabled) { - code += " force += ATTRACTOR_FORCE;\n\n"; + code += " force += ATTRACTOR_FORCE;\n\n"; } - code += " // apply attractor forces\n"; - code += " VELOCITY += force * DELTA;\n"; - code += " // orbit velocity\n"; + code += " // apply attractor forces\n"; + code += " VELOCITY += force * DELTA;\n"; + code += " // orbit velocity\n"; if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { - code += " float orbit_amount = (orbit_velocity + tex_orbit_velocity) * mix(1.0, rand_from_seed(alt_seed), orbit_velocity_random);\n"; - code += " if (orbit_amount != 0.0) {\n"; - code += " float ang = orbit_amount * DELTA * pi * 2.0;\n"; - code += " mat2 rot = mat2(vec2(cos(ang), -sin(ang)), vec2(sin(ang), cos(ang)));\n"; - code += " TRANSFORM[3].xy -= diff.xy;\n"; - code += " TRANSFORM[3].xy += rot * diff.xy;\n"; - code += " }\n"; + code += " float orbit_amount = (orbit_velocity + tex_orbit_velocity) * mix(1.0, rand_from_seed(alt_seed), orbit_velocity_random);\n"; + code += " if (orbit_amount != 0.0) {\n"; + code += " float ang = orbit_amount * DELTA * pi * 2.0;\n"; + code += " mat2 rot = mat2(vec2(cos(ang), -sin(ang)), vec2(sin(ang), cos(ang)));\n"; + code += " TRANSFORM[3].xy -= diff.xy;\n"; + code += " TRANSFORM[3].xy += rot * diff.xy;\n"; + code += " }\n"; } if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { - code += " VELOCITY = normalize(VELOCITY) * tex_linear_velocity;\n"; - } - code += " if (damping + tex_damping > 0.0) {\n"; - code += " float v = length(VELOCITY);\n"; - code += " float damp = (damping + tex_damping) * mix(1.0, rand_from_seed(alt_seed), damping_random);\n"; - code += " v -= damp * DELTA;\n"; - code += " if (v < 0.0) {\n"; - code += " VELOCITY = vec3(0.0);\n"; - code += " } else {\n"; - code += " VELOCITY = normalize(VELOCITY) * v;\n"; - code += " }\n"; + code += " VELOCITY = normalize(VELOCITY) * tex_linear_velocity;\n"; + } + code += " if (damping + tex_damping > 0.0) {\n"; + code += " float v = length(VELOCITY);\n"; + code += " float damp = (damping + tex_damping) * mix(1.0, rand_from_seed(alt_seed), damping_random);\n"; + code += " v -= damp * DELTA;\n"; + code += " if (v < 0.0) {\n"; + code += " VELOCITY = vec3(0.0);\n"; + code += " } else {\n"; + code += " VELOCITY = normalize(VELOCITY) * v;\n"; code += " }\n"; - code += " float base_angle = (initial_angle + tex_angle) * mix(1.0, angle_rand, initial_angle_random);\n"; - code += " base_angle += CUSTOM.y * LIFETIME * (angular_velocity + tex_angular_velocity) * mix(1.0, rand_from_seed(alt_seed) * 2.0 - 1.0, angular_velocity_random);\n"; - code += " CUSTOM.x = base_angle * degree_to_rad;\n"; // angle - code += " CUSTOM.z = (anim_offset + tex_anim_offset) * mix(1.0, anim_offset_rand, anim_offset_random) + CUSTOM.y * (anim_speed + tex_anim_speed) * mix(1.0, rand_from_seed(alt_seed), anim_speed_random);\n"; // angle code += " }\n"; + code += " float base_angle = (initial_angle + tex_angle) * mix(1.0, angle_rand, initial_angle_random);\n"; + code += " base_angle += CUSTOM.y * LIFETIME * (angular_velocity + tex_angular_velocity) * mix(1.0, rand_from_seed(alt_seed) * 2.0 - 1.0, angular_velocity_random);\n"; + code += " CUSTOM.x = base_angle * degree_to_rad;\n"; // angle + code += " CUSTOM.z = (anim_offset + tex_anim_offset) * mix(1.0, anim_offset_rand, anim_offset_random) + CUSTOM.y * (anim_speed + tex_anim_speed) * mix(1.0, rand_from_seed(alt_seed), anim_speed_random);\n"; // angle + // apply color // apply hue rotation if (tex_parameters[PARAM_SCALE].is_valid()) { @@ -601,7 +615,7 @@ void ParticlesMaterial::_update_shader() { } // turn particle by rotation in Y if (particle_flags[PARTICLE_FLAG_ROTATE_Y]) { - code += " TRANSFORM = TRANSFORM * mat4(vec4(cos(CUSTOM.x), 0.0, -sin(CUSTOM.x), 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(sin(CUSTOM.x), 0.0, cos(CUSTOM.x), 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n"; + code += " TRANSFORM = mat4(vec4(cos(CUSTOM.x), 0.0, -sin(CUSTOM.x), 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(sin(CUSTOM.x), 0.0, cos(CUSTOM.x), 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n"; } } //scale by scale @@ -652,7 +666,7 @@ void ParticlesMaterial::_update_shader() { code += " }"; } - code += " if (CUSTOM.y > CUSTOM.w) {"; + code += " if (CUSTOM.y > CUSTOM.w) {\n"; code += " ACTIVE = false;\n"; code += " }\n"; code += "}\n"; @@ -1075,7 +1089,7 @@ ParticlesMaterial::SubEmitterMode ParticlesMaterial::get_sub_emitter_mode() cons void ParticlesMaterial::set_sub_emitter_frequency(float p_frequency) { sub_emitter_frequency = p_frequency; - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->sub_emitter_frequency, 1.0 / p_frequency); //pas delta instead of frequency, since its easier to compute + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->sub_emitter_frequency, 1.0 / p_frequency); //pass delta instead of frequency, since its easier to compute } float ParticlesMaterial::get_sub_emitter_frequency() const { return sub_emitter_frequency; diff --git a/scene/resources/physics_material.cpp b/scene/resources/physics_material.cpp index d65b0c8927..31df35aa51 100644 --- a/scene/resources/physics_material.cpp +++ b/scene/resources/physics_material.cpp @@ -43,9 +43,9 @@ void PhysicsMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_absorbent", "absorbent"), &PhysicsMaterial::set_absorbent); ClassDB::bind_method(D_METHOD("is_absorbent"), &PhysicsMaterial::is_absorbent); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_friction", "get_friction"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "friction", PROPERTY_HINT_RANGE, "0,1,0.01,or_greater"), "set_friction", "get_friction"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rough"), "set_rough", "is_rough"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01,or_greater"), "set_bounce", "get_bounce"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "absorbent"), "set_absorbent", "is_absorbent"); } diff --git a/scene/resources/polygon_path_finder.cpp b/scene/resources/polygon_path_finder.cpp index f292140d6b..a08684a506 100644 --- a/scene/resources/polygon_path_finder.cpp +++ b/scene/resources/polygon_path_finder.cpp @@ -77,7 +77,7 @@ void PolygonPathFinder::setup(const Vector<Vector2> &p_points, const Vector<int> outside_point.x += 20.451 + Math::randf() * 10.2039; outside_point.y += 21.193 + Math::randf() * 12.5412; - //insert edges (which are also connetions) + //insert edges (which are also connections) for (int i = 0; i < p_connections.size(); i += 2) { Edge e(p_connections[i], p_connections[i + 1]); @@ -335,7 +335,7 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector //oh this was visited already, can we win the cost? if (p.distance > distance) { - p.prev = least_cost_point; //reasign previous + p.prev = least_cost_point; //reassign previous p.distance = distance; } } else { @@ -368,7 +368,7 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector path.push_back(points[at].pos); } while (at != aidx); - path.invert(); + path.reverse(); } for (int i = 0; i < points.size() - 2; i++) { diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp index ba6c4591c9..64b43f82c6 100644 --- a/scene/resources/primitive_meshes.cpp +++ b/scene/resources/primitive_meshes.cpp @@ -175,6 +175,9 @@ StringName PrimitiveMesh::get_blend_shape_name(int p_index) const { return StringName(); } +void PrimitiveMesh::set_blend_shape_name(int p_index, const StringName &p_name) { +} + AABB PrimitiveMesh::get_aabb() const { if (pending_request) { _update(); @@ -1130,7 +1133,7 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const { Vector3 normal_left, normal_right; normal_left = Vector3(-size.y, size.x * left_to_right, 0.0); - normal_right = Vector3(size.y, size.x * left_to_right, 0.0); + normal_right = Vector3(size.y, size.x * (1.0 - left_to_right), 0.0); normal_left.normalize(); normal_right.normalize(); @@ -1535,3 +1538,552 @@ void PointMesh::_create_mesh_array(Array &p_arr) const { PointMesh::PointMesh() { primitive_type = PRIMITIVE_POINTS; } +// TUBE TRAIL + +void TubeTrailMesh::set_radius(const float p_radius) { + radius = p_radius; + _request_update(); +} +float TubeTrailMesh::get_radius() const { + return radius; +} + +void TubeTrailMesh::set_radial_steps(const int p_radial_steps) { + ERR_FAIL_COND(p_radial_steps < 3 || p_radial_steps > 128); + radial_steps = p_radial_steps; + _request_update(); +} +int TubeTrailMesh::get_radial_steps() const { + return radial_steps; +} + +void TubeTrailMesh::set_sections(const int p_sections) { + ERR_FAIL_COND(p_sections < 2 || p_sections > 128); + sections = p_sections; + _request_update(); +} +int TubeTrailMesh::get_sections() const { + return sections; +} + +void TubeTrailMesh::set_section_length(float p_section_length) { + section_length = p_section_length; + _request_update(); +} +float TubeTrailMesh::get_section_length() const { + return section_length; +} + +void TubeTrailMesh::set_section_rings(const int p_section_rings) { + ERR_FAIL_COND(p_section_rings < 1 || p_section_rings > 1024); + section_rings = p_section_rings; + _request_update(); +} +int TubeTrailMesh::get_section_rings() const { + return section_rings; +} + +void TubeTrailMesh::set_curve(const Ref<Curve> &p_curve) { + if (curve == p_curve) { + return; + } + if (curve.is_valid()) { + curve->disconnect("changed", callable_mp(this, &TubeTrailMesh::_curve_changed)); + } + curve = p_curve; + if (curve.is_valid()) { + curve->connect("changed", callable_mp(this, &TubeTrailMesh::_curve_changed)); + } + _request_update(); +} +Ref<Curve> TubeTrailMesh::get_curve() const { + return curve; +} + +void TubeTrailMesh::_curve_changed() { + _request_update(); +} +int TubeTrailMesh::get_builtin_bind_pose_count() const { + return sections + 1; +} + +Transform TubeTrailMesh::get_builtin_bind_pose(int p_index) const { + float depth = section_length * sections; + + Transform xform; + xform.origin.y = depth / 2.0 - section_length * float(p_index); + xform.origin.y = -xform.origin.y; //bind is an inverse transform, so negate y + + return xform; +} + +void TubeTrailMesh::_create_mesh_array(Array &p_arr) const { + PackedVector3Array points; + PackedVector3Array normals; + PackedFloat32Array tangents; + PackedVector2Array uvs; + PackedInt32Array bone_indices; + PackedFloat32Array bone_weights; + PackedInt32Array indices; + + int point = 0; + +#define ADD_TANGENT(m_x, m_y, m_z, m_d) \ + tangents.push_back(m_x); \ + tangents.push_back(m_y); \ + tangents.push_back(m_z); \ + tangents.push_back(m_d); + + int thisrow = 0; + int prevrow = 0; + + int total_rings = section_rings * sections; + float depth = section_length * sections; + + for (int j = 0; j <= total_rings; j++) { + float v = j; + v /= total_rings; + + float y = depth * v; + y = (depth * 0.5) - y; + + int bone = j / section_rings; + float blend = 1.0 - float(j % section_rings) / float(section_rings); + + for (int i = 0; i <= radial_steps; i++) { + float u = i; + u /= radial_steps; + + float r = radius; + if (curve.is_valid() && curve->get_point_count() > 0) { + r *= curve->interpolate_baked(v); + } + float x = sin(u * Math_TAU); + float z = cos(u * Math_TAU); + + Vector3 p = Vector3(x * r, y, z * r); + points.push_back(p); + normals.push_back(Vector3(x, 0, z)); + ADD_TANGENT(z, 0.0, -x, 1.0) + uvs.push_back(Vector2(u, v * 0.5)); + point++; + { + bone_indices.push_back(bone); + bone_indices.push_back(MIN(sections, bone + 1)); + bone_indices.push_back(0); + bone_indices.push_back(0); + + bone_weights.push_back(blend); + bone_weights.push_back(1.0 - blend); + bone_weights.push_back(0); + bone_weights.push_back(0); + } + + if (i > 0 && j > 0) { + indices.push_back(prevrow + i - 1); + indices.push_back(prevrow + i); + indices.push_back(thisrow + i - 1); + + indices.push_back(prevrow + i); + indices.push_back(thisrow + i); + indices.push_back(thisrow + i - 1); + } + } + + prevrow = thisrow; + thisrow = point; + } + + // add top + float scale_pos = 1.0; + if (curve.is_valid() && curve->get_point_count() > 0) { + scale_pos = curve->interpolate_baked(0); + } + + if (scale_pos > CMP_EPSILON) { + float y = depth * 0.5; + + thisrow = point; + points.push_back(Vector3(0.0, y, 0)); + normals.push_back(Vector3(0.0, 1.0, 0.0)); + ADD_TANGENT(1.0, 0.0, 0.0, 1.0) + uvs.push_back(Vector2(0.25, 0.75)); + point++; + + bone_indices.push_back(0); + bone_indices.push_back(0); + bone_indices.push_back(0); + bone_indices.push_back(0); + + bone_weights.push_back(1.0); + bone_weights.push_back(0); + bone_weights.push_back(0); + bone_weights.push_back(0); + + float rm = radius * scale_pos; + + for (int i = 0; i <= radial_steps; i++) { + float r = i; + r /= radial_steps; + + float x = sin(r * Math_TAU); + float z = cos(r * Math_TAU); + + float u = ((x + 1.0) * 0.25); + float v = 0.5 + ((z + 1.0) * 0.25); + + Vector3 p = Vector3(x * rm, y, z * rm); + points.push_back(p); + normals.push_back(Vector3(0.0, 1.0, 0.0)); + ADD_TANGENT(1.0, 0.0, 0.0, 1.0) + uvs.push_back(Vector2(u, v)); + point++; + + bone_indices.push_back(0); + bone_indices.push_back(0); + bone_indices.push_back(0); + bone_indices.push_back(0); + + bone_weights.push_back(1.0); + bone_weights.push_back(0); + bone_weights.push_back(0); + bone_weights.push_back(0); + + if (i > 0) { + indices.push_back(thisrow); + indices.push_back(point - 1); + indices.push_back(point - 2); + }; + }; + }; + + float scale_neg = 1.0; + if (curve.is_valid() && curve->get_point_count() > 0) { + scale_neg = curve->interpolate_baked(1.0); + } + + // add bottom + if (scale_neg > CMP_EPSILON) { + float y = depth * -0.5; + + thisrow = point; + points.push_back(Vector3(0.0, y, 0.0)); + normals.push_back(Vector3(0.0, -1.0, 0.0)); + ADD_TANGENT(1.0, 0.0, 0.0, 1.0) + uvs.push_back(Vector2(0.75, 0.75)); + point++; + + bone_indices.push_back(sections); + bone_indices.push_back(0); + bone_indices.push_back(0); + bone_indices.push_back(0); + + bone_weights.push_back(1.0); + bone_weights.push_back(0); + bone_weights.push_back(0); + bone_weights.push_back(0); + + float rm = radius * scale_neg; + + for (int i = 0; i <= radial_steps; i++) { + float r = i; + r /= radial_steps; + + float x = sin(r * Math_TAU); + float z = cos(r * Math_TAU); + + float u = 0.5 + ((x + 1.0) * 0.25); + float v = 1.0 - ((z + 1.0) * 0.25); + + Vector3 p = Vector3(x * rm, y, z * rm); + points.push_back(p); + normals.push_back(Vector3(0.0, -1.0, 0.0)); + ADD_TANGENT(1.0, 0.0, 0.0, 1.0) + uvs.push_back(Vector2(u, v)); + point++; + + bone_indices.push_back(sections); + bone_indices.push_back(0); + bone_indices.push_back(0); + bone_indices.push_back(0); + + bone_weights.push_back(1.0); + bone_weights.push_back(0); + bone_weights.push_back(0); + bone_weights.push_back(0); + + if (i > 0) { + indices.push_back(thisrow); + indices.push_back(point - 2); + indices.push_back(point - 1); + }; + }; + }; + + p_arr[RS::ARRAY_VERTEX] = points; + p_arr[RS::ARRAY_NORMAL] = normals; + p_arr[RS::ARRAY_TANGENT] = tangents; + p_arr[RS::ARRAY_TEX_UV] = uvs; + p_arr[RS::ARRAY_BONES] = bone_indices; + p_arr[RS::ARRAY_WEIGHTS] = bone_weights; + p_arr[RS::ARRAY_INDEX] = indices; +} + +void TubeTrailMesh::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_radius", "radius"), &TubeTrailMesh::set_radius); + ClassDB::bind_method(D_METHOD("get_radius"), &TubeTrailMesh::get_radius); + + ClassDB::bind_method(D_METHOD("set_radial_steps", "radial_steps"), &TubeTrailMesh::set_radial_steps); + ClassDB::bind_method(D_METHOD("get_radial_steps"), &TubeTrailMesh::get_radial_steps); + + ClassDB::bind_method(D_METHOD("set_sections", "sections"), &TubeTrailMesh::set_sections); + ClassDB::bind_method(D_METHOD("get_sections"), &TubeTrailMesh::get_sections); + + ClassDB::bind_method(D_METHOD("set_section_length", "section_length"), &TubeTrailMesh::set_section_length); + ClassDB::bind_method(D_METHOD("get_section_length"), &TubeTrailMesh::get_section_length); + + ClassDB::bind_method(D_METHOD("set_section_rings", "section_rings"), &TubeTrailMesh::set_section_rings); + ClassDB::bind_method(D_METHOD("get_section_rings"), &TubeTrailMesh::get_section_rings); + + ClassDB::bind_method(D_METHOD("set_curve", "curve"), &TubeTrailMesh::set_curve); + ClassDB::bind_method(D_METHOD("get_curve"), &TubeTrailMesh::get_curve); + + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_radius", "get_radius"); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_steps", PROPERTY_HINT_RANGE, "3,128,1"), "set_radial_steps", "get_radial_steps"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "sections", PROPERTY_HINT_RANGE, "2,128,1"), "set_sections", "get_sections"); + + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "section_length", PROPERTY_HINT_RANGE, "0.001,1024.0,0.001,or_greater"), "set_section_length", "get_section_length"); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "section_rings", PROPERTY_HINT_RANGE, "1,128,1"), "set_section_rings", "get_section_rings"); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve", "get_curve"); +} + +TubeTrailMesh::TubeTrailMesh() { +} + +// TUBE TRAIL + +void RibbonTrailMesh::set_shape(Shape p_shape) { + shape = p_shape; + _request_update(); +} +RibbonTrailMesh::Shape RibbonTrailMesh::get_shape() const { + return shape; +} + +void RibbonTrailMesh::set_size(const float p_size) { + size = p_size; + _request_update(); +} +float RibbonTrailMesh::get_size() const { + return size; +} + +void RibbonTrailMesh::set_sections(const int p_sections) { + ERR_FAIL_COND(p_sections < 2 || p_sections > 128); + sections = p_sections; + _request_update(); +} +int RibbonTrailMesh::get_sections() const { + return sections; +} + +void RibbonTrailMesh::set_section_length(float p_section_length) { + section_length = p_section_length; + _request_update(); +} +float RibbonTrailMesh::get_section_length() const { + return section_length; +} + +void RibbonTrailMesh::set_section_segments(const int p_section_segments) { + ERR_FAIL_COND(p_section_segments < 1 || p_section_segments > 1024); + section_segments = p_section_segments; + _request_update(); +} +int RibbonTrailMesh::get_section_segments() const { + return section_segments; +} + +void RibbonTrailMesh::set_curve(const Ref<Curve> &p_curve) { + if (curve == p_curve) { + return; + } + if (curve.is_valid()) { + curve->disconnect("changed", callable_mp(this, &RibbonTrailMesh::_curve_changed)); + } + curve = p_curve; + if (curve.is_valid()) { + curve->connect("changed", callable_mp(this, &RibbonTrailMesh::_curve_changed)); + } + _request_update(); +} +Ref<Curve> RibbonTrailMesh::get_curve() const { + return curve; +} + +void RibbonTrailMesh::_curve_changed() { + _request_update(); +} +int RibbonTrailMesh::get_builtin_bind_pose_count() const { + return sections + 1; +} + +Transform RibbonTrailMesh::get_builtin_bind_pose(int p_index) const { + float depth = section_length * sections; + + Transform xform; + xform.origin.y = depth / 2.0 - section_length * float(p_index); + xform.origin.y = -xform.origin.y; //bind is an inverse transform, so negate y + + return xform; +} + +void RibbonTrailMesh::_create_mesh_array(Array &p_arr) const { + PackedVector3Array points; + PackedVector3Array normals; + PackedFloat32Array tangents; + PackedVector2Array uvs; + PackedInt32Array bone_indices; + PackedFloat32Array bone_weights; + PackedInt32Array indices; + +#define ADD_TANGENT(m_x, m_y, m_z, m_d) \ + tangents.push_back(m_x); \ + tangents.push_back(m_y); \ + tangents.push_back(m_z); \ + tangents.push_back(m_d); + + int total_segments = section_segments * sections; + float depth = section_length * sections; + + for (int j = 0; j <= total_segments; j++) { + float v = j; + v /= total_segments; + + float y = depth * v; + y = (depth * 0.5) - y; + + int bone = j / section_segments; + float blend = 1.0 - float(j % section_segments) / float(section_segments); + + float s = size; + + if (curve.is_valid() && curve->get_point_count() > 0) { + s *= curve->interpolate_baked(v); + } + + points.push_back(Vector3(-s * 0.5, y, 0)); + points.push_back(Vector3(+s * 0.5, y, 0)); + if (shape == SHAPE_CROSS) { + points.push_back(Vector3(0, y, -s * 0.5)); + points.push_back(Vector3(0, y, +s * 0.5)); + } + + normals.push_back(Vector3(0, 0, 1)); + normals.push_back(Vector3(0, 0, 1)); + if (shape == SHAPE_CROSS) { + normals.push_back(Vector3(1, 0, 0)); + normals.push_back(Vector3(1, 0, 0)); + } + + uvs.push_back(Vector2(0, v)); + uvs.push_back(Vector2(1, v)); + if (shape == SHAPE_CROSS) { + uvs.push_back(Vector2(0, v)); + uvs.push_back(Vector2(1, v)); + } + + ADD_TANGENT(0.0, 1.0, 0.0, 1.0) + ADD_TANGENT(0.0, 1.0, 0.0, 1.0) + if (shape == SHAPE_CROSS) { + ADD_TANGENT(0.0, 1.0, 0.0, 1.0) + ADD_TANGENT(0.0, 1.0, 0.0, 1.0) + } + + for (int i = 0; i < (shape == SHAPE_CROSS ? 4 : 2); i++) { + bone_indices.push_back(bone); + bone_indices.push_back(MIN(sections, bone + 1)); + bone_indices.push_back(0); + bone_indices.push_back(0); + + bone_weights.push_back(blend); + bone_weights.push_back(1.0 - blend); + bone_weights.push_back(0); + bone_weights.push_back(0); + } + + if (j > 0) { + if (shape == SHAPE_CROSS) { + int base = j * 4 - 4; + indices.push_back(base + 0); + indices.push_back(base + 1); + indices.push_back(base + 4); + + indices.push_back(base + 1); + indices.push_back(base + 5); + indices.push_back(base + 4); + + indices.push_back(base + 2); + indices.push_back(base + 3); + indices.push_back(base + 6); + + indices.push_back(base + 3); + indices.push_back(base + 7); + indices.push_back(base + 6); + } else { + int base = j * 2 - 2; + indices.push_back(base + 0); + indices.push_back(base + 1); + indices.push_back(base + 2); + + indices.push_back(base + 1); + indices.push_back(base + 3); + indices.push_back(base + 2); + } + } + } + + p_arr[RS::ARRAY_VERTEX] = points; + p_arr[RS::ARRAY_NORMAL] = normals; + p_arr[RS::ARRAY_TANGENT] = tangents; + p_arr[RS::ARRAY_TEX_UV] = uvs; + p_arr[RS::ARRAY_BONES] = bone_indices; + p_arr[RS::ARRAY_WEIGHTS] = bone_weights; + p_arr[RS::ARRAY_INDEX] = indices; +} + +void RibbonTrailMesh::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_size", "size"), &RibbonTrailMesh::set_size); + ClassDB::bind_method(D_METHOD("get_size"), &RibbonTrailMesh::get_size); + + ClassDB::bind_method(D_METHOD("set_sections", "sections"), &RibbonTrailMesh::set_sections); + ClassDB::bind_method(D_METHOD("get_sections"), &RibbonTrailMesh::get_sections); + + ClassDB::bind_method(D_METHOD("set_section_length", "section_length"), &RibbonTrailMesh::set_section_length); + ClassDB::bind_method(D_METHOD("get_section_length"), &RibbonTrailMesh::get_section_length); + + ClassDB::bind_method(D_METHOD("set_section_segments", "section_segments"), &RibbonTrailMesh::set_section_segments); + ClassDB::bind_method(D_METHOD("get_section_segments"), &RibbonTrailMesh::get_section_segments); + + ClassDB::bind_method(D_METHOD("set_curve", "curve"), &RibbonTrailMesh::set_curve); + ClassDB::bind_method(D_METHOD("get_curve"), &RibbonTrailMesh::get_curve); + + ClassDB::bind_method(D_METHOD("set_shape", "shape"), &RibbonTrailMesh::set_shape); + ClassDB::bind_method(D_METHOD("get_shape"), &RibbonTrailMesh::get_shape); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "shape", PROPERTY_HINT_ENUM, "Flat,Cross"), "set_shape", "get_shape"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_size", "get_size"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "sections", PROPERTY_HINT_RANGE, "2,128,1"), "set_sections", "get_sections"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "section_length", PROPERTY_HINT_RANGE, "0.001,1024.0,0.001,or_greater"), "set_section_length", "get_section_length"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "section_segments", PROPERTY_HINT_RANGE, "1,128,1"), "set_section_segments", "get_section_segments"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve", "get_curve"); + + BIND_ENUM_CONSTANT(SHAPE_FLAT) + BIND_ENUM_CONSTANT(SHAPE_CROSS) +} + +RibbonTrailMesh::RibbonTrailMesh() { +} diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h index bb3df9d10e..ec5806489e 100644 --- a/scene/resources/primitive_meshes.h +++ b/scene/resources/primitive_meshes.h @@ -81,6 +81,7 @@ public: virtual Ref<Material> surface_get_material(int p_idx) const override; virtual int get_blend_shape_count() const override; virtual StringName get_blend_shape_name(int p_index) const override; + virtual void set_blend_shape_name(int p_index, const StringName &p_name) override; virtual AABB get_aabb() const override; virtual RID get_rid() const override; @@ -335,4 +336,98 @@ public: PointMesh(); }; +class TubeTrailMesh : public PrimitiveMesh { + GDCLASS(TubeTrailMesh, PrimitiveMesh); + +private: + float radius = 1.0; + int radial_steps = 8; + int sections = 5; + float section_length = 0.2; + int section_rings = 3; + + Ref<Curve> curve; + + void _curve_changed(); + +protected: + static void _bind_methods(); + virtual void _create_mesh_array(Array &p_arr) const override; + +public: + void set_radius(const float p_radius); + float get_radius() const; + + void set_radial_steps(const int p_radial_steps); + int get_radial_steps() const; + + void set_sections(const int p_sections); + int get_sections() const; + + void set_section_length(float p_sectionlength); + float get_section_length() const; + + void set_section_rings(const int p_section_rings); + int get_section_rings() const; + + void set_curve(const Ref<Curve> &p_curve); + Ref<Curve> get_curve() const; + + virtual int get_builtin_bind_pose_count() const override; + virtual Transform get_builtin_bind_pose(int p_index) const override; + + TubeTrailMesh(); +}; + +class RibbonTrailMesh : public PrimitiveMesh { + GDCLASS(RibbonTrailMesh, PrimitiveMesh); + +public: + enum Shape { + SHAPE_FLAT, + SHAPE_CROSS + }; + +private: + float size = 1.0; + int sections = 5; + float section_length = 0.2; + int section_segments = 3; + + Shape shape = SHAPE_CROSS; + + Ref<Curve> curve; + + void _curve_changed(); + +protected: + static void _bind_methods(); + virtual void _create_mesh_array(Array &p_arr) const override; + +public: + void set_shape(Shape p_shape); + Shape get_shape() const; + + void set_size(const float p_size); + float get_size() const; + + void set_sections(const int p_sections); + int get_sections() const; + + void set_section_length(float p_sectionlength); + float get_section_length() const; + + void set_section_segments(const int p_section_segments); + int get_section_segments() const; + + void set_curve(const Ref<Curve> &p_curve); + Ref<Curve> get_curve() const; + + virtual int get_builtin_bind_pose_count() const override; + virtual Transform get_builtin_bind_pose(int p_index) const override; + + RibbonTrailMesh(); +}; + +VARIANT_ENUM_CAST(RibbonTrailMesh::Shape) #endif diff --git a/scene/resources/ray_shape_2d.cpp b/scene/resources/ray_shape_2d.cpp index d2125445fa..fb8f4b9985 100644 --- a/scene/resources/ray_shape_2d.cpp +++ b/scene/resources/ray_shape_2d.cpp @@ -42,17 +42,33 @@ void RayShape2D::_update_shape() { } void RayShape2D::draw(const RID &p_to_rid, const Color &p_color) { - Vector2 tip = Vector2(0, get_length()); - RS::get_singleton()->canvas_item_add_line(p_to_rid, Vector2(), tip, p_color, 3); + const Vector2 target_position = Vector2(0, get_length()); + + const float max_arrow_size = 6; + const float line_width = 1.4; + bool no_line = target_position.length() < line_width; + float arrow_size = CLAMP(target_position.length() * 2 / 3, line_width, max_arrow_size); + + if (no_line) { + arrow_size = target_position.length(); + } else { + RS::get_singleton()->canvas_item_add_line(p_to_rid, Vector2(), target_position - target_position.normalized() * arrow_size, p_color, line_width); + } + + Transform2D xf; + xf.rotate(target_position.angle()); + xf.translate(Vector2(no_line ? 0 : target_position.length() - arrow_size, 0)); + Vector<Vector2> pts; - float tsize = 4.0; - pts.push_back(tip + Vector2(0, tsize)); - pts.push_back(tip + Vector2(Math_SQRT12 * tsize, 0)); - pts.push_back(tip + Vector2(-Math_SQRT12 * tsize, 0)); + pts.push_back(xf.xform(Vector2(arrow_size, 0))); + pts.push_back(xf.xform(Vector2(0, 0.5 * arrow_size))); + pts.push_back(xf.xform(Vector2(0, -0.5 * arrow_size))); + Vector<Color> cols; for (int i = 0; i < 3; i++) { cols.push_back(p_color); } + RS::get_singleton()->canvas_item_add_primitive(p_to_rid, pts, cols, Vector<Point2>(), RID()); } diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index 7ca532e1d6..2414704a57 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -978,7 +978,7 @@ Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path) } wf->store_32(0); //string table size, will not be in use - size_t ext_res_count_pos = wf->get_position(); + uint64_t ext_res_count_pos = wf->get_position(); wf->store_32(0); //zero ext resources, still parsing them @@ -1041,7 +1041,7 @@ Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path) //now, save resources to a separate file, for now - size_t sub_res_count_pos = wf->get_position(); + uint64_t sub_res_count_pos = wf->get_position(); wf->store_32(0); //zero sub resources, still parsing them String temp_file = p_path + ".temp"; @@ -1050,8 +1050,8 @@ Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path) return ERR_CANT_OPEN; } - Vector<size_t> local_offsets; - Vector<size_t> local_pointers_pos; + Vector<uint64_t> local_offsets; + Vector<uint64_t> local_pointers_pos; while (next_tag.name == "sub_resource" || next_tag.name == "resource") { String type; @@ -1089,7 +1089,7 @@ Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path) wf->store_64(0); //temp local offset bs_save_unicode_string(wf2, type); - size_t propcount_ofs = wf2->get_position(); + uint64_t propcount_ofs = wf2->get_position(); wf2->store_32(0); int prop_count = 0; @@ -1159,7 +1159,7 @@ Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path) local_offsets.push_back(wf2->get_position()); bs_save_unicode_string(wf2, "PackedScene"); - size_t propcount_ofs = wf2->get_position(); + uint64_t propcount_ofs = wf2->get_position(); wf2->store_32(0); int prop_count = 0; @@ -1185,7 +1185,7 @@ Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path) wf2->close(); - size_t offset_from = wf->get_position(); + uint64_t offset_from = wf->get_position(); wf->seek(sub_res_count_pos); //plus one because the saved one wf->store_32(local_offsets.size()); @@ -1321,7 +1321,7 @@ String ResourceFormatLoaderText::get_resource_type(const String &p_path) const { FileAccess *f = FileAccess::open(p_path, FileAccess::READ); if (!f) { - return ""; //could not rwead + return ""; //could not read } ResourceLoaderText loader; diff --git a/scene/resources/shape_3d.cpp b/scene/resources/shape_3d.cpp index 5761a405ce..cb44e059a3 100644 --- a/scene/resources/shape_3d.cpp +++ b/scene/resources/shape_3d.cpp @@ -102,6 +102,8 @@ void Shape3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_margin", "margin"), &Shape3D::set_margin); ClassDB::bind_method(D_METHOD("get_margin"), &Shape3D::get_margin); + ClassDB::bind_method(D_METHOD("get_debug_mesh"), &Shape3D::get_debug_mesh); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin", PROPERTY_HINT_RANGE, "0.001,10,0.001"), "set_margin", "get_margin"); } diff --git a/scene/resources/sky_material.cpp b/scene/resources/sky_material.cpp index b2efecb1cb..f50ee9c4c8 100644 --- a/scene/resources/sky_material.cpp +++ b/scene/resources/sky_material.cpp @@ -194,7 +194,7 @@ ProceduralSkyMaterial::ProceduralSkyMaterial() { code += "uniform float sun_angle_max = 1.74;\n"; code += "uniform float sun_curve : hint_range(0, 1) = 0.05;\n\n"; code += "const float PI = 3.1415926535897932384626433833;\n\n"; - code += "void fragment() {\n"; + code += "void sky() {\n"; code += "\tfloat v_angle = acos(clamp(EYEDIR.y, -1.0, 1.0));\n"; code += "\tfloat c = (1.0 - v_angle / (PI * 0.5));\n"; code += "\tvec3 sky = mix(sky_horizon_color.rgb, sky_top_color.rgb, clamp(1.0 - pow(1.0 - c, 1.0 / sky_curve), 0.0, 1.0));\n"; @@ -301,7 +301,7 @@ PanoramaSkyMaterial::PanoramaSkyMaterial() { String code = "shader_type sky;\n\n"; code += "uniform sampler2D source_panorama : filter_linear;\n"; - code += "void fragment() {\n"; + code += "void sky() {\n"; code += "\tCOLOR = texture(source_panorama, SKY_COORDS).rgb;\n"; code += "}"; @@ -521,7 +521,7 @@ PhysicalSkyMaterial::PhysicalSkyMaterial() { code += "\treturn fract(p.x * p.y * p.z * (p.x + p.y + p.z));\n"; code += "}\n\n"; - code += "void fragment() {\n"; + code += "void sky() {\n"; code += "\tif (LIGHT0_ENABLED) {\n"; code += "\t\tfloat zenith_angle = clamp( dot(UP, normalize(LIGHT0_DIRECTION)), -1.0, 1.0 );\n"; code += "\t\tfloat sun_energy = max(0.0, 1.0 - exp(-((PI * 0.5) - acos(zenith_angle)))) * SUN_ENERGY * LIGHT0_ENERGY;\n"; diff --git a/scene/resources/sprite_frames.cpp b/scene/resources/sprite_frames.cpp new file mode 100644 index 0000000000..90c702081b --- /dev/null +++ b/scene/resources/sprite_frames.cpp @@ -0,0 +1,241 @@ +/*************************************************************************/ +/* sprite_frames.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (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 "sprite_frames.h" + +#include "scene/scene_string_names.h" + +void SpriteFrames::add_frame(const StringName &p_anim, const Ref<Texture2D> &p_frame, int p_at_pos) { + Map<StringName, Anim>::Element *E = animations.find(p_anim); + ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist."); + + if (p_at_pos >= 0 && p_at_pos < E->get().frames.size()) { + E->get().frames.insert(p_at_pos, p_frame); + } else { + E->get().frames.push_back(p_frame); + } + + emit_changed(); +} + +int SpriteFrames::get_frame_count(const StringName &p_anim) const { + const Map<StringName, Anim>::Element *E = animations.find(p_anim); + ERR_FAIL_COND_V_MSG(!E, 0, "Animation '" + String(p_anim) + "' doesn't exist."); + + return E->get().frames.size(); +} + +void SpriteFrames::remove_frame(const StringName &p_anim, int p_idx) { + Map<StringName, Anim>::Element *E = animations.find(p_anim); + ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist."); + + E->get().frames.remove(p_idx); + emit_changed(); +} + +void SpriteFrames::clear(const StringName &p_anim) { + Map<StringName, Anim>::Element *E = animations.find(p_anim); + ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist."); + + E->get().frames.clear(); + emit_changed(); +} + +void SpriteFrames::clear_all() { + animations.clear(); + add_animation("default"); +} + +void SpriteFrames::add_animation(const StringName &p_anim) { + ERR_FAIL_COND_MSG(animations.has(p_anim), "SpriteFrames already has animation '" + p_anim + "'."); + + animations[p_anim] = Anim(); +} + +bool SpriteFrames::has_animation(const StringName &p_anim) const { + return animations.has(p_anim); +} + +void SpriteFrames::remove_animation(const StringName &p_anim) { + animations.erase(p_anim); +} + +void SpriteFrames::rename_animation(const StringName &p_prev, const StringName &p_next) { + ERR_FAIL_COND_MSG(!animations.has(p_prev), "SpriteFrames doesn't have animation '" + String(p_prev) + "'."); + ERR_FAIL_COND_MSG(animations.has(p_next), "Animation '" + String(p_next) + "' already exists."); + + Anim anim = animations[p_prev]; + animations.erase(p_prev); + animations[p_next] = anim; +} + +Vector<String> SpriteFrames::_get_animation_list() const { + Vector<String> ret; + List<StringName> al; + get_animation_list(&al); + for (List<StringName>::Element *E = al.front(); E; E = E->next()) { + ret.push_back(E->get()); + } + + return ret; +} + +void SpriteFrames::get_animation_list(List<StringName> *r_animations) const { + for (const Map<StringName, Anim>::Element *E = animations.front(); E; E = E->next()) { + r_animations->push_back(E->key()); + } +} + +Vector<String> SpriteFrames::get_animation_names() const { + Vector<String> names; + for (const Map<StringName, Anim>::Element *E = animations.front(); E; E = E->next()) { + names.push_back(E->key()); + } + names.sort(); + return names; +} + +void SpriteFrames::set_animation_speed(const StringName &p_anim, float p_fps) { + ERR_FAIL_COND_MSG(p_fps < 0, "Animation speed cannot be negative (" + itos(p_fps) + ")."); + Map<StringName, Anim>::Element *E = animations.find(p_anim); + ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist."); + E->get().speed = p_fps; +} + +float SpriteFrames::get_animation_speed(const StringName &p_anim) const { + const Map<StringName, Anim>::Element *E = animations.find(p_anim); + ERR_FAIL_COND_V_MSG(!E, 0, "Animation '" + String(p_anim) + "' doesn't exist."); + return E->get().speed; +} + +void SpriteFrames::set_animation_loop(const StringName &p_anim, bool p_loop) { + Map<StringName, Anim>::Element *E = animations.find(p_anim); + ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist."); + E->get().loop = p_loop; +} + +bool SpriteFrames::get_animation_loop(const StringName &p_anim) const { + const Map<StringName, Anim>::Element *E = animations.find(p_anim); + ERR_FAIL_COND_V_MSG(!E, false, "Animation '" + String(p_anim) + "' doesn't exist."); + return E->get().loop; +} + +void SpriteFrames::_set_frames(const Array &p_frames) { + clear_all(); + Map<StringName, Anim>::Element *E = animations.find(SceneStringNames::get_singleton()->_default); + ERR_FAIL_COND(!E); + + E->get().frames.resize(p_frames.size()); + for (int i = 0; i < E->get().frames.size(); i++) { + E->get().frames.write[i] = p_frames[i]; + } +} + +Array SpriteFrames::_get_frames() const { + return Array(); +} + +Array SpriteFrames::_get_animations() const { + Array anims; + for (Map<StringName, Anim>::Element *E = animations.front(); E; E = E->next()) { + Dictionary d; + d["name"] = E->key(); + d["speed"] = E->get().speed; + d["loop"] = E->get().loop; + Array frames; + for (int i = 0; i < E->get().frames.size(); i++) { + frames.push_back(E->get().frames[i]); + } + d["frames"] = frames; + anims.push_back(d); + } + + return anims; +} + +void SpriteFrames::_set_animations(const Array &p_animations) { + animations.clear(); + for (int i = 0; i < p_animations.size(); i++) { + Dictionary d = p_animations[i]; + + ERR_CONTINUE(!d.has("name")); + ERR_CONTINUE(!d.has("speed")); + ERR_CONTINUE(!d.has("loop")); + ERR_CONTINUE(!d.has("frames")); + + Anim anim; + anim.speed = d["speed"]; + anim.loop = d["loop"]; + Array frames = d["frames"]; + for (int j = 0; j < frames.size(); j++) { + RES res = frames[j]; + anim.frames.push_back(res); + } + + animations[d["name"]] = anim; + } +} + +void SpriteFrames::_bind_methods() { + ClassDB::bind_method(D_METHOD("add_animation", "anim"), &SpriteFrames::add_animation); + ClassDB::bind_method(D_METHOD("has_animation", "anim"), &SpriteFrames::has_animation); + ClassDB::bind_method(D_METHOD("remove_animation", "anim"), &SpriteFrames::remove_animation); + ClassDB::bind_method(D_METHOD("rename_animation", "anim", "newname"), &SpriteFrames::rename_animation); + + ClassDB::bind_method(D_METHOD("get_animation_names"), &SpriteFrames::get_animation_names); + + ClassDB::bind_method(D_METHOD("set_animation_speed", "anim", "speed"), &SpriteFrames::set_animation_speed); + ClassDB::bind_method(D_METHOD("get_animation_speed", "anim"), &SpriteFrames::get_animation_speed); + + ClassDB::bind_method(D_METHOD("set_animation_loop", "anim", "loop"), &SpriteFrames::set_animation_loop); + ClassDB::bind_method(D_METHOD("get_animation_loop", "anim"), &SpriteFrames::get_animation_loop); + + ClassDB::bind_method(D_METHOD("add_frame", "anim", "frame", "at_position"), &SpriteFrames::add_frame, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("get_frame_count", "anim"), &SpriteFrames::get_frame_count); + ClassDB::bind_method(D_METHOD("get_frame", "anim", "idx"), &SpriteFrames::get_frame); + ClassDB::bind_method(D_METHOD("set_frame", "anim", "idx", "txt"), &SpriteFrames::set_frame); + ClassDB::bind_method(D_METHOD("remove_frame", "anim", "idx"), &SpriteFrames::remove_frame); + ClassDB::bind_method(D_METHOD("clear", "anim"), &SpriteFrames::clear); + ClassDB::bind_method(D_METHOD("clear_all"), &SpriteFrames::clear_all); + + ClassDB::bind_method(D_METHOD("_set_frames"), &SpriteFrames::_set_frames); + ClassDB::bind_method(D_METHOD("_get_frames"), &SpriteFrames::_get_frames); + + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "frames", PROPERTY_HINT_NONE, "", 0), "_set_frames", "_get_frames"); //compatibility + + ClassDB::bind_method(D_METHOD("_set_animations"), &SpriteFrames::_set_animations); + ClassDB::bind_method(D_METHOD("_get_animations"), &SpriteFrames::_get_animations); + + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "animations", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_animations", "_get_animations"); //compatibility +} + +SpriteFrames::SpriteFrames() { + add_animation(SceneStringNames::get_singleton()->_default); +} diff --git a/scene/2d/navigation_2d.h b/scene/resources/sprite_frames.h index 12847e52ac..282c5f20ab 100644 --- a/scene/2d/navigation_2d.h +++ b/scene/resources/sprite_frames.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* navigation_2d.h */ +/* sprite_frames.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,44 +28,75 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef NAVIGATION_2D_H -#define NAVIGATION_2D_H +#ifndef SPRITE_FRAMES_H +#define SPRITE_FRAMES_H -#include "scene/2d/navigation_region_2d.h" -#include "scene/2d/node_2d.h" +#include "scene/resources/texture.h" -class Navigation2D : public Node2D { - GDCLASS(Navigation2D, Node2D); +class SpriteFrames : public Resource { + GDCLASS(SpriteFrames, Resource); - RID map; - real_t cell_size; - real_t edge_connection_margin; + struct Anim { + float speed = 5.0; + bool loop = true; + Vector<Ref<Texture2D>> frames; + }; + + Map<StringName, Anim> animations; + + Array _get_frames() const; + void _set_frames(const Array &p_frames); + + Array _get_animations() const; + void _set_animations(const Array &p_animations); + + Vector<String> _get_animation_list() const; protected: static void _bind_methods(); - void _notification(int p_what); public: - RID get_rid() const { - return map; - } + void add_animation(const StringName &p_anim); + bool has_animation(const StringName &p_anim) const; + void remove_animation(const StringName &p_anim); + void rename_animation(const StringName &p_prev, const StringName &p_next); - void set_cell_size(float p_cell_size); - float get_cell_size() const { - return cell_size; - } + void get_animation_list(List<StringName> *r_animations) const; + Vector<String> get_animation_names() const; + + void set_animation_speed(const StringName &p_anim, float p_fps); + float get_animation_speed(const StringName &p_anim) const; + + void set_animation_loop(const StringName &p_anim, bool p_loop); + bool get_animation_loop(const StringName &p_anim) const; - void set_edge_connection_margin(float p_edge_connection_margin); - float get_edge_connection_margin() const { - return edge_connection_margin; + void add_frame(const StringName &p_anim, const Ref<Texture2D> &p_frame, int p_at_pos = -1); + int get_frame_count(const StringName &p_anim) const; + _FORCE_INLINE_ Ref<Texture2D> get_frame(const StringName &p_anim, int p_idx) const { + const Map<StringName, Anim>::Element *E = animations.find(p_anim); + ERR_FAIL_COND_V_MSG(!E, Ref<Texture2D>(), "Animation '" + String(p_anim) + "' doesn't exist."); + ERR_FAIL_COND_V(p_idx < 0, Ref<Texture2D>()); + if (p_idx >= E->get().frames.size()) { + return Ref<Texture2D>(); + } + + return E->get().frames[p_idx]; } - Vector<Vector2> get_simple_path(const Vector2 &p_start, const Vector2 &p_end, bool p_optimize = true) const; - Vector2 get_closest_point(const Vector2 &p_point) const; - RID get_closest_point_owner(const Vector2 &p_point) const; + void set_frame(const StringName &p_anim, int p_idx, const Ref<Texture2D> &p_frame) { + Map<StringName, Anim>::Element *E = animations.find(p_anim); + ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist."); + ERR_FAIL_COND(p_idx < 0); + if (p_idx >= E->get().frames.size()) { + return; + } + E->get().frames.write[p_idx] = p_frame; + } + void remove_frame(const StringName &p_anim, int p_idx); + void clear(const StringName &p_anim); + void clear_all(); - Navigation2D(); - ~Navigation2D(); + SpriteFrames(); }; -#endif // NAVIGATION_2D_H +#endif // SPRITE_FRAMES_H diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp index 9b80224c3f..2159f1bc97 100644 --- a/scene/resources/style_box.cpp +++ b/scene/resources/style_box.cpp @@ -460,6 +460,7 @@ Point2 StyleBoxFlat::get_shadow_offset() const { void StyleBoxFlat::set_anti_aliased(const bool &p_anti_aliased) { anti_aliased = p_anti_aliased; emit_changed(); + notify_property_list_changed(); } bool StyleBoxFlat::is_anti_aliased() const { @@ -781,6 +782,12 @@ float StyleBoxFlat::get_style_margin(Side p_side) const { return border_width[p_side]; } +void StyleBoxFlat::_validate_property(PropertyInfo &property) const { + if (!anti_aliased && property.name == "anti_aliasing_size") { + property.usage = PROPERTY_USAGE_NOEDITOR; + } +} + void StyleBoxFlat::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bg_color", "color"), &StyleBoxFlat::set_bg_color); ClassDB::bind_method(D_METHOD("get_bg_color"), &StyleBoxFlat::get_bg_color); diff --git a/scene/resources/style_box.h b/scene/resources/style_box.h index 8a273afbfd..dd5c873a00 100644 --- a/scene/resources/style_box.h +++ b/scene/resources/style_box.h @@ -159,6 +159,7 @@ class StyleBoxFlat : public StyleBox { protected: virtual float get_style_margin(Side p_side) const override; static void _bind_methods(); + void _validate_property(PropertyInfo &property) const override; public: //Color diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp index 47933bd69a..ff682a40f4 100644 --- a/scene/resources/surface_tool.cpp +++ b/scene/resources/surface_tool.cpp @@ -663,6 +663,8 @@ void SurfaceTool::deindex() { } void SurfaceTool::_create_list(const Ref<Mesh> &p_existing, int p_surface, LocalVector<Vertex> *r_vertex, LocalVector<int> *r_index, uint32_t &lformat) { + ERR_FAIL_NULL_MSG(p_existing, "First argument in SurfaceTool::_create_list() must be a valid object of type Mesh"); + Array arr = p_existing->surface_get_arrays(p_surface); ERR_FAIL_COND(arr.size() != RS::ARRAY_MAX); _create_list_from_arrays(arr, r_vertex, r_index, lformat); @@ -824,6 +826,8 @@ void SurfaceTool::create_from_triangle_arrays(const Array &p_arrays) { } void SurfaceTool::create_from(const Ref<Mesh> &p_existing, int p_surface) { + ERR_FAIL_NULL_MSG(p_existing, "First argument in SurfaceTool::create_from() must be a valid object of type Mesh"); + clear(); primitive = p_existing->surface_get_primitive_type(p_surface); _create_list(p_existing, p_surface, &vertex_array, &index_array, format); @@ -831,6 +835,8 @@ void SurfaceTool::create_from(const Ref<Mesh> &p_existing, int p_surface) { } void SurfaceTool::create_from_blend_shape(const Ref<Mesh> &p_existing, int p_surface, const String &p_blend_shape_name) { + ERR_FAIL_NULL_MSG(p_existing, "First argument in SurfaceTool::create_from_blend_shape() must be a valid object of type Mesh"); + clear(); primitive = p_existing->surface_get_primitive_type(p_surface); Array arr = p_existing->surface_get_blend_shape_arrays(p_surface); @@ -851,6 +857,8 @@ void SurfaceTool::create_from_blend_shape(const Ref<Mesh> &p_existing, int p_sur } void SurfaceTool::append_from(const Ref<Mesh> &p_existing, int p_surface, const Transform &p_xform) { + ERR_FAIL_NULL_MSG(p_existing, "First argument in SurfaceTool::append_from() must be a valid object of type Mesh"); + if (vertex_array.size() == 0) { primitive = p_existing->surface_get_primitive_type(p_surface); format = 0; @@ -1059,6 +1067,10 @@ void SurfaceTool::set_material(const Ref<Material> &p_material) { material = p_material; } +Ref<Material> SurfaceTool::get_material() const { + return material; +} + void SurfaceTool::clear() { begun = false; primitive = Mesh::PRIMITIVE_LINES; @@ -1088,6 +1100,10 @@ void SurfaceTool::set_custom_format(int p_index, CustomFormat p_format) { ERR_FAIL_COND(begun); last_custom_format[p_index] = p_format; } + +Mesh::PrimitiveType SurfaceTool::get_primitive() const { + return primitive; +} SurfaceTool::CustomFormat SurfaceTool::get_custom_format(int p_index) const { ERR_FAIL_INDEX_V(p_index, RS::ARRAY_CUSTOM_COUNT, CUSTOM_MAX); return last_custom_format[p_index]; @@ -1097,7 +1113,7 @@ void SurfaceTool::optimize_indices_for_cache() { ERR_FAIL_COND(index_array.size() == 0); LocalVector old_index_array = index_array; - zeromem(index_array.ptr(), index_array.size() * sizeof(int)); + memset(index_array.ptr(), 0, index_array.size() * sizeof(int)); optimize_vertex_cache_func((unsigned int *)index_array.ptr(), (unsigned int *)old_index_array.ptr(), old_index_array.size(), vertex_array.size()); } @@ -1174,6 +1190,7 @@ void SurfaceTool::_bind_methods() { ClassDB::bind_method(D_METHOD("generate_lod", "nd_threshold", "target_index_count"), &SurfaceTool::generate_lod, DEFVAL(3)); ClassDB::bind_method(D_METHOD("set_material", "material"), &SurfaceTool::set_material); + ClassDB::bind_method(D_METHOD("get_primitive"), &SurfaceTool::get_primitive); ClassDB::bind_method(D_METHOD("clear"), &SurfaceTool::clear); diff --git a/scene/resources/surface_tool.h b/scene/resources/surface_tool.h index 17efdcba71..28addf2245 100644 --- a/scene/resources/surface_tool.h +++ b/scene/resources/surface_tool.h @@ -143,6 +143,8 @@ public: void set_custom_format(int p_index, CustomFormat p_format); CustomFormat get_custom_format(int p_index) const; + Mesh::PrimitiveType get_primitive() const; + void begin(Mesh::PrimitiveType p_primitive); void set_color(Color p_color); @@ -171,6 +173,7 @@ public: Vector<int> generate_lod(float p_threshold, int p_target_index_count = 3); void set_material(const Ref<Material> &p_material); + Ref<Material> get_material() const; void clear(); diff --git a/scene/resources/text_file.cpp b/scene/resources/text_file.cpp index cf07003720..b71909b6bb 100644 --- a/scene/resources/text_file.cpp +++ b/scene/resources/text_file.cpp @@ -55,10 +55,10 @@ Error TextFile::load_text(const String &p_path) { ERR_FAIL_COND_V_MSG(err, err, "Cannot open TextFile '" + p_path + "'."); - int len = f->get_len(); + uint64_t len = f->get_len(); sourcef.resize(len + 1); uint8_t *w = sourcef.ptrw(); - int r = f->get_buffer(w, len); + uint64_t r = f->get_buffer(w, len); f->close(); memdelete(f); ERR_FAIL_COND_V(r != len, ERR_CANT_OPEN); diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp index 444a4bb22a..341f5abd80 100644 --- a/scene/resources/text_paragraph.cpp +++ b/scene/resources/text_paragraph.cpp @@ -98,6 +98,9 @@ void TextParagraph::_bind_methods() { ClassDB::bind_method(D_METHOD("get_line_underline_position", "line"), &TextParagraph::get_line_underline_position); ClassDB::bind_method(D_METHOD("get_line_underline_thickness", "line"), &TextParagraph::get_line_underline_thickness); + ClassDB::bind_method(D_METHOD("get_spacing_top"), &TextParagraph::get_spacing_top); + ClassDB::bind_method(D_METHOD("get_spacing_bottom"), &TextParagraph::get_spacing_bottom); + ClassDB::bind_method(D_METHOD("get_dropcap_size"), &TextParagraph::get_dropcap_size); ClassDB::bind_method(D_METHOD("get_dropcap_lines"), &TextParagraph::get_dropcap_lines); @@ -266,6 +269,14 @@ bool TextParagraph::add_string(const String &p_text, const Ref<Font> &p_fonts, i return res; } +int TextParagraph::get_spacing_top() const { + return spacing_top; +} + +int TextParagraph::get_spacing_bottom() const { + return spacing_bottom; +} + void TextParagraph::_set_bidi_override(const Array &p_override) { Vector<Vector2i> overrides; for (int i = 0; i < p_override.size(); i++) { @@ -609,11 +620,13 @@ int TextParagraph::hit_test(const Point2 &p_coords) const { const_cast<TextParagraph *>(this)->_shape_lines(); Vector2 ofs; if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { - if (ofs.y < 0) + if (ofs.y < 0) { return 0; + } } else { - if (ofs.x < 0) + if (ofs.x < 0) { return 0; + } } for (int i = 0; i < lines.size(); i++) { if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) { diff --git a/scene/resources/text_paragraph.h b/scene/resources/text_paragraph.h index a16fa8c3c4..4396b07130 100644 --- a/scene/resources/text_paragraph.h +++ b/scene/resources/text_paragraph.h @@ -116,6 +116,9 @@ public: float get_line_underline_position(int p_line) const; float get_line_underline_thickness(int p_line) const; + int get_spacing_top() const; + int get_spacing_bottom() const; + Size2 get_dropcap_size() const; int get_dropcap_lines() const; diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index d2bb1338d8..1b2176d30a 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -71,7 +71,7 @@ void Texture2D::_bind_methods() { ClassDB::bind_method(D_METHOD("draw", "canvas_item", "position", "modulate", "transpose"), &Texture2D::draw, DEFVAL(Color(1, 1, 1)), DEFVAL(false)); ClassDB::bind_method(D_METHOD("draw_rect", "canvas_item", "rect", "tile", "modulate", "transpose"), &Texture2D::draw_rect, DEFVAL(Color(1, 1, 1)), DEFVAL(false)); ClassDB::bind_method(D_METHOD("draw_rect_region", "canvas_item", "rect", "src_rect", "modulate", "transpose", "clip_uv"), &Texture2D::draw_rect_region, DEFVAL(Color(1, 1, 1)), DEFVAL(false), DEFVAL(true)); - ClassDB::bind_method(D_METHOD("get_data"), &Texture2D::get_data); + ClassDB::bind_method(D_METHOD("get_image"), &Texture2D::get_image); ADD_GROUP("", ""); } @@ -116,7 +116,7 @@ bool ImageTexture::_set(const StringName &p_name, const Variant &p_value) { bool ImageTexture::_get(const StringName &p_name, Variant &r_ret) const { if (p_name == "image") { - r_ret = get_data(); + r_ret = get_image(); } else if (p_name == "size") { r_ret = Size2(w, h); } else { @@ -200,7 +200,7 @@ void ImageTexture::_resource_path_changed() { String path = get_path(); } -Ref<Image> ImageTexture::get_data() const { +Ref<Image> ImageTexture::get_image() const { if (image_stored) { return RenderingServer::get_singleton()->texture_2d_get(texture); } else { @@ -251,7 +251,7 @@ void ImageTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, cons bool ImageTexture::is_pixel_opaque(int p_x, int p_y) const { if (!alpha_cache.is_valid()) { - Ref<Image> img = get_data(); + Ref<Image> img = get_image(); if (img.is_valid()) { if (img->is_compressed()) { //must decompress, if compressed Ref<Image> decom = img->duplicate(); @@ -335,7 +335,7 @@ Ref<Image> StreamTexture2D::load_image_from_file(FileAccess *f, int p_size_limit //mipmaps need to be read independently, they will be later combined Vector<Ref<Image>> mipmap_images; - int total_size = 0; + uint64_t total_size = 0; bool first = true; @@ -410,7 +410,7 @@ Ref<Image> StreamTexture2D::load_image_from_file(FileAccess *f, int p_size_limit Vector<uint8_t> id = mipmap_images[i]->get_data(); int len = id.size(); const uint8_t *r = id.ptr(); - copymem(&wr[ofs], r, len); + memcpy(&wr[ofs], r, len); ofs += len; } } @@ -661,7 +661,7 @@ bool StreamTexture2D::has_alpha() const { return false; } -Ref<Image> StreamTexture2D::get_data() const { +Ref<Image> StreamTexture2D::get_image() const { if (texture.is_valid()) { return RS::get_singleton()->texture_2d_get(texture); } else { @@ -671,7 +671,7 @@ Ref<Image> StreamTexture2D::get_data() const { bool StreamTexture2D::is_pixel_opaque(int p_x, int p_y) const { if (!alpha_cache.is_valid()) { - Ref<Image> img = get_data(); + Ref<Image> img = get_image(); if (img.is_valid()) { if (img->is_compressed()) { //must decompress, if compressed Ref<Image> decom = img->duplicate(); @@ -1405,185 +1405,6 @@ MeshTexture::MeshTexture() { ////////////////////////////////////////// -int LargeTexture::get_width() const { - return size.width; -} - -int LargeTexture::get_height() const { - return size.height; -} - -RID LargeTexture::get_rid() const { - return RID(); -} - -bool LargeTexture::has_alpha() const { - for (int i = 0; i < pieces.size(); i++) { - if (pieces[i].texture->has_alpha()) { - return true; - } - } - - return false; -} - -int LargeTexture::add_piece(const Point2 &p_offset, const Ref<Texture2D> &p_texture) { - ERR_FAIL_COND_V(p_texture.is_null(), -1); - Piece p; - p.offset = p_offset; - p.texture = p_texture; - pieces.push_back(p); - - return pieces.size() - 1; -} - -void LargeTexture::set_piece_offset(int p_idx, const Point2 &p_offset) { - ERR_FAIL_INDEX(p_idx, pieces.size()); - pieces.write[p_idx].offset = p_offset; -}; - -void LargeTexture::set_piece_texture(int p_idx, const Ref<Texture2D> &p_texture) { - ERR_FAIL_COND(p_texture == this); - ERR_FAIL_COND(p_texture.is_null()); - ERR_FAIL_INDEX(p_idx, pieces.size()); - pieces.write[p_idx].texture = p_texture; -}; - -void LargeTexture::set_size(const Size2 &p_size) { - size = p_size; -} - -void LargeTexture::clear() { - pieces.clear(); - size = Size2i(); -} - -Array LargeTexture::_get_data() const { - Array arr; - for (int i = 0; i < pieces.size(); i++) { - arr.push_back(pieces[i].offset); - arr.push_back(pieces[i].texture); - } - arr.push_back(Size2(size)); - return arr; -} - -void LargeTexture::_set_data(const Array &p_array) { - ERR_FAIL_COND(p_array.size() < 1); - ERR_FAIL_COND(!(p_array.size() & 1)); - clear(); - for (int i = 0; i < p_array.size() - 1; i += 2) { - add_piece(p_array[i], p_array[i + 1]); - } - size = Size2(p_array[p_array.size() - 1]); -} - -int LargeTexture::get_piece_count() const { - return pieces.size(); -} - -Vector2 LargeTexture::get_piece_offset(int p_idx) const { - ERR_FAIL_INDEX_V(p_idx, pieces.size(), Vector2()); - return pieces[p_idx].offset; -} - -Ref<Texture2D> LargeTexture::get_piece_texture(int p_idx) const { - ERR_FAIL_INDEX_V(p_idx, pieces.size(), Ref<Texture2D>()); - return pieces[p_idx].texture; -} - -Ref<Image> LargeTexture::to_image() const { - Ref<Image> img = memnew(Image(this->get_width(), this->get_height(), false, Image::FORMAT_RGBA8)); - for (int i = 0; i < pieces.size(); i++) { - Ref<Image> src_img = pieces[i].texture->get_data(); - img->blit_rect(src_img, Rect2(0, 0, src_img->get_width(), src_img->get_height()), pieces[i].offset); - } - - return img; -} - -void LargeTexture::_bind_methods() { - ClassDB::bind_method(D_METHOD("add_piece", "ofs", "texture"), &LargeTexture::add_piece); - ClassDB::bind_method(D_METHOD("set_piece_offset", "idx", "ofs"), &LargeTexture::set_piece_offset); - ClassDB::bind_method(D_METHOD("set_piece_texture", "idx", "texture"), &LargeTexture::set_piece_texture); - ClassDB::bind_method(D_METHOD("set_size", "size"), &LargeTexture::set_size); - ClassDB::bind_method(D_METHOD("clear"), &LargeTexture::clear); - - ClassDB::bind_method(D_METHOD("get_piece_count"), &LargeTexture::get_piece_count); - ClassDB::bind_method(D_METHOD("get_piece_offset", "idx"), &LargeTexture::get_piece_offset); - ClassDB::bind_method(D_METHOD("get_piece_texture", "idx"), &LargeTexture::get_piece_texture); - - ClassDB::bind_method(D_METHOD("_set_data", "data"), &LargeTexture::_set_data); - ClassDB::bind_method(D_METHOD("_get_data"), &LargeTexture::_get_data); - - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); -} - -void LargeTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const { - for (int i = 0; i < pieces.size(); i++) { - // TODO - pieces[i].texture->draw(p_canvas_item, pieces[i].offset + p_pos, p_modulate, p_transpose); - } -} - -void LargeTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const { - //tiling not supported for this - if (size.x == 0 || size.y == 0) { - return; - } - - Size2 scale = p_rect.size / size; - - for (int i = 0; i < pieces.size(); i++) { - // TODO - pieces[i].texture->draw_rect(p_canvas_item, Rect2(pieces[i].offset * scale + p_rect.position, pieces[i].texture->get_size() * scale), false, p_modulate, p_transpose); - } -} - -void LargeTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const { - //tiling not supported for this - if (p_src_rect.size.x == 0 || p_src_rect.size.y == 0) { - return; - } - - Size2 scale = p_rect.size / p_src_rect.size; - - for (int i = 0; i < pieces.size(); i++) { - // TODO - Rect2 rect(pieces[i].offset, pieces[i].texture->get_size()); - if (!p_src_rect.intersects(rect)) { - continue; - } - Rect2 local = p_src_rect.intersection(rect); - Rect2 target = local; - target.size *= scale; - target.position = p_rect.position + (p_src_rect.position + rect.position) * scale; - local.position -= rect.position; - pieces[i].texture->draw_rect_region(p_canvas_item, target, local, p_modulate, p_transpose, false); - } -} - -bool LargeTexture::is_pixel_opaque(int p_x, int p_y) const { - for (int i = 0; i < pieces.size(); i++) { - // TODO - if (!pieces[i].texture.is_valid()) { - continue; - } - - Rect2 rect(pieces[i].offset, pieces[i].texture->get_size()); - if (rect.has_point(Point2(p_x, p_y))) { - return pieces[i].texture->is_pixel_opaque(p_x - rect.position.x, p_y - rect.position.y); - } - } - - return true; -} - -LargeTexture::LargeTexture() { -} - -/////////////////// - void CurveTexture::_bind_methods() { ClassDB::bind_method(D_METHOD("set_width", "width"), &CurveTexture::set_width); @@ -1782,7 +1603,7 @@ int GradientTexture::get_width() const { return width; } -Ref<Image> GradientTexture::get_data() const { +Ref<Image> GradientTexture::get_image() const { if (!texture.is_valid()) { return Ref<Image>(); } @@ -2031,14 +1852,14 @@ bool AnimatedTexture::has_alpha() const { return frames[current_frame].texture->has_alpha(); } -Ref<Image> AnimatedTexture::get_data() const { +Ref<Image> AnimatedTexture::get_image() const { RWLockRead r(rw_lock); if (!frames[current_frame].texture.is_valid()) { return Ref<Image>(); } - return frames[current_frame].texture->get_data(); + return frames[current_frame].texture->get_image(); } bool AnimatedTexture::is_pixel_opaque(int p_x, int p_y) const { @@ -2543,7 +2364,7 @@ uint32_t CameraTexture::get_flags() const { return 0; } -Ref<Image> CameraTexture::get_data() const { +Ref<Image> CameraTexture::get_image() const { // not (yet) supported return Ref<Image>(); } diff --git a/scene/resources/texture.h b/scene/resources/texture.h index a0d917fd86..264d85d187 100644 --- a/scene/resources/texture.h +++ b/scene/resources/texture.h @@ -71,7 +71,7 @@ public: virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = true) const; virtual bool get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, Rect2 &r_rect, Rect2 &r_src_rect) const; - virtual Ref<Image> get_data() const { return Ref<Image>(); } + virtual Ref<Image> get_image() const { return Ref<Image>(); } Texture2D(); }; @@ -108,7 +108,7 @@ public: Image::Format get_format() const; void update(const Ref<Image> &p_image, bool p_immediate = false); - Ref<Image> get_data() const override; + Ref<Image> get_image() const override; int get_width() const override; int get_height() const override; @@ -203,7 +203,7 @@ public: virtual bool has_alpha() const override; bool is_pixel_opaque(int p_x, int p_y) const override; - virtual Ref<Image> get_data() const override; + virtual Ref<Image> get_image() const override; StreamTexture2D(); ~StreamTexture2D(); @@ -297,51 +297,6 @@ public: MeshTexture(); }; -class LargeTexture : public Texture2D { - GDCLASS(LargeTexture, Texture2D); - RES_BASE_EXTENSION("largetex"); - -protected: - struct Piece { - Point2 offset; - Ref<Texture2D> texture; - }; - - Vector<Piece> pieces; - Size2i size; - - Array _get_data() const; - void _set_data(const Array &p_array); - static void _bind_methods(); - -public: - virtual int get_width() const override; - virtual int get_height() const override; - virtual RID get_rid() const override; - - virtual bool has_alpha() const override; - - int add_piece(const Point2 &p_offset, const Ref<Texture2D> &p_texture); - void set_piece_offset(int p_idx, const Point2 &p_offset); - void set_piece_texture(int p_idx, const Ref<Texture2D> &p_texture); - - void set_size(const Size2 &p_size); - void clear(); - - int get_piece_count() const; - Vector2 get_piece_offset(int p_idx) const; - Ref<Texture2D> get_piece_texture(int p_idx) const; - Ref<Image> to_image() const; - - virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override; - virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override; - virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = true) const override; - - bool is_pixel_opaque(int p_x, int p_y) const override; - - LargeTexture(); -}; - class TextureLayered : public Texture { GDCLASS(TextureLayered, Texture); @@ -716,7 +671,7 @@ public: virtual int get_height() const override { return 1; } virtual bool has_alpha() const override { return true; } - virtual Ref<Image> get_data() const override; + virtual Ref<Image> get_image() const override; GradientTexture(); virtual ~GradientTexture(); @@ -812,7 +767,7 @@ public: virtual bool has_alpha() const override; - virtual Ref<Image> get_data() const override; + virtual Ref<Image> get_image() const override; bool is_pixel_opaque(int p_x, int p_y) const override; @@ -839,7 +794,7 @@ public: virtual void set_flags(uint32_t p_flags); virtual uint32_t get_flags() const; - virtual Ref<Image> get_data() const override; + virtual Ref<Image> get_image() const override; void set_camera_feed_id(int p_new_id); int get_camera_feed_id() const; diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp index 0405ea98bb..786a96501a 100644 --- a/scene/resources/theme.cpp +++ b/scene/resources/theme.cpp @@ -36,11 +36,11 @@ void Theme::_emit_theme_changed() { emit_changed(); } -Vector<String> Theme::_get_icon_list(const String &p_node_type) const { +Vector<String> Theme::_get_icon_list(const String &p_theme_type) const { Vector<String> ilret; List<StringName> il; - get_icon_list(p_node_type, &il); + get_icon_list(p_theme_type, &il); ilret.resize(il.size()); int i = 0; @@ -66,11 +66,11 @@ Vector<String> Theme::_get_icon_type_list() const { return ilret; } -Vector<String> Theme::_get_stylebox_list(const String &p_node_type) const { +Vector<String> Theme::_get_stylebox_list(const String &p_theme_type) const { Vector<String> ilret; List<StringName> il; - get_stylebox_list(p_node_type, &il); + get_stylebox_list(p_theme_type, &il); ilret.resize(il.size()); int i = 0; @@ -96,11 +96,11 @@ Vector<String> Theme::_get_stylebox_type_list() const { return ilret; } -Vector<String> Theme::_get_font_list(const String &p_node_type) const { +Vector<String> Theme::_get_font_list(const String &p_theme_type) const { Vector<String> ilret; List<StringName> il; - get_font_list(p_node_type, &il); + get_font_list(p_theme_type, &il); ilret.resize(il.size()); int i = 0; @@ -126,11 +126,11 @@ Vector<String> Theme::_get_font_type_list() const { return ilret; } -Vector<String> Theme::_get_font_size_list(const String &p_node_type) const { +Vector<String> Theme::_get_font_size_list(const String &p_theme_type) const { Vector<String> ilret; List<StringName> il; - get_font_size_list(p_node_type, &il); + get_font_size_list(p_theme_type, &il); ilret.resize(il.size()); int i = 0; @@ -141,11 +141,26 @@ Vector<String> Theme::_get_font_size_list(const String &p_node_type) const { return ilret; } -Vector<String> Theme::_get_color_list(const String &p_node_type) const { +Vector<String> Theme::_get_font_size_type_list() const { Vector<String> ilret; List<StringName> il; - get_color_list(p_node_type, &il); + get_font_size_type_list(&il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector<String> Theme::_get_color_list(const String &p_theme_type) const { + Vector<String> ilret; + List<StringName> il; + + get_color_list(p_theme_type, &il); ilret.resize(il.size()); int i = 0; @@ -171,11 +186,11 @@ Vector<String> Theme::_get_color_type_list() const { return ilret; } -Vector<String> Theme::_get_constant_list(const String &p_node_type) const { +Vector<String> Theme::_get_constant_list(const String &p_theme_type) const { Vector<String> ilret; List<StringName> il; - get_constant_list(p_node_type, &il); + get_constant_list(p_theme_type, &il); ilret.resize(il.size()); int i = 0; @@ -201,6 +216,48 @@ Vector<String> Theme::_get_constant_type_list() const { return ilret; } +Vector<String> Theme::_get_theme_item_list(DataType p_data_type, const String &p_theme_type) const { + switch (p_data_type) { + case DATA_TYPE_COLOR: + return _get_color_list(p_theme_type); + case DATA_TYPE_CONSTANT: + return _get_constant_list(p_theme_type); + case DATA_TYPE_FONT: + return _get_font_list(p_theme_type); + case DATA_TYPE_FONT_SIZE: + return _get_font_size_list(p_theme_type); + case DATA_TYPE_ICON: + return _get_icon_list(p_theme_type); + case DATA_TYPE_STYLEBOX: + return _get_stylebox_list(p_theme_type); + case DATA_TYPE_MAX: + break; // Can't happen, but silences warning. + } + + return Vector<String>(); +} + +Vector<String> Theme::_get_theme_item_type_list(DataType p_data_type) const { + switch (p_data_type) { + case DATA_TYPE_COLOR: + return _get_color_type_list(); + case DATA_TYPE_CONSTANT: + return _get_constant_type_list(); + case DATA_TYPE_FONT: + return _get_font_type_list(); + case DATA_TYPE_FONT_SIZE: + return _get_font_size_type_list(); + case DATA_TYPE_ICON: + return _get_icon_type_list(); + case DATA_TYPE_STYLEBOX: + return _get_stylebox_type_list(); + case DATA_TYPE_MAX: + break; // Can't happen, but silences warning. + } + + return Vector<String>(); +} + Vector<String> Theme::_get_type_list() const { Vector<String> ilret; List<StringName> il; @@ -221,19 +278,19 @@ bool Theme::_set(const StringName &p_name, const Variant &p_value) { if (sname.find("/") != -1) { String type = sname.get_slicec('/', 1); - String node_type = sname.get_slicec('/', 0); + String theme_type = sname.get_slicec('/', 0); String name = sname.get_slicec('/', 2); if (type == "icons") { - set_icon(name, node_type, p_value); + set_icon(name, theme_type, p_value); } else if (type == "styles") { - set_stylebox(name, node_type, p_value); + set_stylebox(name, theme_type, p_value); } else if (type == "fonts") { - set_font(name, node_type, p_value); + set_font(name, theme_type, p_value); } else if (type == "colors") { - set_color(name, node_type, p_value); + set_color(name, theme_type, p_value); } else if (type == "constants") { - set_constant(name, node_type, p_value); + set_constant(name, theme_type, p_value); } else { return false; } @@ -249,31 +306,31 @@ bool Theme::_get(const StringName &p_name, Variant &r_ret) const { if (sname.find("/") != -1) { String type = sname.get_slicec('/', 1); - String node_type = sname.get_slicec('/', 0); + String theme_type = sname.get_slicec('/', 0); String name = sname.get_slicec('/', 2); if (type == "icons") { - if (!has_icon(name, node_type)) { + if (!has_icon(name, theme_type)) { r_ret = Ref<Texture2D>(); } else { - r_ret = get_icon(name, node_type); + r_ret = get_icon(name, theme_type); } } else if (type == "styles") { - if (!has_stylebox(name, node_type)) { + if (!has_stylebox(name, theme_type)) { r_ret = Ref<StyleBox>(); } else { - r_ret = get_stylebox(name, node_type); + r_ret = get_stylebox(name, theme_type); } } else if (type == "fonts") { - if (!has_font(name, node_type)) { + if (!has_font(name, theme_type)) { r_ret = Ref<Font>(); } else { - r_ret = get_font(name, node_type); + r_ret = get_font(name, theme_type); } } else if (type == "colors") { - r_ret = get_color(name, node_type); + r_ret = get_color(name, theme_type); } else if (type == "constants") { - r_ret = get_constant(name, node_type); + r_ret = get_constant(name, theme_type); } else { return false; } @@ -420,19 +477,17 @@ void Theme::set_default_font_size(int p_font_size) { default_font_size = p_font_size; } -void Theme::set_icon(const StringName &p_name, const StringName &p_node_type, const Ref<Texture2D> &p_icon) { - //ERR_FAIL_COND(p_icon.is_null()); - - bool new_value = !icon_map.has(p_node_type) || !icon_map[p_node_type].has(p_name); +void Theme::set_icon(const StringName &p_name, const StringName &p_theme_type, const Ref<Texture2D> &p_icon) { + bool new_value = !icon_map.has(p_theme_type) || !icon_map[p_theme_type].has(p_name); - if (icon_map[p_node_type].has(p_name) && icon_map[p_node_type][p_name].is_valid()) { - icon_map[p_node_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); + if (icon_map[p_theme_type].has(p_name) && icon_map[p_theme_type][p_name].is_valid()) { + icon_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); } - icon_map[p_node_type][p_name] = p_icon; + icon_map[p_theme_type][p_name] = p_icon; if (p_icon.is_valid()) { - icon_map[p_node_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED); + icon_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED); } if (new_value) { @@ -441,46 +496,66 @@ void Theme::set_icon(const StringName &p_name, const StringName &p_node_type, co } } -Ref<Texture2D> Theme::get_icon(const StringName &p_name, const StringName &p_node_type) const { - if (icon_map.has(p_node_type) && icon_map[p_node_type].has(p_name) && icon_map[p_node_type][p_name].is_valid()) { - return icon_map[p_node_type][p_name]; +Ref<Texture2D> Theme::get_icon(const StringName &p_name, const StringName &p_theme_type) const { + if (icon_map.has(p_theme_type) && icon_map[p_theme_type].has(p_name) && icon_map[p_theme_type][p_name].is_valid()) { + return icon_map[p_theme_type][p_name]; } else { return default_icon; } } -bool Theme::has_icon(const StringName &p_name, const StringName &p_node_type) const { - return (icon_map.has(p_node_type) && icon_map[p_node_type].has(p_name) && icon_map[p_node_type][p_name].is_valid()); +bool Theme::has_icon(const StringName &p_name, const StringName &p_theme_type) const { + return (icon_map.has(p_theme_type) && icon_map[p_theme_type].has(p_name) && icon_map[p_theme_type][p_name].is_valid()); +} + +bool Theme::has_icon_nocheck(const StringName &p_name, const StringName &p_theme_type) const { + return (icon_map.has(p_theme_type) && icon_map[p_theme_type].has(p_name)); } -void Theme::clear_icon(const StringName &p_name, const StringName &p_node_type) { - ERR_FAIL_COND(!icon_map.has(p_node_type)); - ERR_FAIL_COND(!icon_map[p_node_type].has(p_name)); +void Theme::rename_icon(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!icon_map.has(p_theme_type), "Cannot rename the icon '" + String(p_old_name) + "' because the node type '" + String(p_theme_type) + "' does not exist."); + ERR_FAIL_COND_MSG(icon_map[p_theme_type].has(p_name), "Cannot rename the icon '" + String(p_old_name) + "' because the new name '" + String(p_name) + "' already exists."); + ERR_FAIL_COND_MSG(!icon_map[p_theme_type].has(p_old_name), "Cannot rename the icon '" + String(p_old_name) + "' because it does not exist."); - if (icon_map[p_node_type][p_name].is_valid()) { - icon_map[p_node_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); + icon_map[p_theme_type][p_name] = icon_map[p_theme_type][p_old_name]; + icon_map[p_theme_type].erase(p_old_name); + + notify_property_list_changed(); + emit_changed(); +} + +void Theme::clear_icon(const StringName &p_name, const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!icon_map.has(p_theme_type), "Cannot clear the icon '" + String(p_name) + "' because the node type '" + String(p_theme_type) + "' does not exist."); + ERR_FAIL_COND_MSG(!icon_map[p_theme_type].has(p_name), "Cannot clear the icon '" + String(p_name) + "' because it does not exist."); + + if (icon_map[p_theme_type][p_name].is_valid()) { + icon_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); } - icon_map[p_node_type].erase(p_name); + icon_map[p_theme_type].erase(p_name); notify_property_list_changed(); emit_changed(); } -void Theme::get_icon_list(StringName p_node_type, List<StringName> *p_list) const { +void Theme::get_icon_list(StringName p_theme_type, List<StringName> *p_list) const { ERR_FAIL_NULL(p_list); - if (!icon_map.has(p_node_type)) { + if (!icon_map.has(p_theme_type)) { return; } const StringName *key = nullptr; - while ((key = icon_map[p_node_type].next(key))) { + while ((key = icon_map[p_theme_type].next(key))) { p_list->push_back(*key); } } +void Theme::add_icon_type(const StringName &p_theme_type) { + icon_map[p_theme_type] = HashMap<StringName, Ref<Texture2D>>(); +} + void Theme::get_icon_type_list(List<StringName> *p_list) const { ERR_FAIL_NULL(p_list); @@ -490,19 +565,17 @@ void Theme::get_icon_type_list(List<StringName> *p_list) const { } } -void Theme::set_stylebox(const StringName &p_name, const StringName &p_node_type, const Ref<StyleBox> &p_style) { - //ERR_FAIL_COND(p_style.is_null()); - - bool new_value = !style_map.has(p_node_type) || !style_map[p_node_type].has(p_name); +void Theme::set_stylebox(const StringName &p_name, const StringName &p_theme_type, const Ref<StyleBox> &p_style) { + bool new_value = !style_map.has(p_theme_type) || !style_map[p_theme_type].has(p_name); - if (style_map[p_node_type].has(p_name) && style_map[p_node_type][p_name].is_valid()) { - style_map[p_node_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); + if (style_map[p_theme_type].has(p_name) && style_map[p_theme_type][p_name].is_valid()) { + style_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); } - style_map[p_node_type][p_name] = p_style; + style_map[p_theme_type][p_name] = p_style; if (p_style.is_valid()) { - style_map[p_node_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED); + style_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED); } if (new_value) { @@ -511,46 +584,66 @@ void Theme::set_stylebox(const StringName &p_name, const StringName &p_node_type emit_changed(); } -Ref<StyleBox> Theme::get_stylebox(const StringName &p_name, const StringName &p_node_type) const { - if (style_map.has(p_node_type) && style_map[p_node_type].has(p_name) && style_map[p_node_type][p_name].is_valid()) { - return style_map[p_node_type][p_name]; +Ref<StyleBox> Theme::get_stylebox(const StringName &p_name, const StringName &p_theme_type) const { + if (style_map.has(p_theme_type) && style_map[p_theme_type].has(p_name) && style_map[p_theme_type][p_name].is_valid()) { + return style_map[p_theme_type][p_name]; } else { return default_style; } } -bool Theme::has_stylebox(const StringName &p_name, const StringName &p_node_type) const { - return (style_map.has(p_node_type) && style_map[p_node_type].has(p_name) && style_map[p_node_type][p_name].is_valid()); +bool Theme::has_stylebox(const StringName &p_name, const StringName &p_theme_type) const { + return (style_map.has(p_theme_type) && style_map[p_theme_type].has(p_name) && style_map[p_theme_type][p_name].is_valid()); } -void Theme::clear_stylebox(const StringName &p_name, const StringName &p_node_type) { - ERR_FAIL_COND(!style_map.has(p_node_type)); - ERR_FAIL_COND(!style_map[p_node_type].has(p_name)); +bool Theme::has_stylebox_nocheck(const StringName &p_name, const StringName &p_theme_type) const { + return (style_map.has(p_theme_type) && style_map[p_theme_type].has(p_name)); +} + +void Theme::rename_stylebox(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!style_map.has(p_theme_type), "Cannot rename the stylebox '" + String(p_old_name) + "' because the node type '" + String(p_theme_type) + "' does not exist."); + ERR_FAIL_COND_MSG(style_map[p_theme_type].has(p_name), "Cannot rename the stylebox '" + String(p_old_name) + "' because the new name '" + String(p_name) + "' already exists."); + ERR_FAIL_COND_MSG(!style_map[p_theme_type].has(p_old_name), "Cannot rename the stylebox '" + String(p_old_name) + "' because it does not exist."); + + style_map[p_theme_type][p_name] = style_map[p_theme_type][p_old_name]; + style_map[p_theme_type].erase(p_old_name); + + notify_property_list_changed(); + emit_changed(); +} - if (style_map[p_node_type][p_name].is_valid()) { - style_map[p_node_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); +void Theme::clear_stylebox(const StringName &p_name, const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!style_map.has(p_theme_type), "Cannot clear the stylebox '" + String(p_name) + "' because the node type '" + String(p_theme_type) + "' does not exist."); + ERR_FAIL_COND_MSG(!style_map[p_theme_type].has(p_name), "Cannot clear the stylebox '" + String(p_name) + "' because it does not exist."); + + if (style_map[p_theme_type][p_name].is_valid()) { + style_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); } - style_map[p_node_type].erase(p_name); + style_map[p_theme_type].erase(p_name); notify_property_list_changed(); emit_changed(); } -void Theme::get_stylebox_list(StringName p_node_type, List<StringName> *p_list) const { +void Theme::get_stylebox_list(StringName p_theme_type, List<StringName> *p_list) const { ERR_FAIL_NULL(p_list); - if (!style_map.has(p_node_type)) { + if (!style_map.has(p_theme_type)) { return; } const StringName *key = nullptr; - while ((key = style_map[p_node_type].next(key))) { + while ((key = style_map[p_theme_type].next(key))) { p_list->push_back(*key); } } +void Theme::add_stylebox_type(const StringName &p_theme_type) { + style_map[p_theme_type] = HashMap<StringName, Ref<StyleBox>>(); +} + void Theme::get_stylebox_type_list(List<StringName> *p_list) const { ERR_FAIL_NULL(p_list); @@ -560,19 +653,17 @@ void Theme::get_stylebox_type_list(List<StringName> *p_list) const { } } -void Theme::set_font(const StringName &p_name, const StringName &p_node_type, const Ref<Font> &p_font) { - //ERR_FAIL_COND(p_font.is_null()); - - bool new_value = !font_map.has(p_node_type) || !font_map[p_node_type].has(p_name); +void Theme::set_font(const StringName &p_name, const StringName &p_theme_type, const Ref<Font> &p_font) { + bool new_value = !font_map.has(p_theme_type) || !font_map[p_theme_type].has(p_name); - if (font_map[p_node_type][p_name].is_valid()) { - font_map[p_node_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); + if (font_map[p_theme_type][p_name].is_valid()) { + font_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); } - font_map[p_node_type][p_name] = p_font; + font_map[p_theme_type][p_name] = p_font; if (p_font.is_valid()) { - font_map[p_node_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED); + font_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED); } if (new_value) { @@ -581,9 +672,9 @@ void Theme::set_font(const StringName &p_name, const StringName &p_node_type, co } } -Ref<Font> Theme::get_font(const StringName &p_name, const StringName &p_node_type) const { - if (font_map.has(p_node_type) && font_map[p_node_type].has(p_name) && font_map[p_node_type][p_name].is_valid()) { - return font_map[p_node_type][p_name]; +Ref<Font> Theme::get_font(const StringName &p_name, const StringName &p_theme_type) const { + if (font_map.has(p_theme_type) && font_map[p_theme_type].has(p_name) && font_map[p_theme_type][p_name].is_valid()) { + return font_map[p_theme_type][p_name]; } else if (default_theme_font.is_valid()) { return default_theme_font; } else { @@ -591,37 +682,57 @@ Ref<Font> Theme::get_font(const StringName &p_name, const StringName &p_node_typ } } -bool Theme::has_font(const StringName &p_name, const StringName &p_node_type) const { - return ((font_map.has(p_node_type) && font_map[p_node_type].has(p_name) && font_map[p_node_type][p_name].is_valid()) || default_theme_font.is_valid()); +bool Theme::has_font(const StringName &p_name, const StringName &p_theme_type) const { + return ((font_map.has(p_theme_type) && font_map[p_theme_type].has(p_name) && font_map[p_theme_type][p_name].is_valid()) || default_theme_font.is_valid()); } -void Theme::clear_font(const StringName &p_name, const StringName &p_node_type) { - ERR_FAIL_COND(!font_map.has(p_node_type)); - ERR_FAIL_COND(!font_map[p_node_type].has(p_name)); +bool Theme::has_font_nocheck(const StringName &p_name, const StringName &p_theme_type) const { + return (font_map.has(p_theme_type) && font_map[p_theme_type].has(p_name)); +} + +void Theme::rename_font(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!font_map.has(p_theme_type), "Cannot rename the font '" + String(p_old_name) + "' because the node type '" + String(p_theme_type) + "' does not exist."); + ERR_FAIL_COND_MSG(font_map[p_theme_type].has(p_name), "Cannot rename the font '" + String(p_old_name) + "' because the new name '" + String(p_name) + "' already exists."); + ERR_FAIL_COND_MSG(!font_map[p_theme_type].has(p_old_name), "Cannot rename the font '" + String(p_old_name) + "' because it does not exist."); + + font_map[p_theme_type][p_name] = font_map[p_theme_type][p_old_name]; + font_map[p_theme_type].erase(p_old_name); - if (font_map[p_node_type][p_name].is_valid()) { - font_map[p_node_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); + notify_property_list_changed(); + emit_changed(); +} + +void Theme::clear_font(const StringName &p_name, const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!font_map.has(p_theme_type), "Cannot clear the font '" + String(p_name) + "' because the node type '" + String(p_theme_type) + "' does not exist."); + ERR_FAIL_COND_MSG(!font_map[p_theme_type].has(p_name), "Cannot clear the font '" + String(p_name) + "' because it does not exist."); + + if (font_map[p_theme_type][p_name].is_valid()) { + font_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); } - font_map[p_node_type].erase(p_name); + font_map[p_theme_type].erase(p_name); notify_property_list_changed(); emit_changed(); } -void Theme::get_font_list(StringName p_node_type, List<StringName> *p_list) const { +void Theme::get_font_list(StringName p_theme_type, List<StringName> *p_list) const { ERR_FAIL_NULL(p_list); - if (!font_map.has(p_node_type)) { + if (!font_map.has(p_theme_type)) { return; } const StringName *key = nullptr; - while ((key = font_map[p_node_type].next(key))) { + while ((key = font_map[p_theme_type].next(key))) { p_list->push_back(*key); } } +void Theme::add_font_type(const StringName &p_theme_type) { + font_map[p_theme_type] = HashMap<StringName, Ref<Font>>(); +} + void Theme::get_font_type_list(List<StringName> *p_list) const { ERR_FAIL_NULL(p_list); @@ -631,10 +742,10 @@ void Theme::get_font_type_list(List<StringName> *p_list) const { } } -void Theme::set_font_size(const StringName &p_name, const StringName &p_node_type, int p_font_size) { - bool new_value = !font_size_map.has(p_node_type) || !font_size_map[p_node_type].has(p_name); +void Theme::set_font_size(const StringName &p_name, const StringName &p_theme_type, int p_font_size) { + bool new_value = !font_size_map.has(p_theme_type) || !font_size_map[p_theme_type].has(p_name); - font_size_map[p_node_type][p_name] = p_font_size; + font_size_map[p_theme_type][p_name] = p_font_size; if (new_value) { notify_property_list_changed(); @@ -642,9 +753,9 @@ void Theme::set_font_size(const StringName &p_name, const StringName &p_node_typ } } -int Theme::get_font_size(const StringName &p_name, const StringName &p_node_type) const { - if (font_size_map.has(p_node_type) && font_size_map[p_node_type].has(p_name) && (font_size_map[p_node_type][p_name] > 0)) { - return font_size_map[p_node_type][p_name]; +int Theme::get_font_size(const StringName &p_name, const StringName &p_theme_type) const { + if (font_size_map.has(p_theme_type) && font_size_map[p_theme_type].has(p_name) && (font_size_map[p_theme_type][p_name] > 0)) { + return font_size_map[p_theme_type][p_name]; } else if (default_theme_font_size > 0) { return default_theme_font_size; } else { @@ -652,37 +763,66 @@ int Theme::get_font_size(const StringName &p_name, const StringName &p_node_type } } -bool Theme::has_font_size(const StringName &p_name, const StringName &p_node_type) const { - return ((font_size_map.has(p_node_type) && font_size_map[p_node_type].has(p_name) && (font_size_map[p_node_type][p_name] > 0)) || (default_theme_font_size > 0)); +bool Theme::has_font_size(const StringName &p_name, const StringName &p_theme_type) const { + return ((font_size_map.has(p_theme_type) && font_size_map[p_theme_type].has(p_name) && (font_size_map[p_theme_type][p_name] > 0)) || (default_theme_font_size > 0)); } -void Theme::clear_font_size(const StringName &p_name, const StringName &p_node_type) { - ERR_FAIL_COND(!font_size_map.has(p_node_type)); - ERR_FAIL_COND(!font_size_map[p_node_type].has(p_name)); +bool Theme::has_font_size_nocheck(const StringName &p_name, const StringName &p_theme_type) const { + return (font_size_map.has(p_theme_type) && font_size_map[p_theme_type].has(p_name)); +} + +void Theme::rename_font_size(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!font_size_map.has(p_theme_type), "Cannot rename the font size '" + String(p_old_name) + "' because the node type '" + String(p_theme_type) + "' does not exist."); + ERR_FAIL_COND_MSG(font_size_map[p_theme_type].has(p_name), "Cannot rename the font size '" + String(p_old_name) + "' because the new name '" + String(p_name) + "' already exists."); + ERR_FAIL_COND_MSG(!font_size_map[p_theme_type].has(p_old_name), "Cannot rename the font size '" + String(p_old_name) + "' because it does not exist."); + + font_size_map[p_theme_type][p_name] = font_size_map[p_theme_type][p_old_name]; + font_size_map[p_theme_type].erase(p_old_name); - font_size_map[p_node_type].erase(p_name); notify_property_list_changed(); emit_changed(); } -void Theme::get_font_size_list(StringName p_node_type, List<StringName> *p_list) const { +void Theme::clear_font_size(const StringName &p_name, const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!font_size_map.has(p_theme_type), "Cannot clear the font size '" + String(p_name) + "' because the node type '" + String(p_theme_type) + "' does not exist."); + ERR_FAIL_COND_MSG(!font_size_map[p_theme_type].has(p_name), "Cannot clear the font size '" + String(p_name) + "' because it does not exist."); + + font_size_map[p_theme_type].erase(p_name); + notify_property_list_changed(); + emit_changed(); +} + +void Theme::get_font_size_list(StringName p_theme_type, List<StringName> *p_list) const { ERR_FAIL_NULL(p_list); - if (!font_size_map.has(p_node_type)) { + if (!font_size_map.has(p_theme_type)) { return; } const StringName *key = nullptr; - while ((key = font_size_map[p_node_type].next(key))) { + while ((key = font_size_map[p_theme_type].next(key))) { + p_list->push_back(*key); + } +} + +void Theme::add_font_size_type(const StringName &p_theme_type) { + font_size_map[p_theme_type] = HashMap<StringName, int>(); +} + +void Theme::get_font_size_type_list(List<StringName> *p_list) const { + ERR_FAIL_NULL(p_list); + + const StringName *key = nullptr; + while ((key = font_size_map.next(key))) { p_list->push_back(*key); } } -void Theme::set_color(const StringName &p_name, const StringName &p_node_type, const Color &p_color) { - bool new_value = !color_map.has(p_node_type) || !color_map[p_node_type].has(p_name); +void Theme::set_color(const StringName &p_name, const StringName &p_theme_type, const Color &p_color) { + bool new_value = !color_map.has(p_theme_type) || !color_map[p_theme_type].has(p_name); - color_map[p_node_type][p_name] = p_color; + color_map[p_theme_type][p_name] = p_color; if (new_value) { notify_property_list_changed(); @@ -690,41 +830,61 @@ void Theme::set_color(const StringName &p_name, const StringName &p_node_type, c } } -Color Theme::get_color(const StringName &p_name, const StringName &p_node_type) const { - if (color_map.has(p_node_type) && color_map[p_node_type].has(p_name)) { - return color_map[p_node_type][p_name]; +Color Theme::get_color(const StringName &p_name, const StringName &p_theme_type) const { + if (color_map.has(p_theme_type) && color_map[p_theme_type].has(p_name)) { + return color_map[p_theme_type][p_name]; } else { return Color(); } } -bool Theme::has_color(const StringName &p_name, const StringName &p_node_type) const { - return (color_map.has(p_node_type) && color_map[p_node_type].has(p_name)); +bool Theme::has_color(const StringName &p_name, const StringName &p_theme_type) const { + return (color_map.has(p_theme_type) && color_map[p_theme_type].has(p_name)); } -void Theme::clear_color(const StringName &p_name, const StringName &p_node_type) { - ERR_FAIL_COND(!color_map.has(p_node_type)); - ERR_FAIL_COND(!color_map[p_node_type].has(p_name)); +bool Theme::has_color_nocheck(const StringName &p_name, const StringName &p_theme_type) const { + return (color_map.has(p_theme_type) && color_map[p_theme_type].has(p_name)); +} + +void Theme::rename_color(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!color_map.has(p_theme_type), "Cannot rename the color '" + String(p_old_name) + "' because the node type '" + String(p_theme_type) + "' does not exist."); + ERR_FAIL_COND_MSG(color_map[p_theme_type].has(p_name), "Cannot rename the color '" + String(p_old_name) + "' because the new name '" + String(p_name) + "' already exists."); + ERR_FAIL_COND_MSG(!color_map[p_theme_type].has(p_old_name), "Cannot rename the color '" + String(p_old_name) + "' because it does not exist."); + + color_map[p_theme_type][p_name] = color_map[p_theme_type][p_old_name]; + color_map[p_theme_type].erase(p_old_name); - color_map[p_node_type].erase(p_name); notify_property_list_changed(); emit_changed(); } -void Theme::get_color_list(StringName p_node_type, List<StringName> *p_list) const { +void Theme::clear_color(const StringName &p_name, const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!color_map.has(p_theme_type), "Cannot clear the color '" + String(p_name) + "' because the node type '" + String(p_theme_type) + "' does not exist."); + ERR_FAIL_COND_MSG(!color_map[p_theme_type].has(p_name), "Cannot clear the color '" + String(p_name) + "' because it does not exist."); + + color_map[p_theme_type].erase(p_name); + notify_property_list_changed(); + emit_changed(); +} + +void Theme::get_color_list(StringName p_theme_type, List<StringName> *p_list) const { ERR_FAIL_NULL(p_list); - if (!color_map.has(p_node_type)) { + if (!color_map.has(p_theme_type)) { return; } const StringName *key = nullptr; - while ((key = color_map[p_node_type].next(key))) { + while ((key = color_map[p_theme_type].next(key))) { p_list->push_back(*key); } } +void Theme::add_color_type(const StringName &p_theme_type) { + color_map[p_theme_type] = HashMap<StringName, Color>(); +} + void Theme::get_color_type_list(List<StringName> *p_list) const { ERR_FAIL_NULL(p_list); @@ -734,9 +894,9 @@ void Theme::get_color_type_list(List<StringName> *p_list) const { } } -void Theme::set_constant(const StringName &p_name, const StringName &p_node_type, int p_constant) { - bool new_value = !constant_map.has(p_node_type) || !constant_map[p_node_type].has(p_name); - constant_map[p_node_type][p_name] = p_constant; +void Theme::set_constant(const StringName &p_name, const StringName &p_theme_type, int p_constant) { + bool new_value = !constant_map.has(p_theme_type) || !constant_map[p_theme_type].has(p_name); + constant_map[p_theme_type][p_name] = p_constant; if (new_value) { notify_property_list_changed(); @@ -744,41 +904,61 @@ void Theme::set_constant(const StringName &p_name, const StringName &p_node_type } } -int Theme::get_constant(const StringName &p_name, const StringName &p_node_type) const { - if (constant_map.has(p_node_type) && constant_map[p_node_type].has(p_name)) { - return constant_map[p_node_type][p_name]; +int Theme::get_constant(const StringName &p_name, const StringName &p_theme_type) const { + if (constant_map.has(p_theme_type) && constant_map[p_theme_type].has(p_name)) { + return constant_map[p_theme_type][p_name]; } else { return 0; } } -bool Theme::has_constant(const StringName &p_name, const StringName &p_node_type) const { - return (constant_map.has(p_node_type) && constant_map[p_node_type].has(p_name)); +bool Theme::has_constant(const StringName &p_name, const StringName &p_theme_type) const { + return (constant_map.has(p_theme_type) && constant_map[p_theme_type].has(p_name)); } -void Theme::clear_constant(const StringName &p_name, const StringName &p_node_type) { - ERR_FAIL_COND(!constant_map.has(p_node_type)); - ERR_FAIL_COND(!constant_map[p_node_type].has(p_name)); +bool Theme::has_constant_nocheck(const StringName &p_name, const StringName &p_theme_type) const { + return (constant_map.has(p_theme_type) && constant_map[p_theme_type].has(p_name)); +} + +void Theme::rename_constant(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!constant_map.has(p_theme_type), "Cannot rename the constant '" + String(p_old_name) + "' because the node type '" + String(p_theme_type) + "' does not exist."); + ERR_FAIL_COND_MSG(constant_map[p_theme_type].has(p_name), "Cannot rename the constant '" + String(p_old_name) + "' because the new name '" + String(p_name) + "' already exists."); + ERR_FAIL_COND_MSG(!constant_map[p_theme_type].has(p_old_name), "Cannot rename the constant '" + String(p_old_name) + "' because it does not exist."); + + constant_map[p_theme_type][p_name] = constant_map[p_theme_type][p_old_name]; + constant_map[p_theme_type].erase(p_old_name); - constant_map[p_node_type].erase(p_name); notify_property_list_changed(); emit_changed(); } -void Theme::get_constant_list(StringName p_node_type, List<StringName> *p_list) const { +void Theme::clear_constant(const StringName &p_name, const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!constant_map.has(p_theme_type), "Cannot clear the constant '" + String(p_name) + "' because the node type '" + String(p_theme_type) + "' does not exist."); + ERR_FAIL_COND_MSG(!constant_map[p_theme_type].has(p_name), "Cannot clear the constant '" + String(p_name) + "' because it does not exist."); + + constant_map[p_theme_type].erase(p_name); + notify_property_list_changed(); + emit_changed(); +} + +void Theme::get_constant_list(StringName p_theme_type, List<StringName> *p_list) const { ERR_FAIL_NULL(p_list); - if (!constant_map.has(p_node_type)) { + if (!constant_map.has(p_theme_type)) { return; } const StringName *key = nullptr; - while ((key = constant_map[p_node_type].next(key))) { + while ((key = constant_map[p_theme_type].next(key))) { p_list->push_back(*key); } } +void Theme::add_constant_type(const StringName &p_theme_type) { + constant_map[p_theme_type] = HashMap<StringName, int>(); +} + void Theme::get_constant_type_list(List<StringName> *p_list) const { ERR_FAIL_NULL(p_list); @@ -788,6 +968,237 @@ void Theme::get_constant_type_list(List<StringName> *p_list) const { } } +void Theme::set_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type, const Variant &p_value) { + switch (p_data_type) { + case DATA_TYPE_COLOR: { + ERR_FAIL_COND_MSG(p_value.get_type() != Variant::COLOR, "Theme item's data type (Color) does not match Variant's type (" + Variant::get_type_name(p_value.get_type()) + ")."); + + Color color_value = p_value; + set_color(p_name, p_theme_type, color_value); + } break; + case DATA_TYPE_CONSTANT: { + ERR_FAIL_COND_MSG(p_value.get_type() != Variant::INT, "Theme item's data type (int) does not match Variant's type (" + Variant::get_type_name(p_value.get_type()) + ")."); + + int constant_value = p_value; + set_constant(p_name, p_theme_type, constant_value); + } break; + case DATA_TYPE_FONT: { + ERR_FAIL_COND_MSG(p_value.get_type() != Variant::OBJECT, "Theme item's data type (Object) does not match Variant's type (" + Variant::get_type_name(p_value.get_type()) + ")."); + + Ref<Font> font_value = Object::cast_to<Font>(p_value.get_validated_object()); + set_font(p_name, p_theme_type, font_value); + } break; + case DATA_TYPE_FONT_SIZE: { + ERR_FAIL_COND_MSG(p_value.get_type() != Variant::INT, "Theme item's data type (int) does not match Variant's type (" + Variant::get_type_name(p_value.get_type()) + ")."); + + int font_size_value = p_value; + set_font_size(p_name, p_theme_type, font_size_value); + } break; + case DATA_TYPE_ICON: { + ERR_FAIL_COND_MSG(p_value.get_type() != Variant::OBJECT, "Theme item's data type (Object) does not match Variant's type (" + Variant::get_type_name(p_value.get_type()) + ")."); + + Ref<Texture2D> icon_value = Object::cast_to<Texture2D>(p_value.get_validated_object()); + set_icon(p_name, p_theme_type, icon_value); + } break; + case DATA_TYPE_STYLEBOX: { + ERR_FAIL_COND_MSG(p_value.get_type() != Variant::OBJECT, "Theme item's data type (Object) does not match Variant's type (" + Variant::get_type_name(p_value.get_type()) + ")."); + + Ref<StyleBox> stylebox_value = Object::cast_to<StyleBox>(p_value.get_validated_object()); + set_stylebox(p_name, p_theme_type, stylebox_value); + } break; + case DATA_TYPE_MAX: + break; // Can't happen, but silences warning. + } +} + +Variant Theme::get_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type) const { + switch (p_data_type) { + case DATA_TYPE_COLOR: + return get_color(p_name, p_theme_type); + case DATA_TYPE_CONSTANT: + return get_constant(p_name, p_theme_type); + case DATA_TYPE_FONT: + return get_font(p_name, p_theme_type); + case DATA_TYPE_FONT_SIZE: + return get_font_size(p_name, p_theme_type); + case DATA_TYPE_ICON: + return get_icon(p_name, p_theme_type); + case DATA_TYPE_STYLEBOX: + return get_stylebox(p_name, p_theme_type); + case DATA_TYPE_MAX: + break; // Can't happen, but silences warning. + } + + return Variant(); +} + +bool Theme::has_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type) const { + switch (p_data_type) { + case DATA_TYPE_COLOR: + return has_color(p_name, p_theme_type); + case DATA_TYPE_CONSTANT: + return has_constant(p_name, p_theme_type); + case DATA_TYPE_FONT: + return has_font(p_name, p_theme_type); + case DATA_TYPE_FONT_SIZE: + return has_font_size(p_name, p_theme_type); + case DATA_TYPE_ICON: + return has_icon(p_name, p_theme_type); + case DATA_TYPE_STYLEBOX: + return has_stylebox(p_name, p_theme_type); + case DATA_TYPE_MAX: + break; // Can't happen, but silences warning. + } + + return false; +} + +bool Theme::has_theme_item_nocheck(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type) const { + switch (p_data_type) { + case DATA_TYPE_COLOR: + return has_color_nocheck(p_name, p_theme_type); + case DATA_TYPE_CONSTANT: + return has_constant_nocheck(p_name, p_theme_type); + case DATA_TYPE_FONT: + return has_font_nocheck(p_name, p_theme_type); + case DATA_TYPE_FONT_SIZE: + return has_font_size_nocheck(p_name, p_theme_type); + case DATA_TYPE_ICON: + return has_icon_nocheck(p_name, p_theme_type); + case DATA_TYPE_STYLEBOX: + return has_stylebox_nocheck(p_name, p_theme_type); + case DATA_TYPE_MAX: + break; // Can't happen, but silences warning. + } + + return false; +} + +void Theme::rename_theme_item(DataType p_data_type, const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) { + switch (p_data_type) { + case DATA_TYPE_COLOR: + rename_color(p_old_name, p_name, p_theme_type); + break; + case DATA_TYPE_CONSTANT: + rename_constant(p_old_name, p_name, p_theme_type); + break; + case DATA_TYPE_FONT: + rename_font(p_old_name, p_name, p_theme_type); + break; + case DATA_TYPE_FONT_SIZE: + rename_font_size(p_old_name, p_name, p_theme_type); + break; + case DATA_TYPE_ICON: + rename_icon(p_old_name, p_name, p_theme_type); + break; + case DATA_TYPE_STYLEBOX: + rename_stylebox(p_old_name, p_name, p_theme_type); + break; + case DATA_TYPE_MAX: + break; // Can't happen, but silences warning. + } +} + +void Theme::clear_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type) { + switch (p_data_type) { + case DATA_TYPE_COLOR: + clear_color(p_name, p_theme_type); + break; + case DATA_TYPE_CONSTANT: + clear_constant(p_name, p_theme_type); + break; + case DATA_TYPE_FONT: + clear_font(p_name, p_theme_type); + break; + case DATA_TYPE_FONT_SIZE: + clear_font_size(p_name, p_theme_type); + break; + case DATA_TYPE_ICON: + clear_icon(p_name, p_theme_type); + break; + case DATA_TYPE_STYLEBOX: + clear_stylebox(p_name, p_theme_type); + break; + case DATA_TYPE_MAX: + break; // Can't happen, but silences warning. + } +} + +void Theme::get_theme_item_list(DataType p_data_type, StringName p_theme_type, List<StringName> *p_list) const { + switch (p_data_type) { + case DATA_TYPE_COLOR: + get_color_list(p_theme_type, p_list); + break; + case DATA_TYPE_CONSTANT: + get_constant_list(p_theme_type, p_list); + break; + case DATA_TYPE_FONT: + get_font_list(p_theme_type, p_list); + break; + case DATA_TYPE_FONT_SIZE: + get_font_size_list(p_theme_type, p_list); + break; + case DATA_TYPE_ICON: + get_icon_list(p_theme_type, p_list); + break; + case DATA_TYPE_STYLEBOX: + get_stylebox_list(p_theme_type, p_list); + break; + case DATA_TYPE_MAX: + break; // Can't happen, but silences warning. + } +} + +void Theme::add_theme_item_type(DataType p_data_type, const StringName &p_theme_type) { + switch (p_data_type) { + case DATA_TYPE_COLOR: + add_color_type(p_theme_type); + break; + case DATA_TYPE_CONSTANT: + add_constant_type(p_theme_type); + break; + case DATA_TYPE_FONT: + add_font_type(p_theme_type); + break; + case DATA_TYPE_FONT_SIZE: + add_font_size_type(p_theme_type); + break; + case DATA_TYPE_ICON: + add_icon_type(p_theme_type); + break; + case DATA_TYPE_STYLEBOX: + add_stylebox_type(p_theme_type); + break; + case DATA_TYPE_MAX: + break; // Can't happen, but silences warning. + } +} + +void Theme::get_theme_item_type_list(DataType p_data_type, List<StringName> *p_list) const { + switch (p_data_type) { + case DATA_TYPE_COLOR: + get_color_type_list(p_list); + break; + case DATA_TYPE_CONSTANT: + get_constant_type_list(p_list); + break; + case DATA_TYPE_FONT: + get_font_type_list(p_list); + break; + case DATA_TYPE_FONT_SIZE: + get_font_size_type_list(p_list); + break; + case DATA_TYPE_ICON: + get_icon_type_list(p_list); + break; + case DATA_TYPE_STYLEBOX: + get_stylebox_type_list(p_list); + break; + case DATA_TYPE_MAX: + break; // Can't happen, but silences warning. + } +} + void Theme::clear() { //these need disconnecting { @@ -847,11 +1258,10 @@ void Theme::copy_default_theme() { void Theme::copy_theme(const Ref<Theme> &p_other) { if (p_other.is_null()) { clear(); - return; } - //these need reconnecting, so add normally + // These items need reconnecting, so add them normally. { const StringName *K = nullptr; while ((K = p_other->icon_map.next(K))) { @@ -882,8 +1292,8 @@ void Theme::copy_theme(const Ref<Theme> &p_other) { } } - //these are ok to just copy - + // These items can be simply copied. + font_size_map = p_other->font_size_map; color_map = p_other->color_map; constant_map = p_other->constant_map; @@ -930,49 +1340,66 @@ void Theme::get_type_list(List<StringName> *p_list) const { } } +void Theme::get_type_dependencies(const StringName &p_theme_type, List<StringName> *p_list) { + ERR_FAIL_NULL(p_list); + + StringName class_name = p_theme_type; + while (class_name != StringName()) { + p_list->push_back(class_name); + class_name = ClassDB::get_parent_class_nocheck(class_name); + } +} + void Theme::reset_state() { clear(); } void Theme::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_icon", "name", "node_type", "texture"), &Theme::set_icon); - ClassDB::bind_method(D_METHOD("get_icon", "name", "node_type"), &Theme::get_icon); - ClassDB::bind_method(D_METHOD("has_icon", "name", "node_type"), &Theme::has_icon); - ClassDB::bind_method(D_METHOD("clear_icon", "name", "node_type"), &Theme::clear_icon); - ClassDB::bind_method(D_METHOD("get_icon_list", "node_type"), &Theme::_get_icon_list); + ClassDB::bind_method(D_METHOD("set_icon", "name", "theme_type", "texture"), &Theme::set_icon); + ClassDB::bind_method(D_METHOD("get_icon", "name", "theme_type"), &Theme::get_icon); + ClassDB::bind_method(D_METHOD("has_icon", "name", "theme_type"), &Theme::has_icon); + ClassDB::bind_method(D_METHOD("rename_icon", "old_name", "name", "theme_type"), &Theme::rename_icon); + ClassDB::bind_method(D_METHOD("clear_icon", "name", "theme_type"), &Theme::clear_icon); + ClassDB::bind_method(D_METHOD("get_icon_list", "theme_type"), &Theme::_get_icon_list); ClassDB::bind_method(D_METHOD("get_icon_type_list"), &Theme::_get_icon_type_list); - ClassDB::bind_method(D_METHOD("set_stylebox", "name", "node_type", "texture"), &Theme::set_stylebox); - ClassDB::bind_method(D_METHOD("get_stylebox", "name", "node_type"), &Theme::get_stylebox); - ClassDB::bind_method(D_METHOD("has_stylebox", "name", "node_type"), &Theme::has_stylebox); - ClassDB::bind_method(D_METHOD("clear_stylebox", "name", "node_type"), &Theme::clear_stylebox); - ClassDB::bind_method(D_METHOD("get_stylebox_list", "node_type"), &Theme::_get_stylebox_list); + ClassDB::bind_method(D_METHOD("set_stylebox", "name", "theme_type", "texture"), &Theme::set_stylebox); + ClassDB::bind_method(D_METHOD("get_stylebox", "name", "theme_type"), &Theme::get_stylebox); + ClassDB::bind_method(D_METHOD("has_stylebox", "name", "theme_type"), &Theme::has_stylebox); + ClassDB::bind_method(D_METHOD("rename_stylebox", "old_name", "name", "theme_type"), &Theme::rename_stylebox); + ClassDB::bind_method(D_METHOD("clear_stylebox", "name", "theme_type"), &Theme::clear_stylebox); + ClassDB::bind_method(D_METHOD("get_stylebox_list", "theme_type"), &Theme::_get_stylebox_list); ClassDB::bind_method(D_METHOD("get_stylebox_type_list"), &Theme::_get_stylebox_type_list); - ClassDB::bind_method(D_METHOD("set_font", "name", "node_type", "font"), &Theme::set_font); - ClassDB::bind_method(D_METHOD("get_font", "name", "node_type"), &Theme::get_font); - ClassDB::bind_method(D_METHOD("has_font", "name", "node_type"), &Theme::has_font); - ClassDB::bind_method(D_METHOD("clear_font", "name", "node_type"), &Theme::clear_font); - ClassDB::bind_method(D_METHOD("get_font_list", "node_type"), &Theme::_get_font_list); + ClassDB::bind_method(D_METHOD("set_font", "name", "theme_type", "font"), &Theme::set_font); + ClassDB::bind_method(D_METHOD("get_font", "name", "theme_type"), &Theme::get_font); + ClassDB::bind_method(D_METHOD("has_font", "name", "theme_type"), &Theme::has_font); + ClassDB::bind_method(D_METHOD("rename_font", "old_name", "name", "theme_type"), &Theme::rename_font); + ClassDB::bind_method(D_METHOD("clear_font", "name", "theme_type"), &Theme::clear_font); + ClassDB::bind_method(D_METHOD("get_font_list", "theme_type"), &Theme::_get_font_list); ClassDB::bind_method(D_METHOD("get_font_type_list"), &Theme::_get_font_type_list); - ClassDB::bind_method(D_METHOD("set_font_size", "name", "node_type", "font_size"), &Theme::set_font_size); - ClassDB::bind_method(D_METHOD("get_font_size", "name", "node_type"), &Theme::get_font_size); - ClassDB::bind_method(D_METHOD("has_font_size", "name", "node_type"), &Theme::has_font_size); - ClassDB::bind_method(D_METHOD("clear_font_size", "name", "node_type"), &Theme::clear_font_size); - ClassDB::bind_method(D_METHOD("get_font_size_list", "node_type"), &Theme::_get_font_size_list); - - ClassDB::bind_method(D_METHOD("set_color", "name", "node_type", "color"), &Theme::set_color); - ClassDB::bind_method(D_METHOD("get_color", "name", "node_type"), &Theme::get_color); - ClassDB::bind_method(D_METHOD("has_color", "name", "node_type"), &Theme::has_color); - ClassDB::bind_method(D_METHOD("clear_color", "name", "node_type"), &Theme::clear_color); - ClassDB::bind_method(D_METHOD("get_color_list", "node_type"), &Theme::_get_color_list); + ClassDB::bind_method(D_METHOD("set_font_size", "name", "theme_type", "font_size"), &Theme::set_font_size); + ClassDB::bind_method(D_METHOD("get_font_size", "name", "theme_type"), &Theme::get_font_size); + ClassDB::bind_method(D_METHOD("has_font_size", "name", "theme_type"), &Theme::has_font_size); + ClassDB::bind_method(D_METHOD("rename_font_size", "old_name", "name", "theme_type"), &Theme::rename_font_size); + ClassDB::bind_method(D_METHOD("clear_font_size", "name", "theme_type"), &Theme::clear_font_size); + ClassDB::bind_method(D_METHOD("get_font_size_list", "theme_type"), &Theme::_get_font_size_list); + ClassDB::bind_method(D_METHOD("get_font_size_type_list"), &Theme::_get_font_size_type_list); + + ClassDB::bind_method(D_METHOD("set_color", "name", "theme_type", "color"), &Theme::set_color); + ClassDB::bind_method(D_METHOD("get_color", "name", "theme_type"), &Theme::get_color); + ClassDB::bind_method(D_METHOD("has_color", "name", "theme_type"), &Theme::has_color); + ClassDB::bind_method(D_METHOD("rename_color", "old_name", "name", "theme_type"), &Theme::rename_color); + ClassDB::bind_method(D_METHOD("clear_color", "name", "theme_type"), &Theme::clear_color); + ClassDB::bind_method(D_METHOD("get_color_list", "theme_type"), &Theme::_get_color_list); ClassDB::bind_method(D_METHOD("get_color_type_list"), &Theme::_get_color_type_list); - ClassDB::bind_method(D_METHOD("set_constant", "name", "node_type", "constant"), &Theme::set_constant); - ClassDB::bind_method(D_METHOD("get_constant", "name", "node_type"), &Theme::get_constant); - ClassDB::bind_method(D_METHOD("has_constant", "name", "node_type"), &Theme::has_constant); - ClassDB::bind_method(D_METHOD("clear_constant", "name", "node_type"), &Theme::clear_constant); - ClassDB::bind_method(D_METHOD("get_constant_list", "node_type"), &Theme::_get_constant_list); + ClassDB::bind_method(D_METHOD("set_constant", "name", "theme_type", "constant"), &Theme::set_constant); + ClassDB::bind_method(D_METHOD("get_constant", "name", "theme_type"), &Theme::get_constant); + ClassDB::bind_method(D_METHOD("has_constant", "name", "theme_type"), &Theme::has_constant); + ClassDB::bind_method(D_METHOD("rename_constant", "old_name", "name", "theme_type"), &Theme::rename_constant); + ClassDB::bind_method(D_METHOD("clear_constant", "name", "theme_type"), &Theme::clear_constant); + ClassDB::bind_method(D_METHOD("get_constant_list", "theme_type"), &Theme::_get_constant_list); ClassDB::bind_method(D_METHOD("get_constant_type_list"), &Theme::_get_constant_type_list); ClassDB::bind_method(D_METHOD("clear"), &Theme::clear); @@ -983,6 +1410,14 @@ void Theme::_bind_methods() { ClassDB::bind_method(D_METHOD("set_default_font_size", "font_size"), &Theme::set_default_theme_font_size); ClassDB::bind_method(D_METHOD("get_default_font_size"), &Theme::get_default_theme_font_size); + ClassDB::bind_method(D_METHOD("set_theme_item", "data_type", "name", "theme_type", "value"), &Theme::set_theme_item); + ClassDB::bind_method(D_METHOD("get_theme_item", "data_type", "name", "theme_type"), &Theme::get_theme_item); + ClassDB::bind_method(D_METHOD("has_theme_item", "data_type", "name", "theme_type"), &Theme::has_theme_item); + ClassDB::bind_method(D_METHOD("rename_theme_item", "data_type", "old_name", "name", "theme_type"), &Theme::rename_theme_item); + ClassDB::bind_method(D_METHOD("clear_theme_item", "data_type", "name", "theme_type"), &Theme::clear_theme_item); + ClassDB::bind_method(D_METHOD("get_theme_item_list", "data_type", "theme_type"), &Theme::_get_theme_item_list); + ClassDB::bind_method(D_METHOD("get_theme_item_type_list", "data_type"), &Theme::_get_theme_item_type_list); + ClassDB::bind_method(D_METHOD("get_type_list"), &Theme::_get_type_list); ClassDB::bind_method("copy_default_theme", &Theme::copy_default_theme); @@ -990,6 +1425,14 @@ void Theme::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "default_font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_default_font", "get_default_font"); ADD_PROPERTY(PropertyInfo(Variant::INT, "default_font_size"), "set_default_font_size", "get_default_font_size"); + + BIND_ENUM_CONSTANT(DATA_TYPE_COLOR); + BIND_ENUM_CONSTANT(DATA_TYPE_CONSTANT); + BIND_ENUM_CONSTANT(DATA_TYPE_FONT); + BIND_ENUM_CONSTANT(DATA_TYPE_FONT_SIZE); + BIND_ENUM_CONSTANT(DATA_TYPE_ICON); + BIND_ENUM_CONSTANT(DATA_TYPE_STYLEBOX); + BIND_ENUM_CONSTANT(DATA_TYPE_MAX); } Theme::Theme() { diff --git a/scene/resources/theme.h b/scene/resources/theme.h index 35481126ea..4de1f065e1 100644 --- a/scene/resources/theme.h +++ b/scene/resources/theme.h @@ -41,6 +41,18 @@ class Theme : public Resource { GDCLASS(Theme, Resource); RES_BASE_EXTENSION("theme"); +public: + enum DataType { + DATA_TYPE_COLOR, + DATA_TYPE_CONSTANT, + DATA_TYPE_FONT, + DATA_TYPE_FONT_SIZE, + DATA_TYPE_ICON, + DATA_TYPE_STYLEBOX, + DATA_TYPE_MAX + }; + +private: void _emit_theme_changed(); HashMap<StringName, HashMap<StringName, Ref<Texture2D>>> icon_map; @@ -50,17 +62,21 @@ class Theme : public Resource { HashMap<StringName, HashMap<StringName, Color>> color_map; HashMap<StringName, HashMap<StringName, int>> constant_map; - Vector<String> _get_icon_list(const String &p_node_type) const; + Vector<String> _get_icon_list(const String &p_theme_type) const; Vector<String> _get_icon_type_list() const; - Vector<String> _get_stylebox_list(const String &p_node_type) const; + Vector<String> _get_stylebox_list(const String &p_theme_type) const; Vector<String> _get_stylebox_type_list() const; - Vector<String> _get_font_list(const String &p_node_type) const; + Vector<String> _get_font_list(const String &p_theme_type) const; Vector<String> _get_font_type_list() const; - Vector<String> _get_font_size_list(const String &p_node_type) const; - Vector<String> _get_color_list(const String &p_node_type) const; + Vector<String> _get_font_size_list(const String &p_theme_type) const; + Vector<String> _get_font_size_type_list() const; + Vector<String> _get_color_list(const String &p_theme_type) const; Vector<String> _get_color_type_list() const; - Vector<String> _get_constant_list(const String &p_node_type) const; + Vector<String> _get_constant_list(const String &p_theme_type) const; Vector<String> _get_constant_type_list() const; + + Vector<String> _get_theme_item_list(DataType p_data_type, const String &p_theme_type) const; + Vector<String> _get_theme_item_type_list(DataType p_data_type) const; Vector<String> _get_type_list() const; protected: @@ -100,48 +116,78 @@ public: void set_default_theme_font_size(int p_font_size); int get_default_theme_font_size() const; - void set_icon(const StringName &p_name, const StringName &p_node_type, const Ref<Texture2D> &p_icon); - Ref<Texture2D> get_icon(const StringName &p_name, const StringName &p_node_type) const; - bool has_icon(const StringName &p_name, const StringName &p_node_type) const; - void clear_icon(const StringName &p_name, const StringName &p_node_type); - void get_icon_list(StringName p_node_type, List<StringName> *p_list) const; + void set_icon(const StringName &p_name, const StringName &p_theme_type, const Ref<Texture2D> &p_icon); + Ref<Texture2D> get_icon(const StringName &p_name, const StringName &p_theme_type) const; + bool has_icon(const StringName &p_name, const StringName &p_theme_type) const; + bool has_icon_nocheck(const StringName &p_name, const StringName &p_theme_type) const; + void rename_icon(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type); + void clear_icon(const StringName &p_name, const StringName &p_theme_type); + void get_icon_list(StringName p_theme_type, List<StringName> *p_list) const; + void add_icon_type(const StringName &p_theme_type); void get_icon_type_list(List<StringName> *p_list) const; - void set_stylebox(const StringName &p_name, const StringName &p_node_type, const Ref<StyleBox> &p_style); - Ref<StyleBox> get_stylebox(const StringName &p_name, const StringName &p_node_type) const; - bool has_stylebox(const StringName &p_name, const StringName &p_node_type) const; - void clear_stylebox(const StringName &p_name, const StringName &p_node_type); - void get_stylebox_list(StringName p_node_type, List<StringName> *p_list) const; + void set_stylebox(const StringName &p_name, const StringName &p_theme_type, const Ref<StyleBox> &p_style); + Ref<StyleBox> get_stylebox(const StringName &p_name, const StringName &p_theme_type) const; + bool has_stylebox(const StringName &p_name, const StringName &p_theme_type) const; + bool has_stylebox_nocheck(const StringName &p_name, const StringName &p_theme_type) const; + void rename_stylebox(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type); + void clear_stylebox(const StringName &p_name, const StringName &p_theme_type); + void get_stylebox_list(StringName p_theme_type, List<StringName> *p_list) const; + void add_stylebox_type(const StringName &p_theme_type); void get_stylebox_type_list(List<StringName> *p_list) const; - void set_font(const StringName &p_name, const StringName &p_node_type, const Ref<Font> &p_font); - Ref<Font> get_font(const StringName &p_name, const StringName &p_node_type) const; - bool has_font(const StringName &p_name, const StringName &p_node_type) const; - void clear_font(const StringName &p_name, const StringName &p_node_type); - void get_font_list(StringName p_node_type, List<StringName> *p_list) const; + void set_font(const StringName &p_name, const StringName &p_theme_type, const Ref<Font> &p_font); + Ref<Font> get_font(const StringName &p_name, const StringName &p_theme_type) const; + bool has_font(const StringName &p_name, const StringName &p_theme_type) const; + bool has_font_nocheck(const StringName &p_name, const StringName &p_theme_type) const; + void rename_font(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type); + void clear_font(const StringName &p_name, const StringName &p_theme_type); + void get_font_list(StringName p_theme_type, List<StringName> *p_list) const; + void add_font_type(const StringName &p_theme_type); void get_font_type_list(List<StringName> *p_list) const; - void set_font_size(const StringName &p_name, const StringName &p_node_type, int p_font_size); - int get_font_size(const StringName &p_name, const StringName &p_node_type) const; - bool has_font_size(const StringName &p_name, const StringName &p_node_type) const; - void clear_font_size(const StringName &p_name, const StringName &p_node_type); - void get_font_size_list(StringName p_node_type, List<StringName> *p_list) const; - - void set_color(const StringName &p_name, const StringName &p_node_type, const Color &p_color); - Color get_color(const StringName &p_name, const StringName &p_node_type) const; - bool has_color(const StringName &p_name, const StringName &p_node_type) const; - void clear_color(const StringName &p_name, const StringName &p_node_type); - void get_color_list(StringName p_node_type, List<StringName> *p_list) const; + void set_font_size(const StringName &p_name, const StringName &p_theme_type, int p_font_size); + int get_font_size(const StringName &p_name, const StringName &p_theme_type) const; + bool has_font_size(const StringName &p_name, const StringName &p_theme_type) const; + bool has_font_size_nocheck(const StringName &p_name, const StringName &p_theme_type) const; + void rename_font_size(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type); + void clear_font_size(const StringName &p_name, const StringName &p_theme_type); + void get_font_size_list(StringName p_theme_type, List<StringName> *p_list) const; + void add_font_size_type(const StringName &p_theme_type); + void get_font_size_type_list(List<StringName> *p_list) const; + + void set_color(const StringName &p_name, const StringName &p_theme_type, const Color &p_color); + Color get_color(const StringName &p_name, const StringName &p_theme_type) const; + bool has_color(const StringName &p_name, const StringName &p_theme_type) const; + bool has_color_nocheck(const StringName &p_name, const StringName &p_theme_type) const; + void rename_color(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type); + void clear_color(const StringName &p_name, const StringName &p_theme_type); + void get_color_list(StringName p_theme_type, List<StringName> *p_list) const; + void add_color_type(const StringName &p_theme_type); void get_color_type_list(List<StringName> *p_list) const; - void set_constant(const StringName &p_name, const StringName &p_node_type, int p_constant); - int get_constant(const StringName &p_name, const StringName &p_node_type) const; - bool has_constant(const StringName &p_name, const StringName &p_node_type) const; - void clear_constant(const StringName &p_name, const StringName &p_node_type); - void get_constant_list(StringName p_node_type, List<StringName> *p_list) const; + void set_constant(const StringName &p_name, const StringName &p_theme_type, int p_constant); + int get_constant(const StringName &p_name, const StringName &p_theme_type) const; + bool has_constant(const StringName &p_name, const StringName &p_theme_type) const; + bool has_constant_nocheck(const StringName &p_name, const StringName &p_theme_type) const; + void rename_constant(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type); + void clear_constant(const StringName &p_name, const StringName &p_theme_type); + void get_constant_list(StringName p_theme_type, List<StringName> *p_list) const; + void add_constant_type(const StringName &p_theme_type); void get_constant_type_list(List<StringName> *p_list) const; + void set_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type, const Variant &p_value); + Variant get_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type) const; + bool has_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type) const; + bool has_theme_item_nocheck(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type) const; + void rename_theme_item(DataType p_data_type, const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type); + void clear_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type); + void get_theme_item_list(DataType p_data_type, StringName p_theme_type, List<StringName> *p_list) const; + void add_theme_item_type(DataType p_data_type, const StringName &p_theme_type); + void get_theme_item_type_list(DataType p_data_type, List<StringName> *p_list) const; + void get_type_list(List<StringName> *p_list) const; + static void get_type_dependencies(const StringName &p_theme_type, List<StringName> *p_list); void copy_default_theme(); void copy_theme(const Ref<Theme> &p_other); @@ -151,4 +197,6 @@ public: ~Theme(); }; +VARIANT_ENUM_CAST(Theme::DataType); + #endif diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index 84be69d0d6..c4b8a56f54 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -30,1150 +30,4221 @@ #include "tile_set.h" -#include "core/config/engine.h" #include "core/math/geometry_2d.h" -#include "core/variant/array.h" +#include "scene/2d/navigation_region_2d.h" +#include "scene/gui/control.h" +#include "scene/resources/convex_polygon_shape_2d.h" +#include "servers/navigation_server_2d.h" -bool TileSet::_set(const StringName &p_name, const Variant &p_value) { - String n = p_name; - int slash = n.find("/"); - if (slash == -1) { - return false; - } - int id = String::to_int(n.get_data(), slash); - - if (!tile_map.has(id)) { - create_tile(id); - } - String what = n.substr(slash + 1, n.length()); - - if (what == "name") { - tile_set_name(id, p_value); - } else if (what == "texture") { - tile_set_texture(id, p_value); - } else if (what == "tex_offset") { - tile_set_texture_offset(id, p_value); - } else if (what == "material") { - tile_set_material(id, p_value); - } else if (what == "modulate") { - tile_set_modulate(id, p_value); - } else if (what == "region") { - tile_set_region(id, p_value); - } else if (what == "tile_mode") { - tile_set_tile_mode(id, (TileMode)((int)p_value)); - } else if (what == "is_autotile") { - // backward compatibility for Godot 3.0.x - // autotile used to be a bool, it's now an enum - bool is_autotile = p_value; - if (is_autotile) { - tile_set_tile_mode(id, AUTO_TILE); - } - } else if (what.left(9) == "autotile/") { - what = what.right(9); - if (what == "bitmask_mode") { - autotile_set_bitmask_mode(id, (BitmaskMode)((int)p_value)); - } else if (what == "icon_coordinate") { - autotile_set_icon_coordinate(id, p_value); - } else if (what == "tile_size") { - autotile_set_size(id, p_value); - } else if (what == "spacing") { - autotile_set_spacing(id, p_value); - } else if (what == "bitmask_flags") { - tile_map[id].autotile_data.flags.clear(); - if (p_value.is_array()) { - Array p = p_value; - Vector2 last_coord; - while (p.size() > 0) { - if (p[0].get_type() == Variant::VECTOR2) { - last_coord = p[0]; - } else if (p[0].get_type() == Variant::INT) { - autotile_set_bitmask(id, last_coord, p[0]); - } - p.pop_front(); - } - } - } else if (what == "occluder_map") { - tile_map[id].autotile_data.occluder_map.clear(); - Array p = p_value; - Vector2 last_coord; - while (p.size() > 0) { - if (p[0].get_type() == Variant::VECTOR2) { - last_coord = p[0]; - } else if (p[0].get_type() == Variant::OBJECT) { - autotile_set_light_occluder(id, p[0], last_coord); - } - p.pop_front(); - } - } else if (what == "navpoly_map") { - tile_map[id].autotile_data.navpoly_map.clear(); - Array p = p_value; - Vector2 last_coord; - while (p.size() > 0) { - if (p[0].get_type() == Variant::VECTOR2) { - last_coord = p[0]; - } else if (p[0].get_type() == Variant::OBJECT) { - autotile_set_navigation_polygon(id, p[0], last_coord); - } - p.pop_front(); - } - } else if (what == "priority_map") { - tile_map[id].autotile_data.priority_map.clear(); - Array p = p_value; - Vector3 val; - Vector2 v; - int priority; - while (p.size() > 0) { - val = p[0]; - if (val.z > 1) { - v.x = val.x; - v.y = val.y; - priority = (int)val.z; - tile_map[id].autotile_data.priority_map[v] = priority; - } - p.pop_front(); - } - } else if (what == "z_index_map") { - tile_map[id].autotile_data.z_index_map.clear(); - Array p = p_value; - Vector3 val; - Vector2 v; - int z_index; - while (p.size() > 0) { - val = p[0]; - if (val.z != 0) { - v.x = val.x; - v.y = val.y; - z_index = (int)val.z; - tile_map[id].autotile_data.z_index_map[v] = z_index; - } - p.pop_front(); - } - } - } else if (what == "shape") { - if (tile_get_shape_count(id) > 0) { - for (int i = 0; i < tile_get_shape_count(id); i++) { - tile_set_shape(id, i, p_value); - } - } else { - tile_set_shape(id, 0, p_value); - } - } else if (what == "shape_offset") { - if (tile_get_shape_count(id) > 0) { - for (int i = 0; i < tile_get_shape_count(id); i++) { - tile_set_shape_offset(id, i, p_value); - } - } else { - tile_set_shape_offset(id, 0, p_value); - } - } else if (what == "shape_transform") { - if (tile_get_shape_count(id) > 0) { - for (int i = 0; i < tile_get_shape_count(id); i++) { - tile_set_shape_transform(id, i, p_value); - } - } else { - tile_set_shape_transform(id, 0, p_value); - } - } else if (what == "shape_one_way") { - if (tile_get_shape_count(id) > 0) { - for (int i = 0; i < tile_get_shape_count(id); i++) { - tile_set_shape_one_way(id, i, p_value); - } - } else { - tile_set_shape_one_way(id, 0, p_value); - } - } else if (what == "shape_one_way_margin") { - if (tile_get_shape_count(id) > 0) { - for (int i = 0; i < tile_get_shape_count(id); i++) { - tile_set_shape_one_way_margin(id, i, p_value); - } - } else { - tile_set_shape_one_way_margin(id, 0, p_value); - } - } else if (what == "shapes") { - _tile_set_shapes(id, p_value); - } else if (what == "occluder") { - tile_set_light_occluder(id, p_value); - } else if (what == "occluder_offset") { - tile_set_occluder_offset(id, p_value); - } else if (what == "navigation") { - tile_set_navigation_polygon(id, p_value); - } else if (what == "navigation_offset") { - tile_set_navigation_polygon_offset(id, p_value); - } else if (what == "z_index") { - tile_set_z_index(id, p_value); - } else { - return false; - } +/////////////////////////////// TileSet ////////////////////////////////////// - return true; +// --- Plugins --- +Vector<TileSetPlugin *> TileSet::get_tile_set_atlas_plugins() const { + return tile_set_plugins_vector; } -bool TileSet::_get(const StringName &p_name, Variant &r_ret) const { - String n = p_name; - int slash = n.find("/"); - if (slash == -1) { - return false; +// -- Shape and layout -- +void TileSet::set_tile_shape(TileSet::TileShape p_shape) { + tile_shape = p_shape; + + for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) { + E_source->get()->notify_tile_data_properties_should_change(); } - int id = String::to_int(n.get_data(), slash); - - ERR_FAIL_COND_V(!tile_map.has(id), false); - - String what = n.substr(slash + 1, n.length()); - - if (what == "name") { - r_ret = tile_get_name(id); - } else if (what == "texture") { - r_ret = tile_get_texture(id); - } else if (what == "tex_offset") { - r_ret = tile_get_texture_offset(id); - } else if (what == "material") { - r_ret = tile_get_material(id); - } else if (what == "modulate") { - r_ret = tile_get_modulate(id); - } else if (what == "region") { - r_ret = tile_get_region(id); - } else if (what == "tile_mode") { - r_ret = tile_get_tile_mode(id); - } else if (what.left(9) == "autotile/") { - what = what.right(9); - if (what == "bitmask_mode") { - r_ret = autotile_get_bitmask_mode(id); - } else if (what == "icon_coordinate") { - r_ret = autotile_get_icon_coordinate(id); - } else if (what == "tile_size") { - r_ret = autotile_get_size(id); - } else if (what == "spacing") { - r_ret = autotile_get_spacing(id); - } else if (what == "bitmask_flags") { - Array p; - for (Map<Vector2, uint32_t>::Element *E = tile_map[id].autotile_data.flags.front(); E; E = E->next()) { - p.push_back(E->key()); - p.push_back(E->value()); - } - r_ret = p; - } else if (what == "occluder_map") { - Array p; - for (Map<Vector2, Ref<OccluderPolygon2D>>::Element *E = tile_map[id].autotile_data.occluder_map.front(); E; E = E->next()) { - p.push_back(E->key()); - p.push_back(E->value()); - } - r_ret = p; - } else if (what == "navpoly_map") { - Array p; - for (Map<Vector2, Ref<NavigationPolygon>>::Element *E = tile_map[id].autotile_data.navpoly_map.front(); E; E = E->next()) { - p.push_back(E->key()); - p.push_back(E->value()); - } - r_ret = p; - } else if (what == "priority_map") { - Array p; - Vector3 v; - for (Map<Vector2, int>::Element *E = tile_map[id].autotile_data.priority_map.front(); E; E = E->next()) { - if (E->value() > 1) { - //Don't save default value - v.x = E->key().x; - v.y = E->key().y; - v.z = E->value(); - p.push_back(v); - } - } - r_ret = p; - } else if (what == "z_index_map") { - Array p; - Vector3 v; - for (Map<Vector2, int>::Element *E = tile_map[id].autotile_data.z_index_map.front(); E; E = E->next()) { - if (E->value() != 0) { - //Don't save default value - v.x = E->key().x; - v.y = E->key().y; - v.z = E->value(); - p.push_back(v); - } - } - r_ret = p; - } - } else if (what == "shape") { - r_ret = tile_get_shape(id, 0); - } else if (what == "shape_offset") { - r_ret = tile_get_shape_offset(id, 0); - } else if (what == "shape_transform") { - r_ret = tile_get_shape_transform(id, 0); - } else if (what == "shape_one_way") { - r_ret = tile_get_shape_one_way(id, 0); - } else if (what == "shape_one_way_margin") { - r_ret = tile_get_shape_one_way_margin(id, 0); - } else if (what == "shapes") { - r_ret = _tile_get_shapes(id); - } else if (what == "occluder") { - r_ret = tile_get_light_occluder(id); - } else if (what == "occluder_offset") { - r_ret = tile_get_occluder_offset(id); - } else if (what == "navigation") { - r_ret = tile_get_navigation_polygon(id); - } else if (what == "navigation_offset") { - r_ret = tile_get_navigation_polygon_offset(id); - } else if (what == "z_index") { - r_ret = tile_get_z_index(id); - } else { - return false; + + emit_changed(); +} +TileSet::TileShape TileSet::get_tile_shape() const { + return tile_shape; +} + +void TileSet::set_tile_layout(TileSet::TileLayout p_layout) { + tile_layout = p_layout; + emit_changed(); +} +TileSet::TileLayout TileSet::get_tile_layout() const { + return tile_layout; +} + +void TileSet::set_tile_offset_axis(TileSet::TileOffsetAxis p_alignment) { + tile_offset_axis = p_alignment; + + for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) { + E_source->get()->notify_tile_data_properties_should_change(); } - return true; + emit_changed(); +} +TileSet::TileOffsetAxis TileSet::get_tile_offset_axis() const { + return tile_offset_axis; } -void TileSet::_get_property_list(List<PropertyInfo> *p_list) const { - for (Map<int, TileData>::Element *E = tile_map.front(); E; E = E->next()) { - int id = E->key(); - String pre = itos(id) + "/"; - p_list->push_back(PropertyInfo(Variant::STRING, pre + "name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::OBJECT, pre + "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "tex_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::OBJECT, pre + "material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::COLOR, pre + "modulate", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::RECT2, pre + "region", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::INT, pre + "tile_mode", PROPERTY_HINT_ENUM, "SINGLE_TILE,AUTO_TILE,ATLAS_TILE", PROPERTY_USAGE_NOEDITOR)); - if (tile_get_tile_mode(id) == AUTO_TILE) { - p_list->push_back(PropertyInfo(Variant::INT, pre + "autotile/bitmask_mode", PROPERTY_HINT_ENUM, "2X2,3X3 (minimal),3X3", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); - p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/bitmask_flags", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); - p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "autotile/icon_coordinate", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); - p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "autotile/tile_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); - p_list->push_back(PropertyInfo(Variant::INT, pre + "autotile/spacing", PROPERTY_HINT_RANGE, "0,256,1", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); - p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/occluder_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); - p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/navpoly_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); - p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/priority_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); - p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/z_index_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); - } else if (tile_get_tile_mode(id) == ATLAS_TILE) { - p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "autotile/icon_coordinate", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); - p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "autotile/tile_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); - p_list->push_back(PropertyInfo(Variant::INT, pre + "autotile/spacing", PROPERTY_HINT_RANGE, "0,256,1", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); - p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/occluder_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); - p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/navpoly_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); - p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/priority_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); - p_list->push_back(PropertyInfo(Variant::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", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::OBJECT, pre + "occluder", PROPERTY_HINT_RESOURCE_TYPE, "OccluderPolygon2D", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "navigation_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::OBJECT, pre + "navigation", PROPERTY_HINT_RESOURCE_TYPE, "NavigationPolygon", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "shape_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "shape_transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::OBJECT, pre + "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape2D", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::BOOL, pre + "shape_one_way", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::FLOAT, pre + "shape_one_way_margin", PROPERTY_HINT_RANGE, "0,128,0.01", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "shapes", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::INT, pre + "z_index", PROPERTY_HINT_RANGE, itos(RS::CANVAS_ITEM_Z_MIN) + "," + itos(RS::CANVAS_ITEM_Z_MAX) + ",1", PROPERTY_USAGE_NOEDITOR)); - } -} - -void TileSet::create_tile(int p_id) { - ERR_FAIL_COND(tile_map.has(p_id)); - tile_map[p_id] = TileData(); - tile_map[p_id].autotile_data = AutotileData(); - notify_property_list_changed(); +void TileSet::set_tile_size(Size2i p_size) { + ERR_FAIL_COND(p_size.x < 1 || p_size.y < 1); + tile_size = p_size; emit_changed(); } +Size2i TileSet::get_tile_size() const { + return tile_size; +} -void TileSet::autotile_set_bitmask_mode(int p_id, BitmaskMode p_mode) { - ERR_FAIL_COND(!tile_map.has(p_id)); - tile_map[p_id].autotile_data.bitmask_mode = p_mode; - notify_property_list_changed(); +void TileSet::set_tile_skew(Vector2 p_skew) { emit_changed(); + tile_skew = p_skew; +} +Vector2 TileSet::get_tile_skew() const { + return tile_skew; +} + +int TileSet::get_next_source_id() const { + return next_source_id; } -TileSet::BitmaskMode TileSet::autotile_get_bitmask_mode(int p_id) const { - ERR_FAIL_COND_V(!tile_map.has(p_id), BITMASK_2X2); - return tile_map[p_id].autotile_data.bitmask_mode; +void TileSet::_compute_next_source_id() { + while (sources.has(next_source_id)) { + next_source_id = (next_source_id + 1) % 1073741824; // 2 ** 30 + }; } -void TileSet::tile_set_texture(int p_id, const Ref<Texture2D> &p_texture) { - ERR_FAIL_COND(!tile_map.has(p_id)); - tile_map[p_id].texture = p_texture; +// Sources management +int TileSet::add_source(Ref<TileSetAtlasSource> p_tile_atlas_source, int p_atlas_source_id_override) { + ERR_FAIL_COND_V(!p_tile_atlas_source.is_valid(), -1); + ERR_FAIL_COND_V_MSG(p_atlas_source_id_override >= 0 && (sources.has(p_atlas_source_id_override)), -1, vformat("Cannot create TileSet atlas source. Another atlas source exists with id %d.", p_atlas_source_id_override)); + + int new_source_id = p_atlas_source_id_override >= 0 ? p_atlas_source_id_override : next_source_id; + sources[new_source_id] = p_tile_atlas_source; + source_ids.append(new_source_id); + source_ids.sort(); + p_tile_atlas_source->set_tile_set(this); + _compute_next_source_id(); + + sources[new_source_id]->connect("changed", callable_mp(this, &TileSet::_source_changed)); + emit_changed(); + + return new_source_id; } -Ref<Texture2D> TileSet::tile_get_texture(int p_id) const { - ERR_FAIL_COND_V(!tile_map.has(p_id), Ref<Texture2D>()); - return tile_map[p_id].texture; +void TileSet::remove_source(int p_source_id) { + ERR_FAIL_COND_MSG(!sources.has(p_source_id), vformat("Cannot remove TileSet atlas source. No tileset atlas source with id %d.", p_source_id)); + + sources[p_source_id]->disconnect("changed", callable_mp(this, &TileSet::_source_changed)); + + sources[p_source_id]->set_tile_set(nullptr); + sources.erase(p_source_id); + source_ids.erase(p_source_id); + source_ids.sort(); + + emit_changed(); } -void TileSet::tile_set_material(int p_id, const Ref<ShaderMaterial> &p_material) { - ERR_FAIL_COND(!tile_map.has(p_id)); - tile_map[p_id].material = p_material; +void TileSet::set_source_id(int p_source_id, int p_new_source_id) { + ERR_FAIL_COND(p_new_source_id < 0); + ERR_FAIL_COND_MSG(!sources.has(p_source_id), vformat("Cannot change TileSet atlas source ID. No tileset atlas source with id %d.", p_source_id)); + if (p_source_id == p_new_source_id) { + return; + } + + ERR_FAIL_COND_MSG(sources.has(p_new_source_id), vformat("Cannot change TileSet atlas source ID. Another atlas source exists with id %d.", p_new_source_id)); + + sources[p_new_source_id] = sources[p_source_id]; + sources.erase(p_source_id); + + source_ids.erase(p_source_id); + source_ids.append(p_new_source_id); + source_ids.sort(); + emit_changed(); } -Ref<ShaderMaterial> TileSet::tile_get_material(int p_id) const { - ERR_FAIL_COND_V(!tile_map.has(p_id), Ref<ShaderMaterial>()); - return tile_map[p_id].material; +bool TileSet::has_source(int p_source_id) const { + return sources.has(p_source_id); +} + +Ref<TileSetSource> TileSet::get_source(int p_source_id) const { + ERR_FAIL_COND_V_MSG(!sources.has(p_source_id), nullptr, vformat("No TileSet atlas source with id %d.", p_source_id)); + + return sources[p_source_id]; +} + +int TileSet::get_source_count() const { + return source_ids.size(); } -void TileSet::tile_set_modulate(int p_id, const Color &p_modulate) { - ERR_FAIL_COND(!tile_map.has(p_id)); - tile_map[p_id].modulate = p_modulate; +int TileSet::get_source_id(int p_index) const { + ERR_FAIL_INDEX_V(p_index, source_ids.size(), -1); + return source_ids[p_index]; +} + +// Rendering +void TileSet::set_uv_clipping(bool p_uv_clipping) { + if (uv_clipping == p_uv_clipping) { + return; + } + uv_clipping = p_uv_clipping; emit_changed(); } +bool TileSet::is_uv_clipping() const { + return uv_clipping; +}; -Color TileSet::tile_get_modulate(int p_id) const { - ERR_FAIL_COND_V(!tile_map.has(p_id), Color(1, 1, 1)); - return tile_map[p_id].modulate; +void TileSet::set_y_sorting(bool p_y_sort) { + if (y_sorting == p_y_sort) { + return; + } + y_sorting = p_y_sort; + emit_changed(); } +bool TileSet::is_y_sorting() const { + return y_sorting; +}; -void TileSet::tile_set_texture_offset(int p_id, const Vector2 &p_offset) { - ERR_FAIL_COND(!tile_map.has(p_id)); - tile_map[p_id].offset = p_offset; +void TileSet::set_occlusion_layers_count(int p_occlusion_layers_count) { + ERR_FAIL_COND(p_occlusion_layers_count < 0); + if (occlusion_layers.size() == p_occlusion_layers_count) { + return; + } + + occlusion_layers.resize(p_occlusion_layers_count); + + for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) { + E_source->get()->notify_tile_data_properties_should_change(); + } + + notify_property_list_changed(); emit_changed(); } -Vector2 TileSet::tile_get_texture_offset(int p_id) const { - ERR_FAIL_COND_V(!tile_map.has(p_id), Vector2()); - return tile_map[p_id].offset; +int TileSet::get_occlusion_layers_count() const { + return occlusion_layers.size(); +}; + +void TileSet::set_occlusion_layer_light_mask(int p_layer_index, int p_light_mask) { + ERR_FAIL_INDEX(p_layer_index, occlusion_layers.size()); + occlusion_layers.write[p_layer_index].light_mask = p_light_mask; + emit_changed(); +} + +int TileSet::get_occlusion_layer_light_mask(int p_layer_index) const { + ERR_FAIL_INDEX_V(p_layer_index, occlusion_layers.size(), 0); + return occlusion_layers[p_layer_index].light_mask; } -void TileSet::tile_set_region(int p_id, const Rect2 &p_region) { - ERR_FAIL_COND(!tile_map.has(p_id)); - tile_map[p_id].region = p_region; +void TileSet::set_occlusion_layer_sdf_collision(int p_layer_index, int p_sdf_collision) { + ERR_FAIL_INDEX(p_layer_index, occlusion_layers.size()); + occlusion_layers.write[p_layer_index].sdf_collision = p_sdf_collision; emit_changed(); } -Rect2 TileSet::tile_get_region(int p_id) const { - ERR_FAIL_COND_V(!tile_map.has(p_id), Rect2()); - return tile_map[p_id].region; +bool TileSet::get_occlusion_layer_sdf_collision(int p_layer_index) const { + ERR_FAIL_INDEX_V(p_layer_index, occlusion_layers.size(), false); + return occlusion_layers[p_layer_index].sdf_collision; +} + +void TileSet::draw_tile_shape(CanvasItem *p_canvas_item, Rect2 p_region, Color p_color, bool p_filled, Ref<Texture2D> p_texture) { + // TODO: optimize this with 2D meshes when they work again. + if (get_tile_shape() == TileSet::TILE_SHAPE_SQUARE) { + if (p_filled && p_texture.is_valid()) { + p_canvas_item->draw_texture_rect(p_texture, p_region, false, p_color); + } else { + p_canvas_item->draw_rect(p_region, p_color, p_filled); + } + } else { + float overlap = 0.0; + switch (get_tile_shape()) { + case TileSet::TILE_SHAPE_ISOMETRIC: + overlap = 0.5; + break; + case TileSet::TILE_SHAPE_HEXAGON: + overlap = 0.25; + break; + case TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE: + overlap = 0.0; + break; + default: + break; + } + + Vector<Vector2> uvs; + uvs.append(Vector2(0.5, 0.0)); + uvs.append(Vector2(0.0, overlap)); + uvs.append(Vector2(0.0, 1.0 - overlap)); + uvs.append(Vector2(0.5, 1.0)); + uvs.append(Vector2(1.0, 1.0 - overlap)); + uvs.append(Vector2(1.0, overlap)); + uvs.append(Vector2(0.5, 0.0)); + if (get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL) { + for (int i = 0; i < uvs.size(); i++) { + uvs.write[i] = Vector2(uvs[i].y, uvs[i].x); + } + } + + Vector<Vector2> points; + for (int i = 0; i < uvs.size(); i++) { + points.append(p_region.position + uvs[i] * p_region.size); + } + + if (p_filled) { + // This does hurt performances a lot. We should use a mesh if possible instead. + p_canvas_item->draw_colored_polygon(points, p_color, uvs, p_texture); + + // Should improve performances, but does not work as draw_primitive does not work with textures :/ : + /*for (int i = 0; i < 6; i += 3) { + Vector<Vector2> quad; + quad.append(points[i]); + quad.append(points[(i + 1) % points.size()]); + quad.append(points[(i + 2) % points.size()]); + quad.append(points[(i + 3) % points.size()]); + + Vector<Vector2> uv_quad; + uv_quad.append(uvs[i]); + uv_quad.append(uvs[(i + 1) % uvs.size()]); + uv_quad.append(uvs[(i + 2) % uvs.size()]); + uv_quad.append(uvs[(i + 3) % uvs.size()]); + + p_control->draw_primitive(quad, Vector<Color>(), uv_quad, p_texture); + }*/ + + } else { + // This does hurt performances a lot. We should use a mesh if possible instead. + // tile_shape_grid->draw_polyline(points, p_color); + for (int i = 0; i < points.size() - 1; i++) { + p_canvas_item->draw_line(points[i], points[i + 1], p_color); + } + } + } } -void TileSet::tile_set_tile_mode(int p_id, TileMode p_tile_mode) { - ERR_FAIL_COND(!tile_map.has(p_id)); - tile_map[p_id].tile_mode = p_tile_mode; +// Physics +void TileSet::set_physics_layers_count(int p_physics_layers_count) { + ERR_FAIL_COND(p_physics_layers_count < 0); + if (physics_layers.size() == p_physics_layers_count) { + return; + } + + physics_layers.resize(p_physics_layers_count); + + for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) { + E_source->get()->notify_tile_data_properties_should_change(); + } + + notify_property_list_changed(); emit_changed(); } -TileSet::TileMode TileSet::tile_get_tile_mode(int p_id) const { - ERR_FAIL_COND_V(!tile_map.has(p_id), SINGLE_TILE); - return tile_map[p_id].tile_mode; +int TileSet::get_physics_layers_count() const { + return physics_layers.size(); } -void TileSet::autotile_set_icon_coordinate(int p_id, Vector2 coord) { - ERR_FAIL_COND(!tile_map.has(p_id)); - tile_map[p_id].autotile_data.icon_coord = coord; +void TileSet::set_physics_layer_collision_layer(int p_layer_index, uint32_t p_layer) { + ERR_FAIL_INDEX(p_layer_index, physics_layers.size()); + physics_layers.write[p_layer_index].collision_layer = p_layer; emit_changed(); } -Vector2 TileSet::autotile_get_icon_coordinate(int p_id) const { - ERR_FAIL_COND_V(!tile_map.has(p_id), Vector2()); - return tile_map[p_id].autotile_data.icon_coord; +uint32_t TileSet::get_physics_layer_collision_layer(int p_layer_index) const { + ERR_FAIL_INDEX_V(p_layer_index, physics_layers.size(), 0); + return physics_layers[p_layer_index].collision_layer; } -void TileSet::autotile_set_spacing(int p_id, int p_spacing) { - ERR_FAIL_COND(!tile_map.has(p_id)); - ERR_FAIL_COND(p_spacing < 0); - tile_map[p_id].autotile_data.spacing = p_spacing; +void TileSet::set_physics_layer_collision_mask(int p_layer_index, uint32_t p_mask) { + ERR_FAIL_INDEX(p_layer_index, physics_layers.size()); + physics_layers.write[p_layer_index].collision_mask = p_mask; emit_changed(); } -int TileSet::autotile_get_spacing(int p_id) const { - ERR_FAIL_COND_V(!tile_map.has(p_id), 0); - return tile_map[p_id].autotile_data.spacing; +uint32_t TileSet::get_physics_layer_collision_mask(int p_layer_index) const { + ERR_FAIL_INDEX_V(p_layer_index, physics_layers.size(), 0); + return physics_layers[p_layer_index].collision_mask; } -void TileSet::autotile_set_size(int p_id, Size2 p_size) { - ERR_FAIL_COND(!tile_map.has(p_id)); - ERR_FAIL_COND(p_size.x <= 0 || p_size.y <= 0); - tile_map[p_id].autotile_data.size = p_size; +void TileSet::set_physics_layer_physics_material(int p_layer_index, Ref<PhysicsMaterial> p_physics_material) { + ERR_FAIL_INDEX(p_layer_index, physics_layers.size()); + physics_layers.write[p_layer_index].physics_material = p_physics_material; } -Size2 TileSet::autotile_get_size(int p_id) const { - ERR_FAIL_COND_V(!tile_map.has(p_id), Size2()); - return tile_map[p_id].autotile_data.size; +Ref<PhysicsMaterial> TileSet::get_physics_layer_physics_material(int p_layer_index) const { + ERR_FAIL_INDEX_V(p_layer_index, physics_layers.size(), Ref<PhysicsMaterial>()); + return physics_layers[p_layer_index].physics_material; } -void TileSet::autotile_clear_bitmask_map(int p_id) { - ERR_FAIL_COND(!tile_map.has(p_id)); - tile_map[p_id].autotile_data.flags.clear(); +// Terrains +void TileSet::set_terrain_sets_count(int p_terrains_sets_count) { + ERR_FAIL_COND(p_terrains_sets_count < 0); + + terrain_sets.resize(p_terrains_sets_count); + + notify_property_list_changed(); + emit_changed(); } -void TileSet::autotile_set_subtile_priority(int p_id, const Vector2 &p_coord, int p_priority) { - ERR_FAIL_COND(!tile_map.has(p_id)); - ERR_FAIL_COND(p_priority <= 0); - tile_map[p_id].autotile_data.priority_map[p_coord] = p_priority; +int TileSet::get_terrain_sets_count() const { + return terrain_sets.size(); } -int TileSet::autotile_get_subtile_priority(int p_id, const Vector2 &p_coord) { - ERR_FAIL_COND_V(!tile_map.has(p_id), 1); - if (tile_map[p_id].autotile_data.priority_map.has(p_coord)) { - return tile_map[p_id].autotile_data.priority_map[p_coord]; +void TileSet::set_terrain_set_mode(int p_terrain_set, TerrainMode p_terrain_mode) { + ERR_FAIL_INDEX(p_terrain_set, terrain_sets.size()); + terrain_sets.write[p_terrain_set].mode = p_terrain_mode; + for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) { + E_source->get()->notify_tile_data_properties_should_change(); } - //When not custom priority set return the default value - return 1; + + notify_property_list_changed(); + emit_changed(); } -const Map<Vector2, int> &TileSet::autotile_get_priority_map(int p_id) const { - static Map<Vector2, int> dummy; - ERR_FAIL_COND_V(!tile_map.has(p_id), dummy); - return tile_map[p_id].autotile_data.priority_map; +TileSet::TerrainMode TileSet::get_terrain_set_mode(int p_terrain_set) const { + ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES); + return terrain_sets[p_terrain_set].mode; } -void TileSet::autotile_set_z_index(int p_id, const Vector2 &p_coord, int p_z_index) { - ERR_FAIL_COND(!tile_map.has(p_id)); - tile_map[p_id].autotile_data.z_index_map[p_coord] = p_z_index; +void TileSet::set_terrains_count(int p_terrain_set, int p_terrains_layers_count) { + ERR_FAIL_INDEX(p_terrain_set, terrain_sets.size()); + ERR_FAIL_COND(p_terrains_layers_count < 0); + if (terrain_sets[p_terrain_set].terrains.size() == p_terrains_layers_count) { + return; + } + + int old_size = terrain_sets[p_terrain_set].terrains.size(); + terrain_sets.write[p_terrain_set].terrains.resize(p_terrains_layers_count); + + // Default name and color + for (int i = old_size; i < terrain_sets.write[p_terrain_set].terrains.size(); i++) { + float hue_rotate = (i * 2 % 16) / 16.0; + Color c; + c.set_hsv(Math::fmod(float(hue_rotate), float(1.0)), 0.5, 0.5); + terrain_sets.write[p_terrain_set].terrains.write[i].color = c; + terrain_sets.write[p_terrain_set].terrains.write[i].name = String(vformat("Terrain %d", i)); + } + + for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) { + E_source->get()->notify_tile_data_properties_should_change(); + } + + notify_property_list_changed(); emit_changed(); } -int TileSet::autotile_get_z_index(int p_id, const Vector2 &p_coord) { - ERR_FAIL_COND_V(!tile_map.has(p_id), 1); - if (tile_map[p_id].autotile_data.z_index_map.has(p_coord)) { - return tile_map[p_id].autotile_data.z_index_map[p_coord]; +int TileSet::get_terrains_count(int p_terrain_set) const { + ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), -1); + return terrain_sets[p_terrain_set].terrains.size(); +} + +void TileSet::set_terrain_name(int p_terrain_set, int p_terrain_index, String p_name) { + ERR_FAIL_INDEX(p_terrain_set, terrain_sets.size()); + ERR_FAIL_INDEX(p_terrain_index, terrain_sets[p_terrain_set].terrains.size()); + terrain_sets.write[p_terrain_set].terrains.write[p_terrain_index].name = p_name; + emit_changed(); +} + +String TileSet::get_terrain_name(int p_terrain_set, int p_terrain_index) const { + ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), String()); + ERR_FAIL_INDEX_V(p_terrain_index, terrain_sets[p_terrain_set].terrains.size(), String()); + return terrain_sets[p_terrain_set].terrains[p_terrain_index].name; +} + +void TileSet::set_terrain_color(int p_terrain_set, int p_terrain_index, Color p_color) { + ERR_FAIL_INDEX(p_terrain_set, terrain_sets.size()); + ERR_FAIL_INDEX(p_terrain_index, terrain_sets[p_terrain_set].terrains.size()); + if (p_color.a != 1.0) { + WARN_PRINT("Terrain color should have alpha == 1.0"); + p_color.a = 1.0; } - //When not custom z index set return the default value - return 0; + terrain_sets.write[p_terrain_set].terrains.write[p_terrain_index].color = p_color; + emit_changed(); } -const Map<Vector2, int> &TileSet::autotile_get_z_index_map(int p_id) const { - static Map<Vector2, int> dummy; - ERR_FAIL_COND_V(!tile_map.has(p_id), dummy); - return tile_map[p_id].autotile_data.z_index_map; +Color TileSet::get_terrain_color(int p_terrain_set, int p_terrain_index) const { + ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), Color()); + ERR_FAIL_INDEX_V(p_terrain_index, terrain_sets[p_terrain_set].terrains.size(), Color()); + return terrain_sets[p_terrain_set].terrains[p_terrain_index].color; } -void TileSet::autotile_set_bitmask(int p_id, Vector2 p_coord, uint32_t p_flag) { - ERR_FAIL_COND(!tile_map.has(p_id)); - if (p_flag == 0) { - if (tile_map[p_id].autotile_data.flags.has(p_coord)) { - tile_map[p_id].autotile_data.flags.erase(p_coord); +bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeighbor p_peering_bit) const { + if (p_terrain_set < 0 || p_terrain_set >= get_terrain_sets_count()) { + return false; + } + + TileSet::TerrainMode terrain_mode = get_terrain_set_mode(p_terrain_set); + if (tile_shape == TileSet::TILE_SHAPE_SQUARE) { + if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) { + if (p_peering_bit == TileSet::CELL_NEIGHBOR_RIGHT_SIDE || + p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE || + p_peering_bit == TileSet::CELL_NEIGHBOR_LEFT_SIDE || + p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_SIDE) { + return true; + } + } + if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + if (p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER || + p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER || + p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER || + p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER) { + return true; + } + } + } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) { + if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) { + if (p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE || + p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE || + p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE || + p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) { + return true; + } + } + if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + if (p_peering_bit == TileSet::CELL_NEIGHBOR_RIGHT_CORNER || + p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER || + p_peering_bit == TileSet::CELL_NEIGHBOR_LEFT_CORNER || + p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_CORNER) { + return true; + } } } else { - tile_map[p_id].autotile_data.flags[p_coord] = p_flag; + if (get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { + if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) { + if (p_peering_bit == TileSet::CELL_NEIGHBOR_RIGHT_SIDE || + p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE || + p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE || + p_peering_bit == TileSet::CELL_NEIGHBOR_LEFT_SIDE || + p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE || + p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) { + return true; + } + } + if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + if (p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER || + p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER || + p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER || + p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER || + p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_CORNER || + p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER) { + return true; + } + } + } else { + if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) { + if (p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE || + p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE || + p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE || + p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE || + p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_SIDE || + p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) { + return true; + } + } + if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + if (p_peering_bit == TileSet::CELL_NEIGHBOR_RIGHT_CORNER || + p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER || + p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER || + p_peering_bit == TileSet::CELL_NEIGHBOR_LEFT_CORNER || + p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER || + p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER) { + return true; + } + } + } + } + return false; +} + +// Navigation +void TileSet::set_navigation_layers_count(int p_navigation_layers_count) { + ERR_FAIL_COND(p_navigation_layers_count < 0); + if (navigation_layers.size() == p_navigation_layers_count) { + return; + } + + navigation_layers.resize(p_navigation_layers_count); + + for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) { + E_source->get()->notify_tile_data_properties_should_change(); } + + notify_property_list_changed(); + emit_changed(); +} + +int TileSet::get_navigation_layers_count() const { + return navigation_layers.size(); +} + +void TileSet::set_navigation_layer_layers(int p_layer_index, uint32_t p_layers) { + ERR_FAIL_INDEX(p_layer_index, navigation_layers.size()); + navigation_layers.write[p_layer_index].layers = p_layers; + emit_changed(); } -uint32_t TileSet::autotile_get_bitmask(int p_id, Vector2 p_coord) { - ERR_FAIL_COND_V(!tile_map.has(p_id), 0); - if (!tile_map[p_id].autotile_data.flags.has(p_coord)) { - return 0; +uint32_t TileSet::get_navigation_layer_layers(int p_layer_index) const { + ERR_FAIL_INDEX_V(p_layer_index, navigation_layers.size(), 0); + return navigation_layers[p_layer_index].layers; +} + +// Custom data. +void TileSet::set_custom_data_layers_count(int p_custom_data_layers_count) { + ERR_FAIL_COND(p_custom_data_layers_count < 0); + if (custom_data_layers.size() == p_custom_data_layers_count) { + return; } - return tile_map[p_id].autotile_data.flags[p_coord]; + + custom_data_layers.resize(p_custom_data_layers_count); + + for (Map<String, int>::Element *E = custom_data_layers_by_name.front(); E; E = E->next()) { + if (E->get() >= custom_data_layers.size()) { + custom_data_layers_by_name.erase(E); + } + } + + for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) { + E_source->get()->notify_tile_data_properties_should_change(); + } + + notify_property_list_changed(); + emit_changed(); } -const Map<Vector2, uint32_t> &TileSet::autotile_get_bitmask_map(int p_id) { - static Map<Vector2, uint32_t> dummy; - static Map<Vector2, uint32_t> dummy_atlas; - ERR_FAIL_COND_V(!tile_map.has(p_id), dummy); - if (tile_get_tile_mode(p_id) == ATLAS_TILE) { - dummy_atlas = Map<Vector2, uint32_t>(); - Rect2 region = tile_get_region(p_id); - Size2 size = autotile_get_size(p_id); - float spacing = autotile_get_spacing(p_id); - for (int x = 0; x < (region.size.x / (size.x + spacing)); x++) { - for (int y = 0; y < (region.size.y / (size.y + spacing)); y++) { - dummy_atlas.insert(Vector2(x, y), 0); +int TileSet::get_custom_data_layers_count() const { + return custom_data_layers.size(); +} + +int TileSet::get_custom_data_layer_by_name(String p_value) const { + if (custom_data_layers_by_name.has(p_value)) { + return custom_data_layers_by_name[p_value]; + } else { + return -1; + } +} + +void TileSet::set_custom_data_name(int p_layer_id, String p_value) { + ERR_FAIL_INDEX(p_layer_id, custom_data_layers.size()); + + // Exit if another property has the same name. + if (!p_value.is_empty()) { + for (int other_layer_id = 0; other_layer_id < get_custom_data_layers_count(); other_layer_id++) { + if (other_layer_id != p_layer_id && get_custom_data_name(other_layer_id) == p_value) { + ERR_FAIL_MSG(vformat("There is already a custom property named %s", p_value)); } } - return dummy_atlas; + } + + if (p_value.is_empty() && custom_data_layers_by_name.has(p_value)) { + custom_data_layers_by_name.erase(p_value); } else { - return tile_map[p_id].autotile_data.flags; + custom_data_layers_by_name[p_value] = p_layer_id; } + + custom_data_layers.write[p_layer_id].name = p_value; + emit_changed(); +} + +String TileSet::get_custom_data_name(int p_layer_id) const { + ERR_FAIL_INDEX_V(p_layer_id, custom_data_layers.size(), ""); + return custom_data_layers[p_layer_id].name; +} + +void TileSet::set_custom_data_type(int p_layer_id, Variant::Type p_value) { + ERR_FAIL_INDEX(p_layer_id, custom_data_layers.size()); + custom_data_layers.write[p_layer_id].type = p_value; + + for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) { + E_source->get()->notify_tile_data_properties_should_change(); + } + + emit_changed(); +} + +Variant::Type TileSet::get_custom_data_type(int p_layer_id) const { + ERR_FAIL_INDEX_V(p_layer_id, custom_data_layers.size(), Variant::NIL); + return custom_data_layers[p_layer_id].type; +} + +void TileSet::_source_changed() { + emit_changed(); + notify_property_list_changed(); +} + +void TileSet::reset_state() { + occlusion_layers.clear(); + physics_layers.clear(); + custom_data_layers.clear(); } -Vector2 TileSet::autotile_get_subtile_for_bitmask(int p_id, uint16_t p_bitmask, 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 (p_tilemap_node->get_class_name() == "TileMap") { - if (get_script_instance() != nullptr) { - if (get_script_instance()->has_method("_forward_subtile_selection")) { - Variant ret = get_script_instance()->call("_forward_subtile_selection", p_id, p_bitmask, p_tilemap_node, p_tile_location); - if (ret.get_type() == Variant::VECTOR2) { - return ret; +const Vector2i TileSetAtlasSource::INVALID_ATLAS_COORDS = Vector2i(-1, -1); +const int TileSetAtlasSource::INVALID_TILE_ALTERNATIVE = -1; + +#ifndef DISABLE_DEPRECATED +void TileSet::compatibility_conversion() { + for (Map<int, CompatibilityTileData *>::Element *E = compatibility_data.front(); E; E = E->next()) { + CompatibilityTileData *ctd = E->value(); + + // Add the texture + TileSetAtlasSource *atlas_source = memnew(TileSetAtlasSource); + int source_id = add_source(Ref<TileSetSource>(atlas_source)); + + atlas_source->set_texture(ctd->texture); + + // Handle each tile as a new source. Not optimal but at least it should stay compatible. + switch (ctd->tile_mode) { + case 0: // SINGLE_TILE + // TODO + break; + case 1: // AUTO_TILE + // TODO + break; + case 2: // ATLAS_TILE + atlas_source->set_margins(ctd->region.get_position()); + atlas_source->set_separation(Vector2i(ctd->autotile_spacing, ctd->autotile_spacing)); + atlas_source->set_texture_region_size(ctd->autotile_tile_size); + + Size2i atlas_size = ctd->region.get_size() / (ctd->autotile_tile_size + atlas_source->get_separation()); + for (int i = 0; i < atlas_size.x; i++) { + for (int j = 0; j < atlas_size.y; j++) { + Vector2i coords = Vector2i(i, j); + + for (int flags = 0; flags < 8; flags++) { + bool flip_h = flags & 1; + bool flip_v = flags & 2; + bool transpose = flags & 4; + + int alternative_tile = 0; + if (!atlas_source->has_tile(coords)) { + atlas_source->create_tile(coords); + } else { + alternative_tile = atlas_source->create_alternative_tile(coords); + } + TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(coords, alternative_tile)); + + tile_data->set_flip_h(flip_h); + tile_data->set_flip_v(flip_v); + tile_data->set_transpose(transpose); + tile_data->tile_set_material(ctd->material); + tile_data->set_modulate(ctd->modulate); + tile_data->set_z_index(ctd->z_index); + if (ctd->autotile_occluder_map.has(coords)) { + if (get_occlusion_layers_count() < 1) { + set_occlusion_layers_count(1); + } + tile_data->set_occluder(0, ctd->autotile_occluder_map[coords]); + } + if (ctd->autotile_navpoly_map.has(coords)) { + if (get_navigation_layers_count() < 1) { + set_navigation_layers_count(1); + } + tile_data->set_navigation_polygon(0, ctd->autotile_navpoly_map[coords]); + } + if (ctd->autotile_priority_map.has(coords)) { + tile_data->set_probability(ctd->autotile_priority_map[coords]); + } + if (ctd->autotile_z_index_map.has(coords)) { + tile_data->set_z_index(ctd->autotile_z_index_map[coords]); + } + + // Add the shapes. + if (ctd->shapes.size() > 0) { + if (get_physics_layers_count() < 1) { + set_physics_layers_count(1); + } + } + for (int k = 0; k < ctd->shapes.size(); k++) { + CompatibilityShapeData csd = ctd->shapes[k]; + if (csd.autotile_coords == coords) { + tile_data->set_collision_shapes_count(0, tile_data->get_collision_shapes_count(0) + 1); + int index = tile_data->get_collision_shapes_count(0) - 1; + tile_data->set_collision_shape_one_way(0, index, csd.one_way); + tile_data->set_collision_shape_one_way_margin(0, index, csd.one_way_margin); + tile_data->set_collision_shape_shape(0, index, csd.shape); + // Ignores transform for now. + } + } + + // -- TODO: handle -- + // Those are offset for the whole atlas, they are likely useless for the atlases, but might make sense for single tiles. + // texture offset + // occluder_offset + // navigation_offset + + // For terrains, ignored for now? + // bitmask_mode + // bitmask_flags + } + } + } + break; + } + + // Offset all shapes + for (int k = 0; k < ctd->shapes.size(); k++) { + Ref<ConvexPolygonShape2D> convex = ctd->shapes[k].shape; + if (convex.is_valid()) { + Vector<Vector2> points = convex->get_points(); + for (int i_point = 0; i_point < points.size(); i_point++) { + points.write[i_point] = points[i_point] - get_tile_size() / 2; } + convex->set_points(points); } } + + // Add the mapping to the map + compatibility_source_mapping.insert(E->key(), source_id); + } + + // Reset compatibility data + for (Map<int, CompatibilityTileData *>::Element *E = compatibility_data.front(); E; E = E->next()) { + memdelete(E->get()); } + compatibility_data = Map<int, CompatibilityTileData *>(); +} +#endif // DISABLE_DEPRECATED - List<Vector2> coords; - List<uint32_t> priorities; - uint32_t priority_sum = 0; - uint32_t mask; - uint16_t mask_; - uint16_t mask_ignore; - for (Map<Vector2, uint32_t>::Element *E = tile_map[p_id].autotile_data.flags.front(); E; E = E->next()) { - mask = E->get(); - if (tile_map[p_id].autotile_data.bitmask_mode == BITMASK_2X2) { - mask |= (BIND_IGNORE_TOP | BIND_IGNORE_LEFT | BIND_IGNORE_CENTER | BIND_IGNORE_RIGHT | BIND_IGNORE_BOTTOM); +bool TileSet::_set(const StringName &p_name, const Variant &p_value) { + Vector<String> components = String(p_name).split("/", true, 2); + +#ifndef DISABLE_DEPRECATED + // TODO: THIS IS HOW WE CHECK IF WE HAVE A DEPRECATED RESOURCE + // This should be moved to a dedicated conversion system + if (components.size() >= 1 && components[0].is_valid_integer()) { + int id = components[0].to_int(); + + // Get or create the compatibility object + CompatibilityTileData *ctd; + Map<int, CompatibilityTileData *>::Element *E = compatibility_data.find(id); + if (!E) { + ctd = memnew(CompatibilityTileData); + compatibility_data.insert(id, ctd); + } else { + ctd = E->get(); } - mask_ = mask & 0xFFFF; - mask_ignore = mask >> 16; + if (components.size() < 2) { + return false; + } - if (((mask_ & (~mask_ignore)) == (p_bitmask & (~mask_ignore))) && (((~mask_) | mask_ignore) == ((~p_bitmask) | mask_ignore))) { - uint32_t priority = autotile_get_subtile_priority(p_id, E->key()); - priority_sum += priority; - priorities.push_back(priority); - coords.push_back(E->key()); + String what = components[1]; + + if (what == "name") { + ctd->name = p_value; + } else if (what == "texture") { + ctd->texture = p_value; + } else if (what == "tex_offset") { + ctd->tex_offset = p_value; + } else if (what == "material") { + ctd->material = p_value; + } else if (what == "modulate") { + ctd->modulate = p_value; + } else if (what == "region") { + ctd->region = p_value; + } else if (what == "tile_mode") { + ctd->tile_mode = p_value; + } else if (what.left(9) == "autotile") { + what = what.right(9); + if (what == "bitmask_mode") { + ctd->autotile_bitmask_mode = p_value; + } else if (what == "icon_coordinate") { + ctd->autotile_icon_coordinate = p_value; + } else if (what == "tile_size") { + ctd->autotile_tile_size = p_value; + } else if (what == "spacing") { + ctd->autotile_spacing = p_value; + } else if (what == "bitmask_flags") { + if (p_value.is_array()) { + Array p = p_value; + Vector2i last_coord; + while (p.size() > 0) { + if (p[0].get_type() == Variant::VECTOR2) { + last_coord = p[0]; + } else if (p[0].get_type() == Variant::INT) { + ctd->autotile_bitmask_flags.insert(last_coord, p[0]); + } + p.pop_front(); + } + } + } else if (what == "occluder_map") { + Array p = p_value; + Vector2 last_coord; + while (p.size() > 0) { + if (p[0].get_type() == Variant::VECTOR2) { + last_coord = p[0]; + } else if (p[0].get_type() == Variant::OBJECT) { + ctd->autotile_occluder_map.insert(last_coord, p[0]); + } + p.pop_front(); + } + } else if (what == "navpoly_map") { + Array p = p_value; + Vector2 last_coord; + while (p.size() > 0) { + if (p[0].get_type() == Variant::VECTOR2) { + last_coord = p[0]; + } else if (p[0].get_type() == Variant::OBJECT) { + ctd->autotile_navpoly_map.insert(last_coord, p[0]); + } + p.pop_front(); + } + } else if (what == "priority_map") { + Array p = p_value; + Vector3 val; + Vector2 v; + int priority; + while (p.size() > 0) { + val = p[0]; + if (val.z > 1) { + v.x = val.x; + v.y = val.y; + priority = (int)val.z; + ctd->autotile_priority_map.insert(v, priority); + } + p.pop_front(); + } + } else if (what == "z_index_map") { + Array p = p_value; + Vector3 val; + Vector2 v; + int z_index; + while (p.size() > 0) { + val = p[0]; + if (val.z != 0) { + v.x = val.x; + v.y = val.y; + z_index = (int)val.z; + ctd->autotile_z_index_map.insert(v, z_index); + } + p.pop_front(); + } + } + + } else if (what == "shapes") { + Array p = p_value; + for (int i = 0; i < p.size(); i++) { + CompatibilityShapeData csd; + Dictionary d = p[i]; + for (int j = 0; j < d.size(); j++) { + String key = d.get_key_at_index(j); + if (key == "autotile_coord") { + csd.autotile_coords = d[key]; + } else if (key == "one_way") { + csd.one_way = d[key]; + } else if (key == "one_way_margin") { + csd.one_way_margin = d[key]; + } else if (key == "shape") { + csd.shape = d[key]; + } else if (key == "shape_transform") { + csd.transform = d[key]; + } + } + ctd->shapes.push_back(csd); + } + + /* + // IGNORED FOR NOW, they seem duplicated data compared to the shapes array + } else if (what == "shape") { + // TODO + } else if (what == "shape_offset") { + // TODO + } else if (what == "shape_transform") { + // TODO + } else if (what == "shape_one_way") { + // TODO + } else if (what == "shape_one_way_margin") { + // TODO } - } + // IGNORED FOR NOW, maybe useless ? + else if (what == "occluder_offset") { + // Not + } else if (what == "navigation_offset") { + // TODO + } + */ - if (coords.size() == 0) { - return autotile_get_icon_coordinate(p_id); + } else if (what == "z_index") { + ctd->z_index = p_value; + + // TODO: remove the conversion from here, it's not where it should be done + compatibility_conversion(); + } else { + return false; + } } else { - uint32_t picked_value = Math::rand() % priority_sum; - uint32_t upper_bound; - uint32_t lower_bound = 0; - Vector2 result = coords.front()->get(); - List<Vector2>::Element *coords_E = coords.front(); - List<uint32_t>::Element *priorities_E = priorities.front(); - while (priorities_E) { - upper_bound = lower_bound + priorities_E->get(); - if (lower_bound <= picked_value && picked_value < upper_bound) { - result = coords_E->get(); - break; +#endif // DISABLE_DEPRECATED + + // This is now a new property. + if (components.size() == 2 && components[0].begins_with("occlusion_layer_") && components[0].trim_prefix("occlusion_layer_").is_valid_integer()) { + // Occlusion layers. + int index = components[0].trim_prefix("occlusion_layer_").to_int(); + ERR_FAIL_COND_V(index < 0, false); + if (components[1] == "light_mask") { + ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false); + if (index >= occlusion_layers.size()) { + set_occlusion_layers_count(index + 1); + } + set_occlusion_layer_light_mask(index, p_value); + return true; + } else if (components[1] == "sdf_collision") { + ERR_FAIL_COND_V(p_value.get_type() != Variant::BOOL, false); + if (index >= occlusion_layers.size()) { + set_occlusion_layers_count(index + 1); + } + set_occlusion_layer_sdf_collision(index, p_value); + return true; + } + } else if (components.size() == 2 && components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_integer()) { + // Physics layers. + int index = components[0].trim_prefix("physics_layer_").to_int(); + ERR_FAIL_COND_V(index < 0, false); + if (components[1] == "collision_layer") { + ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false); + if (index >= physics_layers.size()) { + set_physics_layers_count(index + 1); + } + set_physics_layer_collision_layer(index, p_value); + return true; + } else if (components[1] == "collision_mask") { + ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false); + if (index >= physics_layers.size()) { + set_physics_layers_count(index + 1); + } + set_physics_layer_collision_mask(index, p_value); + return true; + } else if (components[1] == "physics_material") { + Ref<PhysicsMaterial> physics_material = p_value; + ERR_FAIL_COND_V(!physics_material.is_valid(), false); + if (index >= physics_layers.size()) { + set_physics_layers_count(index + 1); + } + set_physics_layer_physics_material(index, physics_material); + return true; + } + } else if (components.size() >= 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_integer()) { + // Terrains. + int terrain_set_index = components[0].trim_prefix("terrain_set_").to_int(); + ERR_FAIL_COND_V(terrain_set_index < 0, false); + if (components[1] == "mode") { + ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false); + if (terrain_set_index >= terrain_sets.size()) { + set_terrain_sets_count(terrain_set_index + 1); + } + set_terrain_set_mode(terrain_set_index, TerrainMode(int(p_value))); + } else if (components[1] == "terrains_count") { + ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false); + if (terrain_set_index >= terrain_sets.size()) { + set_terrain_sets_count(terrain_set_index + 1); + } + set_terrains_count(terrain_set_index, p_value); + return true; + } else if (components.size() >= 3 && components[1].begins_with("terrain_") && components[1].trim_prefix("terrain_").is_valid_integer()) { + int terrain_index = components[1].trim_prefix("terrain_").to_int(); + ERR_FAIL_COND_V(terrain_index < 0, false); + if (components[2] == "name") { + ERR_FAIL_COND_V(p_value.get_type() != Variant::STRING, false); + if (terrain_set_index >= terrain_sets.size()) { + set_terrain_sets_count(terrain_set_index + 1); + } + if (terrain_index >= terrain_sets[terrain_set_index].terrains.size()) { + set_terrains_count(terrain_set_index, terrain_index + 1); + } + set_terrain_name(terrain_set_index, terrain_index, p_value); + return true; + } else if (components[2] == "color") { + ERR_FAIL_COND_V(p_value.get_type() != Variant::COLOR, false); + if (terrain_set_index >= terrain_sets.size()) { + set_terrain_sets_count(terrain_set_index + 1); + } + if (terrain_index >= terrain_sets[terrain_set_index].terrains.size()) { + set_terrains_count(terrain_set_index, terrain_index + 1); + } + set_terrain_color(terrain_set_index, terrain_index, p_value); + return true; + } + } + } else if (components.size() == 2 && components[0].begins_with("navigation_layer_") && components[0].trim_prefix("navigation_layer_").is_valid_integer()) { + // Navigation layers. + int index = components[0].trim_prefix("navigation_layer_").to_int(); + ERR_FAIL_COND_V(index < 0, false); + if (components[1] == "layers") { + ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false); + if (index >= navigation_layers.size()) { + set_navigation_layers_count(index + 1); + } + set_navigation_layer_layers(index, p_value); + return true; + } + } else if (components.size() == 2 && components[0].begins_with("custom_data_layer_") && components[0].trim_prefix("custom_data_layer_").is_valid_integer()) { + // Custom data layers. + int index = components[0].trim_prefix("custom_data_layer_").to_int(); + ERR_FAIL_COND_V(index < 0, false); + if (components[1] == "name") { + ERR_FAIL_COND_V(p_value.get_type() != Variant::STRING, false); + if (index >= custom_data_layers.size()) { + set_custom_data_layers_count(index + 1); + } + set_custom_data_name(index, p_value); + return true; + } else if (components[1] == "type") { + ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false); + if (index >= custom_data_layers.size()) { + set_custom_data_layers_count(index + 1); + } + set_custom_data_type(index, Variant::Type(int(p_value))); + return true; } - lower_bound = upper_bound; - priorities_E = priorities_E->next(); - coords_E = coords_E->next(); + } else if (components.size() == 2 && components[0] == "sources" && components[1].is_valid_integer()) { + // Create atlas if it does not exists. + int source_id = components[1].to_int(); + + if (!has_source(source_id)) { + add_source(p_value, source_id); + } + return true; } - return result; +#ifndef DISABLE_DEPRECATED } +#endif // DISABLE_DEPRECATED + + return false; } -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() != nullptr) { - 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; +bool TileSet::_get(const StringName &p_name, Variant &r_ret) const { + Vector<String> components = String(p_name).split("/", true, 2); + + if (components.size() == 2 && components[0].begins_with("occlusion_layer_") && components[0].trim_prefix("occlusion_layer_").is_valid_integer()) { + // Occlusion layers. + int index = components[0].trim_prefix("occlusion_layer_").to_int(); + if (index < 0 || index >= occlusion_layers.size()) { + return false; + } + if (components[1] == "light_mask") { + r_ret = get_occlusion_layer_light_mask(index); + return true; + } else if (components[1] == "sdf_collision") { + r_ret = get_occlusion_layer_sdf_collision(index); + return true; + } + } else if (components.size() == 2 && components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_integer()) { + // Physics layers. + int index = components[0].trim_prefix("physics_layer_").to_int(); + if (index < 0 || index >= physics_layers.size()) { + return false; + } + if (components[1] == "collision_layer") { + r_ret = get_physics_layer_collision_layer(index); + return true; + } else if (components[1] == "collision_mask") { + r_ret = get_physics_layer_collision_mask(index); + return true; + } else if (components[1] == "physics_material") { + r_ret = get_physics_layer_physics_material(index); + return true; + } + } else if (components.size() >= 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_integer()) { + // Terrains. + int terrain_set_index = components[0].trim_prefix("terrain_set_").to_int(); + if (terrain_set_index < 0 || terrain_set_index >= terrain_sets.size()) { + return false; + } + if (components[1] == "mode") { + r_ret = get_terrain_set_mode(terrain_set_index); + return true; + } else if (components[1] == "terrains_count") { + r_ret = get_terrains_count(terrain_set_index); + return true; + } else if (components.size() >= 3 && components[1].begins_with("terrain_") && components[1].trim_prefix("terrain_").is_valid_integer()) { + int terrain_index = components[1].trim_prefix("terrain_").to_int(); + if (terrain_index < 0 || terrain_index >= terrain_sets[terrain_set_index].terrains.size()) { + return false; + } + if (components[2] == "name") { + r_ret = get_terrain_name(terrain_set_index, terrain_index); + return true; + } else if (components[2] == "color") { + r_ret = get_terrain_color(terrain_set_index, terrain_index); + return true; } } + } else if (components.size() == 2 && components[0].begins_with("navigation_layer_") && components[0].trim_prefix("navigation_layer_").is_valid_integer()) { + // navigation layers. + int index = components[0].trim_prefix("navigation_layer_").to_int(); + if (index < 0 || index >= navigation_layers.size()) { + return false; + } + if (components[1] == "layers") { + r_ret = get_navigation_layer_layers(index); + return true; + } + } else if (components.size() == 2 && components[0].begins_with("custom_data_layer_") && components[0].trim_prefix("custom_data_layer_").is_valid_integer()) { + // Custom data layers. + int index = components[0].trim_prefix("custom_data_layer_").to_int(); + if (index < 0 || index >= custom_data_layers.size()) { + return false; + } + if (components[1] == "name") { + r_ret = get_custom_data_name(index); + return true; + } else if (components[1] == "type") { + r_ret = get_custom_data_type(index); + return true; + } + } else if (components.size() == 2 && components[0] == "sources" && components[1].is_valid_integer()) { + // Atlases data. + int source_id = components[1].to_int(); + + if (has_source(source_id)) { + r_ret = get_source(source_id); + return true; + } else { + return false; + } } - Vector2 coord = tile_get_region(p_id).size / autotile_get_size(p_id); + return false; +} - 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)); - } +void TileSet::_get_property_list(List<PropertyInfo> *p_list) const { + PropertyInfo property_info; + // Rendering. + p_list->push_back(PropertyInfo(Variant::NIL, "Rendering", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); + for (int i = 0; i < occlusion_layers.size(); i++) { + p_list->push_back(PropertyInfo(Variant::INT, vformat("occlusion_layer_%d/light_mask", i), PROPERTY_HINT_LAYERS_2D_RENDER)); + + // occlusion_layer_%d/sdf_collision + property_info = PropertyInfo(Variant::BOOL, vformat("occlusion_layer_%d/sdf_collision", i)); + if (occlusion_layers[i].sdf_collision == false) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; } + p_list->push_back(property_info); } - if (coords.size() == 0) { - return autotile_get_icon_coordinate(p_id); - } else { - return coords[Math::random(0, (int)coords.size())]; + + // Physics. + p_list->push_back(PropertyInfo(Variant::NIL, "Physics", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); + for (int i = 0; i < physics_layers.size(); i++) { + p_list->push_back(PropertyInfo(Variant::INT, vformat("physics_layer_%d/collision_layer", i), PROPERTY_HINT_LAYERS_2D_PHYSICS)); + + // physics_layer_%d/collision_mask + property_info = PropertyInfo(Variant::INT, vformat("physics_layer_%d/collision_mask", i), PROPERTY_HINT_LAYERS_2D_PHYSICS); + if (physics_layers[i].collision_mask == 1) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + p_list->push_back(property_info); + + // physics_layer_%d/physics_material + property_info = PropertyInfo(Variant::OBJECT, vformat("physics_layer_%d/physics_material", i), PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"); + if (!physics_layers[i].physics_material.is_valid()) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + p_list->push_back(property_info); } -} -void TileSet::tile_set_name(int p_id, const String &p_name) { - ERR_FAIL_COND(!tile_map.has(p_id)); - tile_map[p_id].name = p_name; - emit_changed(); + // Terrains. + p_list->push_back(PropertyInfo(Variant::NIL, "Terrains", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); + for (int terrain_set_index = 0; terrain_set_index < terrain_sets.size(); terrain_set_index++) { + p_list->push_back(PropertyInfo(Variant::INT, vformat("terrain_set_%d/mode", terrain_set_index), PROPERTY_HINT_ENUM, "Match corners and sides,Match corners,Match sides")); + p_list->push_back(PropertyInfo(Variant::INT, vformat("terrain_set_%d/terrains_count", terrain_set_index), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); + for (int terrain_index = 0; terrain_index < terrain_sets[terrain_set_index].terrains.size(); terrain_index++) { + p_list->push_back(PropertyInfo(Variant::STRING, vformat("terrain_set_%d/terrain_%d/name", terrain_set_index, terrain_index))); + p_list->push_back(PropertyInfo(Variant::COLOR, vformat("terrain_set_%d/terrain_%d/color", terrain_set_index, terrain_index))); + } + } + + // Navigation. + p_list->push_back(PropertyInfo(Variant::NIL, "Navigation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); + for (int i = 0; i < navigation_layers.size(); i++) { + p_list->push_back(PropertyInfo(Variant::INT, vformat("navigation_layer_%d/layers", i), PROPERTY_HINT_LAYERS_2D_NAVIGATION)); + } + + // Custom data. + String argt = "Any"; + for (int i = 1; i < Variant::VARIANT_MAX; i++) { + argt += "," + Variant::get_type_name(Variant::Type(i)); + } + p_list->push_back(PropertyInfo(Variant::NIL, "Custom data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); + for (int i = 0; i < custom_data_layers.size(); i++) { + p_list->push_back(PropertyInfo(Variant::STRING, vformat("custom_data_layer_%d/name", i))); + p_list->push_back(PropertyInfo(Variant::INT, vformat("custom_data_layer_%d/type", i), PROPERTY_HINT_ENUM, argt)); + } + + // Sources. + // Note: sources have to be listed in at the end as some TileData rely on the TileSet properties being initialized first. + for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) { + p_list->push_back(PropertyInfo(Variant::INT, vformat("sources/%d", E_source->key()), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + } } -String TileSet::tile_get_name(int p_id) const { - ERR_FAIL_COND_V(!tile_map.has(p_id), String()); - return tile_map[p_id].name; +void TileSet::_bind_methods() { + // Sources management. + ClassDB::bind_method(D_METHOD("get_next_source_id"), &TileSet::get_next_source_id); + ClassDB::bind_method(D_METHOD("add_source", "atlas_source_id_override"), &TileSet::add_source, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("remove_source", "source_id"), &TileSet::remove_source); + ClassDB::bind_method(D_METHOD("set_source_id", "source_id"), &TileSet::set_source_id); + ClassDB::bind_method(D_METHOD("get_source_count"), &TileSet::get_source_count); + ClassDB::bind_method(D_METHOD("get_source_id", "index"), &TileSet::get_source_id); + ClassDB::bind_method(D_METHOD("has_source", "index"), &TileSet::has_source); + ClassDB::bind_method(D_METHOD("get_source", "index"), &TileSet::get_source); + + // Shape and layout. + ClassDB::bind_method(D_METHOD("set_tile_shape", "shape"), &TileSet::set_tile_shape); + ClassDB::bind_method(D_METHOD("get_tile_shape"), &TileSet::get_tile_shape); + ClassDB::bind_method(D_METHOD("set_tile_layout", "layout"), &TileSet::set_tile_layout); + ClassDB::bind_method(D_METHOD("get_tile_layout"), &TileSet::get_tile_layout); + ClassDB::bind_method(D_METHOD("set_tile_offset_axis", "alignment"), &TileSet::set_tile_offset_axis); + ClassDB::bind_method(D_METHOD("get_tile_offset_axis"), &TileSet::get_tile_offset_axis); + ClassDB::bind_method(D_METHOD("set_tile_size", "size"), &TileSet::set_tile_size); + ClassDB::bind_method(D_METHOD("get_tile_size"), &TileSet::get_tile_size); + ClassDB::bind_method(D_METHOD("set_tile_skew", "skew"), &TileSet::set_tile_skew); + ClassDB::bind_method(D_METHOD("get_tile_skew"), &TileSet::get_tile_skew); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "tile_shape", PROPERTY_HINT_ENUM, "Square,Isometric,Half-offset square,Hexagon"), "set_tile_shape", "get_tile_shape"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "tile_layout", PROPERTY_HINT_ENUM, "Stacked,Stacked Offset,Stairs Right,Stairs Down,Diamond Right,Diamond Down"), "set_tile_layout", "get_tile_layout"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "tile_offset_axis", PROPERTY_HINT_ENUM, "Horizontal Offset,Vertical Offset"), "set_tile_offset_axis", "get_tile_offset_axis"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "tile_size"), "set_tile_size", "get_tile_size"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "tile_skew"), "set_tile_skew", "get_tile_skew"); + + // Rendering. + ClassDB::bind_method(D_METHOD("set_uv_clipping", "uv_clipping"), &TileSet::set_uv_clipping); + ClassDB::bind_method(D_METHOD("is_uv_clipping"), &TileSet::is_uv_clipping); + ClassDB::bind_method(D_METHOD("set_y_sorting", "y_sorting"), &TileSet::set_y_sorting); + ClassDB::bind_method(D_METHOD("is_y_sorting"), &TileSet::is_y_sorting); + + ClassDB::bind_method(D_METHOD("set_occlusion_layers_count", "occlusion_layers_count"), &TileSet::set_occlusion_layers_count); + ClassDB::bind_method(D_METHOD("get_occlusion_layers_count"), &TileSet::get_occlusion_layers_count); + ClassDB::bind_method(D_METHOD("set_occlusion_layer_light_mask", "layer_index", "light_mask"), &TileSet::set_occlusion_layer_light_mask); + ClassDB::bind_method(D_METHOD("get_occlusion_layer_light_mask"), &TileSet::get_occlusion_layer_light_mask); + ClassDB::bind_method(D_METHOD("set_occlusion_layer_sdf_collision", "layer_index", "sdf_collision"), &TileSet::set_occlusion_layer_sdf_collision); + ClassDB::bind_method(D_METHOD("get_occlusion_layer_sdf_collision"), &TileSet::get_occlusion_layer_sdf_collision); + + // Physics + ClassDB::bind_method(D_METHOD("set_physics_layers_count", "physics_layers_count"), &TileSet::set_physics_layers_count); + ClassDB::bind_method(D_METHOD("get_physics_layers_count"), &TileSet::get_physics_layers_count); + ClassDB::bind_method(D_METHOD("set_physics_layer_collision_layer", "layer_index", "layer"), &TileSet::set_physics_layer_collision_layer); + ClassDB::bind_method(D_METHOD("get_physics_layer_collision_layer", "layer_index"), &TileSet::get_physics_layer_collision_layer); + ClassDB::bind_method(D_METHOD("set_physics_layer_collision_mask", "layer_index", "mask"), &TileSet::set_physics_layer_collision_mask); + ClassDB::bind_method(D_METHOD("get_physics_layer_collision_mask", "layer_index"), &TileSet::get_physics_layer_collision_mask); + ClassDB::bind_method(D_METHOD("set_physics_layer_physics_material", "layer_index", "physics_material"), &TileSet::set_physics_layer_physics_material); + ClassDB::bind_method(D_METHOD("get_physics_layer_physics_material", "layer_index"), &TileSet::get_physics_layer_physics_material); + + // Terrains + ClassDB::bind_method(D_METHOD("set_terrain_sets_count", "terrain_sets_count"), &TileSet::set_terrain_sets_count); + ClassDB::bind_method(D_METHOD("get_terrain_sets_count"), &TileSet::get_terrain_sets_count); + ClassDB::bind_method(D_METHOD("set_terrain_set_mode", "terrain_set", "mode"), &TileSet::set_terrain_set_mode); + ClassDB::bind_method(D_METHOD("get_terrain_set_mode", "terrain_set"), &TileSet::get_terrain_set_mode); + + ClassDB::bind_method(D_METHOD("set_terrains_count", "terrain_set", "terrains_count"), &TileSet::set_terrains_count); + ClassDB::bind_method(D_METHOD("get_terrains_count", "terrain_set"), &TileSet::get_terrains_count); + ClassDB::bind_method(D_METHOD("set_terrain_name", "terrain_set", "terrain_index", "name"), &TileSet::set_terrain_name); + ClassDB::bind_method(D_METHOD("get_terrain_name", "terrain_set", "terrain_index"), &TileSet::get_terrain_name); + ClassDB::bind_method(D_METHOD("set_terrain_color", "terrain_set", "terrain_index", "color"), &TileSet::set_terrain_color); + ClassDB::bind_method(D_METHOD("get_terrain_color", "terrain_set", "terrain_index"), &TileSet::get_terrain_color); + + // Navigation + ClassDB::bind_method(D_METHOD("set_navigation_layers_count", "navigation_layers_count"), &TileSet::set_navigation_layers_count); + ClassDB::bind_method(D_METHOD("get_navigation_layers_count"), &TileSet::get_navigation_layers_count); + ClassDB::bind_method(D_METHOD("set_navigation_layer_layers", "layer_index", "layers"), &TileSet::set_navigation_layer_layers); + ClassDB::bind_method(D_METHOD("get_navigation_layer_layers", "layer_index"), &TileSet::get_navigation_layer_layers); + + // Custom data + ClassDB::bind_method(D_METHOD("set_custom_data_layers_count", "custom_data_layers_count"), &TileSet::set_custom_data_layers_count); + ClassDB::bind_method(D_METHOD("get_custom_data_layers_count"), &TileSet::get_custom_data_layers_count); + + ADD_GROUP("Rendering", ""); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uv_clipping"), "set_uv_clipping", "is_uv_clipping"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "y_sorting"), "set_y_sorting", "is_y_sorting"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "occlusion_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_occlusion_layers_count", "get_occlusion_layers_count"); + + ADD_GROUP("Physics", ""); + ADD_PROPERTY(PropertyInfo(Variant::INT, "physics_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_physics_layers_count", "get_physics_layers_count"); + + ADD_GROUP("Terrains", ""); + ADD_PROPERTY(PropertyInfo(Variant::INT, "terrains_sets_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_terrain_sets_count", "get_terrain_sets_count"); + + ADD_GROUP("Navigation", ""); + ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_navigation_layers_count", "get_navigation_layers_count"); + + ADD_GROUP("Custom data", ""); + ADD_PROPERTY(PropertyInfo(Variant::INT, "custom_data_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_custom_data_layers_count", "get_custom_data_layers_count"); + + // -- Enum binding -- + BIND_ENUM_CONSTANT(TILE_SHAPE_SQUARE); + BIND_ENUM_CONSTANT(TILE_SHAPE_ISOMETRIC); + BIND_ENUM_CONSTANT(TILE_SHAPE_HALF_OFFSET_SQUARE); + BIND_ENUM_CONSTANT(TILE_SHAPE_HEXAGON); + + BIND_ENUM_CONSTANT(TILE_LAYOUT_STACKED); + BIND_ENUM_CONSTANT(TILE_LAYOUT_STACKED_OFFSET); + BIND_ENUM_CONSTANT(TILE_LAYOUT_STAIRS_RIGHT); + BIND_ENUM_CONSTANT(TILE_LAYOUT_STAIRS_DOWN); + BIND_ENUM_CONSTANT(TILE_LAYOUT_DIAMOND_RIGHT); + BIND_ENUM_CONSTANT(TILE_LAYOUT_DIAMOND_DOWN); + + BIND_ENUM_CONSTANT(TILE_OFFSET_AXIS_HORIZONTAL); + BIND_ENUM_CONSTANT(TILE_OFFSET_AXIS_VERTICAL); + + BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_RIGHT_SIDE); + BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_RIGHT_CORNER); + BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE); + BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER); + BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE); + BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_BOTTOM_CORNER); + BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE); + BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER); + BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_LEFT_SIDE); + BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_LEFT_CORNER); + BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); + BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER); + BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_TOP_SIDE); + BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_TOP_CORNER); + BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); + BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER); + + BIND_ENUM_CONSTANT(TERRAIN_MODE_MATCH_CORNERS_AND_SIDES); + BIND_ENUM_CONSTANT(TERRAIN_MODE_MATCH_CORNERS); + BIND_ENUM_CONSTANT(TERRAIN_MODE_MATCH_SIDES); } -void TileSet::tile_clear_shapes(int p_id) { - ERR_FAIL_COND(!tile_map.has(p_id)); - tile_map[p_id].shapes_data.clear(); +TileSet::TileSet() { + // Instanciatie and list all plugins. + tile_set_plugins_vector.append(memnew(TileSetAtlasPluginRendering)); + tile_set_plugins_vector.append(memnew(TileSetAtlasPluginPhysics)); + tile_set_plugins_vector.append(memnew(TileSetAtlasPluginTerrain)); + tile_set_plugins_vector.append(memnew(TileSetAtlasPluginNavigation)); } -void TileSet::tile_add_shape(int p_id, const Ref<Shape2D> &p_shape, const Transform2D &p_transform, bool p_one_way, const Vector2 &p_autotile_coord) { - ERR_FAIL_COND(!tile_map.has(p_id)); +TileSet::~TileSet() { + for (Map<int, CompatibilityTileData *>::Element *E = compatibility_data.front(); E; E = E->next()) { + memdelete(E->get()); + } + while (!source_ids.is_empty()) { + remove_source(source_ids[0]); + } + for (int i = 0; i < tile_set_plugins_vector.size(); i++) { + memdelete(tile_set_plugins_vector[i]); + } +} - ShapeData new_data = ShapeData(); - new_data.shape = p_shape; - new_data.shape_transform = p_transform; - new_data.one_way_collision = p_one_way; - new_data.autotile_coord = p_autotile_coord; +/////////////////////////////// TileSetSource ////////////////////////////////////// - tile_map[p_id].shapes_data.push_back(new_data); +void TileSetSource::set_tile_set(const TileSet *p_tile_set) { + tile_set = p_tile_set; } -int TileSet::tile_get_shape_count(int p_id) const { - ERR_FAIL_COND_V(!tile_map.has(p_id), 0); - return tile_map[p_id].shapes_data.size(); +/////////////////////////////// TileSetAtlasSource ////////////////////////////////////// + +void TileSetAtlasSource::set_tile_set(const TileSet *p_tile_set) { + tile_set = p_tile_set; + + // Set the TileSet on all TileData. + for (Map<Vector2i, TileAlternativesData>::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) { + for (Map<int, TileData *>::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) { + E_alternative->get()->set_tile_set(tile_set); + } + } } -void TileSet::tile_set_shape(int p_id, int p_shape_id, const Ref<Shape2D> &p_shape) { - ERR_FAIL_COND(!tile_map.has(p_id)); - ERR_FAIL_COND(p_shape_id < 0); +void TileSetAtlasSource::notify_tile_data_properties_should_change() { + // Set the TileSet on all TileData. + for (Map<Vector2i, TileAlternativesData>::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) { + for (Map<int, TileData *>::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) { + E_alternative->get()->notify_tile_data_properties_should_change(); + } + } +} - if (p_shape_id >= tile_map[p_id].shapes_data.size()) { - tile_map[p_id].shapes_data.resize(p_shape_id + 1); +void TileSetAtlasSource::reset_state() { + // Reset all TileData. + for (Map<Vector2i, TileAlternativesData>::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) { + for (Map<int, TileData *>::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) { + E_alternative->get()->reset_state(); + } } - tile_map[p_id].shapes_data.write[p_shape_id].shape = p_shape; - _decompose_convex_shape(p_shape); +} + +void TileSetAtlasSource::set_texture(Ref<Texture2D> p_texture) { + texture = p_texture; + emit_changed(); } -Ref<Shape2D> TileSet::tile_get_shape(int p_id, int p_shape_id) const { - ERR_FAIL_COND_V(!tile_map.has(p_id), Ref<Shape2D>()); - ERR_FAIL_COND_V(p_shape_id < 0, Ref<Shape2D>()); +Ref<Texture2D> TileSetAtlasSource::get_texture() const { + return texture; +} - if (p_shape_id < tile_map[p_id].shapes_data.size()) { - return tile_map[p_id].shapes_data[p_shape_id].shape; +void TileSetAtlasSource::set_margins(Vector2i p_margins) { + if (p_margins.x < 0 || p_margins.y < 0) { + WARN_PRINT("Atlas source margins should be positive."); + margins = Vector2i(MAX(0, p_margins.x), MAX(0, p_margins.y)); + } else { + margins = p_margins; } - return Ref<Shape2D>(); + emit_changed(); +} +Vector2i TileSetAtlasSource::get_margins() const { + return margins; } -void TileSet::tile_set_shape_transform(int p_id, int p_shape_id, const Transform2D &p_offset) { - ERR_FAIL_COND(!tile_map.has(p_id)); - ERR_FAIL_COND(p_shape_id < 0); +void TileSetAtlasSource::set_separation(Vector2i p_separation) { + if (p_separation.x < 0 || p_separation.y < 0) { + WARN_PRINT("Atlas source separation should be positive."); + separation = Vector2i(MAX(0, p_separation.x), MAX(0, p_separation.y)); + } else { + separation = p_separation; + } + + emit_changed(); +} +Vector2i TileSetAtlasSource::get_separation() const { + return separation; +} - if (p_shape_id >= tile_map[p_id].shapes_data.size()) { - tile_map[p_id].shapes_data.resize(p_shape_id + 1); +void TileSetAtlasSource::set_texture_region_size(Vector2i p_tile_size) { + if (p_tile_size.x <= 0 || p_tile_size.y <= 0) { + WARN_PRINT("Atlas source tile_size should be strictly positive."); + texture_region_size = Vector2i(MAX(1, p_tile_size.x), MAX(1, p_tile_size.y)); + } else { + texture_region_size = p_tile_size; } - tile_map[p_id].shapes_data.write[p_shape_id].shape_transform = p_offset; + emit_changed(); } +Vector2i TileSetAtlasSource::get_texture_region_size() const { + return texture_region_size; +} + +Vector2i TileSetAtlasSource::get_atlas_grid_size() const { + Ref<Texture2D> texture = get_texture(); + if (!texture.is_valid()) { + return Vector2i(); + } + + ERR_FAIL_COND_V(texture_region_size.x <= 0 || texture_region_size.y <= 0, Vector2i()); + + Size2i valid_area = texture->get_size() - margins; + + // Compute the number of valid tiles in the tiles atlas + Size2i grid_size = Size2i(); + if (valid_area.x >= texture_region_size.x && valid_area.y >= texture_region_size.y) { + valid_area -= texture_region_size; + grid_size = Size2i(1, 1) + valid_area / (texture_region_size + separation); + } + return grid_size; +} -Transform2D TileSet::tile_get_shape_transform(int p_id, int p_shape_id) const { - ERR_FAIL_COND_V(!tile_map.has(p_id), Transform2D()); - ERR_FAIL_COND_V(p_shape_id < 0, Transform2D()); +bool TileSetAtlasSource::_set(const StringName &p_name, const Variant &p_value) { + Vector<String> components = String(p_name).split("/", true, 2); - if (p_shape_id < tile_map[p_id].shapes_data.size()) { - return tile_map[p_id].shapes_data[p_shape_id].shape_transform; + // Compute the vector2i if we have coordinates. + Vector<String> coords_split = components[0].split(":"); + Vector2i coords = TileSetAtlasSource::INVALID_ATLAS_COORDS; + if (coords_split.size() == 2 && coords_split[0].is_valid_integer() && coords_split[1].is_valid_integer()) { + coords = Vector2i(coords_split[0].to_int(), coords_split[1].to_int()); } - return Transform2D(); + // Properties. + if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) { + // Create the tile if needed. + if (!has_tile(coords)) { + create_tile(coords); + } + if (components.size() >= 2) { + // Properties. + if (components[1] == "size_in_atlas") { + move_tile_in_atlas(coords, coords, p_value); + } else if (components[1] == "next_alternative_id") { + tiles[coords].next_alternative_id = p_value; + } else if (components[1].is_valid_integer()) { + int alternative_id = components[1].to_int(); + if (alternative_id != TileSetAtlasSource::INVALID_TILE_ALTERNATIVE) { + // Create the alternative if needed ? + if (!has_alternative_tile(coords, alternative_id)) { + create_alternative_tile(coords, alternative_id); + } + if (!tiles[coords].alternatives.has(alternative_id)) { + tiles[coords].alternatives[alternative_id] = memnew(TileData); + tiles[coords].alternatives[alternative_id]->set_tile_set(tile_set); + tiles[coords].alternatives[alternative_id]->set_allow_transform(alternative_id > 0); + tiles[coords].alternatives_ids.append(alternative_id); + } + if (components.size() >= 3) { + bool valid; + tiles[coords].alternatives[alternative_id]->set(components[2], p_value, &valid); + return valid; + } else { + // Only create the alternative if it did not exist yet. + return true; + } + } + } + } + } + + return false; } -void TileSet::tile_set_shape_offset(int p_id, int p_shape_id, const Vector2 &p_offset) { - Transform2D transform = tile_get_shape_transform(p_id, p_shape_id); - transform.set_origin(p_offset); - tile_set_shape_transform(p_id, p_shape_id, transform); +bool TileSetAtlasSource::_get(const StringName &p_name, Variant &r_ret) const { + Vector<String> components = String(p_name).split("/", true, 2); + + // Properties. + Vector<String> coords_split = components[0].split(":"); + if (coords_split.size() == 2 && coords_split[0].is_valid_integer() && coords_split[1].is_valid_integer()) { + Vector2i coords = Vector2i(coords_split[0].to_int(), coords_split[1].to_int()); + if (tiles.has(coords)) { + if (components.size() >= 2) { + // Properties. + if (components[1] == "size_in_atlas") { + r_ret = tiles[coords].size_in_atlas; + return true; + } else if (components[1] == "next_alternative_id") { + r_ret = tiles[coords].next_alternative_id; + return true; + } else if (components[1].is_valid_integer()) { + int alternative_id = components[1].to_int(); + if (alternative_id != TileSetAtlasSource::INVALID_TILE_ALTERNATIVE && tiles[coords].alternatives.has(alternative_id)) { + if (components.size() >= 3) { + bool valid; + r_ret = tiles[coords].alternatives[alternative_id]->get(components[2], &valid); + return valid; + } else { + // Only to notify the tile alternative exists. + r_ret = alternative_id; + return true; + } + } + } + } + } + } + + return false; } -Vector2 TileSet::tile_get_shape_offset(int p_id, int p_shape_id) const { - return tile_get_shape_transform(p_id, p_shape_id).get_origin(); +void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const { + // Atlases data. + PropertyInfo property_info; + for (Map<Vector2i, TileAlternativesData>::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) { + List<PropertyInfo> tile_property_list; + + // size_in_atlas + property_info = PropertyInfo(Variant::VECTOR2I, "size_in_atlas", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); + if (E_tile->get().size_in_atlas == Vector2i(1, 1)) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + tile_property_list.push_back(property_info); + + // next_alternative_id + property_info = PropertyInfo(Variant::INT, "next_alternative_id", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); + if (E_tile->get().next_alternative_id == 1) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + tile_property_list.push_back(property_info); + + for (Map<int, TileData *>::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) { + // Add a dummy property to show the alternative exists. + tile_property_list.push_back(PropertyInfo(Variant::INT, vformat("%d", E_alternative->key()), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + + // Get the alternative tile's properties and append them to the list of properties. + List<PropertyInfo> alternative_property_list; + E_alternative->get()->get_property_list(&alternative_property_list); + for (List<PropertyInfo>::Element *E_property = alternative_property_list.front(); E_property; E_property = E_property->next()) { + property_info = E_property->get(); + bool valid; + Variant default_value = ClassDB::class_get_default_property_value("TileData", property_info.name, &valid); + Variant value = E_alternative->get()->get(property_info.name); + if (valid && value == default_value) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + property_info.name = vformat("%s/%s", vformat("%d", E_alternative->key()), property_info.name); + tile_property_list.push_back(property_info); + } + } + + // Add all alternative. + for (List<PropertyInfo>::Element *E_property = tile_property_list.front(); E_property; E_property = E_property->next()) { + E_property->get().name = vformat("%s/%s", vformat("%d:%d", E_tile->key().x, E_tile->key().y), E_property->get().name); + p_list->push_back(E_property->get()); + } + } } -void TileSet::tile_set_shape_one_way(int p_id, int p_shape_id, const bool p_one_way) { - ERR_FAIL_COND(!tile_map.has(p_id)); - ERR_FAIL_COND(p_shape_id < 0); +void TileSetAtlasSource::create_tile(const Vector2i p_atlas_coords, const Vector2i p_size) { + // Create a tile if it does not exists. + ERR_FAIL_COND(p_atlas_coords.x < 0 || p_atlas_coords.y < 0); + ERR_FAIL_COND(p_size.x <= 0 || p_size.y <= 0); + for (int x = 0; x < p_size.x; x++) { + for (int y = 0; y < p_size.y; y++) { + Vector2i coords = p_atlas_coords + Vector2i(x, y); + ERR_FAIL_COND_MSG(tiles.has(coords), vformat("Cannot create tile at position %s with size %s. Already a tile present at %s.", p_atlas_coords, p_size, coords)); + } + } - if (p_shape_id >= tile_map[p_id].shapes_data.size()) { - tile_map[p_id].shapes_data.resize(p_shape_id + 1); + // Create and resize the tile. + tiles.insert(p_atlas_coords, TileSetAtlasSource::TileAlternativesData()); + tiles_ids.append(p_atlas_coords); + tiles_ids.sort(); + + tiles[p_atlas_coords].size_in_atlas = p_size; + tiles[p_atlas_coords].alternatives[0] = memnew(TileData); + tiles[p_atlas_coords].alternatives[0]->set_tile_set(tile_set); + tiles[p_atlas_coords].alternatives[0]->set_allow_transform(false); + tiles[p_atlas_coords].alternatives[0]->connect("changed", callable_mp((Resource *)this, &TileSetAtlasSource::emit_changed)); + tiles[p_atlas_coords].alternatives[0]->notify_property_list_changed(); + tiles[p_atlas_coords].alternatives_ids.append(0); + + // Add all covered positions to the mapping cache + for (int x = 0; x < p_size.x; x++) { + for (int y = 0; y < p_size.y; y++) { + Vector2i coords = p_atlas_coords + Vector2i(x, y); + _coords_mapping_cache[coords] = p_atlas_coords; + } } - tile_map[p_id].shapes_data.write[p_shape_id].one_way_collision = p_one_way; - emit_changed(); + + emit_signal("changed"); } -bool TileSet::tile_get_shape_one_way(int p_id, int p_shape_id) const { - ERR_FAIL_COND_V(!tile_map.has(p_id), false); - ERR_FAIL_COND_V(p_shape_id < 0, false); +void TileSetAtlasSource::remove_tile(Vector2i p_atlas_coords) { + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); + + // Remove all covered positions from the mapping cache + Size2i size = tiles[p_atlas_coords].size_in_atlas; - if (p_shape_id < tile_map[p_id].shapes_data.size()) { - return tile_map[p_id].shapes_data[p_shape_id].one_way_collision; + for (int x = 0; x < size.x; x++) { + for (int y = 0; y < size.y; y++) { + Vector2i coords = p_atlas_coords + Vector2i(x, y); + _coords_mapping_cache.erase(coords); + } } - return false; + // Free tile data. + for (Map<int, TileData *>::Element *E_tile_data = tiles[p_atlas_coords].alternatives.front(); E_tile_data; E_tile_data = E_tile_data->next()) { + memdelete(E_tile_data->get()); + } + + // Delete the tile + tiles.erase(p_atlas_coords); + tiles_ids.erase(p_atlas_coords); + tiles_ids.sort(); + + emit_signal("changed"); } -void TileSet::tile_set_shape_one_way_margin(int p_id, int p_shape_id, float p_margin) { - ERR_FAIL_COND(!tile_map.has(p_id)); - ERR_FAIL_COND(p_shape_id < 0); +bool TileSetAtlasSource::has_tile(Vector2i p_atlas_coords) const { + return tiles.has(p_atlas_coords); +} - if (p_shape_id >= tile_map[p_id].shapes_data.size()) { - tile_map[p_id].shapes_data.resize(p_shape_id + 1); +Vector2i TileSetAtlasSource::get_tile_at_coords(Vector2i p_atlas_coords) const { + if (!_coords_mapping_cache.has(p_atlas_coords)) { + return INVALID_ATLAS_COORDS; } - tile_map[p_id].shapes_data.write[p_shape_id].one_way_collision_margin = p_margin; - emit_changed(); + + return _coords_mapping_cache[p_atlas_coords]; +} + +Vector2i TileSetAtlasSource::get_tile_size_in_atlas(Vector2i p_atlas_coords) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Vector2i(-1, -1), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); + + return tiles[p_atlas_coords].size_in_atlas; } -float TileSet::tile_get_shape_one_way_margin(int p_id, int p_shape_id) const { - ERR_FAIL_COND_V(!tile_map.has(p_id), 0); - ERR_FAIL_COND_V(p_shape_id < 0, 0); +int TileSetAtlasSource::get_tiles_count() const { + return tiles_ids.size(); +} + +Vector2i TileSetAtlasSource::get_tile_id(int p_index) const { + ERR_FAIL_INDEX_V(p_index, tiles_ids.size(), TileSetAtlasSource::INVALID_ATLAS_COORDS); + return tiles_ids[p_index]; +} + +Rect2i TileSetAtlasSource::get_tile_texture_region(Vector2i p_atlas_coords) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Rect2i(), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); + + Vector2i size_in_atlas = tiles[p_atlas_coords].size_in_atlas; + Vector2 region_size = texture_region_size * size_in_atlas + separation * (size_in_atlas - Vector2i(1, 1)); + + Vector2 origin = margins + (p_atlas_coords * (texture_region_size + separation)); + + return Rect2(origin, region_size); + ; +} - if (p_shape_id < tile_map[p_id].shapes_data.size()) { - return tile_map[p_id].shapes_data[p_shape_id].one_way_collision_margin; +Vector2i TileSetAtlasSource::get_tile_effective_texture_offset(Vector2i p_atlas_coords, int p_alternative_tile) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Vector2i(), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + ERR_FAIL_COND_V_MSG(!has_alternative_tile(p_atlas_coords, p_alternative_tile), Vector2i(), vformat("TileSetAtlasSource has no alternative tile with id %d at %s.", p_alternative_tile, String(p_atlas_coords))); + ERR_FAIL_COND_V(!tile_set, Vector2i()); + + Vector2 margin = (get_tile_texture_region(p_atlas_coords).size - tile_set->get_tile_size()) / 2; + margin = Vector2i(MAX(0, margin.x), MAX(0, margin.y)); + Vector2i effective_texture_offset = Object::cast_to<TileData>(get_tile_data(p_atlas_coords, p_alternative_tile))->get_texture_offset(); + if (ABS(effective_texture_offset.x) > margin.x || ABS(effective_texture_offset.y) > margin.y) { + effective_texture_offset.x = CLAMP(effective_texture_offset.x, -margin.x, margin.x); + effective_texture_offset.y = CLAMP(effective_texture_offset.y, -margin.y, margin.y); } - return 0; + return effective_texture_offset; } -void TileSet::tile_set_light_occluder(int p_id, const Ref<OccluderPolygon2D> &p_light_occluder) { - ERR_FAIL_COND(!tile_map.has(p_id)); - tile_map[p_id].occluder = p_light_occluder; +bool TileSetAtlasSource::can_move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords, Vector2i p_new_size) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), false, vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); + + Vector2i new_atlas_coords = (p_new_atlas_coords != INVALID_ATLAS_COORDS) ? p_new_atlas_coords : p_atlas_coords; + if (new_atlas_coords.x < 0 || new_atlas_coords.y < 0) { + return false; + } + + Vector2i size = (p_new_size != Vector2i(-1, -1)) ? p_new_size : tiles[p_atlas_coords].size_in_atlas; + ERR_FAIL_COND_V(size.x <= 0 || size.y <= 0, false); + + Size2i grid_size = get_atlas_grid_size(); + if (new_atlas_coords.x + size.x > grid_size.x || new_atlas_coords.y + size.y > grid_size.y) { + return false; + } + + Rect2i new_rect = Rect2i(new_atlas_coords, size); + // Check if the new tile can fit in the new rect. + for (int x = new_rect.position.x; x < new_rect.get_end().x; x++) { + for (int y = new_rect.position.y; y < new_rect.get_end().y; y++) { + Vector2i coords = get_tile_at_coords(Vector2i(x, y)); + if (coords != p_atlas_coords && coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) { + return false; + } + } + } + + return true; } -Ref<OccluderPolygon2D> TileSet::tile_get_light_occluder(int p_id) const { - ERR_FAIL_COND_V(!tile_map.has(p_id), Ref<OccluderPolygon2D>()); - return tile_map[p_id].occluder; +void TileSetAtlasSource::move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords, Vector2i p_new_size) { + bool can_move = can_move_tile_in_atlas(p_atlas_coords, p_new_atlas_coords, p_new_size); + ERR_FAIL_COND_MSG(!can_move, vformat("Cannot move tile at position %s with size %s. Tile already present.", p_new_atlas_coords, p_new_size)); + + // Compute the actual new rect from arguments. + Vector2i new_atlas_coords = (p_new_atlas_coords != INVALID_ATLAS_COORDS) ? p_new_atlas_coords : p_atlas_coords; + Vector2i size = (p_new_size != Vector2i(-1, -1)) ? p_new_size : tiles[p_atlas_coords].size_in_atlas; + + if (new_atlas_coords == p_atlas_coords && size == tiles[p_atlas_coords].size_in_atlas) { + return; + } + + // Remove all covered positions from the mapping cache. + Size2i old_size = tiles[p_atlas_coords].size_in_atlas; + for (int x = 0; x < old_size.x; x++) { + for (int y = 0; y < old_size.y; y++) { + Vector2i coords = p_atlas_coords + Vector2i(x, y); + _coords_mapping_cache.erase(coords); + } + } + + // Move the tile and update its size. + if (new_atlas_coords != p_atlas_coords) { + tiles[new_atlas_coords] = tiles[p_atlas_coords]; + tiles.erase(p_atlas_coords); + + tiles_ids.erase(p_atlas_coords); + tiles_ids.append(new_atlas_coords); + tiles_ids.sort(); + } + tiles[new_atlas_coords].size_in_atlas = size; + + // Add all covered positions to the mapping cache again. + for (int x = 0; x < size.x; x++) { + for (int y = 0; y < size.y; y++) { + Vector2i coords = new_atlas_coords + Vector2i(x, y); + _coords_mapping_cache[coords] = new_atlas_coords; + } + } + + emit_signal("changed"); } -void TileSet::autotile_set_light_occluder(int p_id, const Ref<OccluderPolygon2D> &p_light_occluder, const Vector2 &p_coord) { - ERR_FAIL_COND(!tile_map.has(p_id)); - if (p_light_occluder.is_null()) { - if (tile_map[p_id].autotile_data.occluder_map.has(p_coord)) { - tile_map[p_id].autotile_data.occluder_map.erase(p_coord); +bool TileSetAtlasSource::has_tiles_outside_texture() { + Vector2i grid_size = get_atlas_grid_size(); + Vector<Vector2i> to_remove; + + for (Map<Vector2i, TileSetAtlasSource::TileAlternativesData>::Element *E = tiles.front(); E; E = E->next()) { + if (E->key().x >= grid_size.x || E->key().y >= grid_size.y) { + return true; } - } else { - tile_map[p_id].autotile_data.occluder_map[p_coord] = p_light_occluder; } + + return false; } -Ref<OccluderPolygon2D> TileSet::autotile_get_light_occluder(int p_id, const Vector2 &p_coord) const { - ERR_FAIL_COND_V(!tile_map.has(p_id), Ref<OccluderPolygon2D>()); +void TileSetAtlasSource::clear_tiles_outside_texture() { + Vector2i grid_size = get_atlas_grid_size(); + Vector<Vector2i> to_remove; - if (!tile_map[p_id].autotile_data.occluder_map.has(p_coord)) { - return Ref<OccluderPolygon2D>(); - } else { - return tile_map[p_id].autotile_data.occluder_map[p_coord]; + for (Map<Vector2i, TileSetAtlasSource::TileAlternativesData>::Element *E = tiles.front(); E; E = E->next()) { + if (E->key().x >= grid_size.x || E->key().y >= grid_size.y) { + to_remove.append(E->key()); + } + } + + for (int i = 0; i < to_remove.size(); i++) { + remove_tile(to_remove[i]); } } -void TileSet::tile_set_navigation_polygon_offset(int p_id, const Vector2 &p_offset) { - ERR_FAIL_COND(!tile_map.has(p_id)); - tile_map[p_id].navigation_polygon_offset = p_offset; +int TileSetAtlasSource::create_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_id_override) { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), -1, vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); + ERR_FAIL_COND_V_MSG(p_alternative_id_override >= 0 && (tiles[p_atlas_coords].alternatives.has(p_alternative_id_override) || tiles[p_atlas_coords].alternatives.has(p_alternative_id_override)), -1, vformat("Cannot create alternative tile. Another alternative exists with id %d.", p_alternative_id_override)); + + int new_alternative_id = p_alternative_id_override >= 0 ? p_alternative_id_override : tiles[p_atlas_coords].next_alternative_id; + + tiles[p_atlas_coords].alternatives[new_alternative_id] = memnew(TileData); + tiles[p_atlas_coords].alternatives[new_alternative_id]->set_tile_set(tile_set); + tiles[p_atlas_coords].alternatives[new_alternative_id]->set_allow_transform(true); + tiles[p_atlas_coords].alternatives[new_alternative_id]->notify_property_list_changed(); + tiles[p_atlas_coords].alternatives_ids.append(new_alternative_id); + tiles[p_atlas_coords].alternatives_ids.sort(); + _compute_next_alternative_id(p_atlas_coords); + + emit_signal("changed"); + + return new_alternative_id; +} + +void TileSetAtlasSource::remove_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile) { + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); + ERR_FAIL_COND_MSG(!tiles[p_atlas_coords].alternatives.has(p_alternative_tile), vformat("TileSetAtlasSource has no alternative with id %d for tile coords %s.", p_alternative_tile, String(p_atlas_coords))); + ERR_FAIL_COND_MSG(p_alternative_tile == 0, "Cannot remove the alternative with id 0, the base tile alternative cannot be removed."); + + memdelete(tiles[p_atlas_coords].alternatives[p_alternative_tile]); + tiles[p_atlas_coords].alternatives.erase(p_alternative_tile); + tiles[p_atlas_coords].alternatives_ids.erase(p_alternative_tile); + tiles[p_atlas_coords].alternatives_ids.sort(); + + emit_signal("changed"); +} + +void TileSetAtlasSource::set_alternative_tile_id(const Vector2i p_atlas_coords, int p_alternative_tile, int p_new_id) { + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); + ERR_FAIL_COND_MSG(!tiles[p_atlas_coords].alternatives.has(p_alternative_tile), vformat("TileSetAtlasSource has no alternative with id %d for tile coords %s.", p_alternative_tile, String(p_atlas_coords))); + ERR_FAIL_COND_MSG(p_alternative_tile == 0, "Cannot change the alternative with id 0, the base tile alternative cannot be modified."); + + ERR_FAIL_COND_MSG(tiles[p_atlas_coords].alternatives.has(p_new_id), vformat("TileSetAtlasSource has already an alternative with id %d at %s.", p_new_id, String(p_atlas_coords))); + + tiles[p_atlas_coords].alternatives[p_new_id] = tiles[p_atlas_coords].alternatives[p_alternative_tile]; + tiles[p_atlas_coords].alternatives_ids.append(p_new_id); + + tiles[p_atlas_coords].alternatives.erase(p_alternative_tile); + tiles[p_atlas_coords].alternatives_ids.erase(p_alternative_tile); + tiles[p_atlas_coords].alternatives_ids.sort(); + + emit_signal("changed"); +} + +bool TileSetAtlasSource::has_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), false, vformat("The TileSetAtlasSource atlas has no tile at %s.", String(p_atlas_coords))); + return tiles[p_atlas_coords].alternatives.has(p_alternative_tile); +} + +int TileSetAtlasSource::get_next_alternative_tile_id(const Vector2i p_atlas_coords) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), -1, vformat("The TileSetAtlasSource atlas has no tile at %s.", String(p_atlas_coords))); + return tiles[p_atlas_coords].next_alternative_id; } -Vector2 TileSet::tile_get_navigation_polygon_offset(int p_id) const { - ERR_FAIL_COND_V(!tile_map.has(p_id), Vector2()); - return tile_map[p_id].navigation_polygon_offset; +int TileSetAtlasSource::get_alternative_tiles_count(const Vector2i p_atlas_coords) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), -1, vformat("The TileSetAtlasSource atlas has no tile at %s.", String(p_atlas_coords))); + return tiles[p_atlas_coords].alternatives_ids.size(); } -void TileSet::tile_set_navigation_polygon(int p_id, const Ref<NavigationPolygon> &p_navigation_polygon) { - ERR_FAIL_COND(!tile_map.has(p_id)); - tile_map[p_id].navigation_polygon = p_navigation_polygon; +int TileSetAtlasSource::get_alternative_tile_id(const Vector2i p_atlas_coords, int p_index) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), -1, vformat("The TileSetAtlasSource atlas has no tile at %s.", String(p_atlas_coords))); + ERR_FAIL_INDEX_V(p_index, tiles[p_atlas_coords].alternatives_ids.size(), -1); + + return tiles[p_atlas_coords].alternatives_ids[p_index]; } -Ref<NavigationPolygon> TileSet::tile_get_navigation_polygon(int p_id) const { - ERR_FAIL_COND_V(!tile_map.has(p_id), Ref<NavigationPolygon>()); - return tile_map[p_id].navigation_polygon; +Object *TileSetAtlasSource::get_tile_data(const Vector2i p_atlas_coords, int p_alternative_tile) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), nullptr, vformat("The TileSetAtlasSource atlas has no tile at %s.", String(p_atlas_coords))); + ERR_FAIL_COND_V_MSG(!tiles[p_atlas_coords].alternatives.has(p_alternative_tile), nullptr, vformat("TileSetAtlasSource has no alternative with id %d for tile coords %s.", p_alternative_tile, String(p_atlas_coords))); + + return tiles[p_atlas_coords].alternatives[p_alternative_tile]; } -const Map<Vector2, Ref<OccluderPolygon2D>> &TileSet::autotile_get_light_oclusion_map(int p_id) const { - static Map<Vector2, Ref<OccluderPolygon2D>> dummy; - ERR_FAIL_COND_V(!tile_map.has(p_id), dummy); - return tile_map[p_id].autotile_data.occluder_map; +void TileSetAtlasSource::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_texture", "texture"), &TileSetAtlasSource::set_texture); + ClassDB::bind_method(D_METHOD("get_texture"), &TileSetAtlasSource::get_texture); + ClassDB::bind_method(D_METHOD("set_margins", "margins"), &TileSetAtlasSource::set_margins); + ClassDB::bind_method(D_METHOD("get_margins"), &TileSetAtlasSource::get_margins); + ClassDB::bind_method(D_METHOD("set_separation", "separation"), &TileSetAtlasSource::set_separation); + ClassDB::bind_method(D_METHOD("get_separation"), &TileSetAtlasSource::get_separation); + ClassDB::bind_method(D_METHOD("set_texture_region_size", "texture_region_size"), &TileSetAtlasSource::set_texture_region_size); + ClassDB::bind_method(D_METHOD("get_texture_region_size"), &TileSetAtlasSource::get_texture_region_size); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_NOEDITOR), "set_texture", "get_texture"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "margins", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_margins", "get_margins"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "separation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_separation", "get_separation"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "tile_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_texture_region_size", "get_texture_region_size"); + + // Base tiles + ClassDB::bind_method(D_METHOD("create_tile", "atlas_coords", "size"), &TileSetAtlasSource::create_tile, DEFVAL(Vector2i(1, 1))); + ClassDB::bind_method(D_METHOD("remove_tile", "atlas_coords"), &TileSetAtlasSource::remove_tile); // Remove a tile. If p_tile_key.alternative_tile if different from 0, remove the alternative + ClassDB::bind_method(D_METHOD("has_tile", "atlas_coords"), &TileSetAtlasSource::has_tile); + ClassDB::bind_method(D_METHOD("can_move_tile_in_atlas", "atlas_coords", "new_atlas_coords", "new_size"), &TileSetAtlasSource::can_move_tile_in_atlas, DEFVAL(INVALID_ATLAS_COORDS), DEFVAL(Vector2i(-1, -1))); + ClassDB::bind_method(D_METHOD("move_tile_in_atlas", "atlas_coords", "new_atlas_coords", "new_size"), &TileSetAtlasSource::move_tile_in_atlas, DEFVAL(INVALID_ATLAS_COORDS), DEFVAL(Vector2i(-1, -1))); + ClassDB::bind_method(D_METHOD("get_tile_size_in_atlas", "atlas_coords"), &TileSetAtlasSource::get_tile_size_in_atlas); + + ClassDB::bind_method(D_METHOD("get_tiles_count"), &TileSetAtlasSource::get_tiles_count); + ClassDB::bind_method(D_METHOD("get_tile_id", "index"), &TileSetAtlasSource::get_tile_id); + + ClassDB::bind_method(D_METHOD("get_tile_at_coords", "atlas_coords"), &TileSetAtlasSource::get_tile_at_coords); + + // Alternative tiles + ClassDB::bind_method(D_METHOD("create_alternative_tile", "atlas_coords", "alternative_id_override"), &TileSetAtlasSource::create_alternative_tile, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("remove_alternative_tile", "atlas_coords", "alternative_tile"), &TileSetAtlasSource::remove_alternative_tile); + ClassDB::bind_method(D_METHOD("set_alternative_tile_id", "atlas_coords", "alternative_tile", "new_id"), &TileSetAtlasSource::set_alternative_tile_id); + ClassDB::bind_method(D_METHOD("has_alternative_tile", "atlas_coords", "alternative_tile"), &TileSetAtlasSource::has_alternative_tile); + ClassDB::bind_method(D_METHOD("get_next_alternative_tile_id", "atlas_coords"), &TileSetAtlasSource::get_next_alternative_tile_id); + + ClassDB::bind_method(D_METHOD("get_alternative_tiles_count", "atlas_coords"), &TileSetAtlasSource::get_alternative_tiles_count); + ClassDB::bind_method(D_METHOD("get_alternative_tile_id", "atlas_coords", "index"), &TileSetAtlasSource::get_alternative_tile_id); + + ClassDB::bind_method(D_METHOD("get_tile_data", "atlas_coords", "index"), &TileSetAtlasSource::get_tile_data); + + // Helpers. + ClassDB::bind_method(D_METHOD("get_atlas_grid_size"), &TileSetAtlasSource::get_atlas_grid_size); + ClassDB::bind_method(D_METHOD("has_tiles_outside_texture"), &TileSetAtlasSource::has_tiles_outside_texture); + ClassDB::bind_method(D_METHOD("clear_tiles_outside_texture"), &TileSetAtlasSource::clear_tiles_outside_texture); + ClassDB::bind_method(D_METHOD("get_tile_texture_region", "atlas_coords"), &TileSetAtlasSource::get_tile_texture_region); } -void TileSet::autotile_set_navigation_polygon(int p_id, const Ref<NavigationPolygon> &p_navigation_polygon, const Vector2 &p_coord) { - ERR_FAIL_COND(!tile_map.has(p_id)); - if (p_navigation_polygon.is_null()) { - if (tile_map[p_id].autotile_data.navpoly_map.has(p_coord)) { - tile_map[p_id].autotile_data.navpoly_map.erase(p_coord); +TileSetAtlasSource::~TileSetAtlasSource() { + // Free everything needed. + for (Map<Vector2i, TileAlternativesData>::Element *E_alternatives = tiles.front(); E_alternatives; E_alternatives = E_alternatives->next()) { + for (Map<int, TileData *>::Element *E_tile_data = E_alternatives->get().alternatives.front(); E_tile_data; E_tile_data = E_tile_data->next()) { + memdelete(E_tile_data->get()); } - } else { - tile_map[p_id].autotile_data.navpoly_map[p_coord] = p_navigation_polygon; } } -Ref<NavigationPolygon> TileSet::autotile_get_navigation_polygon(int p_id, const Vector2 &p_coord) const { - ERR_FAIL_COND_V(!tile_map.has(p_id), Ref<NavigationPolygon>()); - if (!tile_map[p_id].autotile_data.navpoly_map.has(p_coord)) { - return Ref<NavigationPolygon>(); - } else { - return tile_map[p_id].autotile_data.navpoly_map[p_coord]; +TileData *TileSetAtlasSource::_get_atlas_tile_data(Vector2i p_atlas_coords, int p_alternative_tile) { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), nullptr, vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); + ERR_FAIL_COND_V_MSG(!tiles[p_atlas_coords].alternatives.has(p_alternative_tile), nullptr, vformat("TileSetAtlasSource has no alternative with id %d for tile coords %s.", p_alternative_tile, String(p_atlas_coords))); + + return tiles[p_atlas_coords].alternatives[p_alternative_tile]; +} + +const TileData *TileSetAtlasSource::_get_atlas_tile_data(Vector2i p_atlas_coords, int p_alternative_tile) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), nullptr, vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); + ERR_FAIL_COND_V_MSG(!tiles[p_atlas_coords].alternatives.has(p_alternative_tile), nullptr, vformat("TileSetAtlasSource has no alternative with id %d for tile coords %s.", p_alternative_tile, String(p_atlas_coords))); + + return tiles[p_atlas_coords].alternatives[p_alternative_tile]; +} + +void TileSetAtlasSource::_compute_next_alternative_id(const Vector2i p_atlas_coords) { + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); + + while (tiles[p_atlas_coords].alternatives.has(tiles[p_atlas_coords].next_alternative_id)) { + tiles[p_atlas_coords].next_alternative_id = (tiles[p_atlas_coords].next_alternative_id % 1073741823) + 1; // 2 ** 30 + }; +} + +/////////////////////////////// TileData ////////////////////////////////////// + +void TileData::set_tile_set(const TileSet *p_tile_set) { + tile_set = p_tile_set; + if (tile_set) { + occluders.resize(tile_set->get_occlusion_layers_count()); + physics.resize(tile_set->get_physics_layers_count()); + navigation.resize(tile_set->get_navigation_layers_count()); + custom_data.resize(tile_set->get_custom_data_layers_count()); + } + notify_property_list_changed(); +} + +void TileData::notify_tile_data_properties_should_change() { + occluders.resize(tile_set->get_occlusion_layers_count()); + physics.resize(tile_set->get_physics_layers_count()); + for (int bit_index = 0; bit_index < 16; bit_index++) { + if (terrain_set < 0 || terrain_peering_bits[bit_index] >= tile_set->get_terrains_count(terrain_set)) { + terrain_peering_bits[bit_index] = -1; + } + } + navigation.resize(tile_set->get_navigation_layers_count()); + + // Convert custom data to the new type. + custom_data.resize(tile_set->get_custom_data_layers_count()); + for (int i = 0; i < custom_data.size(); i++) { + if (custom_data[i].get_type() != tile_set->get_custom_data_type(i)) { + Variant new_val; + Callable::CallError error; + if (Variant::can_convert(custom_data[i].get_type(), tile_set->get_custom_data_type(i))) { + const Variant *args[] = { &custom_data[i] }; + Variant::construct(tile_set->get_custom_data_type(i), new_val, args, 1, error); + } else { + Variant::construct(tile_set->get_custom_data_type(i), new_val, nullptr, 0, error); + } + custom_data.write[i] = new_val; + } } + + notify_property_list_changed(); + emit_signal("changed"); +} + +void TileData::reset_state() { + occluders.clear(); + physics.clear(); + navigation.clear(); + custom_data.clear(); +} + +void TileData::set_allow_transform(bool p_allow_transform) { + allow_transform = p_allow_transform; +} + +bool TileData::is_allowing_transform() const { + return allow_transform; +} + +// Rendering +void TileData::set_flip_h(bool p_flip_h) { + ERR_FAIL_COND_MSG(!allow_transform && p_flip_h, "Transform is only allowed for alternative tiles (with its alternative_id != 0)"); + flip_h = p_flip_h; + emit_signal("changed"); +} +bool TileData::get_flip_h() const { + return flip_h; +} + +void TileData::set_flip_v(bool p_flip_v) { + ERR_FAIL_COND_MSG(!allow_transform && p_flip_v, "Transform is only allowed for alternative tiles (with its alternative_id != 0)"); + flip_v = p_flip_v; + emit_signal("changed"); +} + +bool TileData::get_flip_v() const { + return flip_v; +} + +void TileData::set_transpose(bool p_transpose) { + ERR_FAIL_COND_MSG(!allow_transform && p_transpose, "Transform is only allowed for alternative tiles (with its alternative_id != 0)"); + transpose = p_transpose; + emit_signal("changed"); +} +bool TileData::get_transpose() const { + return transpose; +} + +void TileData::set_texture_offset(Vector2i p_texture_offset) { + tex_offset = p_texture_offset; + emit_signal("changed"); +} + +Vector2i TileData::get_texture_offset() const { + return tex_offset; +} + +void TileData::tile_set_material(Ref<ShaderMaterial> p_material) { + material = p_material; + emit_signal("changed"); +} +Ref<ShaderMaterial> TileData::tile_get_material() const { + return material; +} + +void TileData::set_modulate(Color p_modulate) { + modulate = p_modulate; + emit_signal("changed"); +} +Color TileData::get_modulate() const { + return modulate; +} + +void TileData::set_z_index(int p_z_index) { + z_index = p_z_index; + emit_signal("changed"); +} +int TileData::get_z_index() const { + return z_index; +} + +void TileData::set_y_sort_origin(Vector2i p_y_sort_origin) { + y_sort_origin = p_y_sort_origin; + emit_signal("changed"); +} +Vector2i TileData::get_y_sort_origin() const { + return y_sort_origin; +} + +void TileData::set_occluder(int p_layer_id, Ref<OccluderPolygon2D> p_occluder_polygon) { + ERR_FAIL_INDEX(p_layer_id, occluders.size()); + occluders.write[p_layer_id] = p_occluder_polygon; + emit_signal("changed"); +} + +Ref<OccluderPolygon2D> TileData::get_occluder(int p_layer_id) const { + ERR_FAIL_INDEX_V(p_layer_id, occluders.size(), Ref<OccluderPolygon2D>()); + return occluders[p_layer_id]; } -const Map<Vector2, Ref<NavigationPolygon>> &TileSet::autotile_get_navigation_map(int p_id) const { - static Map<Vector2, Ref<NavigationPolygon>> dummy; - ERR_FAIL_COND_V(!tile_map.has(p_id), dummy); - return tile_map[p_id].autotile_data.navpoly_map; +// Physics +int TileData::get_collision_shapes_count(int p_layer_id) const { + ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0); + return physics[p_layer_id].shapes.size(); } -void TileSet::tile_set_occluder_offset(int p_id, const Vector2 &p_offset) { - ERR_FAIL_COND(!tile_map.has(p_id)); - tile_map[p_id].occluder_offset = p_offset; +void TileData::set_collision_shapes_count(int p_layer_id, int p_shapes_count) { + ERR_FAIL_INDEX(p_layer_id, physics.size()); + ERR_FAIL_COND(p_shapes_count < 0); + physics.write[p_layer_id].shapes.resize(p_shapes_count); + notify_property_list_changed(); + emit_signal("changed"); +} + +void TileData::add_collision_shape(int p_layer_id) { + ERR_FAIL_INDEX(p_layer_id, physics.size()); + physics.write[p_layer_id].shapes.push_back(PhysicsLayerTileData::ShapeTileData()); + emit_signal("changed"); +} + +void TileData::remove_collision_shape(int p_layer_id, int p_shape_index) { + ERR_FAIL_INDEX(p_layer_id, physics.size()); + ERR_FAIL_INDEX(p_shape_index, physics[p_layer_id].shapes.size()); + physics.write[p_layer_id].shapes.remove(p_shape_index); + emit_signal("changed"); +} + +void TileData::set_collision_shape_shape(int p_layer_id, int p_shape_index, Ref<Shape2D> p_shape) { + ERR_FAIL_INDEX(p_layer_id, physics.size()); + ERR_FAIL_INDEX(p_shape_index, physics[p_layer_id].shapes.size()); + physics.write[p_layer_id].shapes.write[p_shape_index].shape = p_shape; + emit_signal("changed"); } -Vector2 TileSet::tile_get_occluder_offset(int p_id) const { - ERR_FAIL_COND_V(!tile_map.has(p_id), Vector2()); - return tile_map[p_id].occluder_offset; +Ref<Shape2D> TileData::get_collision_shape_shape(int p_layer_id, int p_shape_index) const { + ERR_FAIL_INDEX_V(p_layer_id, physics.size(), Ref<Shape2D>()); + ERR_FAIL_INDEX_V(p_shape_index, physics[p_layer_id].shapes.size(), Ref<Shape2D>()); + return physics[p_layer_id].shapes[p_shape_index].shape; } -void TileSet::tile_set_shapes(int p_id, const Vector<ShapeData> &p_shapes) { - ERR_FAIL_COND(!tile_map.has(p_id)); - tile_map[p_id].shapes_data = p_shapes; - for (int i = 0; i < p_shapes.size(); i++) { - _decompose_convex_shape(p_shapes[i].shape); +void TileData::set_collision_shape_one_way(int p_layer_id, int p_shape_index, bool p_one_way) { + ERR_FAIL_INDEX(p_layer_id, physics.size()); + ERR_FAIL_INDEX(p_shape_index, physics[p_layer_id].shapes.size()); + physics.write[p_layer_id].shapes.write[p_shape_index].one_way = p_one_way; + emit_signal("changed"); +} + +bool TileData::is_collision_shape_one_way(int p_layer_id, int p_shape_index) const { + ERR_FAIL_INDEX_V(p_layer_id, physics.size(), false); + ERR_FAIL_INDEX_V(p_shape_index, physics[p_layer_id].shapes.size(), false); + return physics[p_layer_id].shapes[p_shape_index].one_way; +} + +void TileData::set_collision_shape_one_way_margin(int p_layer_id, int p_shape_index, float p_one_way_margin) { + ERR_FAIL_INDEX(p_layer_id, physics.size()); + ERR_FAIL_INDEX(p_shape_index, physics[p_layer_id].shapes.size()); + physics.write[p_layer_id].shapes.write[p_shape_index].one_way_margin = p_one_way_margin; + emit_signal("changed"); +} + +float TileData::get_collision_shape_one_way_margin(int p_layer_id, int p_shape_index) const { + ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0.0); + ERR_FAIL_INDEX_V(p_shape_index, physics[p_layer_id].shapes.size(), 0.0); + return physics[p_layer_id].shapes[p_shape_index].one_way_margin; +} + +// Terrain +void TileData::set_terrain_set(int p_terrain_set) { + ERR_FAIL_COND(p_terrain_set < -1); + if (tile_set) { + ERR_FAIL_COND(p_terrain_set >= tile_set->get_terrain_sets_count()); } - emit_changed(); + terrain_set = p_terrain_set; + notify_property_list_changed(); + emit_signal("changed"); } -Vector<TileSet::ShapeData> TileSet::tile_get_shapes(int p_id) const { - ERR_FAIL_COND_V(!tile_map.has(p_id), Vector<ShapeData>()); +int TileData::get_terrain_set() const { + return terrain_set; +} - return tile_map[p_id].shapes_data; +void TileData::set_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit, int p_terrain_index) { + ERR_FAIL_COND(p_terrain_index < -1); + if (tile_set) { + ERR_FAIL_COND(p_terrain_index >= tile_set->get_terrains_count(terrain_set)); + ERR_FAIL_COND(!is_valid_peering_bit_terrain(p_peering_bit)); + } + terrain_peering_bits[p_peering_bit] = p_terrain_index; + emit_signal("changed"); } -int TileSet::tile_get_z_index(int p_id) const { - ERR_FAIL_COND_V(!tile_map.has(p_id), 0); - return tile_map[p_id].z_index; +int TileData::get_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit) const { + return terrain_peering_bits[p_peering_bit]; } -void TileSet::tile_set_z_index(int p_id, int p_z_index) { - ERR_FAIL_COND(!tile_map.has(p_id)); - tile_map[p_id].z_index = p_z_index; - emit_changed(); +bool TileData::is_valid_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit) const { + ERR_FAIL_COND_V(!tile_set, false); + + return tile_set->is_valid_peering_bit_terrain(terrain_set, p_peering_bit); } -void TileSet::_tile_set_shapes(int p_id, const Array &p_shapes) { - ERR_FAIL_COND(!tile_map.has(p_id)); - Vector<ShapeData> shapes_data; - Transform2D default_transform = tile_get_shape_transform(p_id, 0); - bool default_one_way = tile_get_shape_one_way(p_id, 0); - Vector2 default_autotile_coord = Vector2(); - for (int i = 0; i < p_shapes.size(); i++) { - ShapeData s = ShapeData(); +// Navigation +void TileData::set_navigation_polygon(int p_layer_id, Ref<NavigationPolygon> p_navigation_polygon) { + ERR_FAIL_INDEX(p_layer_id, navigation.size()); + navigation.write[p_layer_id] = p_navigation_polygon; + emit_signal("changed"); +} - if (p_shapes[i].get_type() == Variant::OBJECT) { - Ref<Shape2D> shape = p_shapes[i]; - if (shape.is_null()) { - continue; +Ref<NavigationPolygon> TileData::get_navigation_polygon(int p_layer_id) const { + ERR_FAIL_INDEX_V(p_layer_id, navigation.size(), Ref<NavigationPolygon>()); + return navigation[p_layer_id]; +} + +// Misc +void TileData::set_probability(float p_probability) { + ERR_FAIL_COND(p_probability <= 0.0); + probability = p_probability; + emit_signal("changed"); +} +float TileData::get_probability() const { + return probability; +} + +// Custom data +void TileData::set_custom_data(String p_layer_name, Variant p_value) { + ERR_FAIL_COND(!tile_set); + int p_layer_id = tile_set->get_custom_data_layer_by_name(p_layer_name); + ERR_FAIL_COND_MSG(p_layer_id < 0, vformat("TileSet has no layer with name: %s", p_layer_name)); + set_custom_data_by_layer_id(p_layer_id, p_value); +} + +Variant TileData::get_custom_data(String p_layer_name) const { + ERR_FAIL_COND_V(!tile_set, Variant()); + int p_layer_id = tile_set->get_custom_data_layer_by_name(p_layer_name); + ERR_FAIL_COND_V_MSG(p_layer_id < 0, Variant(), vformat("TileSet has no layer with name: %s", p_layer_name)); + return get_custom_data_by_layer_id(p_layer_id); +} + +void TileData::set_custom_data_by_layer_id(int p_layer_id, Variant p_value) { + ERR_FAIL_INDEX(p_layer_id, custom_data.size()); + custom_data.write[p_layer_id] = p_value; + emit_signal("changed"); +} + +Variant TileData::get_custom_data_by_layer_id(int p_layer_id) const { + ERR_FAIL_INDEX_V(p_layer_id, custom_data.size(), Variant()); + return custom_data[p_layer_id]; +} + +bool TileData::_set(const StringName &p_name, const Variant &p_value) { + Vector<String> components = String(p_name).split("/", true, 2); + + if (components.size() == 2 && components[0].begins_with("occlusion_layer_") && components[0].trim_prefix("occlusion_layer_").is_valid_integer()) { + // Occlusion layers. + int layer_index = components[0].trim_prefix("occlusion_layer_").to_int(); + ERR_FAIL_COND_V(layer_index < 0, false); + if (components[1] == "polygon") { + Ref<OccluderPolygon2D> polygon = p_value; + if (!polygon.is_valid()) { + return false; } - s.shape = shape; - s.shape_transform = default_transform; - s.one_way_collision = default_one_way; - s.autotile_coord = default_autotile_coord; - } else if (p_shapes[i].get_type() == Variant::DICTIONARY) { - Dictionary d = p_shapes[i]; + if (layer_index >= occluders.size()) { + if (tile_set) { + return false; + } else { + occluders.resize(layer_index + 1); + } + } + set_occluder(layer_index, polygon); + return true; + } + } else if (components.size() >= 2 && components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_integer()) { + // Physics layers. + int layer_index = components[0].trim_prefix("physics_layer_").to_int(); + ERR_FAIL_COND_V(layer_index < 0, false); + if (components.size() == 2 && components[1] == "shapes_count") { + if (p_value.get_type() != Variant::INT) { + return false; + } - if (d.has("shape") && d["shape"].get_type() == Variant::OBJECT) { - s.shape = d["shape"]; - _decompose_convex_shape(s.shape); - } else { - continue; + if (layer_index >= physics.size()) { + if (tile_set) { + return false; + } else { + physics.resize(layer_index + 1); + } } + set_collision_shapes_count(layer_index, p_value); + return true; + } else if (components.size() == 3 && components[1].begins_with("shape_") && components[1].trim_prefix("shape_").is_valid_integer()) { + int shape_index = components[1].trim_prefix("shape_").to_int(); + ERR_FAIL_COND_V(shape_index < 0, false); + + if (components[2] == "shape" || components[2] == "one_way" || components[2] == "one_way_margin") { + if (layer_index >= physics.size()) { + if (tile_set) { + return false; + } else { + physics.resize(layer_index + 1); + } + } - if (d.has("shape_transform") && d["shape_transform"].get_type() == Variant::TRANSFORM2D) { - s.shape_transform = d["shape_transform"]; - } else if (d.has("shape_offset") && d["shape_offset"].get_type() == Variant::VECTOR2) { - s.shape_transform = Transform2D(0, (Vector2)d["shape_offset"]); - } else { - s.shape_transform = default_transform; + if (shape_index >= physics[layer_index].shapes.size()) { + physics.write[layer_index].shapes.resize(shape_index + 1); + } + } + if (components[2] == "shape") { + Ref<Shape2D> shape = p_value; + set_collision_shape_shape(layer_index, shape_index, shape); + return true; + } else if (components[2] == "one_way") { + set_collision_shape_one_way(layer_index, shape_index, p_value); + return true; + } else if (components[2] == "one_way_margin") { + set_collision_shape_one_way_margin(layer_index, shape_index, p_value); + return true; + } + } + } else if (components.size() == 2 && components[0].begins_with("navigation_layer_") && components[0].trim_prefix("navigation_layer_").is_valid_integer()) { + // Navigation layers. + int layer_index = components[0].trim_prefix("navigation_layer_").to_int(); + ERR_FAIL_COND_V(layer_index < 0, false); + if (components[1] == "polygon") { + Ref<NavigationPolygon> polygon = p_value; + if (!polygon.is_valid()) { + return false; } - if (d.has("one_way") && d["one_way"].get_type() == Variant::BOOL) { - s.one_way_collision = d["one_way"]; + if (layer_index >= navigation.size()) { + if (tile_set) { + return false; + } else { + navigation.resize(layer_index + 1); + } + } + set_navigation_polygon(layer_index, polygon); + return true; + } + } else if (components.size() == 2 && components[0] == "terrains_peering_bit") { + // Terrains. + if (components[1] == "right_side") { + set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_SIDE, p_value); + } else if (components[1] == "right_corner") { + set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_CORNER, p_value); + } else if (components[1] == "bottom_right_side") { + set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, p_value); + } else if (components[1] == "bottom_right_corner") { + set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER, p_value); + } else if (components[1] == "bottom_side") { + set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE, p_value); + } else if (components[1] == "bottom_corner") { + set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_CORNER, p_value); + } else if (components[1] == "bottom_left_side") { + set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, p_value); + } else if (components[1] == "bottom_left_corner") { + set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER, p_value); + } else if (components[1] == "left_side") { + set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_SIDE, p_value); + } else if (components[1] == "left_corner") { + set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_CORNER, p_value); + } else if (components[1] == "top_left_side") { + set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, p_value); + } else if (components[1] == "top_left_corner") { + set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER, p_value); + } else if (components[1] == "top_side") { + set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_SIDE, p_value); + } else if (components[1] == "top_corner") { + set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_CORNER, p_value); + } else if (components[1] == "top_right_side") { + set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, p_value); + } else if (components[1] == "top_right_corner") { + set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER, p_value); + } else { + return false; + } + return true; + } else if (components.size() == 1 && components[0].begins_with("custom_data_") && components[0].trim_prefix("custom_data_").is_valid_integer()) { + // Custom data layers. + int layer_index = components[0].trim_prefix("custom_data_").to_int(); + ERR_FAIL_COND_V(layer_index < 0, false); + + if (layer_index >= custom_data.size()) { + if (tile_set) { + return false; } else { - s.one_way_collision = default_one_way; + custom_data.resize(layer_index + 1); } + } + set_custom_data_by_layer_id(layer_index, p_value); + + return true; + } + + return false; +} - if (d.has("one_way_margin") && d["one_way_margin"].is_num()) { - s.one_way_collision_margin = d["one_way_margin"]; +bool TileData::_get(const StringName &p_name, Variant &r_ret) const { + Vector<String> components = String(p_name).split("/", true, 2); + + if (tile_set) { + if (components.size() == 2 && components[0].begins_with("occlusion_layer") && components[0].trim_prefix("occlusion_layer_").is_valid_integer()) { + // Occlusion layers. + int layer_index = components[0].trim_prefix("occlusion_layer_").to_int(); + ERR_FAIL_COND_V(layer_index < 0, false); + if (layer_index >= occluders.size()) { + return false; + } + if (components[1] == "polygon") { + r_ret = get_occluder(layer_index); + return true; + } + } else if (components.size() >= 2 && components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_integer()) { + // Physics layers. + int layer_index = components[0].trim_prefix("physics_layer_").to_int(); + ERR_FAIL_COND_V(layer_index < 0, false); + if (layer_index >= physics.size()) { + return false; + } + if (components.size() == 2 && components[1] == "shapes_count") { + r_ret = get_collision_shapes_count(layer_index); + return true; + } else if (components.size() == 3 && components[1].begins_with("shape_") && components[1].trim_prefix("shape_").is_valid_integer()) { + int shape_index = components[1].trim_prefix("shape_").to_int(); + ERR_FAIL_COND_V(shape_index < 0, false); + if (shape_index >= physics[layer_index].shapes.size()) { + return false; + } + if (components[2] == "shape") { + r_ret = get_collision_shape_shape(layer_index, shape_index); + return true; + } else if (components[2] == "one_way") { + r_ret = is_collision_shape_one_way(layer_index, shape_index); + return true; + } else if (components[2] == "one_way_margin") { + r_ret = get_collision_shape_one_way_margin(layer_index, shape_index); + return true; + } + } + } else if (components.size() == 2 && components[0] == "terrains_peering_bit") { + // Terrains. + if (components[1] == "right_side") { + r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_RIGHT_SIDE]; + } else if (components[1] == "right_corner") { + r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_RIGHT_CORNER]; + } else if (components[1] == "bottom_right_side") { + r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE]; + } else if (components[1] == "bottom_right_corner") { + r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER]; + } else if (components[1] == "bottom_side") { + r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_BOTTOM_SIDE]; + } else if (components[1] == "bottom_corner") { + r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_BOTTOM_CORNER]; + } else if (components[1] == "bottom_left_side") { + r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE]; + } else if (components[1] == "bottom_left_corner") { + r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER]; + } else if (components[1] == "left_side") { + r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_LEFT_SIDE]; + } else if (components[1] == "left_corner") { + r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_LEFT_CORNER]; + } else if (components[1] == "top_left_side") { + r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE]; + } else if (components[1] == "top_left_corner") { + r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER]; + } else if (components[1] == "top_side") { + r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_TOP_SIDE]; + } else if (components[1] == "top_corner") { + r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_TOP_CORNER]; + } else if (components[1] == "top_right_side") { + r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE]; + } else if (components[1] == "top_right_corner") { + r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER]; } else { - s.one_way_collision_margin = 1.0; + return false; + } + return true; + } else if (components.size() == 2 && components[0].begins_with("navigation_layer_") && components[0].trim_prefix("navigation_layer_").is_valid_integer()) { + // Occlusion layers. + int layer_index = components[0].trim_prefix("navigation_layer_").to_int(); + ERR_FAIL_COND_V(layer_index < 0, false); + if (layer_index >= navigation.size()) { + return false; + } + if (components[1] == "polygon") { + r_ret = get_navigation_polygon(layer_index); + return true; + } + } else if (components.size() == 1 && components[0].begins_with("custom_data_") && components[0].trim_prefix("custom_data_").is_valid_integer()) { + // Custom data layers. + int layer_index = components[0].trim_prefix("custom_data_").to_int(); + ERR_FAIL_COND_V(layer_index < 0, false); + if (layer_index >= custom_data.size()) { + return false; } + r_ret = get_custom_data_by_layer_id(layer_index); + return true; + } + } - if (d.has("autotile_coord") && d["autotile_coord"].get_type() == Variant::VECTOR2) { - s.autotile_coord = d["autotile_coord"]; - } else { - s.autotile_coord = default_autotile_coord; + return false; +} + +void TileData::_get_property_list(List<PropertyInfo> *p_list) const { + PropertyInfo property_info; + // Add the groups manually. + if (tile_set) { + // Occlusion layers. + p_list->push_back(PropertyInfo(Variant::NIL, "Rendering", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); + for (int i = 0; i < occluders.size(); i++) { + // occlusion_layer_%d/polygon + property_info = PropertyInfo(Variant::OBJECT, vformat("occlusion_layer_%d/polygon", i), PROPERTY_HINT_RESOURCE_TYPE, "OccluderPolygon2D", PROPERTY_USAGE_DEFAULT); + if (!occluders[i].is_valid()) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; } + p_list->push_back(property_info); + } - } else { - ERR_CONTINUE_MSG(true, "Expected an array of objects or dictionaries for tile_set_shapes."); + // Physics layers. + p_list->push_back(PropertyInfo(Variant::NIL, "Physics", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); + for (int i = 0; i < physics.size(); i++) { + p_list->push_back(PropertyInfo(Variant::INT, vformat("physics_layer_%d/shapes_count", i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); + + for (int j = 0; j < physics[i].shapes.size(); j++) { + // physics_layer_%d/shapes_count + property_info = PropertyInfo(Variant::OBJECT, vformat("physics_layer_%d/shape_%d/shape", i, j), PROPERTY_HINT_RESOURCE_TYPE, "Shape2D", PROPERTY_USAGE_DEFAULT); + if (!physics[i].shapes[j].shape.is_valid()) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + p_list->push_back(property_info); + + // physics_layer_%d/shape_%d/one_way + property_info = PropertyInfo(Variant::BOOL, vformat("physics_layer_%d/shape_%d/one_way", i, j)); + if (physics[i].shapes[j].one_way == false) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + p_list->push_back(property_info); + + // physics_layer_%d/shape_%d/one_way_margin + property_info = PropertyInfo(Variant::FLOAT, vformat("physics_layer_%d/shape_%d/one_way_margin", i, j)); + if (physics[i].shapes[j].one_way_margin == 1.0) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + p_list->push_back(property_info); + } + } + + // Terrain data + if (terrain_set >= 0) { + p_list->push_back(PropertyInfo(Variant::NIL, "Terrains", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); + if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_SIDE)) { + property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/right_side"); + if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_SIDE) == -1) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + p_list->push_back(property_info); + } + if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_CORNER)) { + property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/right_corner"); + if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_CORNER) == -1) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + p_list->push_back(property_info); + } + if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)) { + property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/bottom_right_side"); + if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) == -1) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + p_list->push_back(property_info); + } + if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER)) { + property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/bottom_right_corner"); + if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER) == -1) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + p_list->push_back(property_info); + } + if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)) { + property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/bottom_side"); + if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE) == -1) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + p_list->push_back(property_info); + } + if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_CORNER)) { + property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/bottom_corner"); + if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) == -1) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + p_list->push_back(property_info); + } + if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)) { + property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/bottom_left_side"); + if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) == -1) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + p_list->push_back(property_info); + } + if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER)) { + property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/bottom_left_corner"); + if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER) == -1) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + p_list->push_back(property_info); + } + if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_SIDE)) { + property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/left_side"); + if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_SIDE) == -1) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + p_list->push_back(property_info); + } + if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_CORNER)) { + property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/left_corner"); + if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_CORNER) == -1) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + p_list->push_back(property_info); + } + if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE)) { + property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/top_left_side"); + if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) == -1) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + p_list->push_back(property_info); + } + if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER)) { + property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/top_left_corner"); + if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER) == -1) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + p_list->push_back(property_info); + } + if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_SIDE)) { + property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/top_side"); + if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_SIDE) == -1) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + p_list->push_back(property_info); + } + if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_CORNER)) { + property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/top_corner"); + if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_CORNER) == -1) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + p_list->push_back(property_info); + } + if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE)) { + property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/top_right_side"); + if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) == -1) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + p_list->push_back(property_info); + } + if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER)) { + property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/top_right_corner"); + if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER) == -1) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + p_list->push_back(property_info); + } } - shapes_data.push_back(s); + // Navigation layers. + p_list->push_back(PropertyInfo(Variant::NIL, "Navigation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); + for (int i = 0; i < navigation.size(); i++) { + property_info = PropertyInfo(Variant::OBJECT, vformat("navigation_layer_%d/polygon", i), PROPERTY_HINT_RESOURCE_TYPE, "NavigationPolygon", PROPERTY_USAGE_DEFAULT); + if (!navigation[i].is_valid()) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + p_list->push_back(property_info); + } + + // Custom data layers. + p_list->push_back(PropertyInfo(Variant::NIL, "Custom data", PROPERTY_HINT_NONE, "custom_data_", PROPERTY_USAGE_GROUP)); + for (int i = 0; i < custom_data.size(); i++) { + Variant default_val; + Callable::CallError error; + Variant::construct(custom_data[i].get_type(), default_val, nullptr, 0, error); + property_info = PropertyInfo(tile_set->get_custom_data_type(i), vformat("custom_data_%d", i), PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT); + if (custom_data[i] == default_val) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + p_list->push_back(property_info); + } } +} - tile_map[p_id].shapes_data = shapes_data; - emit_changed(); +void TileData::_bind_methods() { + // Rendering. + ClassDB::bind_method(D_METHOD("set_flip_h", "flip_h"), &TileData::set_flip_h); + ClassDB::bind_method(D_METHOD("get_flip_h"), &TileData::get_flip_h); + ClassDB::bind_method(D_METHOD("set_flip_v", "flip_v"), &TileData::set_flip_v); + ClassDB::bind_method(D_METHOD("get_flip_v"), &TileData::get_flip_v); + ClassDB::bind_method(D_METHOD("set_transpose", "transpose"), &TileData::set_transpose); + ClassDB::bind_method(D_METHOD("get_transpose"), &TileData::get_transpose); + ClassDB::bind_method(D_METHOD("tile_set_material", "material"), &TileData::tile_set_material); + ClassDB::bind_method(D_METHOD("tile_get_material"), &TileData::tile_get_material); + ClassDB::bind_method(D_METHOD("set_texture_offset", "texture_offset"), &TileData::set_texture_offset); + ClassDB::bind_method(D_METHOD("get_texture_offset"), &TileData::get_texture_offset); + ClassDB::bind_method(D_METHOD("set_modulate", "modulate"), &TileData::set_modulate); + ClassDB::bind_method(D_METHOD("get_modulate"), &TileData::get_modulate); + ClassDB::bind_method(D_METHOD("set_z_index", "z_index"), &TileData::set_z_index); + ClassDB::bind_method(D_METHOD("get_z_index"), &TileData::get_z_index); + ClassDB::bind_method(D_METHOD("set_y_sort_origin", "y_sort_origin"), &TileData::set_y_sort_origin); + ClassDB::bind_method(D_METHOD("get_y_sort_origin"), &TileData::get_y_sort_origin); + + ClassDB::bind_method(D_METHOD("set_occluder", "layer_id", "occluder_polygon"), &TileData::set_occluder); + ClassDB::bind_method(D_METHOD("get_occluder", "layer_id"), &TileData::get_occluder); + + // Physics. + ClassDB::bind_method(D_METHOD("get_collision_shapes_count", "layer_id"), &TileData::get_collision_shapes_count); + ClassDB::bind_method(D_METHOD("set_collision_shapes_count", "layer_id", "shapes_count"), &TileData::set_collision_shapes_count); + ClassDB::bind_method(D_METHOD("add_collision_shape", "layer_id"), &TileData::add_collision_shape); + ClassDB::bind_method(D_METHOD("remove_collision_shape", "layer_id", "shape_index"), &TileData::remove_collision_shape); + ClassDB::bind_method(D_METHOD("set_collision_shape_shape", "layer_id", "shape_index", "shape"), &TileData::set_collision_shape_shape); + ClassDB::bind_method(D_METHOD("get_collision_shape_shape", "layer_id", "shape_index"), &TileData::get_collision_shape_shape); + ClassDB::bind_method(D_METHOD("set_collision_shape_one_way", "layer_id", "shape_index", "one_way"), &TileData::set_collision_shape_one_way); + ClassDB::bind_method(D_METHOD("is_collision_shape_one_way", "layer_id", "shape_index"), &TileData::is_collision_shape_one_way); + ClassDB::bind_method(D_METHOD("set_collision_shape_one_way_margin", "layer_id", "shape_index", "one_way_margin"), &TileData::set_collision_shape_one_way_margin); + ClassDB::bind_method(D_METHOD("get_collision_shape_one_way_margin", "layer_id", "shape_index"), &TileData::get_collision_shape_one_way_margin); + + // Terrain + ClassDB::bind_method(D_METHOD("set_terrain_set", "terrain_set"), &TileData::set_terrain_set); + ClassDB::bind_method(D_METHOD("get_terrain_set"), &TileData::get_terrain_set); + ClassDB::bind_method(D_METHOD("set_peering_bit_terrain", "peering_bit", "terrain"), &TileData::set_peering_bit_terrain); + ClassDB::bind_method(D_METHOD("get_peering_bit_terrain", "peering_bit"), &TileData::get_peering_bit_terrain); + + // Navigation + ClassDB::bind_method(D_METHOD("set_navigation_polygon", "layer_id", "navigation_polygon"), &TileData::set_navigation_polygon); + ClassDB::bind_method(D_METHOD("get_navigation_polygon", "layer_id"), &TileData::get_navigation_polygon); + + // Misc. + ClassDB::bind_method(D_METHOD("set_probability", "probability"), &TileData::set_probability); + ClassDB::bind_method(D_METHOD("get_probability"), &TileData::get_probability); + + // Custom data. + ClassDB::bind_method(D_METHOD("set_custom_data", "layer_name", "value"), &TileData::set_custom_data); + ClassDB::bind_method(D_METHOD("get_custom_data", "layer_name"), &TileData::get_custom_data); + ClassDB::bind_method(D_METHOD("set_custom_data_by_layer_id", "layer_id", "value"), &TileData::set_custom_data_by_layer_id); + ClassDB::bind_method(D_METHOD("get_custom_data_by_layer_id", "layer_id"), &TileData::get_custom_data_by_layer_id); + + ADD_GROUP("Rendering", ""); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_h"), "set_flip_h", "get_flip_h"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_v"), "set_flip_v", "get_flip_v"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "transpose"), "set_transpose", "get_transpose"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "texture_offset"), "set_texture_offset", "get_texture_offset"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate"), "set_modulate", "get_modulate"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "z_index"), "set_z_index", "get_z_index"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "y_sort_origin"), "set_y_sort_origin", "get_y_sort_origin"); + + ADD_GROUP("Terrains", ""); + ADD_PROPERTY(PropertyInfo(Variant::INT, "terrain_set"), "set_terrain_set", "get_terrain_set"); + + ADD_GROUP("Miscellaneous", ""); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "probability"), "set_probability", "get_probability"); + + ADD_SIGNAL(MethodInfo("changed")); } -Array TileSet::_tile_get_shapes(int p_id) const { - ERR_FAIL_COND_V(!tile_map.has(p_id), Array()); - Array arr; +/////////////////////////////// TileSetAtlasPluginTerrain ////////////////////////////////////// + +// --- PLUGINS --- +void TileSetAtlasPluginTerrain::_draw_square_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) { + Rect2 bit_rect; + bit_rect.size = Vector2(p_size) / 3; + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: + bit_rect.position = Vector2(1, -1); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + bit_rect.position = Vector2(1, 1); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: + bit_rect.position = Vector2(-1, 1); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + bit_rect.position = Vector2(-3, 1); + break; + case TileSet::CELL_NEIGHBOR_LEFT_SIDE: + bit_rect.position = Vector2(-3, -1); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + bit_rect.position = Vector2(-3, -3); + break; + case TileSet::CELL_NEIGHBOR_TOP_SIDE: + bit_rect.position = Vector2(-1, -3); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + bit_rect.position = Vector2(1, -3); + break; + default: + break; + } + bit_rect.position *= Vector2(p_size) / 6.0; + p_canvas_item->draw_rect(bit_rect, p_color); +} - Vector<ShapeData> data = tile_map[p_id].shapes_data; - for (int i = 0; i < data.size(); i++) { - Dictionary shape_data; - shape_data["shape"] = data[i].shape; - shape_data["shape_transform"] = data[i].shape_transform; - shape_data["one_way"] = data[i].one_way_collision; - shape_data["one_way_margin"] = data[i].one_way_collision_margin; - shape_data["autotile_coord"] = data[i].autotile_coord; - arr.push_back(shape_data); +void TileSetAtlasPluginTerrain::_draw_square_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) { + PackedColorArray color_array; + color_array.push_back(p_color); + + Vector2 unit = Vector2(p_size) / 6.0; + PackedVector2Array polygon; + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + polygon.push_back(Vector2(0, 3) * unit); + polygon.push_back(Vector2(3, 3) * unit); + polygon.push_back(Vector2(3, 0) * unit); + polygon.push_back(Vector2(1, 0) * unit); + polygon.push_back(Vector2(1, 1) * unit); + polygon.push_back(Vector2(0, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + polygon.push_back(Vector2(0, 3) * unit); + polygon.push_back(Vector2(-3, 3) * unit); + polygon.push_back(Vector2(-3, 0) * unit); + polygon.push_back(Vector2(-1, 0) * unit); + polygon.push_back(Vector2(-1, 1) * unit); + polygon.push_back(Vector2(0, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + polygon.push_back(Vector2(0, -3) * unit); + polygon.push_back(Vector2(-3, -3) * unit); + polygon.push_back(Vector2(-3, 0) * unit); + polygon.push_back(Vector2(-1, 0) * unit); + polygon.push_back(Vector2(-1, -1) * unit); + polygon.push_back(Vector2(0, -1) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + polygon.push_back(Vector2(0, -3) * unit); + polygon.push_back(Vector2(3, -3) * unit); + polygon.push_back(Vector2(3, 0) * unit); + polygon.push_back(Vector2(1, 0) * unit); + polygon.push_back(Vector2(1, -1) * unit); + polygon.push_back(Vector2(0, -1) * unit); + break; + default: + break; + } + if (!polygon.is_empty()) { + p_canvas_item->draw_polygon(polygon, color_array); } +} - return arr; +void TileSetAtlasPluginTerrain::_draw_square_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) { + PackedColorArray color_array; + color_array.push_back(p_color); + + Vector2 unit = Vector2(p_size) / 6.0; + PackedVector2Array polygon; + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: + polygon.push_back(Vector2(1, -1) * unit); + polygon.push_back(Vector2(3, -3) * unit); + polygon.push_back(Vector2(3, 3) * unit); + polygon.push_back(Vector2(1, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: + polygon.push_back(Vector2(-1, 1) * unit); + polygon.push_back(Vector2(-3, 3) * unit); + polygon.push_back(Vector2(3, 3) * unit); + polygon.push_back(Vector2(1, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_LEFT_SIDE: + polygon.push_back(Vector2(-1, -1) * unit); + polygon.push_back(Vector2(-3, -3) * unit); + polygon.push_back(Vector2(-3, 3) * unit); + polygon.push_back(Vector2(-1, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_SIDE: + polygon.push_back(Vector2(-1, -1) * unit); + polygon.push_back(Vector2(-3, -3) * unit); + polygon.push_back(Vector2(3, -3) * unit); + polygon.push_back(Vector2(1, -1) * unit); + break; + default: + break; + } + if (!polygon.is_empty()) { + p_canvas_item->draw_polygon(polygon, color_array); + } } -Array TileSet::_get_tiles_ids() const { - Array arr; +void TileSetAtlasPluginTerrain::_draw_isometric_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) { + PackedColorArray color_array; + color_array.push_back(p_color); + + Vector2 unit = Vector2(p_size) / 6.0; + PackedVector2Array polygon; + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: + polygon.push_back(Vector2(1, 0) * unit); + polygon.push_back(Vector2(2, -1) * unit); + polygon.push_back(Vector2(3, 0) * unit); + polygon.push_back(Vector2(2, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + polygon.push_back(Vector2(0, 1) * unit); + polygon.push_back(Vector2(1, 2) * unit); + polygon.push_back(Vector2(2, 1) * unit); + polygon.push_back(Vector2(1, 0) * unit); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: + polygon.push_back(Vector2(0, 1) * unit); + polygon.push_back(Vector2(-1, 2) * unit); + polygon.push_back(Vector2(0, 3) * unit); + polygon.push_back(Vector2(1, 2) * unit); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + polygon.push_back(Vector2(0, 1) * unit); + polygon.push_back(Vector2(-1, 2) * unit); + polygon.push_back(Vector2(-2, 1) * unit); + polygon.push_back(Vector2(-1, 0) * unit); + break; + case TileSet::CELL_NEIGHBOR_LEFT_CORNER: + polygon.push_back(Vector2(-1, 0) * unit); + polygon.push_back(Vector2(-2, -1) * unit); + polygon.push_back(Vector2(-3, 0) * unit); + polygon.push_back(Vector2(-2, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + polygon.push_back(Vector2(0, -1) * unit); + polygon.push_back(Vector2(-1, -2) * unit); + polygon.push_back(Vector2(-2, -1) * unit); + polygon.push_back(Vector2(-1, 0) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_CORNER: + polygon.push_back(Vector2(0, -1) * unit); + polygon.push_back(Vector2(-1, -2) * unit); + polygon.push_back(Vector2(0, -3) * unit); + polygon.push_back(Vector2(1, -2) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + polygon.push_back(Vector2(0, -1) * unit); + polygon.push_back(Vector2(1, -2) * unit); + polygon.push_back(Vector2(2, -1) * unit); + polygon.push_back(Vector2(1, 0) * unit); + break; + default: + break; + } + if (!polygon.is_empty()) { + p_canvas_item->draw_polygon(polygon, color_array); + } +} - for (Map<int, TileData>::Element *E = tile_map.front(); E; E = E->next()) { - arr.push_back(E->key()); +void TileSetAtlasPluginTerrain::_draw_isometric_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) { + PackedColorArray color_array; + color_array.push_back(p_color); + + Vector2 unit = Vector2(p_size) / 6.0; + PackedVector2Array polygon; + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: + polygon.push_back(Vector2(0.5, -0.5) * unit); + polygon.push_back(Vector2(1.5, -1.5) * unit); + polygon.push_back(Vector2(3, 0) * unit); + polygon.push_back(Vector2(1.5, 1.5) * unit); + polygon.push_back(Vector2(0.5, 0.5) * unit); + polygon.push_back(Vector2(1, 0) * unit); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: + polygon.push_back(Vector2(-0.5, 0.5) * unit); + polygon.push_back(Vector2(-1.5, 1.5) * unit); + polygon.push_back(Vector2(0, 3) * unit); + polygon.push_back(Vector2(1.5, 1.5) * unit); + polygon.push_back(Vector2(0.5, 0.5) * unit); + polygon.push_back(Vector2(0, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_LEFT_CORNER: + polygon.push_back(Vector2(-0.5, -0.5) * unit); + polygon.push_back(Vector2(-1.5, -1.5) * unit); + polygon.push_back(Vector2(-3, 0) * unit); + polygon.push_back(Vector2(-1.5, 1.5) * unit); + polygon.push_back(Vector2(-0.5, 0.5) * unit); + polygon.push_back(Vector2(-1, 0) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_CORNER: + polygon.push_back(Vector2(-0.5, -0.5) * unit); + polygon.push_back(Vector2(-1.5, -1.5) * unit); + polygon.push_back(Vector2(0, -3) * unit); + polygon.push_back(Vector2(1.5, -1.5) * unit); + polygon.push_back(Vector2(0.5, -0.5) * unit); + polygon.push_back(Vector2(0, -1) * unit); + break; + default: + break; } + if (!polygon.is_empty()) { + p_canvas_item->draw_polygon(polygon, color_array); + } +} - return arr; +void TileSetAtlasPluginTerrain::_draw_isometric_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) { + PackedColorArray color_array; + color_array.push_back(p_color); + + Vector2 unit = Vector2(p_size) / 6.0; + PackedVector2Array polygon; + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + polygon.push_back(Vector2(1, 0) * unit); + polygon.push_back(Vector2(3, 0) * unit); + polygon.push_back(Vector2(0, 3) * unit); + polygon.push_back(Vector2(0, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + polygon.push_back(Vector2(-1, 0) * unit); + polygon.push_back(Vector2(-3, 0) * unit); + polygon.push_back(Vector2(0, 3) * unit); + polygon.push_back(Vector2(0, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + polygon.push_back(Vector2(-1, 0) * unit); + polygon.push_back(Vector2(-3, 0) * unit); + polygon.push_back(Vector2(0, -3) * unit); + polygon.push_back(Vector2(0, -1) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + polygon.push_back(Vector2(1, 0) * unit); + polygon.push_back(Vector2(3, 0) * unit); + polygon.push_back(Vector2(0, -3) * unit); + polygon.push_back(Vector2(0, -1) * unit); + break; + default: + break; + } + if (!polygon.is_empty()) { + p_canvas_item->draw_polygon(polygon, color_array); + } } -void TileSet::_decompose_convex_shape(Ref<Shape2D> p_shape) { - if (Engine::get_singleton()->is_editor_hint()) { - return; +void TileSetAtlasPluginTerrain::_draw_half_offset_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) { + PackedColorArray color_array; + color_array.push_back(p_color); + + PackedVector2Array point_list; + point_list.push_back(Vector2(3, (3.0 * (1.0 - p_overlap * 2.0)) / 2.0)); + point_list.push_back(Vector2(3, 3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(2, 3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0))); + point_list.push_back(Vector2(1, 3.0 - p_overlap * 2.0)); + point_list.push_back(Vector2(0, 3)); + point_list.push_back(Vector2(-1, 3.0 - p_overlap * 2.0)); + point_list.push_back(Vector2(-2, 3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0))); + point_list.push_back(Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(-3, (3.0 * (1.0 - p_overlap * 2.0)) / 2.0)); + point_list.push_back(Vector2(-3, -(3.0 * (1.0 - p_overlap * 2.0)) / 2.0)); + point_list.push_back(Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(-2, -3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0))); + point_list.push_back(Vector2(-1, -(3.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(0, -3)); + point_list.push_back(Vector2(1, -(3.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(2, -3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0))); + point_list.push_back(Vector2(3, -3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(3, -(3.0 * (1.0 - p_overlap * 2.0)) / 2.0)); + + Vector2 unit = Vector2(p_size) / 6.0; + for (int i = 0; i < point_list.size(); i++) { + point_list.write[i] = point_list[i] * unit; + } + + PackedVector2Array polygon; + if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: + polygon.push_back(point_list[17]); + polygon.push_back(point_list[0]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + polygon.push_back(point_list[0]); + polygon.push_back(point_list[1]); + polygon.push_back(point_list[2]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + polygon.push_back(point_list[2]); + polygon.push_back(point_list[3]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: + polygon.push_back(point_list[3]); + polygon.push_back(point_list[4]); + polygon.push_back(point_list[5]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + polygon.push_back(point_list[5]); + polygon.push_back(point_list[6]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + polygon.push_back(point_list[6]); + polygon.push_back(point_list[7]); + polygon.push_back(point_list[8]); + break; + case TileSet::CELL_NEIGHBOR_LEFT_SIDE: + polygon.push_back(point_list[8]); + polygon.push_back(point_list[9]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + polygon.push_back(point_list[9]); + polygon.push_back(point_list[10]); + polygon.push_back(point_list[11]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + polygon.push_back(point_list[11]); + polygon.push_back(point_list[12]); + break; + case TileSet::CELL_NEIGHBOR_TOP_CORNER: + polygon.push_back(point_list[12]); + polygon.push_back(point_list[13]); + polygon.push_back(point_list[14]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + polygon.push_back(point_list[14]); + polygon.push_back(point_list[15]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + polygon.push_back(point_list[15]); + polygon.push_back(point_list[16]); + polygon.push_back(point_list[17]); + break; + default: + break; + } + } else { + if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) { + for (int i = 0; i < point_list.size(); i++) { + point_list.write[i] = Vector2(point_list[i].y, point_list[i].x); + } + } + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: + polygon.push_back(point_list[3]); + polygon.push_back(point_list[4]); + polygon.push_back(point_list[5]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + polygon.push_back(point_list[2]); + polygon.push_back(point_list[3]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + polygon.push_back(point_list[0]); + polygon.push_back(point_list[1]); + polygon.push_back(point_list[2]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: + polygon.push_back(point_list[17]); + polygon.push_back(point_list[0]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + polygon.push_back(point_list[15]); + polygon.push_back(point_list[16]); + polygon.push_back(point_list[17]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + polygon.push_back(point_list[14]); + polygon.push_back(point_list[15]); + break; + case TileSet::CELL_NEIGHBOR_LEFT_CORNER: + polygon.push_back(point_list[12]); + polygon.push_back(point_list[13]); + polygon.push_back(point_list[14]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + polygon.push_back(point_list[11]); + polygon.push_back(point_list[12]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + polygon.push_back(point_list[9]); + polygon.push_back(point_list[10]); + polygon.push_back(point_list[11]); + break; + case TileSet::CELL_NEIGHBOR_TOP_SIDE: + polygon.push_back(point_list[8]); + polygon.push_back(point_list[9]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + polygon.push_back(point_list[6]); + polygon.push_back(point_list[7]); + polygon.push_back(point_list[8]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + polygon.push_back(point_list[5]); + polygon.push_back(point_list[6]); + break; + default: + break; + } + } + + int half_polygon_size = polygon.size(); + for (int i = 0; i < half_polygon_size; i++) { + polygon.push_back(polygon[half_polygon_size - 1 - i] / 3.0); + } + + if (!polygon.is_empty()) { + p_canvas_item->draw_polygon(polygon, color_array); + } +} + +void TileSetAtlasPluginTerrain::_draw_half_offset_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) { + PackedColorArray color_array; + color_array.push_back(p_color); + + PackedVector2Array point_list; + point_list.push_back(Vector2(3, 0)); + point_list.push_back(Vector2(3, 3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(1.5, (3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0)); + point_list.push_back(Vector2(0, 3)); + point_list.push_back(Vector2(-1.5, (3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0)); + point_list.push_back(Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(-3, 0)); + point_list.push_back(Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(-1.5, -(3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0)); + point_list.push_back(Vector2(0, -3)); + point_list.push_back(Vector2(1.5, -(3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0)); + point_list.push_back(Vector2(3, -3.0 * (1.0 - p_overlap * 2.0))); + + Vector2 unit = Vector2(p_size) / 6.0; + for (int i = 0; i < point_list.size(); i++) { + point_list.write[i] = point_list[i] * unit; + } + + PackedVector2Array polygon; + if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + polygon.push_back(point_list[0]); + polygon.push_back(point_list[1]); + polygon.push_back(point_list[2]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: + polygon.push_back(point_list[2]); + polygon.push_back(point_list[3]); + polygon.push_back(point_list[4]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + polygon.push_back(point_list[4]); + polygon.push_back(point_list[5]); + polygon.push_back(point_list[6]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + polygon.push_back(point_list[6]); + polygon.push_back(point_list[7]); + polygon.push_back(point_list[8]); + break; + case TileSet::CELL_NEIGHBOR_TOP_CORNER: + polygon.push_back(point_list[8]); + polygon.push_back(point_list[9]); + polygon.push_back(point_list[10]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + polygon.push_back(point_list[10]); + polygon.push_back(point_list[11]); + polygon.push_back(point_list[0]); + break; + default: + break; + } + } else { + if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) { + for (int i = 0; i < point_list.size(); i++) { + point_list.write[i] = Vector2(point_list[i].y, point_list[i].x); + } + } + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: + polygon.push_back(point_list[2]); + polygon.push_back(point_list[3]); + polygon.push_back(point_list[4]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + polygon.push_back(point_list[0]); + polygon.push_back(point_list[1]); + polygon.push_back(point_list[2]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + polygon.push_back(point_list[10]); + polygon.push_back(point_list[11]); + polygon.push_back(point_list[0]); + break; + case TileSet::CELL_NEIGHBOR_LEFT_CORNER: + polygon.push_back(point_list[8]); + polygon.push_back(point_list[9]); + polygon.push_back(point_list[10]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + polygon.push_back(point_list[6]); + polygon.push_back(point_list[7]); + polygon.push_back(point_list[8]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + polygon.push_back(point_list[4]); + polygon.push_back(point_list[5]); + polygon.push_back(point_list[6]); + break; + default: + break; + } + } + + int half_polygon_size = polygon.size(); + for (int i = 0; i < half_polygon_size; i++) { + polygon.push_back(polygon[half_polygon_size - 1 - i] / 3.0); + } + + if (!polygon.is_empty()) { + p_canvas_item->draw_polygon(polygon, color_array); + } +} + +void TileSetAtlasPluginTerrain::_draw_half_offset_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) { + PackedColorArray color_array; + color_array.push_back(p_color); + + PackedVector2Array point_list; + point_list.push_back(Vector2(3, 3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(0, 3)); + point_list.push_back(Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(0, -3)); + point_list.push_back(Vector2(3, -3.0 * (1.0 - p_overlap * 2.0))); + + Vector2 unit = Vector2(p_size) / 6.0; + for (int i = 0; i < point_list.size(); i++) { + point_list.write[i] = point_list[i] * unit; + } + + PackedVector2Array polygon; + if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: + polygon.push_back(point_list[5]); + polygon.push_back(point_list[0]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + polygon.push_back(point_list[0]); + polygon.push_back(point_list[1]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + polygon.push_back(point_list[1]); + polygon.push_back(point_list[2]); + break; + case TileSet::CELL_NEIGHBOR_LEFT_SIDE: + polygon.push_back(point_list[2]); + polygon.push_back(point_list[3]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + polygon.push_back(point_list[3]); + polygon.push_back(point_list[4]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + polygon.push_back(point_list[4]); + polygon.push_back(point_list[5]); + break; + default: + break; + } + } else { + if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) { + for (int i = 0; i < point_list.size(); i++) { + point_list.write[i] = Vector2(point_list[i].y, point_list[i].x); + } + } + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + polygon.push_back(point_list[0]); + polygon.push_back(point_list[1]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: + polygon.push_back(point_list[5]); + polygon.push_back(point_list[0]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + polygon.push_back(point_list[4]); + polygon.push_back(point_list[5]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + polygon.push_back(point_list[3]); + polygon.push_back(point_list[4]); + break; + case TileSet::CELL_NEIGHBOR_TOP_SIDE: + polygon.push_back(point_list[2]); + polygon.push_back(point_list[3]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + polygon.push_back(point_list[1]); + polygon.push_back(point_list[2]); + break; + default: + break; + } + } + + int half_polygon_size = polygon.size(); + for (int i = 0; i < half_polygon_size; i++) { + polygon.push_back(polygon[half_polygon_size - 1 - i] / 3.0); + } + + if (!polygon.is_empty()) { + p_canvas_item->draw_polygon(polygon, color_array); } - Ref<ConvexPolygonShape2D> convex = p_shape; - if (!convex.is_valid()) { +} + +#define TERRAIN_ALPHA 0.8 + +#define DRAW_TERRAIN_BIT(f, bit) \ + { \ + int terrain_id = p_tile_data->get_peering_bit_terrain((bit)); \ + if (terrain_id >= 0) { \ + Color color = p_tile_set->get_terrain_color(terrain_set, terrain_id); \ + color.a = TERRAIN_ALPHA; \ + f(p_canvas_item, color, size, (bit)); \ + } \ + } + +#define DRAW_HALF_OFFSET_TERRAIN_BIT(f, bit, overlap, half_offset_axis) \ + { \ + int terrain_id = p_tile_data->get_peering_bit_terrain((bit)); \ + if (terrain_id >= 0) { \ + Color color = p_tile_set->get_terrain_color(terrain_set, terrain_id); \ + color.a = TERRAIN_ALPHA; \ + f(p_canvas_item, color, size, (bit), overlap, half_offset_axis); \ + } \ + } + +void TileSetAtlasPluginTerrain::draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, const TileData *p_tile_data) { + ERR_FAIL_COND(!p_tile_set); + ERR_FAIL_COND(!p_tile_data); + + int terrain_set = p_tile_data->get_terrain_set(); + if (terrain_set < 0) { return; } - Vector<Vector<Vector2>> decomp = Geometry2D::decompose_polygon_in_convex(convex->get_points()); - if (decomp.size() > 1) { - Array sub_shapes; - for (int i = 0; i < decomp.size(); i++) { - Ref<ConvexPolygonShape2D> _convex = memnew(ConvexPolygonShape2D); - _convex->set_points(decomp[i]); - sub_shapes.append(_convex); + TileSet::TerrainMode terrain_mode = p_tile_set->get_terrain_set_mode(terrain_set); + + TileSet::TileShape shape = p_tile_set->get_tile_shape(); + Vector2i size = p_tile_set->get_tile_size(); + + RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), p_transform); + if (shape == TileSet::TILE_SHAPE_SQUARE) { + if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { + DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_SIDE); + DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER); + DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE); + DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER); + DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_SIDE); + DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER); + DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_SIDE); + DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER); + } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + DRAW_TERRAIN_BIT(_draw_square_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER); + DRAW_TERRAIN_BIT(_draw_square_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER); + DRAW_TERRAIN_BIT(_draw_square_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER); + DRAW_TERRAIN_BIT(_draw_square_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER); + } else { // TileData::TERRAIN_MODE_MATCH_SIDES + DRAW_TERRAIN_BIT(_draw_square_side_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_SIDE); + DRAW_TERRAIN_BIT(_draw_square_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE); + DRAW_TERRAIN_BIT(_draw_square_side_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_SIDE); + DRAW_TERRAIN_BIT(_draw_square_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_SIDE); + } + } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) { + if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { + DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_CORNER); + DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE); + DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER); + DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE); + DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_CORNER); + DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); + DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_CORNER); + DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); + } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + DRAW_TERRAIN_BIT(_draw_isometric_corner_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_CORNER); + DRAW_TERRAIN_BIT(_draw_isometric_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER); + DRAW_TERRAIN_BIT(_draw_isometric_corner_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_CORNER); + DRAW_TERRAIN_BIT(_draw_isometric_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_CORNER); + } else { // TileData::TERRAIN_MODE_MATCH_SIDES + DRAW_TERRAIN_BIT(_draw_isometric_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE); + DRAW_TERRAIN_BIT(_draw_isometric_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE); + DRAW_TERRAIN_BIT(_draw_isometric_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); + DRAW_TERRAIN_BIT(_draw_isometric_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); } - convex->set_meta("decomposed", sub_shapes); } else { - convex->set_meta("decomposed", Variant()); + TileSet::TileOffsetAxis offset_axis = p_tile_set->get_tile_offset_axis(); + float overlap = 0.0; + switch (p_tile_set->get_tile_shape()) { + case TileSet::TILE_SHAPE_HEXAGON: + overlap = 0.25; + break; + case TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE: + overlap = 0.0; + break; + default: + break; + } + if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { + if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_SIDE, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_SIDE, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_CORNER, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER, overlap, offset_axis); + } else { + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_CORNER, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_CORNER, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_SIDE, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, overlap, offset_axis); + } + } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_CORNER, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER, overlap, offset_axis); + } else { + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_CORNER, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_CORNER, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER, overlap, offset_axis); + } + } else { // TileData::TERRAIN_MODE_MATCH_SIDES + if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_SIDE, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_SIDE, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, overlap, offset_axis); + } else { + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_SIDE, overlap, offset_axis); + DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, overlap, offset_axis); + } + } } + RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), Transform2D()); } -void TileSet::get_tile_list(List<int> *p_tiles) const { - for (Map<int, TileData>::Element *E = tile_map.front(); E; E = E->next()) { - p_tiles->push_back(E->key()); +/////////////////////////////// TileSetAtlasPluginRendering ////////////////////////////////////// + +void TileSetAtlasPluginRendering::tilemap_notification(TileMap *p_tile_map, int p_what) { + switch (p_what) { + case CanvasItem::NOTIFICATION_VISIBILITY_CHANGED: { + bool visible = p_tile_map->is_visible_in_tree(); + for (Map<Vector2i, TileMapQuadrant>::Element *E_quadrant = p_tile_map->get_quadrant_map().front(); E_quadrant; E_quadrant = E_quadrant->next()) { + TileMapQuadrant &q = E_quadrant->get(); + + // Update occluders transform. + for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E_cell = q.world_to_map.front(); E_cell; E_cell = E_cell->next()) { + Transform2D xform; + xform.set_origin(E_cell->key()); + for (List<RID>::Element *E_occluder_id = q.occluders.front(); E_occluder_id; E_occluder_id = E_occluder_id->next()) { + RS::get_singleton()->canvas_light_occluder_set_enabled(E_occluder_id->get(), visible); + } + } + } + } break; + case CanvasItem::NOTIFICATION_TRANSFORM_CHANGED: { + if (!p_tile_map->is_inside_tree()) { + return; + } + + for (Map<Vector2i, TileMapQuadrant>::Element *E_quadrant = p_tile_map->get_quadrant_map().front(); E_quadrant; E_quadrant = E_quadrant->next()) { + TileMapQuadrant &q = E_quadrant->get(); + + // Update occluders transform. + for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E_cell = q.world_to_map.front(); E_cell; E_cell = E_cell->next()) { + Transform2D xform; + xform.set_origin(E_cell->key()); + for (List<RID>::Element *E_occluder_id = q.occluders.front(); E_occluder_id; E_occluder_id = E_occluder_id->next()) { + RS::get_singleton()->canvas_light_occluder_set_transform(E_occluder_id->get(), p_tile_map->get_global_transform() * xform); + } + } + } + } break; } } -bool TileSet::has_tile(int p_id) const { - return tile_map.has(p_id); +void TileSetAtlasPluginRendering::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, Color p_modulation) { + ERR_FAIL_COND(!p_tile_set.is_valid()); + ERR_FAIL_COND(!p_tile_set->has_source(p_atlas_source_id)); + ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_tile(p_atlas_coords)); + ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_alternative_tile(p_atlas_coords, p_alternative_tile)); + + TileSetSource *source = *p_tile_set->get_source(p_atlas_source_id); + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + // Get the texture. + Ref<Texture2D> tex = atlas_source->get_texture(); + if (!tex.is_valid()) { + return; + } + + // Get tile data. + TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(p_atlas_coords, p_alternative_tile)); + + // Compute the offset + Rect2i source_rect = atlas_source->get_tile_texture_region(p_atlas_coords); + Vector2i tile_offset = atlas_source->get_tile_effective_texture_offset(p_atlas_coords, p_alternative_tile); + + // Compute the destination rectangle in the CanvasItem. + Rect2 dest_rect; + dest_rect.size = source_rect.size; + dest_rect.size.x += fp_adjust; + dest_rect.size.y += fp_adjust; + + bool transpose = tile_data->get_transpose(); + if (transpose) { + dest_rect.position = (p_position - Vector2(dest_rect.size.y, dest_rect.size.x) / 2 - tile_offset); + } else { + dest_rect.position = (p_position - dest_rect.size / 2 - tile_offset); + } + + if (tile_data->get_flip_h()) { + dest_rect.size.x = -dest_rect.size.x; + } + + if (tile_data->get_flip_v()) { + dest_rect.size.y = -dest_rect.size.y; + } + + // Get the tile modulation. + Color modulate = tile_data->get_modulate(); + modulate = Color(modulate.r * p_modulation.r, modulate.g * p_modulation.g, modulate.b * p_modulation.b, modulate.a * p_modulation.a); + + // Draw the tile. + tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping()); + } } -bool TileSet::is_tile_bound(int p_drawn_id, int p_neighbor_id) { - if (p_drawn_id == p_neighbor_id) { - return true; - } else if (get_script_instance() != nullptr) { - if (get_script_instance()->has_method("_is_tile_bound")) { - Variant ret = get_script_instance()->call("_is_tile_bound", p_drawn_id, p_neighbor_id); - if (ret.get_type() == Variant::BOOL) { - return ret; +void TileSetAtlasPluginRendering::update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) { + ERR_FAIL_COND(!p_tile_map); + ERR_FAIL_COND(!p_tile_map->is_inside_tree()); + Ref<TileSet> tile_set = p_tile_map->get_tileset(); + ERR_FAIL_COND(!tile_set.is_valid()); + + bool visible = p_tile_map->is_visible_in_tree(); + + SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first(); + while (q_list_element) { + TileMapQuadrant &q = *q_list_element->self(); + + RenderingServer *rs = RenderingServer::get_singleton(); + + // Free the canvas items. + for (List<RID>::Element *E = q.canvas_items.front(); E; E = E->next()) { + rs->free(E->get()); + } + q.canvas_items.clear(); + + // Free the occluders. + for (List<RID>::Element *E = q.occluders.front(); E; E = E->next()) { + rs->free(E->get()); + } + q.occluders.clear(); + + // Those allow to group cell per material or z-index. + Ref<ShaderMaterial> prev_material; + int prev_z_index = 0; + RID prev_canvas_item; + + // Iterate over the cells of the quadrant. + for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E_cell = q.world_to_map.front(); E_cell; E_cell = E_cell->next()) { + TileMapCell c = p_tile_map->get_cell(E_cell->value()); + + TileSetSource *source; + if (tile_set->has_source(c.source_id)) { + source = *tile_set->get_source(c.source_id); + + if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { + continue; + } + + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + // Get the tile data. + TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); + Ref<ShaderMaterial> mat = tile_data->tile_get_material(); + int z_index = tile_data->get_z_index(); + + // Quandrant pos. + Vector2 position = p_tile_map->map_to_world(q.coords * p_tile_map->get_effective_quadrant_size()) - tile_set->get_tile_size() / 2; + + // --- CanvasItems --- + // Create two canvas items, for rendering and debug. + RID canvas_item; + + // Check if the material or the z_index changed. + if (prev_canvas_item == RID() || prev_material != mat || prev_z_index != z_index) { + canvas_item = rs->canvas_item_create(); + if (mat.is_valid()) { + rs->canvas_item_set_material(canvas_item, mat->get_rid()); + } + rs->canvas_item_set_parent(canvas_item, p_tile_map->get_canvas_item()); + rs->canvas_item_set_use_parent_material(canvas_item, p_tile_map->get_use_parent_material() || p_tile_map->get_material().is_valid()); + Transform2D xform; + xform.set_origin(position); + + rs->canvas_item_set_transform(canvas_item, xform); + rs->canvas_item_set_light_mask(canvas_item, p_tile_map->get_light_mask()); + rs->canvas_item_set_z_index(canvas_item, z_index); + + rs->canvas_item_set_default_texture_filter(canvas_item, RS::CanvasItemTextureFilter(p_tile_map->CanvasItem::get_texture_filter())); + rs->canvas_item_set_default_texture_repeat(canvas_item, RS::CanvasItemTextureRepeat(p_tile_map->CanvasItem::get_texture_repeat())); + + q.canvas_items.push_back(canvas_item); + + prev_canvas_item = canvas_item; + prev_material = mat; + prev_z_index = z_index; + + } else { + // Keep the same canvas_item to draw on. + canvas_item = prev_canvas_item; + } + + // Drawing the tile in the canvas item. + draw_tile(canvas_item, E_cell->key() - position, tile_set, c.source_id, c.get_atlas_coords(), c.alternative_tile, p_tile_map->get_self_modulate()); + + // --- Occluders --- + for (int i = 0; i < tile_set->get_occlusion_layers_count(); i++) { + Transform2D xform; + xform.set_origin(E_cell->key()); + if (tile_data->get_occluder(i).is_valid()) { + RID occluder_id = rs->canvas_light_occluder_create(); + rs->canvas_light_occluder_set_enabled(occluder_id, visible); + rs->canvas_light_occluder_set_transform(occluder_id, p_tile_map->get_global_transform() * xform); + rs->canvas_light_occluder_set_polygon(occluder_id, tile_data->get_occluder(i)->get_rid()); + rs->canvas_light_occluder_attach_to_canvas(occluder_id, p_tile_map->get_canvas()); + rs->canvas_light_occluder_set_light_mask(occluder_id, tile_set->get_occlusion_layer_light_mask(i)); + q.occluders.push_back(occluder_id); + } + } + } } } + + quadrant_order_dirty = true; + q_list_element = q_list_element->next(); + } + + // Reset the drawing indices + if (quadrant_order_dirty) { + int index = -(int64_t)0x80000000; //always must be drawn below children. + + // Sort the quadrants coords per world coordinates + Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator> world_to_map; + Map<Vector2i, TileMapQuadrant> quadrant_map = p_tile_map->get_quadrant_map(); + for (Map<Vector2i, TileMapQuadrant>::Element *E = quadrant_map.front(); E; E = E->next()) { + world_to_map[p_tile_map->map_to_world(E->key())] = E->key(); + } + + // Sort the quadrants + for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E = world_to_map.front(); E; E = E->next()) { + TileMapQuadrant &q = quadrant_map[E->value()]; + for (List<RID>::Element *F = q.canvas_items.front(); F; F = F->next()) { + RS::get_singleton()->canvas_item_set_draw_index(F->get(), index++); + } + } + + quadrant_order_dirty = false; } - return false; } -void TileSet::remove_tile(int p_id) { - ERR_FAIL_COND(!tile_map.has(p_id)); - tile_map.erase(p_id); - notify_property_list_changed(); - emit_changed(); +void TileSetAtlasPluginRendering::create_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) { + Ref<TileSet> tile_set = p_tile_map->get_tileset(); + ERR_FAIL_COND(!tile_set.is_valid()); + + quadrant_order_dirty = true; } -int TileSet::get_last_unused_tile_id() const { - if (tile_map.size()) { - return tile_map.back()->key() + 1; - } else { - return 0; +void TileSetAtlasPluginRendering::cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) { + // Free the canvas items. + for (List<RID>::Element *E = p_quadrant->canvas_items.front(); E; E = E->next()) { + RenderingServer::get_singleton()->free(E->get()); + } + p_quadrant->canvas_items.clear(); + + // Free the occluders. + for (List<RID>::Element *E = p_quadrant->occluders.front(); E; E = E->next()) { + RenderingServer::get_singleton()->free(E->get()); } + p_quadrant->occluders.clear(); } -int TileSet::find_tile_by_name(const String &p_name) const { - for (Map<int, TileData>::Element *E = tile_map.front(); E; E = E->next()) { - if (p_name == E->get().name) { - return E->key(); +/////////////////////////////// TileSetAtlasPluginPhysics ////////////////////////////////////// + +void TileSetAtlasPluginPhysics::tilemap_notification(TileMap *p_tile_map, int p_what) { + switch (p_what) { + case CanvasItem::NOTIFICATION_TRANSFORM_CHANGED: { + // Update the bodies transforms. + if (p_tile_map->is_inside_tree()) { + Map<Vector2i, TileMapQuadrant> quadrant_map = p_tile_map->get_quadrant_map(); + Transform2D global_transform = p_tile_map->get_global_transform(); + + for (Map<Vector2i, TileMapQuadrant>::Element *E = quadrant_map.front(); E; E = E->next()) { + TileMapQuadrant &q = E->get(); + + Transform2D xform; + xform.set_origin(p_tile_map->map_to_world(E->key() * p_tile_map->get_effective_quadrant_size())); + xform = global_transform * xform; + + for (int body_index = 0; body_index < q.bodies.size(); body_index++) { + PhysicsServer2D::get_singleton()->body_set_state(q.bodies[body_index], PhysicsServer2D::BODY_STATE_TRANSFORM, xform); + } + } + } + } break; + } +} + +void TileSetAtlasPluginPhysics::update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) { + ERR_FAIL_COND(!p_tile_map); + ERR_FAIL_COND(!p_tile_map->is_inside_tree()); + Ref<TileSet> tile_set = p_tile_map->get_tileset(); + ERR_FAIL_COND(!tile_set.is_valid()); + + Transform2D global_transform = p_tile_map->get_global_transform(); + PhysicsServer2D *ps = PhysicsServer2D::get_singleton(); + + SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first(); + while (q_list_element) { + TileMapQuadrant &q = *q_list_element->self(); + + Vector2 quadrant_pos = p_tile_map->map_to_world(q.coords * p_tile_map->get_effective_quadrant_size()); + + // Clear shapes. + for (int body_index = 0; body_index < q.bodies.size(); body_index++) { + ps->body_clear_shapes(q.bodies[body_index]); + + // Position the bodies. + Transform2D xform; + xform.set_origin(quadrant_pos); + xform = global_transform * xform; + ps->body_set_state(q.bodies[body_index], PhysicsServer2D::BODY_STATE_TRANSFORM, xform); } + + for (Set<Vector2i>::Element *E_cell = q.cells.front(); E_cell; E_cell = E_cell->next()) { + TileMapCell c = p_tile_map->get_cell(E_cell->get()); + + TileSetSource *source; + if (tile_set->has_source(c.source_id)) { + source = *tile_set->get_source(c.source_id); + + if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { + continue; + } + + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); + + for (int body_index = 0; body_index < q.bodies.size(); body_index++) { + // Add the shapes again. + for (int shape_index = 0; shape_index < tile_data->get_collision_shapes_count(body_index); shape_index++) { + bool one_way_collision = tile_data->is_collision_shape_one_way(body_index, shape_index); + float one_way_collision_margin = tile_data->get_collision_shape_one_way_margin(body_index, shape_index); + Ref<Shape2D> shape = tile_data->get_collision_shape_shape(body_index, shape_index); + if (shape.is_valid()) { + Transform2D xform = Transform2D(); + xform.set_origin(p_tile_map->map_to_world(E_cell->get()) - quadrant_pos); + + // Add decomposed convex shapes. + ps->body_add_shape(q.bodies[body_index], shape->get_rid(), xform); + ps->body_set_shape_metadata(q.bodies[body_index], shape_index, E_cell->get()); + ps->body_set_shape_as_one_way_collision(q.bodies[body_index], shape_index, one_way_collision, one_way_collision_margin); + } + } + } + } + } + } + + q_list_element = q_list_element->next(); } - return -1; } -void TileSet::reset_state() { - clear(); +void TileSetAtlasPluginPhysics::create_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) { + Ref<TileSet> tile_set = p_tile_map->get_tileset(); + ERR_FAIL_COND(!tile_set.is_valid()); + + //Get the TileMap's gobla transform. + Transform2D global_transform; + if (p_tile_map->is_inside_tree()) { + global_transform = p_tile_map->get_global_transform(); + } + + // Clear all bodies. + p_quadrant->bodies.clear(); + + // Create the body and set its parameters. + for (int layer_index = 0; layer_index < tile_set->get_physics_layers_count(); layer_index++) { + RID body = PhysicsServer2D::get_singleton()->body_create(); + PhysicsServer2D::get_singleton()->body_set_mode(body, PhysicsServer2D::BODY_MODE_STATIC); + + PhysicsServer2D::get_singleton()->body_attach_object_instance_id(body, p_tile_map->get_instance_id()); + PhysicsServer2D::get_singleton()->body_set_collision_layer(body, tile_set->get_physics_layer_collision_layer(layer_index)); + PhysicsServer2D::get_singleton()->body_set_collision_mask(body, tile_set->get_physics_layer_collision_mask(layer_index)); + + Ref<PhysicsMaterial> physics_material = tile_set->get_physics_layer_physics_material(layer_index); + if (!physics_material.is_valid()) { + PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_BOUNCE, 0); + PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_FRICTION, 1); + } else { + PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_BOUNCE, physics_material->computed_bounce()); + PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_FRICTION, physics_material->computed_friction()); + } + + if (p_tile_map->is_inside_tree()) { + RID space = p_tile_map->get_world_2d()->get_space(); + PhysicsServer2D::get_singleton()->body_set_space(body, space); + + Transform2D xform; + xform.set_origin(p_tile_map->map_to_world(p_quadrant->coords * p_tile_map->get_effective_quadrant_size())); + xform = global_transform * xform; + PhysicsServer2D::get_singleton()->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform); + } + + p_quadrant->bodies.push_back(body); + } } -void TileSet::clear() { - tile_map.clear(); - notify_property_list_changed(); - emit_changed(); +void TileSetAtlasPluginPhysics::cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) { + // Remove a quadrant. + for (int body_index = 0; body_index < p_quadrant->bodies.size(); body_index++) { + PhysicsServer2D::get_singleton()->free(p_quadrant->bodies[body_index]); + } + p_quadrant->bodies.clear(); } -void TileSet::_bind_methods() { - ClassDB::bind_method(D_METHOD("create_tile", "id"), &TileSet::create_tile); - ClassDB::bind_method(D_METHOD("autotile_clear_bitmask_map", "id"), &TileSet::autotile_clear_bitmask_map); - ClassDB::bind_method(D_METHOD("autotile_set_icon_coordinate", "id", "coord"), &TileSet::autotile_set_icon_coordinate); - ClassDB::bind_method(D_METHOD("autotile_get_icon_coordinate", "id"), &TileSet::autotile_get_icon_coordinate); - ClassDB::bind_method(D_METHOD("autotile_set_subtile_priority", "id", "coord", "priority"), &TileSet::autotile_set_subtile_priority); - ClassDB::bind_method(D_METHOD("autotile_get_subtile_priority", "id", "coord"), &TileSet::autotile_get_subtile_priority); - ClassDB::bind_method(D_METHOD("autotile_set_z_index", "id", "coord", "z_index"), &TileSet::autotile_set_z_index); - ClassDB::bind_method(D_METHOD("autotile_get_z_index", "id", "coord"), &TileSet::autotile_get_z_index); - ClassDB::bind_method(D_METHOD("autotile_set_light_occluder", "id", "light_occluder", "coord"), &TileSet::autotile_set_light_occluder); - ClassDB::bind_method(D_METHOD("autotile_get_light_occluder", "id", "coord"), &TileSet::autotile_get_light_occluder); - ClassDB::bind_method(D_METHOD("autotile_set_navigation_polygon", "id", "navigation_polygon", "coord"), &TileSet::autotile_set_navigation_polygon); - ClassDB::bind_method(D_METHOD("autotile_get_navigation_polygon", "id", "coord"), &TileSet::autotile_get_navigation_polygon); - ClassDB::bind_method(D_METHOD("autotile_set_bitmask", "id", "bitmask", "flag"), &TileSet::autotile_set_bitmask); - ClassDB::bind_method(D_METHOD("autotile_get_bitmask", "id", "coord"), &TileSet::autotile_get_bitmask); - ClassDB::bind_method(D_METHOD("autotile_set_bitmask_mode", "id", "mode"), &TileSet::autotile_set_bitmask_mode); - ClassDB::bind_method(D_METHOD("autotile_get_bitmask_mode", "id"), &TileSet::autotile_get_bitmask_mode); - ClassDB::bind_method(D_METHOD("autotile_set_spacing", "id", "spacing"), &TileSet::autotile_set_spacing); - ClassDB::bind_method(D_METHOD("autotile_get_spacing", "id"), &TileSet::autotile_get_spacing); - ClassDB::bind_method(D_METHOD("autotile_set_size", "id", "size"), &TileSet::autotile_set_size); - ClassDB::bind_method(D_METHOD("autotile_get_size", "id"), &TileSet::autotile_get_size); - ClassDB::bind_method(D_METHOD("tile_set_name", "id", "name"), &TileSet::tile_set_name); - ClassDB::bind_method(D_METHOD("tile_get_name", "id"), &TileSet::tile_get_name); - ClassDB::bind_method(D_METHOD("tile_set_texture", "id", "texture"), &TileSet::tile_set_texture); - ClassDB::bind_method(D_METHOD("tile_get_texture", "id"), &TileSet::tile_get_texture); - ClassDB::bind_method(D_METHOD("tile_set_material", "id", "material"), &TileSet::tile_set_material); - ClassDB::bind_method(D_METHOD("tile_get_material", "id"), &TileSet::tile_get_material); - ClassDB::bind_method(D_METHOD("tile_set_modulate", "id", "color"), &TileSet::tile_set_modulate); - ClassDB::bind_method(D_METHOD("tile_get_modulate", "id"), &TileSet::tile_get_modulate); - ClassDB::bind_method(D_METHOD("tile_set_texture_offset", "id", "texture_offset"), &TileSet::tile_set_texture_offset); - ClassDB::bind_method(D_METHOD("tile_get_texture_offset", "id"), &TileSet::tile_get_texture_offset); - ClassDB::bind_method(D_METHOD("tile_set_region", "id", "region"), &TileSet::tile_set_region); - ClassDB::bind_method(D_METHOD("tile_get_region", "id"), &TileSet::tile_get_region); - ClassDB::bind_method(D_METHOD("tile_set_shape", "id", "shape_id", "shape"), &TileSet::tile_set_shape); - ClassDB::bind_method(D_METHOD("tile_get_shape", "id", "shape_id"), &TileSet::tile_get_shape); - ClassDB::bind_method(D_METHOD("tile_set_shape_offset", "id", "shape_id", "shape_offset"), &TileSet::tile_set_shape_offset); - ClassDB::bind_method(D_METHOD("tile_get_shape_offset", "id", "shape_id"), &TileSet::tile_get_shape_offset); - ClassDB::bind_method(D_METHOD("tile_set_shape_transform", "id", "shape_id", "shape_transform"), &TileSet::tile_set_shape_transform); - ClassDB::bind_method(D_METHOD("tile_get_shape_transform", "id", "shape_id"), &TileSet::tile_get_shape_transform); - ClassDB::bind_method(D_METHOD("tile_set_shape_one_way", "id", "shape_id", "one_way"), &TileSet::tile_set_shape_one_way); - ClassDB::bind_method(D_METHOD("tile_get_shape_one_way", "id", "shape_id"), &TileSet::tile_get_shape_one_way); - ClassDB::bind_method(D_METHOD("tile_set_shape_one_way_margin", "id", "shape_id", "one_way"), &TileSet::tile_set_shape_one_way_margin); - ClassDB::bind_method(D_METHOD("tile_get_shape_one_way_margin", "id", "shape_id"), &TileSet::tile_get_shape_one_way_margin); - ClassDB::bind_method(D_METHOD("tile_add_shape", "id", "shape", "shape_transform", "one_way", "autotile_coord"), &TileSet::tile_add_shape, DEFVAL(false), DEFVAL(Vector2())); - ClassDB::bind_method(D_METHOD("tile_get_shape_count", "id"), &TileSet::tile_get_shape_count); - ClassDB::bind_method(D_METHOD("tile_set_shapes", "id", "shapes"), &TileSet::_tile_set_shapes); - ClassDB::bind_method(D_METHOD("tile_get_shapes", "id"), &TileSet::_tile_get_shapes); - ClassDB::bind_method(D_METHOD("tile_set_tile_mode", "id", "tilemode"), &TileSet::tile_set_tile_mode); - ClassDB::bind_method(D_METHOD("tile_get_tile_mode", "id"), &TileSet::tile_get_tile_mode); - ClassDB::bind_method(D_METHOD("tile_set_navigation_polygon", "id", "navigation_polygon"), &TileSet::tile_set_navigation_polygon); - ClassDB::bind_method(D_METHOD("tile_get_navigation_polygon", "id"), &TileSet::tile_get_navigation_polygon); - ClassDB::bind_method(D_METHOD("tile_set_navigation_polygon_offset", "id", "navigation_polygon_offset"), &TileSet::tile_set_navigation_polygon_offset); - ClassDB::bind_method(D_METHOD("tile_get_navigation_polygon_offset", "id"), &TileSet::tile_get_navigation_polygon_offset); - ClassDB::bind_method(D_METHOD("tile_set_light_occluder", "id", "light_occluder"), &TileSet::tile_set_light_occluder); - ClassDB::bind_method(D_METHOD("tile_get_light_occluder", "id"), &TileSet::tile_get_light_occluder); - ClassDB::bind_method(D_METHOD("tile_set_occluder_offset", "id", "occluder_offset"), &TileSet::tile_set_occluder_offset); - ClassDB::bind_method(D_METHOD("tile_get_occluder_offset", "id"), &TileSet::tile_get_occluder_offset); - ClassDB::bind_method(D_METHOD("tile_set_z_index", "id", "z_index"), &TileSet::tile_set_z_index); - ClassDB::bind_method(D_METHOD("tile_get_z_index", "id"), &TileSet::tile_get_z_index); - - ClassDB::bind_method(D_METHOD("remove_tile", "id"), &TileSet::remove_tile); - ClassDB::bind_method(D_METHOD("clear"), &TileSet::clear); - ClassDB::bind_method(D_METHOD("get_last_unused_tile_id"), &TileSet::get_last_unused_tile_id); - ClassDB::bind_method(D_METHOD("find_tile_by_name", "name"), &TileSet::find_tile_by_name); - ClassDB::bind_method(D_METHOD("get_tiles_ids"), &TileSet::_get_tiles_ids); - - 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); - BIND_ENUM_CONSTANT(BITMASK_3X3); - - BIND_ENUM_CONSTANT(BIND_TOPLEFT); - BIND_ENUM_CONSTANT(BIND_TOP); - BIND_ENUM_CONSTANT(BIND_TOPRIGHT); - BIND_ENUM_CONSTANT(BIND_LEFT); - BIND_ENUM_CONSTANT(BIND_CENTER); - BIND_ENUM_CONSTANT(BIND_RIGHT); - BIND_ENUM_CONSTANT(BIND_BOTTOMLEFT); - BIND_ENUM_CONSTANT(BIND_BOTTOM); - BIND_ENUM_CONSTANT(BIND_BOTTOMRIGHT); - - BIND_ENUM_CONSTANT(SINGLE_TILE); - BIND_ENUM_CONSTANT(AUTO_TILE); - BIND_ENUM_CONSTANT(ATLAS_TILE); +void TileSetAtlasPluginPhysics::draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) { + // Draw the debug collision shapes. + Ref<TileSet> tile_set = p_tile_map->get_tileset(); + ERR_FAIL_COND(!tile_set.is_valid()); + + if (!p_tile_map->get_tree() || !(Engine::get_singleton()->is_editor_hint() || p_tile_map->get_tree()->is_debugging_collisions_hint())) { + return; + } + + RenderingServer *rs = RenderingServer::get_singleton(); + + Vector2 quadrant_pos = p_tile_map->map_to_world(p_quadrant->coords * p_tile_map->get_effective_quadrant_size()); + + Color debug_collision_color = p_tile_map->get_tree()->get_debug_collisions_color(); + for (Set<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) { + TileMapCell c = p_tile_map->get_cell(E_cell->get()); + + Transform2D xform; + xform.set_origin(p_tile_map->map_to_world(E_cell->get()) - quadrant_pos); + rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform); + + if (tile_set->has_source(c.source_id)) { + TileSetSource *source = *tile_set->get_source(c.source_id); + + if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { + continue; + } + + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); + + for (int body_index = 0; body_index < p_quadrant->bodies.size(); body_index++) { + for (int shape_index = 0; shape_index < tile_data->get_collision_shapes_count(body_index); shape_index++) { + // Draw the debug shape. + Ref<Shape2D> shape = tile_data->get_collision_shape_shape(body_index, shape_index); + if (shape.is_valid()) { + shape->draw(p_quadrant->debug_canvas_item, debug_collision_color); + } + } + } + } + } + rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, Transform2D()); + } +}; + +/////////////////////////////// TileSetAtlasPluginNavigation ////////////////////////////////////// + +void TileSetAtlasPluginNavigation::tilemap_notification(TileMap *p_tile_map, int p_what) { + switch (p_what) { + case CanvasItem::NOTIFICATION_TRANSFORM_CHANGED: { + if (p_tile_map->is_inside_tree()) { + Map<Vector2i, TileMapQuadrant> quadrant_map = p_tile_map->get_quadrant_map(); + Transform2D tilemap_xform = p_tile_map->get_global_transform(); + for (Map<Vector2i, TileMapQuadrant>::Element *E_quadrant = quadrant_map.front(); E_quadrant; E_quadrant = E_quadrant->next()) { + TileMapQuadrant &q = E_quadrant->get(); + for (Map<Vector2i, Vector<RID>>::Element *E_region = q.navigation_regions.front(); E_region; E_region = E_region->next()) { + for (int layer_index = 0; layer_index < E_region->get().size(); layer_index++) { + RID region = E_region->get()[layer_index]; + if (!region.is_valid()) { + continue; + } + Transform2D tile_transform; + tile_transform.set_origin(p_tile_map->map_to_world(E_region->key())); + NavigationServer2D::get_singleton()->region_set_transform(region, tilemap_xform * tile_transform); + } + } + } + } + } break; + } } -TileSet::TileSet() { +void TileSetAtlasPluginNavigation::update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) { + ERR_FAIL_COND(!p_tile_map); + ERR_FAIL_COND(!p_tile_map->is_inside_tree()); + Ref<TileSet> tile_set = p_tile_map->get_tileset(); + ERR_FAIL_COND(!tile_set.is_valid()); + + // Get colors for debug. + SceneTree *st = SceneTree::get_singleton(); + Color debug_navigation_color; + bool debug_navigation = st && st->is_debugging_navigation_hint(); + if (debug_navigation) { + debug_navigation_color = st->get_debug_navigation_color(); + } + + Transform2D tilemap_xform = p_tile_map->get_global_transform(); + SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first(); + while (q_list_element) { + TileMapQuadrant &q = *q_list_element->self(); + + // Clear navigation shapes in the quadrant. + for (Map<Vector2i, Vector<RID>>::Element *E = q.navigation_regions.front(); E; E = E->next()) { + for (int i = 0; i < E->get().size(); i++) { + RID region = E->get()[i]; + if (!region.is_valid()) { + continue; + } + NavigationServer2D::get_singleton()->region_set_map(region, RID()); + } + } + q.navigation_regions.clear(); + + // Get the navigation polygons and create regions. + for (Set<Vector2i>::Element *E_cell = q.cells.front(); E_cell; E_cell = E_cell->next()) { + TileMapCell c = p_tile_map->get_cell(E_cell->get()); + + TileSetSource *source; + if (tile_set->has_source(c.source_id)) { + source = *tile_set->get_source(c.source_id); + + if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { + continue; + } + + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); + q.navigation_regions[E_cell->get()].resize(tile_set->get_navigation_layers_count()); + + for (int layer_index = 0; layer_index < tile_set->get_navigation_layers_count(); layer_index++) { + Ref<NavigationPolygon> navpoly; + navpoly = tile_data->get_navigation_polygon(layer_index); + + if (navpoly.is_valid()) { + Transform2D tile_transform; + tile_transform.set_origin(p_tile_map->map_to_world(E_cell->get())); + + RID region = NavigationServer2D::get_singleton()->region_create(); + NavigationServer2D::get_singleton()->region_set_map(region, p_tile_map->get_world_2d()->get_navigation_map()); + NavigationServer2D::get_singleton()->region_set_transform(region, tilemap_xform * tile_transform); + NavigationServer2D::get_singleton()->region_set_navpoly(region, navpoly); + q.navigation_regions[E_cell->get()].write[layer_index] = region; + } + } + } + } + } + + q_list_element = q_list_element->next(); + } +} + +void TileSetAtlasPluginNavigation::cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) { + // Clear navigation shapes in the quadrant. + for (Map<Vector2i, Vector<RID>>::Element *E = p_quadrant->navigation_regions.front(); E; E = E->next()) { + for (int i = 0; i < E->get().size(); i++) { + RID region = E->get()[i]; + if (!region.is_valid()) { + continue; + } + NavigationServer2D::get_singleton()->free(region); + } + } + p_quadrant->navigation_regions.clear(); +} + +void TileSetAtlasPluginNavigation::draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) { + // Draw the debug collision shapes. + Ref<TileSet> tile_set = p_tile_map->get_tileset(); + ERR_FAIL_COND(!tile_set.is_valid()); + + if (!p_tile_map->get_tree() || !(Engine::get_singleton()->is_editor_hint() || p_tile_map->get_tree()->is_debugging_navigation_hint())) { + return; + } + + RenderingServer *rs = RenderingServer::get_singleton(); + + Color color = p_tile_map->get_tree()->get_debug_navigation_color(); + RandomPCG rand; + + Vector2 quadrant_pos = p_tile_map->map_to_world(p_quadrant->coords * p_tile_map->get_effective_quadrant_size()); + + for (Set<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) { + TileMapCell c = p_tile_map->get_cell(E_cell->get()); + + TileSetSource *source; + if (tile_set->has_source(c.source_id)) { + source = *tile_set->get_source(c.source_id); + + if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { + continue; + } + + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); + + Transform2D xform; + xform.set_origin(p_tile_map->map_to_world(E_cell->get()) - quadrant_pos); + rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform); + + for (int layer_index = 0; layer_index < tile_set->get_navigation_layers_count(); layer_index++) { + Ref<NavigationPolygon> navpoly = tile_data->get_navigation_polygon(layer_index); + if (navpoly.is_valid()) { + PackedVector2Array navigation_polygon_vertices = navpoly->get_vertices(); + + for (int i = 0; i < navpoly->get_polygon_count(); i++) { + // An array of vertices for this polygon. + Vector<int> polygon = navpoly->get_polygon(i); + Vector<Vector2> vertices; + vertices.resize(polygon.size()); + for (int j = 0; j < polygon.size(); j++) { + ERR_FAIL_INDEX(polygon[j], navigation_polygon_vertices.size()); + vertices.write[j] = navigation_polygon_vertices[polygon[j]]; + } + + // Generate the polygon color, slightly randomly modified from the settings one. + Color random_variation_color; + random_variation_color.set_hsv(color.get_h() + rand.random(-1.0, 1.0) * 0.05, color.get_s(), color.get_v() + rand.random(-1.0, 1.0) * 0.1); + random_variation_color.a = color.a; + Vector<Color> colors; + colors.push_back(random_variation_color); + + RS::get_singleton()->canvas_item_add_polygon(p_quadrant->debug_canvas_item, vertices, colors); + } + } + } + } + } + } } diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index 0a8721f35b..20cf183a20 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -32,226 +32,614 @@ #define TILE_SET_H #include "core/io/resource.h" -#include "core/variant/array.h" +#include "core/object/object.h" #include "scene/2d/light_occluder_2d.h" #include "scene/2d/navigation_region_2d.h" +#include "scene/main/canvas_item.h" #include "scene/resources/convex_polygon_shape_2d.h" +#include "scene/resources/packed_scene.h" +#include "scene/resources/physics_material.h" +#include "scene/resources/shape_2d.h" + +#ifndef DISABLE_DEPRECATED +#include "scene/2d/light_occluder_2d.h" +#include "scene/2d/navigation_region_2d.h" +#include "scene/resources/shader.h" #include "scene/resources/shape_2d.h" #include "scene/resources/texture.h" +#endif + +class TileMap; +struct TileMapQuadrant; +class TileSetSource; +class TileSetAtlasSource; +class TileData; + +// Forward-declare the plugins. +class TileSetPlugin; +class TileSetAtlasPluginRendering; +class TileSetAtlasPluginPhysics; +class TileSetAtlasPluginNavigation; +class TileSetAtlasPluginTerrain; class TileSet : public Resource { GDCLASS(TileSet, Resource); -public: - struct ShapeData { +#ifndef DISABLE_DEPRECATED +private: + struct CompatibilityShapeData { + Vector2i autotile_coords; + bool one_way; + float one_way_margin; Ref<Shape2D> shape; - Transform2D shape_transform; - Vector2 autotile_coord; - bool one_way_collision = false; - float one_way_collision_margin = 1.0; + Transform2D transform; + }; - ShapeData() {} + struct CompatibilityTileData { + String name; + Ref<Texture2D> texture; + Vector2 tex_offset; + Ref<ShaderMaterial> material; + Rect2 region; + int tile_mode; + Color modulate; + + // Atlas or autotiles data + int autotile_bitmask_mode; + Vector2 autotile_icon_coordinate; + Size2i autotile_tile_size = Size2i(16, 16); + + int autotile_spacing; + Map<Vector2i, int> autotile_bitmask_flags; + Map<Vector2i, Ref<OccluderPolygon2D>> autotile_occluder_map; + Map<Vector2i, Ref<NavigationPolygon>> autotile_navpoly_map; + Map<Vector2i, int> autotile_priority_map; + Map<Vector2i, int> autotile_z_index_map; + + Vector<CompatibilityShapeData> shapes; + Ref<OccluderPolygon2D> occluder; + Vector2 occluder_offset; + Ref<NavigationPolygon> navigation; + Vector2 navigation_offset; + int z_index; }; - enum BitmaskMode { - BITMASK_2X2, - BITMASK_3X3_MINIMAL, - BITMASK_3X3 + Map<int, CompatibilityTileData *> compatibility_data = Map<int, CompatibilityTileData *>(); + Map<int, int> compatibility_source_mapping = Map<int, int>(); + +private: + void compatibility_conversion(); + +public: + int compatibility_get_source_for_tile_id(int p_old_source) { + return compatibility_source_mapping[p_old_source]; }; - enum AutotileBindings { - BIND_TOPLEFT = 1, - BIND_TOP = 2, - BIND_TOPRIGHT = 4, - BIND_LEFT = 8, - BIND_CENTER = 16, - BIND_RIGHT = 32, - BIND_BOTTOMLEFT = 64, - BIND_BOTTOM = 128, - BIND_BOTTOMRIGHT = 256, - - BIND_IGNORE_TOPLEFT = 1 << 16, - BIND_IGNORE_TOP = 1 << 17, - BIND_IGNORE_TOPRIGHT = 1 << 18, - BIND_IGNORE_LEFT = 1 << 19, - BIND_IGNORE_CENTER = 1 << 20, - BIND_IGNORE_RIGHT = 1 << 21, - BIND_IGNORE_BOTTOMLEFT = 1 << 22, - BIND_IGNORE_BOTTOM = 1 << 23, - BIND_IGNORE_BOTTOMRIGHT = 1 << 24 +#endif // DISABLE_DEPRECATED + +public: + enum CellNeighbor { + CELL_NEIGHBOR_RIGHT_SIDE = 0, + CELL_NEIGHBOR_RIGHT_CORNER, + CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, + CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER, + CELL_NEIGHBOR_BOTTOM_SIDE, + CELL_NEIGHBOR_BOTTOM_CORNER, + CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, + CELL_NEIGHBOR_BOTTOM_LEFT_CORNER, + CELL_NEIGHBOR_LEFT_SIDE, + CELL_NEIGHBOR_LEFT_CORNER, + CELL_NEIGHBOR_TOP_LEFT_SIDE, + CELL_NEIGHBOR_TOP_LEFT_CORNER, + CELL_NEIGHBOR_TOP_SIDE, + CELL_NEIGHBOR_TOP_CORNER, + CELL_NEIGHBOR_TOP_RIGHT_SIDE, + CELL_NEIGHBOR_TOP_RIGHT_CORNER, + CELL_NEIGHBOR_MAX, }; - enum TileMode { - SINGLE_TILE, - AUTO_TILE, - ATLAS_TILE + enum TerrainMode { + TERRAIN_MODE_MATCH_CORNERS_AND_SIDES = 0, + TERRAIN_MODE_MATCH_CORNERS, + TERRAIN_MODE_MATCH_SIDES, }; - struct AutotileData { - BitmaskMode bitmask_mode = BITMASK_2X2; - // Default size to prevent invalid value - Size2 size = Size2(64, 64); - Vector2 icon_coord = Vector2(0, 0); - int spacing = 0; - Map<Vector2, uint32_t> flags; - Map<Vector2, Ref<OccluderPolygon2D>> occluder_map; - Map<Vector2, Ref<NavigationPolygon>> navpoly_map; - Map<Vector2, int> priority_map; - Map<Vector2, int> z_index_map; - - explicit AutotileData() {} + enum TileShape { + TILE_SHAPE_SQUARE, + TILE_SHAPE_ISOMETRIC, + TILE_SHAPE_HALF_OFFSET_SQUARE, + TILE_SHAPE_HEXAGON, }; -private: - struct TileData { - String name; - Ref<Texture2D> texture; - Vector2 offset; - Rect2i region; - Vector<ShapeData> shapes_data; - Vector2 occluder_offset; - Ref<OccluderPolygon2D> occluder; - Vector2 navigation_polygon_offset; - Ref<NavigationPolygon> navigation_polygon; - Ref<ShaderMaterial> material; - TileMode tile_mode = SINGLE_TILE; - // Default modulate for back-compat - Color modulate = Color(1, 1, 1); - AutotileData autotile_data; - int z_index = 0; + enum TileLayout { + TILE_LAYOUT_STACKED, + TILE_LAYOUT_STACKED_OFFSET, + TILE_LAYOUT_STAIRS_RIGHT, + TILE_LAYOUT_STAIRS_DOWN, + TILE_LAYOUT_DIAMOND_RIGHT, + TILE_LAYOUT_DIAMOND_DOWN, + }; - explicit TileData() {} + enum TileOffsetAxis { + TILE_OFFSET_AXIS_HORIZONTAL, + TILE_OFFSET_AXIS_VERTICAL, }; - Map<int, TileData> tile_map; +public: + struct PackedSceneSource { + Ref<PackedScene> scene; + Vector2 offset; + }; protected: bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; - void _tile_set_shapes(int p_id, const Array &p_shapes); - Array _tile_get_shapes(int p_id) const; - Array _get_tiles_ids() const; - void _decompose_convex_shape(Ref<Shape2D> p_shape); +private: + // --- TileSet data --- + // Basic shape and layout. + TileShape tile_shape = TILE_SHAPE_SQUARE; + TileLayout tile_layout = TILE_LAYOUT_STACKED; + TileOffsetAxis tile_offset_axis = TILE_OFFSET_AXIS_HORIZONTAL; + Size2i tile_size = Size2i(16, 16); //Size2(64, 64); + Vector2 tile_skew = Vector2(0, 0); + + // Rendering. + bool y_sorting = false; + bool uv_clipping = false; + struct OcclusionLayer { + uint32_t light_mask = 1; + bool sdf_collision = false; + }; + Vector<OcclusionLayer> occlusion_layers; + + // Physics + struct PhysicsLayer { + uint32_t collision_layer = 1; + uint32_t collision_mask = 1; + Ref<PhysicsMaterial> physics_material; + }; + Vector<PhysicsLayer> physics_layers; + + // Terrains + struct Terrain { + String name; + Color color; + }; + struct TerrainSet { + TerrainMode mode = TERRAIN_MODE_MATCH_CORNERS_AND_SIDES; + Vector<Terrain> terrains; + }; + Vector<TerrainSet> terrain_sets; + + // Navigation + struct Navigationlayer { + uint32_t layers = 1; + }; + Vector<Navigationlayer> navigation_layers; + + // CustomData + struct CustomDataLayer { + String name; + Variant::Type type = Variant::NIL; + }; + Vector<CustomDataLayer> custom_data_layers; + Map<String, int> custom_data_layers_by_name; + + // Per Atlas source data. + Map<int, Ref<TileSetSource>> sources; + Vector<int> source_ids; + int next_source_id = 0; + // --------------------- + + // Plugins themselves. + Vector<TileSetPlugin *> tile_set_plugins_vector; + + void _compute_next_source_id(); + void _source_changed(); + +protected: static void _bind_methods(); +public: + // --- Plugins --- + Vector<TileSetPlugin *> get_tile_set_atlas_plugins() const; + + // --- Accessors for TileSet data --- + + // -- Shape and layout -- + void set_tile_shape(TileShape p_shape); + TileShape get_tile_shape() const; + void set_tile_layout(TileLayout p_layout); + TileLayout get_tile_layout() const; + void set_tile_offset_axis(TileOffsetAxis p_alignment); + TileOffsetAxis get_tile_offset_axis() const; + void set_tile_size(Size2i p_size); + Size2i get_tile_size() const; + void set_tile_skew(Vector2 p_skew); + Vector2 get_tile_skew() const; + + // -- Sources management -- + int get_next_source_id() const; + int get_source_count() const; + int get_source_id(int p_index) const; + int add_source(Ref<TileSetAtlasSource> p_tile_atlas_source, int p_source_id_override = -1); + void set_source_id(int p_source_id, int p_new_id); + void remove_source(int p_source_id); + bool has_source(int p_source_id) const; + Ref<TileSetSource> get_source(int p_source_id) const; + + // Rendering + void set_y_sorting(bool p_y_sort); + bool is_y_sorting() const; + + void set_uv_clipping(bool p_uv_clipping); + bool is_uv_clipping() const; + + void set_occlusion_layers_count(int p_occlusion_layers_count); + int get_occlusion_layers_count() const; + void set_occlusion_layer_light_mask(int p_layer_index, int p_light_mask); + int get_occlusion_layer_light_mask(int p_layer_index) const; + void set_occlusion_layer_sdf_collision(int p_layer_index, int p_sdf_collision); + bool get_occlusion_layer_sdf_collision(int p_layer_index) const; + + // Physics + void set_physics_layers_count(int p_physics_layers_count); + int get_physics_layers_count() const; + void set_physics_layer_collision_layer(int p_layer_index, uint32_t p_layer); + uint32_t get_physics_layer_collision_layer(int p_layer_index) const; + void set_physics_layer_collision_mask(int p_layer_index, uint32_t p_mask); + uint32_t get_physics_layer_collision_mask(int p_layer_index) const; + void set_physics_layer_physics_material(int p_layer_index, Ref<PhysicsMaterial> p_physics_material); + Ref<PhysicsMaterial> get_physics_layer_physics_material(int p_layer_index) const; + + // Terrains + void set_terrain_sets_count(int p_terrains_sets_count); + int get_terrain_sets_count() const; + void set_terrain_set_mode(int p_terrain_set, TerrainMode p_terrain_mode); + TerrainMode get_terrain_set_mode(int p_terrain_set) const; + void set_terrains_count(int p_terrain_set, int p_terrains_count); + int get_terrains_count(int p_terrain_set) const; + void set_terrain_name(int p_terrain_set, int p_terrain_index, String p_name); + String get_terrain_name(int p_terrain_set, int p_terrain_index) const; + void set_terrain_color(int p_terrain_set, int p_terrain_index, Color p_color); + Color get_terrain_color(int p_terrain_set, int p_terrain_index) const; + bool is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeighbor p_peering_bit) const; + + // Navigation + void set_navigation_layers_count(int p_navigation_layers_count); + int get_navigation_layers_count() const; + void set_navigation_layer_layers(int p_layer_index, uint32_t p_layers); + uint32_t get_navigation_layer_layers(int p_layer_index) const; + + // Custom data + void set_custom_data_layers_count(int p_custom_data_layers_count); + int get_custom_data_layers_count() const; + int get_custom_data_layer_by_name(String p_value) const; + void set_custom_data_name(int p_layer_id, String p_value); + String get_custom_data_name(int p_layer_id) const; + void set_custom_data_type(int p_layer_id, Variant::Type p_value); + Variant::Type get_custom_data_type(int p_layer_id) const; + + // Helpers + void draw_tile_shape(CanvasItem *p_canvas_item, Rect2 p_region, Color p_color, bool p_filled = false, Ref<Texture2D> p_texture = Ref<Texture2D>()); + virtual void reset_state() override; + TileSet(); + ~TileSet(); +}; + +class TileSetSource : public Resource { + GDCLASS(TileSetSource, Resource); + +protected: + const TileSet *tile_set = nullptr; + public: - void create_tile(int p_id); + // Not exposed. + virtual void set_tile_set(const TileSet *p_tile_set); + virtual void notify_tile_data_properties_should_change(){}; + virtual void reset_state() override{}; + + // Tiles. + virtual int get_tiles_count() const = 0; + virtual Vector2i get_tile_id(int tile_index) const = 0; + virtual bool has_tile(Vector2i p_atlas_coords) const = 0; + + // Alternative tiles. + virtual int get_alternative_tiles_count(const Vector2i p_atlas_coords) const = 0; + virtual int get_alternative_tile_id(const Vector2i p_atlas_coords, int p_index) const = 0; + virtual bool has_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile) const = 0; +}; - void autotile_set_bitmask_mode(int p_id, BitmaskMode p_mode); - BitmaskMode autotile_get_bitmask_mode(int p_id) const; +class TileSetAtlasSource : public TileSetSource { + GDCLASS(TileSetAtlasSource, TileSetSource); - void tile_set_name(int p_id, const String &p_name); - String tile_get_name(int p_id) const; +public: + static const Vector2i INVALID_ATLAS_COORDS; // Vector2i(-1, -1); + static const int INVALID_TILE_ALTERNATIVE; // -1; + + struct TileAlternativesData { + Vector2i size_in_atlas = Vector2i(1, 1); + Vector2i texture_offset; + Map<int, TileData *> alternatives; + Vector<int> alternatives_ids; + int next_alternative_id = 1; + }; - void tile_set_texture(int p_id, const Ref<Texture2D> &p_texture); - Ref<Texture2D> tile_get_texture(int p_id) const; +private: + Ref<Texture2D> texture; + Vector2i margins; + Vector2i separation; + Size2i texture_region_size = Size2i(16, 16); - void tile_set_texture_offset(int p_id, const Vector2 &p_offset); - Vector2 tile_get_texture_offset(int p_id) const; + Map<Vector2i, TileAlternativesData> tiles; + Vector<Vector2i> tiles_ids; + Map<Vector2i, Vector2i> _coords_mapping_cache; // Maps any coordinate to the including tile - void tile_set_region(int p_id, const Rect2 &p_region); - Rect2 tile_get_region(int p_id) const; + TileData *_get_atlas_tile_data(Vector2i p_atlas_coords, int p_alternative_tile); + const TileData *_get_atlas_tile_data(Vector2i p_atlas_coords, int p_alternative_tile) const; - void tile_set_tile_mode(int p_id, TileMode p_tile_mode); - TileMode tile_get_tile_mode(int p_id) const; + void _compute_next_alternative_id(const Vector2i p_atlas_coords); - void autotile_set_icon_coordinate(int p_id, Vector2 coord); - Vector2 autotile_get_icon_coordinate(int p_id) const; +protected: + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + void _get_property_list(List<PropertyInfo> *p_list) const; - void autotile_set_spacing(int p_id, int p_spacing); - int autotile_get_spacing(int p_id) const; + static void _bind_methods(); - void autotile_set_size(int p_id, Size2 p_size); - Size2 autotile_get_size(int p_id) const; +public: + // Not exposed. + virtual void set_tile_set(const TileSet *p_tile_set) override; + virtual void notify_tile_data_properties_should_change() override; + virtual void reset_state() override; - void autotile_clear_bitmask_map(int p_id); - void autotile_set_subtile_priority(int p_id, const Vector2 &p_coord, int p_priority); - int autotile_get_subtile_priority(int p_id, const Vector2 &p_coord); - const Map<Vector2, int> &autotile_get_priority_map(int p_id) const; + // Base properties. + void set_texture(Ref<Texture2D> p_texture); + Ref<Texture2D> get_texture() const; + void set_margins(Vector2i p_margins); + Vector2i get_margins() const; + void set_separation(Vector2i p_separation); + Vector2i get_separation() const; + void set_texture_region_size(Vector2i p_tile_size); + Vector2i get_texture_region_size() const; + + // Base tiles. + void create_tile(const Vector2i p_atlas_coords, const Vector2i p_size = Vector2i(1, 1)); // Create a tile if it does not exists, or add alternative tile if it does. + void remove_tile(Vector2i p_atlas_coords); // Remove a tile. If p_tile_key.alternative_tile if different from 0, remove the alternative + virtual bool has_tile(Vector2i p_atlas_coords) const override; + bool can_move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords = INVALID_ATLAS_COORDS, Vector2i p_new_size = Vector2i(-1, -1)) const; + void move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords = INVALID_ATLAS_COORDS, Vector2i p_new_size = Vector2i(-1, -1)); + Vector2i get_tile_size_in_atlas(Vector2i p_atlas_coords) const; + + virtual int get_tiles_count() const override; + virtual Vector2i get_tile_id(int p_index) const override; + + Vector2i get_tile_at_coords(Vector2i p_atlas_coords) const; + + // Alternative tiles. + int create_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_id_override = -1); + void remove_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile); + void set_alternative_tile_id(const Vector2i p_atlas_coords, int p_alternative_tile, int p_new_id); + virtual bool has_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile) const override; + int get_next_alternative_tile_id(const Vector2i p_atlas_coords) const; + + virtual int get_alternative_tiles_count(const Vector2i p_atlas_coords) const override; + virtual int get_alternative_tile_id(const Vector2i p_atlas_coords, int p_index) const override; + + // Get data associated to a tile. + Object *get_tile_data(const Vector2i p_atlas_coords, int p_alternative_tile) const; + + // Helpers. + Vector2i get_atlas_grid_size() const; + bool has_tiles_outside_texture(); + void clear_tiles_outside_texture(); + Rect2i get_tile_texture_region(Vector2i p_atlas_coords) const; + Vector2i get_tile_effective_texture_offset(Vector2i p_atlas_coords, int p_alternative_tile) const; + + ~TileSetAtlasSource(); +}; - void autotile_set_z_index(int p_id, const Vector2 &p_coord, int p_z_index); - int autotile_get_z_index(int p_id, const Vector2 &p_coord); - const Map<Vector2, int> &autotile_get_z_index_map(int p_id) const; +class TileData : public Object { + GDCLASS(TileData, Object); - void autotile_set_bitmask(int p_id, Vector2 p_coord, uint32_t p_flag); - 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 = nullptr, const Vector2 &p_tile_location = Vector2()); - Vector2 atlastile_get_subtile_by_priority(int p_id, const Node *p_tilemap_node = nullptr, const Vector2 &p_tile_location = Vector2()); +private: + const TileSet *tile_set = nullptr; + bool allow_transform = true; + + // Rendering + bool flip_h = false; + bool flip_v = false; + bool transpose = false; + Vector2i tex_offset = Vector2i(); + Ref<ShaderMaterial> material = Ref<ShaderMaterial>(); + Color modulate = Color(1.0, 1.0, 1.0, 1.0); + int z_index = 0; + Vector2i y_sort_origin = Vector2i(); + Vector<Ref<OccluderPolygon2D>> occluders; + + // Physics + struct PhysicsLayerTileData { + struct ShapeTileData { + Ref<Shape2D> shape = Ref<Shape2D>(); + bool one_way = false; + float one_way_margin = 1.0; + }; + + Vector<ShapeTileData> shapes; + }; + Vector<PhysicsLayerTileData> physics; + // TODO add support for areas. - 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; + // Terrain + int terrain_set = -1; + int terrain_peering_bits[16] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; - void tile_set_shape_transform(int p_id, int p_shape_id, const Transform2D &p_offset); - Transform2D tile_get_shape_transform(int p_id, int p_shape_id) const; + // Navigation + Vector<Ref<NavigationPolygon>> navigation; - void tile_set_shape_offset(int p_id, int p_shape_id, const Vector2 &p_offset); - Vector2 tile_get_shape_offset(int p_id, int p_shape_id) const; + // Misc + double probability = 1.0; - void tile_set_shape_one_way(int p_id, int p_shape_id, bool p_one_way); - bool tile_get_shape_one_way(int p_id, int p_shape_id) const; + // Custom data + Vector<Variant> custom_data; - void tile_set_shape_one_way_margin(int p_id, int p_shape_id, float p_margin); - float tile_get_shape_one_way_margin(int p_id, int p_shape_id) const; +protected: + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + void _get_property_list(List<PropertyInfo> *p_list) const; + static void _bind_methods(); - void tile_clear_shapes(int p_id); - void tile_add_shape(int p_id, const Ref<Shape2D> &p_shape, const Transform2D &p_transform, bool p_one_way = false, const Vector2 &p_autotile_coord = Vector2()); - int tile_get_shape_count(int p_id) const; +public: + // Not exposed. + void set_tile_set(const TileSet *p_tile_set); + void notify_tile_data_properties_should_change(); + void reset_state(); + void set_allow_transform(bool p_allow_transform); + bool is_allowing_transform() const; + + // Rendering + void set_flip_h(bool p_flip_h); + bool get_flip_h() const; + void set_flip_v(bool p_flip_v); + bool get_flip_v() const; + void set_transpose(bool p_transpose); + bool get_transpose() const; + + void set_texture_offset(Vector2i p_texture_offset); + Vector2i get_texture_offset() const; + void tile_set_material(Ref<ShaderMaterial> p_material); + Ref<ShaderMaterial> tile_get_material() const; + void set_modulate(Color p_modulate); + Color get_modulate() const; + void set_z_index(int p_z_index); + int get_z_index() const; + void set_y_sort_origin(Vector2i p_y_sort_origin); + Vector2i get_y_sort_origin() const; + + void set_occluder(int p_layer_id, Ref<OccluderPolygon2D> p_occluder_polygon); + Ref<OccluderPolygon2D> get_occluder(int p_layer_id) const; + + // Physics + int get_collision_shapes_count(int p_layer_id) const; + void set_collision_shapes_count(int p_layer_id, int p_shapes_count); + void add_collision_shape(int p_layer_id); + void remove_collision_shape(int p_layer_id, int p_shape_index); + void set_collision_shape_shape(int p_layer_id, int p_shape_index, Ref<Shape2D> p_shape); + Ref<Shape2D> get_collision_shape_shape(int p_layer_id, int p_shape_index) const; + void set_collision_shape_one_way(int p_layer_id, int p_shape_index, bool p_one_way); + bool is_collision_shape_one_way(int p_layer_id, int p_shape_index) const; + void set_collision_shape_one_way_margin(int p_layer_id, int p_shape_index, float p_one_way_margin); + float get_collision_shape_one_way_margin(int p_layer_id, int p_shape_index) const; + + // Terrain + void set_terrain_set(int p_terrain_id); + int get_terrain_set() const; + void set_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit, int p_terrain_id); + int get_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit) const; + bool is_valid_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit) const; + + // Navigation + void set_navigation_polygon(int p_layer_id, Ref<NavigationPolygon> p_navigation_polygon); + Ref<NavigationPolygon> get_navigation_polygon(int p_layer_id) const; + + // Misc + void set_probability(float p_probability); + float get_probability() const; + + // Custom data. + void set_custom_data(String p_layer_name, Variant p_value); + Variant get_custom_data(String p_layer_name) const; + void set_custom_data_by_layer_id(int p_layer_id, Variant p_value); + Variant get_custom_data_by_layer_id(int p_layer_id) const; +}; - void tile_set_shapes(int p_id, const Vector<ShapeData> &p_shapes); - Vector<ShapeData> tile_get_shapes(int p_id) const; +#include "scene/2d/tile_map.h" - void tile_set_material(int p_id, const Ref<ShaderMaterial> &p_material); - Ref<ShaderMaterial> tile_get_material(int p_id) const; +class TileSetPlugin : public Object { + GDCLASS(TileSetPlugin, Object); - void tile_set_modulate(int p_id, const Color &p_modulate); - Color tile_get_modulate(int p_id) const; +public: + // Tilemap updates. + virtual void tilemap_notification(TileMap *p_tile_map, int p_what){}; + virtual void update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list){}; + virtual void create_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant){}; + virtual void cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant){}; - void tile_set_occluder_offset(int p_id, const Vector2 &p_offset); - Vector2 tile_get_occluder_offset(int p_id) const; + virtual void draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant){}; +}; - void tile_set_light_occluder(int p_id, const Ref<OccluderPolygon2D> &p_light_occluder); - Ref<OccluderPolygon2D> tile_get_light_occluder(int p_id) const; +class TileSetAtlasPluginRendering : public TileSetPlugin { + GDCLASS(TileSetAtlasPluginRendering, TileSetPlugin); - void autotile_set_light_occluder(int p_id, const Ref<OccluderPolygon2D> &p_light_occluder, const Vector2 &p_coord); - Ref<OccluderPolygon2D> autotile_get_light_occluder(int p_id, const Vector2 &p_coord) const; - const Map<Vector2, Ref<OccluderPolygon2D>> &autotile_get_light_oclusion_map(int p_id) const; +private: + static constexpr float fp_adjust = 0.00001; + bool quadrant_order_dirty = false; - void tile_set_navigation_polygon_offset(int p_id, const Vector2 &p_offset); - Vector2 tile_get_navigation_polygon_offset(int p_id) const; +public: + // Tilemap updates + virtual void tilemap_notification(TileMap *p_tile_map, int p_what) override; + virtual void update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) override; + virtual void create_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override; + virtual void cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override; + + // Other. + static void draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, Color p_modulation = Color(1.0, 1.0, 1.0, 1.0)); +}; - void tile_set_navigation_polygon(int p_id, const Ref<NavigationPolygon> &p_navigation_polygon); - Ref<NavigationPolygon> tile_get_navigation_polygon(int p_id) const; +class TileSetAtlasPluginTerrain : public TileSetPlugin { + GDCLASS(TileSetAtlasPluginTerrain, TileSetPlugin); - void autotile_set_navigation_polygon(int p_id, const Ref<NavigationPolygon> &p_navigation_polygon, const Vector2 &p_coord); - Ref<NavigationPolygon> autotile_get_navigation_polygon(int p_id, const Vector2 &p_coord) const; - const Map<Vector2, Ref<NavigationPolygon>> &autotile_get_navigation_map(int p_id) const; +private: + static void _draw_square_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit); + static void _draw_square_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit); + static void _draw_square_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit); - void tile_set_z_index(int p_id, int p_z_index); - int tile_get_z_index(int p_id) const; + static void _draw_isometric_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit); + static void _draw_isometric_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit); + static void _draw_isometric_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit); - void remove_tile(int p_id); + static void _draw_half_offset_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis); + static void _draw_half_offset_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis); + static void _draw_half_offset_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis); - bool has_tile(int p_id) const; +public: + //virtual void tilemap_notification(const TileMap * p_tile_map, int p_what); - bool is_tile_bound(int p_drawn_id, int p_neighbor_id); + static void draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, const TileData *p_tile_data); +}; - int find_tile_by_name(const String &p_name) const; - void get_tile_list(List<int> *p_tiles) const; +class TileSetAtlasPluginPhysics : public TileSetPlugin { + GDCLASS(TileSetAtlasPluginPhysics, TileSetPlugin); - void clear(); +public: + // Tilemap updates + virtual void tilemap_notification(TileMap *p_tile_map, int p_what) override; + virtual void update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) override; + virtual void create_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override; + virtual void cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override; + virtual void draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override; +}; - int get_last_unused_tile_id() const; +class TileSetAtlasPluginNavigation : public TileSetPlugin { + GDCLASS(TileSetAtlasPluginNavigation, TileSetPlugin); - TileSet(); +public: + // Tilemap updates + virtual void tilemap_notification(TileMap *p_tile_map, int p_what) override; + virtual void update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) override; + //virtual void create_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override; + virtual void cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override; + virtual void draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override; }; -VARIANT_ENUM_CAST(TileSet::AutotileBindings); -VARIANT_ENUM_CAST(TileSet::BitmaskMode); -VARIANT_ENUM_CAST(TileSet::TileMode); +VARIANT_ENUM_CAST(TileSet::CellNeighbor); +VARIANT_ENUM_CAST(TileSet::TerrainMode); +VARIANT_ENUM_CAST(TileSet::TileShape); +VARIANT_ENUM_CAST(TileSet::TileLayout); +VARIANT_ENUM_CAST(TileSet::TileOffsetAxis); #endif // TILE_SET_H diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index 859546694f..da29f2ebd8 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -300,6 +300,30 @@ String VisualShaderNodeCustom::generate_global_per_node(Shader::Mode p_mode, Vis return ""; } +void VisualShaderNodeCustom::set_input_port_default_value(int p_port, const Variant &p_value) { + if (!is_initialized) { + VisualShaderNode::set_input_port_default_value(p_port, p_value); + } +} + +void VisualShaderNodeCustom::set_default_input_values(const Array &p_values) { + if (!is_initialized) { + VisualShaderNode::set_default_input_values(p_values); + } +} + +void VisualShaderNodeCustom::_set_input_port_default_value(int p_port, const Variant &p_value) { + VisualShaderNode::set_input_port_default_value(p_port, p_value); +} + +bool VisualShaderNodeCustom::_is_initialized() { + return is_initialized; +} + +void VisualShaderNodeCustom::_set_initialized(bool p_enabled) { + is_initialized = p_enabled; +} + void VisualShaderNodeCustom::_bind_methods() { BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_name")); BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_description")); @@ -314,6 +338,12 @@ void VisualShaderNodeCustom::_bind_methods() { BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_code", PropertyInfo(Variant::ARRAY, "input_vars"), PropertyInfo(Variant::ARRAY, "output_vars"), PropertyInfo(Variant::INT, "mode"), PropertyInfo(Variant::INT, "type"))); BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_global_code", PropertyInfo(Variant::INT, "mode"))); BIND_VMETHOD(MethodInfo(Variant::BOOL, "_is_highend")); + + ClassDB::bind_method(D_METHOD("_set_initialized", "enabled"), &VisualShaderNodeCustom::_set_initialized); + ClassDB::bind_method(D_METHOD("_is_initialized"), &VisualShaderNodeCustom::_is_initialized); + ClassDB::bind_method(D_METHOD("_set_input_port_default_value", "port", "value"), &VisualShaderNodeCustom::_set_input_port_default_value); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "initialized", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_initialized", "_is_initialized"); } VisualShaderNodeCustom::VisualShaderNodeCustom() { @@ -961,7 +991,8 @@ static const char *type_string[VisualShader::TYPE_MAX] = { "light", "emit", "process", - "end" + "end", + "sky", }; bool VisualShader::_set(const StringName &p_name, const Variant &p_value) { @@ -1476,7 +1507,7 @@ void VisualShader::_update_shader() const { global_code += "render_mode " + render_mode + ";\n\n"; } - static const char *func_name[TYPE_MAX] = { "vertex", "fragment", "light", "emit", "process", "end" }; + static const char *func_name[TYPE_MAX] = { "vertex", "fragment", "light", "emit", "process", "end", "sky" }; String global_expressions; Set<String> used_uniform_names; @@ -1667,6 +1698,7 @@ void VisualShader::_bind_methods() { BIND_ENUM_CONSTANT(TYPE_EMIT); BIND_ENUM_CONSTANT(TYPE_PROCESS); BIND_ENUM_CONSTANT(TYPE_END); + BIND_ENUM_CONSTANT(TYPE_SKY); BIND_ENUM_CONSTANT(TYPE_MAX); BIND_CONSTANT(NODE_ID_INVALID); @@ -1698,7 +1730,6 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "point_size", "POINT_SIZE" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "modelview", "MODELVIEW_MATRIX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "camera", "CAMERA_MATRIX" }, @@ -1721,10 +1752,8 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "point_coord", "vec3(POINT_COORD, 0.0)" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "side", "float(FRONT_FACING ? 1.0 : 0.0)" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_camera", "INV_CAMERA_MATRIX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "camera", "CAMERA_MATRIX" }, @@ -1750,7 +1779,6 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "specular", "SPECULAR_LIGHT" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "metallic", "METALLIC" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_camera", "INV_CAMERA_MATRIX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "camera", "CAMERA_MATRIX" }, @@ -1759,6 +1787,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(VIEWPORT_SIZE, 0.0)" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_BOOLEAN, "output_is_srgb", "OUTPUT_IS_SRGB" }, + // Canvas Item, Vertex { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "vec3(VERTEX, 0.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" }, @@ -1766,12 +1795,12 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "point_size", "POINT_SIZE" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "texture_pixel_size", "vec3(TEXTURE_PIXEL_SIZE, 1.0)" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "canvas", "CANVAS_MATRIX" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "screen", "SCREEN_MATRIX" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_light_pass", "AT_LIGHT_PASS" }, + // Canvas Item, Fragment { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.xyz" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" }, @@ -1789,6 +1818,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "specular_shininess", "SPECULAR_SHININESS.rgb" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "specular_shininess_alpha", "SPECULAR_SHININESS.a" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "specular_shininess_texture", "SPECULAR_SHININESS_TEXTURE" }, + // Canvas Item, Light { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.xyz" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" }, @@ -1856,36 +1886,36 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_TRANSFORM, "emission_transform", "EMISSION_TRANSFORM" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, - // Sky, Fragment - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_cubemap_pass", "AT_CUBEMAP_PASS" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_half_res_pass", "AT_HALF_RES_PASS" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_quarter_res_pass", "AT_QUARTER_RES_PASS" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "eyedir", "EYEDIR" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "half_res_color", "HALF_RES_COLOR.rgb" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "half_res_alpha", "HALF_RES_COLOR.a" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "light0_color", "LIGHT0_COLOR" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "light0_direction", "LIGHT0_DIRECTION" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "light0_enabled", "LIGHT0_ENABLED" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "light0_energy", "LIGHT0_ENERGY" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "light1_color", "LIGHT1_COLOR" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "light1_direction", "LIGHT1_DIRECTION" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "light1_enabled", "LIGHT1_ENABLED" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "light1_energy", "LIGHT1_ENERGY" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "light2_color", "LIGHT2_COLOR" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "light2_direction", "LIGHT2_DIRECTION" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "light2_enabled", "LIGHT2_ENABLED" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "light2_energy", "LIGHT2_ENERGY" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "light3_color", "LIGHT3_COLOR" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "light3_direction", "LIGHT3_DIRECTION" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "light3_enabled", "LIGHT3_ENABLED" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "light3_energy", "LIGHT3_ENERGY" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "position", "POSITION" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "quarter_res_color", "QUARTER_RES_COLOR.rgb" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "quarter_res_alpha", "QUARTER_RES_COLOR.a" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "radiance", "RADIANCE" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "sky_coords", "vec3(SKY_COORDS, 0.0)" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + // Sky, Sky + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_cubemap_pass", "AT_CUBEMAP_PASS" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_half_res_pass", "AT_HALF_RES_PASS" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_quarter_res_pass", "AT_QUARTER_RES_PASS" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "eyedir", "EYEDIR" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "half_res_color", "HALF_RES_COLOR.rgb" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "half_res_alpha", "HALF_RES_COLOR.a" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light0_color", "LIGHT0_COLOR" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light0_direction", "LIGHT0_DIRECTION" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "light0_enabled", "LIGHT0_ENABLED" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "light0_energy", "LIGHT0_ENERGY" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light1_color", "LIGHT1_COLOR" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light1_direction", "LIGHT1_DIRECTION" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "light1_enabled", "LIGHT1_ENABLED" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "light1_energy", "LIGHT1_ENERGY" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light2_color", "LIGHT2_COLOR" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light2_direction", "LIGHT2_DIRECTION" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "light2_enabled", "LIGHT2_ENABLED" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "light2_energy", "LIGHT2_ENERGY" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light3_color", "LIGHT3_COLOR" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light3_direction", "LIGHT3_DIRECTION" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "light3_enabled", "LIGHT3_ENABLED" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "light3_energy", "LIGHT3_ENERGY" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "position", "POSITION" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "quarter_res_color", "QUARTER_RES_COLOR.rgb" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "quarter_res_alpha", "QUARTER_RES_COLOR.a" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SAMPLER, "radiance", "RADIANCE" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "sky_coords", "vec3(SKY_COORDS, 0.0)" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, { Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, nullptr, nullptr }, }; @@ -2449,9 +2479,9 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = { { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" }, - // Sky, Fragment - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "ALPHA" }, + // Sky, Sky + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "ALPHA" }, { Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, nullptr, nullptr }, }; @@ -2667,6 +2697,70 @@ VisualShaderNodeResizableBase::VisualShaderNodeResizableBase() { set_allow_v_resize(true); } +////////////// Comment + +String VisualShaderNodeComment::get_caption() const { + return title; +} + +int VisualShaderNodeComment::get_input_port_count() const { + return 0; +} + +VisualShaderNodeComment::PortType VisualShaderNodeComment::get_input_port_type(int p_port) const { + return PortType::PORT_TYPE_SCALAR; +} + +String VisualShaderNodeComment::get_input_port_name(int p_port) const { + return String(); +} + +int VisualShaderNodeComment::get_output_port_count() const { + return 0; +} + +VisualShaderNodeComment::PortType VisualShaderNodeComment::get_output_port_type(int p_port) const { + return PortType::PORT_TYPE_SCALAR; +} + +String VisualShaderNodeComment::get_output_port_name(int p_port) const { + return String(); +} + +void VisualShaderNodeComment::set_title(const String &p_title) { + title = p_title; +} + +String VisualShaderNodeComment::get_title() const { + return title; +} + +void VisualShaderNodeComment::set_description(const String &p_description) { + description = p_description; +} + +String VisualShaderNodeComment::get_description() const { + return description; +} + +String VisualShaderNodeComment::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { + return String(); +} + +void VisualShaderNodeComment::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_title", "title"), &VisualShaderNodeComment::set_title); + ClassDB::bind_method(D_METHOD("get_title"), &VisualShaderNodeComment::get_title); + + ClassDB::bind_method(D_METHOD("set_description", "description"), &VisualShaderNodeComment::set_description); + ClassDB::bind_method(D_METHOD("get_description"), &VisualShaderNodeComment::get_description); + + ADD_PROPERTY(PropertyInfo(Variant::STRING, "title"), "set_title", "get_title"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "description"), "set_description", "get_description"); +} + +VisualShaderNodeComment::VisualShaderNodeComment() { +} + ////////////// GroupBase void VisualShaderNodeGroupBase::set_inputs(const String &p_inputs) { @@ -3054,11 +3148,11 @@ int VisualShaderNodeGroupBase::get_free_output_port_id() const { return output_ports.size(); } -void VisualShaderNodeGroupBase::set_control(Control *p_control, int p_index) { +void VisualShaderNodeGroupBase::set_ctrl_pressed(Control *p_control, int p_index) { controls[p_index] = p_control; } -Control *VisualShaderNodeGroupBase::get_control(int p_index) { +Control *VisualShaderNodeGroupBase::is_ctrl_pressed(int p_index) { ERR_FAIL_COND_V(!controls.has(p_index), nullptr); return controls[p_index]; } diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h index ef724c7650..a0e3bdf166 100644 --- a/scene/resources/visual_shader.h +++ b/scene/resources/visual_shader.h @@ -54,6 +54,7 @@ public: TYPE_EMIT, TYPE_PROCESS, TYPE_END, + TYPE_SKY, TYPE_MAX }; @@ -222,10 +223,10 @@ public: virtual PortType get_input_port_type(int p_port) const = 0; virtual String get_input_port_name(int p_port) const = 0; - void set_input_port_default_value(int p_port, const Variant &p_value); + virtual void set_input_port_default_value(int p_port, const Variant &p_value); Variant get_input_port_default_value(int p_port) const; // if NIL (default if node does not set anything) is returned, it means no default value is wanted if disconnected, thus no input var must be supplied (empty string will be supplied) Array get_default_input_values() const; - void set_default_input_values(const Array &p_values); + virtual void set_default_input_values(const Array &p_values); virtual int get_output_port_count() const = 0; virtual PortType get_output_port_type(int p_port) const = 0; @@ -271,6 +272,7 @@ class VisualShaderNodeCustom : public VisualShaderNode { int type = 0; }; + bool is_initialized = false; List<Port> input_ports; List<Port> output_ports; @@ -287,7 +289,12 @@ protected: virtual PortType get_output_port_type(int p_port) const override; virtual String get_output_port_name(int p_port) const override; + virtual void set_input_port_default_value(int p_port, const Variant &p_value) override; + virtual void set_default_input_values(const Array &p_values) override; + protected: + void _set_input_port_default_value(int p_port, const Variant &p_value); + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; virtual String generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override; @@ -296,6 +303,9 @@ protected: public: VisualShaderNodeCustom(); void update_ports(); + + bool _is_initialized(); + void _set_initialized(bool p_enabled); }; ///// @@ -421,6 +431,7 @@ public: bool is_global_code_generated() const; virtual bool is_qualifier_supported(Qualifier p_qual) const = 0; + virtual bool is_convertible_to_constant() const = 0; virtual Vector<StringName> get_editable_properties() const override; virtual String get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const override; @@ -510,6 +521,38 @@ public: VisualShaderNodeResizableBase(); }; +class VisualShaderNodeComment : public VisualShaderNodeResizableBase { + GDCLASS(VisualShaderNodeComment, VisualShaderNodeResizableBase); + +protected: + String title = "Comment"; + String description = ""; + +protected: + static void _bind_methods(); + +public: + virtual String get_caption() const override; + + virtual int get_input_port_count() const override; + virtual PortType get_input_port_type(int p_port) const override; + virtual String get_input_port_name(int p_port) const override; + + virtual int get_output_port_count() const override; + virtual PortType get_output_port_type(int p_port) const override; + virtual String get_output_port_name(int p_port) const override; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; + + void set_title(const String &p_title); + String get_title() const; + + void set_description(const String &p_description); + String get_description() const; + + VisualShaderNodeComment(); +}; + class VisualShaderNodeGroupBase : public VisualShaderNodeResizableBase { GDCLASS(VisualShaderNodeGroupBase, VisualShaderNodeResizableBase); @@ -567,8 +610,8 @@ public: 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); + void set_ctrl_pressed(Control *p_control, int p_index); + Control *is_ctrl_pressed(int p_index); void set_editable(bool p_enabled); bool is_editable() const; diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp index a99c09e89c..7943b95177 100644 --- a/scene/resources/visual_shader_nodes.cpp +++ b/scene/resources/visual_shader_nodes.cpp @@ -3744,6 +3744,10 @@ bool VisualShaderNodeFloatUniform::is_qualifier_supported(Qualifier p_qual) cons return true; // all qualifiers are supported } +bool VisualShaderNodeFloatUniform::is_convertible_to_constant() const { + return true; // conversion is allowed +} + Vector<StringName> VisualShaderNodeFloatUniform::get_editable_properties() const { Vector<StringName> props = VisualShaderNodeUniform::get_editable_properties(); props.push_back("hint"); @@ -3911,6 +3915,10 @@ bool VisualShaderNodeIntUniform::is_qualifier_supported(Qualifier p_qual) const return true; // all qualifiers are supported } +bool VisualShaderNodeIntUniform::is_convertible_to_constant() const { + return true; // conversion is allowed +} + Vector<StringName> VisualShaderNodeIntUniform::get_editable_properties() const { Vector<StringName> props = VisualShaderNodeUniform::get_editable_properties(); props.push_back("hint"); @@ -4019,6 +4027,10 @@ bool VisualShaderNodeBooleanUniform::is_qualifier_supported(Qualifier p_qual) co return true; // all qualifiers are supported } +bool VisualShaderNodeBooleanUniform::is_convertible_to_constant() const { + return true; // conversion is allowed +} + Vector<StringName> VisualShaderNodeBooleanUniform::get_editable_properties() const { Vector<StringName> props = VisualShaderNodeUniform::get_editable_properties(); props.push_back("default_value_enabled"); @@ -4113,6 +4125,10 @@ bool VisualShaderNodeColorUniform::is_qualifier_supported(Qualifier p_qual) cons return true; // all qualifiers are supported } +bool VisualShaderNodeColorUniform::is_convertible_to_constant() const { + return true; // conversion is allowed +} + Vector<StringName> VisualShaderNodeColorUniform::get_editable_properties() const { Vector<StringName> props = VisualShaderNodeUniform::get_editable_properties(); props.push_back("default_value_enabled"); @@ -4209,6 +4225,10 @@ bool VisualShaderNodeVec3Uniform::is_qualifier_supported(Qualifier p_qual) const return true; // all qualifiers are supported } +bool VisualShaderNodeVec3Uniform::is_convertible_to_constant() const { + return true; // conversion is allowed +} + Vector<StringName> VisualShaderNodeVec3Uniform::get_editable_properties() const { Vector<StringName> props = VisualShaderNodeUniform::get_editable_properties(); props.push_back("default_value_enabled"); @@ -4309,6 +4329,10 @@ bool VisualShaderNodeTransformUniform::is_qualifier_supported(Qualifier p_qual) return true; // all qualifiers are supported } +bool VisualShaderNodeTransformUniform::is_convertible_to_constant() const { + return true; // conversion is allowed +} + Vector<StringName> VisualShaderNodeTransformUniform::get_editable_properties() const { Vector<StringName> props = VisualShaderNodeUniform::get_editable_properties(); props.push_back("default_value_enabled"); @@ -4494,6 +4518,10 @@ bool VisualShaderNodeTextureUniform::is_qualifier_supported(Qualifier p_qual) co return false; } +bool VisualShaderNodeTextureUniform::is_convertible_to_constant() const { + return false; // conversion is not allowed +} + VisualShaderNodeTextureUniform::VisualShaderNodeTextureUniform() { simple_decl = false; } @@ -4632,16 +4660,18 @@ String VisualShaderNodeTexture2DArrayUniform::generate_global(Shader::Mode p_mod switch (texture_type) { case TYPE_DATA: - if (color_default == COLOR_DEFAULT_BLACK) + if (color_default == COLOR_DEFAULT_BLACK) { code += " : hint_black;\n"; - else + } else { code += ";\n"; + } break; case TYPE_COLOR: - if (color_default == COLOR_DEFAULT_BLACK) + if (color_default == COLOR_DEFAULT_BLACK) { code += " : hint_black_albedo;\n"; - else + } else { code += " : hint_albedo;\n"; + } break; case TYPE_NORMAL_MAP: code += " : hint_normal;\n"; @@ -4700,16 +4730,18 @@ String VisualShaderNodeTexture3DUniform::generate_global(Shader::Mode p_mode, Vi switch (texture_type) { case TYPE_DATA: - if (color_default == COLOR_DEFAULT_BLACK) + if (color_default == COLOR_DEFAULT_BLACK) { code += " : hint_black;\n"; - else + } else { code += ";\n"; + } break; case TYPE_COLOR: - if (color_default == COLOR_DEFAULT_BLACK) + if (color_default == COLOR_DEFAULT_BLACK) { code += " : hint_black_albedo;\n"; - else + } else { code += " : hint_albedo;\n"; + } break; case TYPE_NORMAL_MAP: code += " : hint_normal;\n"; diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h index a5d0fe4649..594a494cf1 100644 --- a/scene/resources/visual_shader_nodes.h +++ b/scene/resources/visual_shader_nodes.h @@ -1574,6 +1574,7 @@ public: float get_default_value() const; bool is_qualifier_supported(Qualifier p_qual) const override; + bool is_convertible_to_constant() const override; virtual Vector<StringName> get_editable_properties() const override; @@ -1639,6 +1640,7 @@ public: int get_default_value() const; bool is_qualifier_supported(Qualifier p_qual) const override; + bool is_convertible_to_constant() const override; virtual Vector<StringName> get_editable_properties() const override; @@ -1683,6 +1685,7 @@ public: bool get_default_value() const; bool is_qualifier_supported(Qualifier p_qual) const override; + bool is_convertible_to_constant() const override; virtual Vector<StringName> get_editable_properties() const override; @@ -1724,6 +1727,7 @@ public: Color get_default_value() const; bool is_qualifier_supported(Qualifier p_qual) const override; + bool is_convertible_to_constant() const override; virtual Vector<StringName> get_editable_properties() const override; @@ -1766,6 +1770,7 @@ public: Vector3 get_default_value() const; bool is_qualifier_supported(Qualifier p_qual) const override; + bool is_convertible_to_constant() const override; virtual Vector<StringName> get_editable_properties() const override; @@ -1808,6 +1813,7 @@ public: Transform get_default_value() const; bool is_qualifier_supported(Qualifier p_qual) const override; + bool is_convertible_to_constant() const override; virtual Vector<StringName> get_editable_properties() const override; @@ -1865,6 +1871,7 @@ public: ColorDefault get_color_default() const; bool is_qualifier_supported(Qualifier p_qual) const override; + bool is_convertible_to_constant() const override; VisualShaderNodeTextureUniform(); }; diff --git a/scene/resources/world_2d.cpp b/scene/resources/world_2d.cpp index 156c7d0576..0a0742753f 100644 --- a/scene/resources/world_2d.cpp +++ b/scene/resources/world_2d.cpp @@ -34,6 +34,7 @@ #include "scene/2d/camera_2d.h" #include "scene/2d/visibility_notifier_2d.h" #include "scene/main/window.h" +#include "servers/navigation_server_2d.h" #include "servers/physics_server_2d.h" #include "servers/rendering_server.h" @@ -315,14 +316,18 @@ void World2D::_update() { indexer->_update(); } -RID World2D::get_canvas() { +RID World2D::get_canvas() const { return canvas; } -RID World2D::get_space() { +RID World2D::get_space() const { return space; } +RID World2D::get_navigation_map() const { + return navigation_map; +} + void World2D::get_viewport_list(List<Viewport *> *r_viewports) { for (Map<Viewport *, SpatialIndexer2D::ViewportData>::Element *E = indexer->viewports.front(); E; E = E->next()) { r_viewports->push_back(E->key()); @@ -332,11 +337,13 @@ void World2D::get_viewport_list(List<Viewport *> *r_viewports) { void World2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_canvas"), &World2D::get_canvas); ClassDB::bind_method(D_METHOD("get_space"), &World2D::get_space); + ClassDB::bind_method(D_METHOD("get_navigation_map"), &World2D::get_navigation_map); ClassDB::bind_method(D_METHOD("get_direct_space_state"), &World2D::get_direct_space_state); ADD_PROPERTY(PropertyInfo(Variant::RID, "canvas", PROPERTY_HINT_NONE, "", 0), "", "get_canvas"); ADD_PROPERTY(PropertyInfo(Variant::RID, "space", PROPERTY_HINT_NONE, "", 0), "", "get_space"); + ADD_PROPERTY(PropertyInfo(Variant::RID, "navigation_map", PROPERTY_HINT_NONE, "", 0), "", "get_navigation_map"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "direct_space_state", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsDirectSpaceState2D", 0), "", "get_direct_space_state"); } @@ -346,21 +353,29 @@ PhysicsDirectSpaceState2D *World2D::get_direct_space_state() { World2D::World2D() { canvas = RenderingServer::get_singleton()->canvas_create(); - space = PhysicsServer2D::get_singleton()->space_create(); - //set space2D to be more friendly with pixels than meters, by adjusting some constants + // Create and configure space2D to be more friendly with pixels than meters + space = PhysicsServer2D::get_singleton()->space_create(); PhysicsServer2D::get_singleton()->space_set_active(space, true); - PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_GRAVITY, GLOBAL_DEF("physics/2d/default_gravity", 98)); + PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_GRAVITY, GLOBAL_DEF("physics/2d/default_gravity", 980.0)); PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_GRAVITY_VECTOR, GLOBAL_DEF("physics/2d/default_gravity_vector", Vector2(0, 1))); PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_LINEAR_DAMP, GLOBAL_DEF("physics/2d/default_linear_damp", 0.1)); ProjectSettings::get_singleton()->set_custom_property_info("physics/2d/default_linear_damp", PropertyInfo(Variant::FLOAT, "physics/2d/default_linear_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater")); PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_ANGULAR_DAMP, GLOBAL_DEF("physics/2d/default_angular_damp", 1.0)); ProjectSettings::get_singleton()->set_custom_property_info("physics/2d/default_angular_damp", PropertyInfo(Variant::FLOAT, "physics/2d/default_angular_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater")); + + // Create and configure the navigation_map to be more friendly with pixels than meters. + navigation_map = NavigationServer2D::get_singleton()->map_create(); + NavigationServer2D::get_singleton()->map_set_active(navigation_map, true); + NavigationServer2D::get_singleton()->map_set_cell_size(navigation_map, GLOBAL_DEF("navigation/2d/default_cell_size", 10)); + NavigationServer2D::get_singleton()->map_set_edge_connection_margin(navigation_map, GLOBAL_DEF("navigation/2d/default_edge_connection_margin", 5)); + indexer = memnew(SpatialIndexer2D); } World2D::~World2D() { RenderingServer::get_singleton()->free(canvas); PhysicsServer2D::get_singleton()->free(space); + NavigationServer2D::get_singleton()->free(navigation_map); memdelete(indexer); } diff --git a/scene/resources/world_2d.h b/scene/resources/world_2d.h index ae13367421..38abf3d7ad 100644 --- a/scene/resources/world_2d.h +++ b/scene/resources/world_2d.h @@ -44,6 +44,7 @@ class World2D : public Resource { RID canvas; RID space; + RID navigation_map; SpatialIndexer2D *indexer; @@ -63,8 +64,9 @@ protected: void _update(); public: - RID get_canvas(); - RID get_space(); + RID get_canvas() const; + RID get_space() const; + RID get_navigation_map() const; PhysicsDirectSpaceState2D *get_direct_space_state(); diff --git a/scene/resources/world_3d.cpp b/scene/resources/world_3d.cpp index 9c0317454b..e811cbf57a 100644 --- a/scene/resources/world_3d.cpp +++ b/scene/resources/world_3d.cpp @@ -35,6 +35,7 @@ #include "scene/3d/camera_3d.h" #include "scene/3d/visibility_notifier_3d.h" #include "scene/scene_string_names.h" +#include "servers/navigation_server_3d.h" struct SpatialIndexer { Octree<VisibilityNotifier3D> octree; @@ -243,6 +244,10 @@ RID World3D::get_space() const { return space; } +RID World3D::get_navigation_map() const { + return navigation_map; +} + RID World3D::get_scenario() const { return scenario; } @@ -310,18 +315,20 @@ void World3D::get_camera_list(List<Camera3D *> *r_cameras) { void World3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_space"), &World3D::get_space); + ClassDB::bind_method(D_METHOD("get_navigation_map"), &World3D::get_navigation_map); ClassDB::bind_method(D_METHOD("get_scenario"), &World3D::get_scenario); ClassDB::bind_method(D_METHOD("set_environment", "env"), &World3D::set_environment); ClassDB::bind_method(D_METHOD("get_environment"), &World3D::get_environment); ClassDB::bind_method(D_METHOD("set_fallback_environment", "env"), &World3D::set_fallback_environment); ClassDB::bind_method(D_METHOD("get_fallback_environment"), &World3D::get_fallback_environment); - ClassDB::bind_method(D_METHOD("set_camera_effects", "env"), &World3D::set_camera_effects); + ClassDB::bind_method(D_METHOD("set_camera_effects", "effects"), &World3D::set_camera_effects); ClassDB::bind_method(D_METHOD("get_camera_effects"), &World3D::get_camera_effects); ClassDB::bind_method(D_METHOD("get_direct_space_state"), &World3D::get_direct_space_state); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "environment", PROPERTY_HINT_RESOURCE_TYPE, "Environment"), "set_environment", "get_environment"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "fallback_environment", PROPERTY_HINT_RESOURCE_TYPE, "Environment"), "set_fallback_environment", "get_fallback_environment"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "camera_effects", PROPERTY_HINT_RESOURCE_TYPE, "CameraEffects"), "set_camera_effects", "get_camera_effects"); ADD_PROPERTY(PropertyInfo(Variant::RID, "space", PROPERTY_HINT_NONE, "", 0), "", "get_space"); + ADD_PROPERTY(PropertyInfo(Variant::RID, "navigation_map", PROPERTY_HINT_NONE, "", 0), "", "get_navigation_map"); ADD_PROPERTY(PropertyInfo(Variant::RID, "scenario", PROPERTY_HINT_NONE, "", 0), "", "get_scenario"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "direct_space_state", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsDirectSpaceState3D", 0), "", "get_direct_space_state"); } @@ -338,6 +345,11 @@ World3D::World3D() { PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_ANGULAR_DAMP, GLOBAL_DEF("physics/3d/default_angular_damp", 0.1)); ProjectSettings::get_singleton()->set_custom_property_info("physics/3d/default_angular_damp", PropertyInfo(Variant::FLOAT, "physics/3d/default_angular_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater")); + navigation_map = NavigationServer3D::get_singleton()->map_create(); + NavigationServer3D::get_singleton()->map_set_active(navigation_map, true); + NavigationServer3D::get_singleton()->map_set_cell_size(navigation_map, GLOBAL_DEF("navigation/3d/default_cell_size", 0.3)); + NavigationServer3D::get_singleton()->map_set_edge_connection_margin(navigation_map, GLOBAL_DEF("navigation/3d/default_edge_connection_margin", 0.3)); + #ifdef _3D_DISABLED indexer = nullptr; #else @@ -348,6 +360,7 @@ World3D::World3D() { World3D::~World3D() { PhysicsServer3D::get_singleton()->free(space); RenderingServer::get_singleton()->free(scenario); + NavigationServer3D::get_singleton()->free(navigation_map); #ifndef _3D_DISABLED memdelete(indexer); diff --git a/scene/resources/world_3d.h b/scene/resources/world_3d.h index 3d6c33997e..4e2717a2bb 100644 --- a/scene/resources/world_3d.h +++ b/scene/resources/world_3d.h @@ -46,6 +46,7 @@ class World3D : public Resource { private: RID space; + RID navigation_map; RID scenario; SpatialIndexer *indexer; Ref<Environment> environment; @@ -70,6 +71,7 @@ protected: public: RID get_space() const; + RID get_navigation_map() const; RID get_scenario() const; void set_environment(const Ref<Environment> &p_environment); diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp index 892802c103..7575ccd5c3 100644 --- a/scene/scene_string_names.cpp +++ b/scene/scene_string_names.cpp @@ -190,10 +190,6 @@ SceneStringNames::SceneStringNames() { _default = StaticCString::create("default"); - for (int i = 0; i < MAX_MATERIALS; i++) { - mesh_materials[i] = "material/" + itos(i); - } - _window_group = StaticCString::create("_window_group"); _window_input = StaticCString::create("_window_input"); window_input = StaticCString::create("window_input"); diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h index 655e49c6f9..a5b489eddc 100644 --- a/scene/scene_string_names.h +++ b/scene/scene_string_names.h @@ -216,10 +216,6 @@ public: StringName use_in_baked_light; StringName use_dynamic_gi; #endif - enum { - MAX_MATERIALS = 32 - }; - StringName mesh_materials[MAX_MATERIALS]; }; #endif // SCENE_STRING_NAMES_H |